Hello all, I decided to open a new thread which I could use to ask questions about how to customize this new MVC application I created https://www.daniweb.com/web-development/aspnet/threads/497464/much-simpler-first-mvc-application, add HTML and change the default look and feel. If the mods are OK with that, I was thinking to use this thread as a general one and pop in any questions related to that when I get stuck, rather than create a new thread every time about the same app. I hope it is OK.
In fact here is the first question.
I'm trying to customize the Book Index view and add a link from there to the Review Index view, saying something like "Go to reviews". I had a good look online, my understanding is that rather than using hardcoded HTML, it is better to use HTML helpers, and so I did and created this <div class="link">@Html.ActionLink("Go to Reviews", "Review")</div>
I read that this helpers can take several parameters, but I thought I'd do with two, the link text and the controller name. Unfortunately when I try that - remember that I'm in the Book view - the URL becomes localhost/xxxx/Book/Review rather than the localhost/xxxx/Review I was expecting, so it's appending the review controller at the end of the URL the link sits in. Had look online of course, some people say that the routing needs amending and remove the controller part, but isn't that a bit excessive? Then, in the list of possible parameters, I found the routevalue, 3rd parameter, so I changed the above to <div class="link">@Html.ActionLink("Go to Reviews", "Review", "Home")</div>, but yet no joy, as the url now becomes localhost/xxxx/Home/Review, which, I would have thought was correct. Obviously not. NOt sure what else to try, any idea?
thanks

Recommended Answers

All 13 Replies

Ah! Hold on, I cracked it, looks like I was using the parameters in the wrong order..eh eh. Here is the winning combination:
<div class="link">@Html.ActionLink("Go to Reviews", "Index", "Review")</div>
So, link text, Action, Controller!! Darn!

Have a look at T4MVC and @Url.Action

thanks DaveAmour, I thought @URL.Action was merely to get URLs, like if I have something like <div>@Url.Action("Return")</div> inside a Book view it will return /Book/Return.
I had a bried look at the T4MVC as well, but I thought that you can specify when you create a controller whether you want a strong view or not
thanks

OK here is another things I'd like to do and it is proving rather difficult. I've made some changes to the CSS just to embellish the app a little and now I was thinking that the first time you land onto the Book/Index or Review/Index and there are no Books/Reviews, I'd like to have a span saying that there are no reviews. Seemed pretty easy, but, it turns out it wasn't. So, let's take myBook/Index view first. I have a foreach loop that adds the details:

@foreach (var item in Model) {        
        <span class="header">@Html.DisplayNameFor(model => model.ReviewText)</span>
        <span>@Html.DisplayFor(modelItem => item.ReviewText)</span>
        <span class="header"> @Html.DisplayNameFor(model => model.Book.BookName)</span>
        <span class="book">@Html.DisplayFor(modelItem => item.Book.BookName)</span>
        <span class="header">What do you want to do with this review?</span>
        <span class="controller">@Html.ActionLink("Edit", "Edit", new { id=item.ReviewID })</span>
        <span class="controller">@Html.ActionLink("Details", "Details", new { id=item.BookID })</span>
        <span class="controller">@Html.ActionLink("Delete", "Delete", new { id=item.BookID })</span>
        <div class="separator"></div>
    }

So, I need to check if there are any books, so I thought that a

@if (Model.Book.Count ) { 

}

would do, but it's complaining saying that

System.COllections.Generic.IEnumerable<BooksApplication.Models.Book doesn't contain a definition for 'Book' and no extension method 'Book' accepting a first argument of type System.COllections.Generic.IEnumerable<BooksApplication.Models could be found (are you missing a using directive or an assembly reference?)

very similar to the message I got previously when I was trying to display a review under the Book details view, and that makes sense as I wasn't iterating through them as I should have. So, I changed the above to sit inside another foreach loop:

@foreach (var book in Model) { 
     if (Model.Book.Count) { }
     }

But again, same message. Also, I'm not 100% I can use the property "Count" as intellisense isn't pivking that up.
Then I thought, wait a minute, surely I can use the already existing foreach loop (shown at the top) to check if there are any books and use the variable item, which, should represent a book - shouldn't it? - but again no joy.
Then after a bit of googling, I've noticed that some people said there is a method called Any() that can check if a model is empty or not, and I came up with the revised foreach loop:

@foreach (var item in Model) {
        if (!Model.Any()) {
            <span>No Books!</span>
        }
        else{
             <span class="header"> @Html.DisplayNameFor(model => model.BookName)</span>        
            <span>@Html.DisplayFor(modelItem => item.BookName)</span>
            <span class="header">@Html.DisplayNameFor(model => model.ISBN)</span>
            <span>@Html.DisplayFor(modelItem => item.ISBN)</span>
            <span class="header">What do you want to do with this book?</span>
            <span class="controller">@Html.ActionLink("Edit", "Edit", new { id=item.BookID })</span>
            <span class="controller">@Html.ActionLink("Details", "Details", new { id=item.BookID })</span>
            <span class="controller">@Html.ActionLink("Delete", "Delete", new { id=item.BookID })</span>
            <div class="separator"></div>
        }
    }

Good news is that I dont' get any error, but I don't get any error.

Sorry, running out of options.
Both index views are strong typed:
@model IEnumerable<BooksApplication.Models.Review>
and
@model IEnumerable<BooksApplication.Models.Book>

Cheers

OK, I think I feel a bit like an idiot for finding my own answers only after I post the problem. Of course I don't need to iterate through the objects to check if the model is empty! D'oh!
So, here is the revised, working code.

@if (!Model.Any()) { 
    <span>Empty!</span>
}
else{
    foreach (var item in Model) {        
        <span class="header"> @Html.DisplayNameFor(model => model.BookName)</span>        
        <span>@Html.DisplayFor(modelItem => item.BookName)</span>
        <span class="header">@Html.DisplayNameFor(model => model.ISBN)</span>
        <span>@Html.DisplayFor(modelItem => item.ISBN)</span>
        <span class="header">What do you want to do with this book?</span>
        <span class="controller">@Html.ActionLink("Edit", "Edit", new { id=item.BookID })</span>
        <span class="controller">@Html.ActionLink("Details", "Details", new { id=item.BookID })</span>
        <span class="controller">@Html.ActionLink("Delete", "Delete", new { id=item.BookID })</span>
        <div class="separator"></div>
    }
}

I knew I wasn't too far off!

OK, this I really don't know about. Basically, whenever I add a new review for a book, whenever I try to view the details of that review or delete it (ie click the details or delete button) I get an error, HTTP Error 404.0 - Not Found. The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
Here is the full screenshot of the error:
http://s2.postimg.org/9yq21kt6x/review.png
But the edit button - calling the edit view - works.
Here are the review details and edit files:

@model BooksApplication.Models.Review

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<fieldset>
    <legend>Review</legend>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.ReviewText)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ReviewText)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Book.BookName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Book.BookName)
    </div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.ReviewID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

And here the delete:

@model BooksApplication.Models.Review

@{
    ViewBag.Title = "Delete";
}

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<fieldset>
    <legend>Review</legend>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.ReviewText)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ReviewText)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Book.BookName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Book.BookName)
    </div>
</fieldset>
@using (Html.BeginForm()) {
    <p>
        <input type="submit" value="Delete" /> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

Any idea/suggestion?
ADDED: if you need further details, here is what happens. Let's look at the database tables:
Books table
books_table.png
Review table
reviews_table.png

So, the IDs don't correspond, meaning, my book has an ID of 10, say, and my review for that book has an ID of 8: the review "details" and "delete" views point to ID 10, which is the ID of the book and not the ID of the review and there is no review with ID 10. That's the problem. How do I get around that?

A quick difference I see is that Edit is a GET request, whereas Delete does a POST.

Update:
You posted an update while I posted this. Perhaps you need to put that ID in a hidden input to pass it back, but not entirely sure about that.

Thanks pritaeas, I've just updated my previous post, and I think I've found the problem, but I have no idea how to resolve it..
About get and post, I haven't touched the details and delete files in that sense, as in whathever method they use it's been put there by the system, so you would think that it is correct?

With the submit you are posting back to your controller. If you set a debug break there, does it hit? If not, you may need the [HttpPost] data annotation (or AcceptVerbs maybe, if you need both GET and POST).

When you say submit, which one do you mean, edit, delete or view button?

OK, I think I got there in the end. The problem wasn't in fact with any of the views I looked at, but in the index view, specifically here:

<span class="controller">@Html.ActionLink("Edit", "Edit", new { id=item.ReviewID })</span>
        <span class="controller">@Html.ActionLink("Details", "Details", new { id=item.BookID })</span>
        <span class="controller">@Html.ActionLink("Delete", "Delete", new { id=item.BookID })</span>

No wonder it was broken...I was calling up details and delete with the Book ID and not the review ID. So by changing the above into:

<span class="controller">@Html.ActionLink("Edit", "Edit", new { id=item.ReviewID })</span>
        <span class="controller">@Html.ActionLink("Details", "Details", new { id=item.ReviewID })</span>
        <span class="controller">@Html.ActionLink("Delete", "Delete", new { id=item.ReviewID })</span>

it works. Thanks for looking at the code anyway:-)!

Ah OK, cool, thanks for that DaveAmour

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.