ASP.NET MVC null ViewResult - asp.net

How should one deal with an MVC controller returning a null ViewResult?
As an example I am creating a simple edit view:
public ActionResult Edit(int id)
{
var person = (from p in context.SWLiftShare_Persons
where p.id == id
select p).SingleOrDefault();
if (person != null)
{
return View(person);
}
else return View();
}
I guess in reality there's no point in checking for a null result in the controller because the view picks out properties from the model:
<h2>Edit - <%= Html.Encode(Model.Name) %></h2>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="id">id:
<%= Html.Encode(Model.id) %></label>
</p>
<p>
<label for="CollarNumber">CollarNumber:</label>
<%= Html.TextBox("CollarNumber", Model.CollarNumber)%>
<%= Html.ValidationMessage("CollarNumber", "*") %>
</p>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name)%>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="EmailAddress">EmailAddress:</label>
<%= Html.TextBox("EmailAddress", Model.EmailAddress, new { style = "width:300px" })%>
<%= Html.ValidationMessage("EmailAddress", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
I could just wrap everything in a <% if(Model != null) { //render edit markup... etc. but that seems rather unelegant. Is there a better way to deal with this?

In this case it seems to be an error if person becomes null. You could render a different (error) view if this is the case. For example:
if (person == null)
{
return View("ErrorView");
}
return View(person);
ErrorView.aspx:
<div>Person was not found. Try again.</div>

In this scenario, I would return another view when the person is null to cleanly separate your view logic:
public ActionResult Edit(int id)
{
var person = (from p in context.SWLiftShare_Persons
where p.id == id
select p).SingleOrDefault();
return (person != null) ? View(person) : View("InvalidPerson");
}

I think...
throw new HttpException(404,"Not found");
and set custom errors.

Related

ActionLink routeValue from a TextBox

I'm working on the following:
1- The user enters a value inside a textBox.
2- then clicks edit to go to the edit view.
This is my code:
<%= Html.TextBox("Name") %>
<%: Html.ActionLink("Edit", "Edit")%>
The problem is I can't figure out how to take the value from the textBox and pass it to the ActionLink, can you help me?
You can't unless you use javascript. A better way to achieve this would be to use a form instead of an ActionLink:
<% using (Html.BeginForm("Edit", "SomeController")) { %>
<%= Html.TextBox("Name") %>
<input type="submit" value="Edit" />
<% } %>
which will automatically send the value entered by the user in the textbox to the controller action:
[HttpPost]
public ActionResult Edit(string name)
{
...
}
And if you wanted to use an ActionLink here's how you could setup a javascript function which will send the value:
<%= Html.TextBox("Name") %>
<%= Html.ActionLink("Edit", "Edit", null, new { id = "edit" })%>
and then:
$(function() {
$('#edit').click(function() {
var name = $('#Name').val();
this.href = this.href + '?name=' + encodeURIComponent(name);
});
});

Form input in a foreach loop returns empty model

I have a list object for which I tried to display text boxes in a foreach loop. However the post returns empty object. I couldn't see the cause.
Here is the code in the view
<%using (Html.BeginForm("makeTransfer", "shareTransfer")) { %>
<% foreach (var i in Model.Inform)//int i = 0; i < Model.Inform.Count(); i++){ %>
<%:Html.HiddenFor(x=>i.shares, new{#value = i.shares}) %>
...
<td style = "width:20px"><%:Html.TextBoxFor(x=>i.sharesRq)%></td> cuddling
<%} %>
<%:Html.HiddenFor(x => x.accSrc, new { #value = Model.accSrc })%>
<%:Html.HiddenFor(x=>x.accDst, new{ #value = Model.accDst}) %>
Date of Transfer<%:Html.TextBoxFor(x => x.date)%>
Transfer with benefit<%:Html.CheckBoxFor(x => x.withBenefit)%>
<input type="submit" name="save" value="Save" /></div>
<input type="submit" name="cancel" value="Cancel" /></div>
<%} %>
And Here is the controller
public ActionResult makeTransfer(vmTransfer transfer, string save, string cancel)
{
if (cancel != null)
return RedirectToAction("startTransfer");
else if (save != null)
{
foreach (var t in transfer.Inform)
{ ...
My problem is, transfer.Inform( 2nd line from the last) which is a list is empty when the form posts. Any help please, ASAP.
I would recommend you using editor templates instead of writing any loops in your views:
<% using (Html.BeginForm("makeTransfer", "shareTransfer")) { %>
<%= Html.EditorFor(x => x.Inform) %>
<%= Html.HiddenFor(x => x.accSrc, new { #value = Model.accSrc }) %>
<%= Html.HiddenFor(x => x.accDst, new { #value = Model.accDst }) %>
Date of Transfer <%= Html.TextBoxFor(x => x.date) %>
Transfer with benefit <%= Html.CheckBoxFor(x => x.withBenefit) %>
<input type="submit" name="save" value="Save" /></div>
<input type="submit" name="cancel" value="Cancel" /></div>
<% } %>
and in the corresponding editor template (~/Views/Shared/EditorTemplates/InformViewModel.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.InformViewModel>"
%>
<%= Html.HiddenFor(x => x.shares) %>
...
<td style="width:20px">
<%= Html.TextBoxFor(x => x.sharesRq) %>
</td>
Remark: you might need to adjust the name of the editor template based on the type of the Inform property.
Editor templates will take care of generating proper id and names of the input fields so that everything binds correctly:
[HttpPost]
public ActionResult makeTransfer(vmTransfer transfer, string save, string cancel)
{
if (cancel != null)
{
return RedirectToAction("startTransfer");
}
else if (save != null)
{
foreach (var t in transfer.Inform)
{
...
}
}
...
}

Simple ASP.NET MVC 2 Question - Using the Edit Scaffold

Good Morning,
I am using the editing scaffold. Here is the two bits of code:
Controller Code:
public ActionResult Edit(int id)
{
var viewModel = new ListingManagerViewModel
{
Listing = AfvClassifiedsDB.Listings.Single(l => l.ListingID == id),
Categories = AfvClassifiedsDB.Categories.ToList(),
};
return View(viewModel);
}
//
// POST: /ListingManager/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var listing = AfvClassifiedsDB.Listings.Single(l => l.ListingID == id);
try
{
// Save the changes to Listing.
UpdateModel(listing, "Listings");
AfvClassifiedsDB.SaveChanges();
return RedirectToAction("Index");
}
catch
{
// An error has occured so redisplay the form instead.
var viewModel = new ListingManagerViewModel
{
Listing = listing,
Categories = AfvClassifiedsDB.Categories.ToList(),
};
return View(viewModel);
}
}
View Code:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AfvClassifieds.ViewModels.ListingManagerViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<%: Html.EditorFor(model => Model.Listing, new { Categories = Model.Categories })%>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
This seems to work, but when you submit the form, the values aren't changed. This has been created using the MVC Music Store as a guide. No errors, but the form values I create aren't submitted.
Can you post the UpdateModel method?
However, it is clearly that you don't update anything there. You didn't do anything to the form collection FormCollection collection. the FormCollection is the actual form data that is inserted by user, so you should take all the values from it and save it in db.

I've created the worst tag soup of all time (ASP.NET MVC 2)

I am staring at the worst mess, my monitor isn't tall enough to see what is going on, and VS 2010 is no help at all.
I do not have any idea how to refactor this trash.
It's 11 AM and I feel like pouring a drink already.
Is this just the final proof that I have no business coding? Be honest.
<div id="followedFriends">
<% if (Model.FollowedFriends.Count() > 0)
{
foreach (var friend in Model.FollowedFriends)
{ %>
<div id="friendContainer">
<div class="followedFriend">
<div class="userName">
<%= Html.ActionLink(friend.FoFriend.UserName, "Visitor", "Home", new {userID = friend.FoFriend.UserId}, null)%></div>
Currently reading:
<br />
<div class="bookLinks">
<% if (friend.BookCount != 0)
{ %>
<% if (friend.BookCount <= 5)
{ %>
<%= friend.BookLinks%>
<%}
else
{ %>
<%:Html.ActionLink(friend.BookCount + " different books.", "Visitor", "Home", new {userID = friend.FoFriend.UserId}, null)%>
<%}
}
else
{ %>
Nothing, it appears...
<%}%>
</div>
<%if (friend.ReviewCount != 0)
{%>
New review for:
<div class="reviewLinks">
<%if (friend.ReviewCount <= 5)
{ %>
<%= friend.ReviewLinks%>
<%}
else
{%>
<%: friend.ReviewCount %>
different books
<%}%></div>
<%}
if (friend.QuoteCount != 0)
{%>
<span class="highlight">&#9656</span>
<%: friend.QuoteCount%>
new
<%if (friend.QuoteCount != 1)
{ %>quotes
<%}
else
{ %>
quote
<%} %>
<%}%>
</div>
</div>
<%}
}%>
</div>
<%} %>
Update
Since someone was asking, here is the pertinent portion of the View Model:
public class FollowedFriend
{
public aspnet_User FoFriend { get; set; }
public string BookLinks { get; set; }
public int BookCount { get; set; }
public string ReviewLinks { get; set; }
public int ReviewCount { get; set; }
public int QuoteCount { get; set; }
public FollowedFriend(Guid userID, DateTime lastVisit)
{
using (var context = new BookNotesEntities())
{
FoFriend = context.aspnet_Users.SingleOrDefault(u => u.UserId == userID);
var reading = context.Books.Where(b => b.UserID == userID && b.CurrentlyReading == true).ToList();
BookCount = reading.Count;
if (BookCount <= 5)
BookLinks = Book.ConvertBooksToLinks("Book/Details", reading);
else
BookLinks = "";
var recentBooks = context.Books.Where(b => b.UserID == userID
&& b.Review.DateCreated >= lastVisit).OrderByDescending(b => b.DateCreated).ToList();
if (recentBooks.Count <= 5)
ReviewLinks = Book.ConvertBooksToLinks("/Book/Details", recentBooks);
else
ReviewLinks = "";
ReviewCount = recentBooks.Count;
QuoteCount = context.Quotes.Count(q => q.UserID == userID && q.DateCreated >= lastVisit);
}
}
}
Let's start with a maxim that was initially proposed by Rob Conery: if there's an embedded "if" somewhere in your View, it's probably an indicator that you should either A.) create an HtmlHelper or B.) retract the logic from the view and insert it into your ViewModel.
By doing so, you can clean up your View to look something like this (you get the idea):
<div id="followedFriends">
<% foreach (var friend in Model.FollowedFriends) { %>
<div id="friendContainer">
<div class="followedFriend">
<div class="userName">
<%: Html.ActionLink(friend.FoFriend.UserName, "Visitor", "Home", new {userID = friend.FoFriend.UserId}, null)%>
</div>
Currently reading:
<br />
<div class="bookLinks">
<%: Html.DisplayBooklinks(friend) %>
</div>
<div class="bookReviews">
<%: Html.DisplayBookReviews(friend) %>
</div>
<div class="bookQuotes">
<%: Html.DisplayQuotes(friend) %>
</div>
</div>
</div>
<% } %>
</div>
At this point, if there's any chance that this piece of UI might be consumed in some other page, you should consider throwing it into a user control. By doing so, your view could now look something like this:
<% Html.RenderPartial("FriendDetails", Model.FollowedFriends); %>
Ultimately, whenever your View starts to look like soup, it's because your view is doing too much thinking. Which, in turn, means: some other layer of your application isn't thinking enough. By pinpointing logic in your Views, and by determining what abstractions might aid you in your attempt to remain DRY, your Views will become much more readable and much more maintainable.
I would factor some of that inline logic to an HtmlHelperExtension method and then create a DisplayTemplate "Friend"
<div id="followedFriends">
<% if (Model.FollowedFriends.Any()) {
foreach (var friend in Model.FollowedFriends) {
<%=Html.DisplayFor(friend) %>
<% } %>
<% } %>
</div>
And your DisplayTemplate "Friend" would look something like this:
<div id="friendContainer">
<div class="followedFriend">
<div class="userName">
<%= Html.ActionLink(friend.FoFriend.UserName, "Visitor", "Home", new {userID = friend.FoFriend.UserId}, null)%></div>
Currently reading:
<br />
<%=Html.BookInformation(friend) %>
<%=Html.BookReview(friend) %>
<%=Html.Quotes(friend) %>
</div>
</div>
</div>
Rather than making HtmlHelperExtensions you could just create more DisplayTemplates and specifically call them: DisplayFor("BookReview", friend)
Let me start by saying, we've all been there...
For your particular problem, I'd consider a few of the following "tools":
Partial Views: when you have a lot of nested <div> tags, for instance, you may want to consider creating some partial views where appropriate.
Use the Controller for Business Logic: Some of your logic should be done in the controller, where it's easier to break things up and call different helper methods to apply the logic. Consider passing to your view a custom object (a ViewModel if you will) that has all the business logic parsed. For example, things like displaying "quote" vs "quotes" could be done in a ViewModel object's "DisplayValue"-type property.
HTML Helpers: These little extensions can help take care of extracting the many if/else statements that you have into an easily readable method - and they could be re-used on other pages if written generally enough.
Lastly, I'd say, go get some fresh air and come back with only one thing in mind: simplify! Often we get caught up with a long list of tasks that need to be done and we get lost in our own soup or spaghetti or what-have-you. You'll need to approach the code with only refactoring in mind, until it's clean enough that you can tell where the next piece of logic should go.
I'm new to ASP.Net MVC, so I try to find coding conventions for me, too:
write <% %> tags in a separate line (except for <%: and <%=)
eliminate sequences of %><% (except for <%: and <%=)
keep C# and HTML indentation separate
indent <%= and <%: as if they where HTML tags (if they do represent HTML tag/s)
I'm not a ASP expert, but like in PHP isn't there a way to echo things? In place of using so much open and close tags? I should place everything above the code, and make variables that you use in your HTML?
In my opinion, this isn't awful. I think the key here would be to try to spend some time walking through it and commenting where blocks start and then their corresponding end tags. It'll be painful now, but it'll save you some time later on.
I'm assuming you're looking for a code review. In that case:
remove the empty ELSE block with the comment "nothing, it appears"
Combine the nested IF conditions into a single check IF( count != 0 && count < 5 )

Ajax.Actionlink, how to get the form data to the controller action

View
<%= Ajax.ActionLink("Create", "Create", ViewData.Model, new AjaxOptions { HttpMethod = "POST" })%>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Ajax.BeginForm("Create", "Customer", ViewData.Model, new AjaxOptions { HttpMethod ="POST" }))
{%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Title">Title:</label>
<%= Html.TextBox("Name")%>
<%= Html.ValidationMessage("Name", "*")%>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextArea("ContactNo")%>
<%= Html.ValidationMessage("Name", "*")%>
</p>
</fieldset>
<% } %>
Controller
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Customer info)
{
//INFO IS NULL???
//WHAT IS THE PROBLEM?
}
You can't pass the model object. This argument expects the route values such as an ID.
if you pass in Ajax.ActionLink("Create", "Create", new { id=23 }, ....
it will create /create/23.

Resources