Hide / View Edit view form - asp.net

I'm working on a small ASP.NET MVC 4 application in combination with MongoDB. Currently I have 4 views: Index, Create, List, Edit. Create is a form to put data in the database. List is a list to display the data. Edit is a form to edit the data. These three views are rendered inside the Index view (RenderAction).
The goal is to display only two views inside the index view. A combination of Index with Create, or a combination of Index with Edit.
At this moment I'm having problems with the Edit View (inside the controller):
[HttpGet]
public ActionResult Edit(string id)
{
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}
Edit view:
#model MvcApplication1.ViewModels.UpdateCarViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>UpdateCarViewModel</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Make)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Make)
#Html.ValidationMessageFor(model => model.Make)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NumberOfDoors)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NumberOfDoors)
#Html.ValidationMessageFor(model => model.NumberOfDoors)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DailyRentalFee)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DailyRentalFee)
#Html.ValidationMessageFor(model => model.DailyRentalFee)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Index View:
#model MvcApplication1.ViewModels.InsertCarViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#{Html.RenderAction("Create", Model);}
</div>
<div>
#{Html.RenderAction("List", Model);}
</div>
<div>
#{Html.RenderAction("Edit", Model);}
</div>
</body>
</html>
Obviously the Edit view needs an ID to display, and it gets an error now when I use RenderAction, because there is no ID when I start the application. I want to hide this view when it is not needed, and only display this view when it is needed. How can I achive this without Javascript / Jquery.
Do I need an if/else statement inside my ActionResult?

The simplest and quickest thing to do would be to just check if id has a value
[HttpGet]
public ActionResult Edit(string id)
{
if (String.IsNullOrEmpty(id))
{
return null;
}
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}

That's not a problem, in MVC Edit Controller typically has an id param, so to eliminate your problem you can just check if id is existing, something like this:
[HttpGet]
public ActionResult Edit(string id)
{
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
if (car != null)
{
return View(car.ConvertToUpdateViewModel());
}
//if we get this far show other view
return View();
}

Related

ASP.NET Razor ViewModel property is modified somewhere

I have an extremely simple Controller + View
public ActionResult Edit(string username)
{
return View(ComponentFactory.GetAdapter<IUserListAdapter>().Get(username));
}
and
#model BAP.Models.UserList
#using GrmanIT.Utils.Web
#using BAP.Models
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Globale Benutzer</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserId)
#Html.ValidationMessageFor(model => model.UserId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Model.UserName
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Bundesland)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Bundesland, new SelectList((IEnumerable<BAP.Models.Bundesland>)ViewData["BundeslandList"], "Value", "Text"))
</div>
<div>
<input type="submit" value="#LocalizationHelper.LocalizedLiteral("Save").ToString()" />
</div>
</fieldset>
}
<div>
#Html.ActionLink(LocalizationHelper.LocalizedLiteral("BackToList").ToString(), "Index")
</div>
#Model.UserName
this is by far the simplest controller and view we have in our MVC4 application, BUT - it does something weird:
I get the TextBox, which is created with #Html.EditorFor(model => model.UserName) prefilled with the UserId of the model instead of the UserName
I debugged it and it and there as always the correct value in UserName and UserId. You can also see, that I added #Model.UserName twice within the View to see if it get also correctly rendered, and yes, it prints the UserName and not the ID.
I've also checked references to the UserName-property and didn't find any, which would modify it. My question is - do you have any idea, where the code could be modified or how could if find it out?
It happens only on this one controller on this one action (out of ~25 controllers and ~200 actions)
Thank you
Ok, it was AGAIN the ModelState - as you can see in the code above - the parameter to the function is called "username" but it's actually the userId. And since there is already the parameter username, it is stored in the ModelState and when I call
#Html.EditorFor(model => model.UserName)
it takes the value from the ModelState (where actually the UserId is stored under the name of the action-parameter)
So the solution would be, either to call ModelState.Clear() or rather, rename the parameter to represent the actual value.

ValidationSummary() not displaying when using RenderPartial in View

When I use
#{Html.RenderPartial("Login");}
inside my main view, the #Html.ValidationSummary() doesn't work, but when I copy the code from "Login" inside main view, it works.
Why is that, and how do I display validation messages from the partial view?
Here is partial view "Login":
#model NyNo.Models.LoginModel
#using (Html.BeginForm())
{
<fieldset>
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.TextBoxFor(m => m.UserName, new { #placeholder = "Username" })
#Html.ValidationMessageFor(m => m.UserName)
#Html.PasswordFor(m => m.Password, new { #placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password)
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
<input type="submit" class="button" value="Log in" />
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Hope you understand, thanks!
Unfortunately, this cannot work. A Partial is just a string.
While RenderPartial actually 'writes' the partial markup rather than sending a string back to the View Generator, it does not rebind your View to a new model. If you want Validation Summary to work it must be bound to a model in your main View.
Your problem could be related to this (maybe you aren't showing the ViewData passed in the RenderPartial()): Pass Additional ViewData to an ASP.NET MVC 4 Partial View While Propagating ModelState Errors
I was having a similar issue and I solved it this way: ValidationSummary inside a partial view not showing errors

Model Binding MVC3 Razor, How do I change input name?

I have setup a basic model binder by passing in a list to a view and running:
Controller:
[Authorize]
public ActionResult AddTracks(int id)
{
TrackRepository trackRepository = new TrackRepository();
//ShowTrackAssociationHelper showTrack = new ShowTrackAssociationHelper();
//showTrack.tracks = trackRepository.GetAssociatedTracks(id).ToList();
//showTrack.show = showRepository.GetShow(id);
TracksViewModel tracksModel = new TracksViewModel();
tracksModel.Tracks = trackRepository.GetAssociatedTracks(id);
ViewBag.ShowID = id;
return View(tracksModel);
}
View:
#model BluesNetwork.Models.TracksViewModel
#Html.EditorFor(model => model.Tracks, "TrackEditor")
TracksView Model:
public class TracksViewModel
{
public IEnumerable<Track> Tracks { get; set; }
}
TackEditor:
#model BluesNetwork.Models.Track
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.TrackID)
#Html.HiddenFor(model => model.ShowID)
<div class="editor-label">
#Html.LabelFor(x => x.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TrackNumber)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.TrackNumber, new { maxlength = 2 })
#Html.ValidationMessageFor(model => model.TrackNumber)
</div>
#Html.HiddenFor(model => model.HQFileID)
#Html.HiddenFor(model => model.LQFileID)
<div class="editor-label">
#Html.LabelFor(model => model.InternalRating)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InternalRating)
#Html.ValidationMessageFor(model => model.InternalRating)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.License)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.License)
#Html.ValidationMessageFor(model => model.License)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LicenseNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LicenseNumber)
#Html.ValidationMessageFor(model => model.LicenseNumber)
</div>
<input type="submit" value="Save" />
}
At first I was getting:
Which gives me output as such on each input:
name="[0].ShowID"
however I wanted it to be:
name="track[0].ShowID"
I've seen examples/tutorials that show output like this but they don't go into detail about it.
After following RPM1984's advice and making the changes I got the error:
The model item passed into the dictionary is of type 'System.Data.Objects.ObjectQuery`1[BluesNetwork.Models.Track]', but this dictionary requires a model item of type 'BluesNetwork.Models.Track'.
Thank you in advance for all help
At
Not sure what you mean by "I have setup a basic model binder". That doesn't look like a model binder, that looks like a template or partial view.
Anyway, moving on....
You should have a ViewModel like this:
public class TracksViewModel
{
public IEnumerable<Track> Tracks { get; set; }
}
Main View:
#model TracksViewModel
#Html.EditorFor(model => model.Tracks)
Editor Template:
#model Track
#Html.EditorFor(model => model.ShowId)
No loops, no magic strings. Nice.
Which will render HTML like this:
<input type="text" name="tracks[0].ShowId" />
<input type="text" name="tracks[1].ShowId" />
<input type="text" name="tracks[2].ShowId" />
Which is what you want, right?
Basically if you want to change the names you will need to bust out some JavaScript or roll your own partial view.
Note that changing id's and names of bound fields is, generally speaking, a poor idea because this will break the baked in binding of your ViewModel. Do you really need to change the name?

Text box default value in Razor syntax

<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AccountModel.UserName)
#Html.ValidationMessageFor(model => model.AccountModel.UserName)
</div>
On this page load (View load), I would like the Username text box to have its value automatically populated from the standard aspnet membership informtion. How can assign this default value to the text box. Please help. thank you
In your controller action you could populate the view model and set the corresponding properties on it. So assuming your view is strongly typed to MyViewModel:
[Authorize]
public ActionResult Foo()
{
var model = new MyViewModel
{
UserName = User.Identity.Name
};
return View(model);
}
and in the view simply:
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
If your view model has some AccountModel property, you will have to instantiate and populate it in the controller action. In my example I have flattened the view model.

Create Views for object properties in model in MVC 3 application?

I have an Asp.Net MVC 3 application with a database "Consultants", accessed by EF. Now, the Consultant table in the db has a one-to-many relationship to several other tables for CV type information (work experience, etc). So a user should be able to fill in their name etc once, but should be able to add a number of "work experiences", and so on.
But these foreign key tables are complex objects in the model, and when creating the Create View I only get the simple properties as editor fields. How do I go about designing the View or Views so that the complex objects can be filled in as well? I picture a View in my mind where the simple properties are simple fields, and then some sort of control where you can click "add work experience", and as many as needed would be added. But how would I do that and still utilize the model binding? In fact, I don't know how to go about it at all. (BTW, Program and Language stand for things like software experience in general, and natural language competence, not programming languages, in case you're wondering about the relationships there).
Any ideas greatly appreciated!
Here's the Create View created by the add View command by default:
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Consultant</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
And here's the EF database diagram:
Update:
According to suggestion, I tried Steven Sanderson's blog solution, but I can't get it to work properly. I added this in the main view:
<div id="editorRows">
#foreach (var item in Model.Programs)
{
Html.RenderPartial("ProgramEditorRow", item);
}
</div>
<input type="button" value="Add program" id="addItem" />
I also added the javascript:
<script type="text/javascript">
var url = '#Url.Action("BlankEditorRow", "Consultant")';
$(document).ready(function () {
$("#addItem").click(function () {
$.ajax({
url: url,
cache: false,
success: function (html) {
alert(html);
$("#editorRows").append(html); }
});
return false;
});
});
</script>
A partial view:
#model Consultants.Models.Program
#using Consultants.Helpers
<div class="editorRow">
#*#using (Html.BeginCollectionItem("programs"))
{*#
#Html.TextBoxFor(model => model.Name)
#*}*#
</div>
And action methods in the controller:
public ActionResult Create()
{
Consultant consultant = new Consultant();
return View(consultant);
}
public ActionResult BlankEditorRow()
{
return PartialView("ProgramEditorRow", new Program());
}
Note that I commented out the Html.BeginCollectionItem part in the partial view, because I can't get that to work. It only gives me the hidden field Steven Sanderson talks about, but not the actual textbox. So I tried commenting that part out and just had a textbox. Well, that gets me the textbox, but I can't get to that info in the post method. I use the Consultant object as return parameter, but the Programs property contains no Program. Perhaps this has to do with the fact that I cannot get the BeginCollectionItem helper to work, but in any case I don't understand how to do that, or how to get to the Program supposedly added in the view. With a simple object I would add the new object in the post method by something like _repository.AddToConsultants(consultant), and when I save to EF it gets its id. But how do I do the same thing with the program object and save it to the database via EF?
Phil Haack has written a great blog post which explains how to model bind to a list. It's specific to MVC2, but I'm not sure if version 3 has improved on this.
Model Binding To A List.
In a scenario where the user is allowed to add an arbitrary number of items, you'll probably want to create new input fields using JavaScript. Steven Sanderson shows here how to achieve that:
Editing a variable length list, ASP.NET MVC 2-style.
Those resources should get you all the way there.
You may take a look at the following blog post about writing custom object templates. Also don't use EF models in your views. Design view models that are classes specifically tailored to the needs of a given view and have your controller map between the EF models and the view models that should be passed to the view. AutoMapper is a good tool that could simplify this mapping.

Resources