How to use #model IEnumerable<> and #model <> in the same page (List / Create) MVC3 - asp.net-mvc-3

Lets say i have a class 'Location', and I'm using MVC3 to create a scaffolding list(index.cshtml). The index html page uses #model IEnumerable. If i want to add a new location to the list, i press create, and i end up with a new html page using #model Project.Models.Location.
Instead of going to a new page, i would like to have the create page as a popup using modal in boostrap(twitter). I've tried using partialview without any luck. Whatever i do i get the dictionary error message.
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Project.Models.Location]', but this dictionary requires a model item of type 'Project.Models.Location'.
The modal(bootstrap) itself is working when i'm not fetching the partialview. The partialview itself is exactly the same as the generated create(CRUD).
EDIT: I rewrote the whole thing.
Index:
#model IEnumerable<LoLStats.Models.Location>
#{
ViewBag.Title = "Index";
}
#foreach (var item in Model) {
<ul>
<li>#Html.DisplayFor(modelItem => item.LocationName)</li>
</ul>
}
<a class="btn" data-toggle="modal" href="#myModal" >Create</a>
<div class="modal hide" id="myModal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Modal header</h3>
</div>
<div class="modal-body">
<p>#Html.Partial("_createLocation")</p>
</div>
<div class="modal-footer">
Close
Save changes
</div>
</div>
_createLocation:
#model LoLStats.Models.Location
#{
ViewBag.Title = "Create"; }
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
Location
<div class="editor-label">
#Html.LabelFor(model => model.LocationName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LocationName)
#Html.ValidationMessageFor(model => model.LocationName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset> }

You could make a view model that has two properties:
The IEnumerable for the parent view
A singular object for the modal
Then strongly type both views to the view model and you should be all set.
Edit
ViewModel:
public class MyViewModel()
{
public IEnumerable<LoLStats.Models.Location> Locations {get; set;}
public LoLStats.Models.Location Location {get; set;}
}
Index:
#model myNamespace.MyViewModel
...
#foreach (var item in Model.Locations) {
<ul>
<li>#Html.DisplayFor(modelItem => item.Locations.LocationName)</li>
</ul>
...
_createLocation:
#model myNamespace.MyViewModel
...
<div class="editor-label">
#Html.LabelFor(model => model.Location.LocationName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Location.LocationName)
#Html.ValidationMessageFor(model => model.Location.LocationName)
</div>
...
Hopefully that's enough detail to get you going

try this in the Index view:
... <p>#Html.Partial("_createLocation", Model.FirstOrDefault())</p>
on the other hand, maybe this will make more sence:
... <p>#Html.Partial("_createLocation", new LoLStats.Models.Location())</p>
hope it works for you.

Your pop-up would need to have a model defined as Project.Models.Car (singular item). After successfully saving the single item you need to update your original list view. Which you can do using jQuery by inserting a new item into the DOM or just by refreshing the page by navigating to the action that gave you the list to begin with.

Related

required attribute not generating in mvc partial razor view (using Edmx Models)

#model eDurar.Models.BuyOnlineAddress
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>BuyOnlineAddress</legend>
<div class="editor-label">
Address Details
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Details, new {#required="required" })
#Html.ValidationMessageFor(model => model.Details)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
This is partial view, in the html generated, there is no required attribute
When I manually insert it in the html editor, works fine, but this method usually works for normal views
I haven't loaded any scripts this time
Thanks I got it now, I changed
#Html.EditorFor(model => model.Details, new {#required="required" })
to
#Html.TextBoxFor(model => model.Details, new {#required="required" })

MetadataType attribute is not working for details view in MVC3

I am newbie in entity framework and I have created Ado.Net Entity Data model which contains two properties FullName and EmailAddress. what I am trying to do is that in my details view I want to display Full Name instead of FullName. To accomplish this I have written code like this:
namespace ScaffFolding.Models
{
[MetadataType(typeof(EmployeeMetaData))]
public partial class Employee
{
}
public class EmployeeMetaData
{
[Display(Name="Full Name")]
public string FullName { get; set; }
[Display(Name = "Email Address")]
public string EmailAddress { get; set; }
}
}
My edmx file also sharing the same namespace and have same class named as Employee
Now the problem is that in Details view this code is not working means showing FullName instead of Full Name but in Create view it's working as expected.
Here is code for my Detail view:
#model ScaffFolding.Models.Employee
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Employee</legend>
<div class="display-label">FullName</div>
<div class="display-field">
#Html.DisplayFor(model => model.FullName)
</div>
<div class="display-label">Gender</div>
<div class="display-field">
#Html.DisplayFor(model => model.Gender)
</div>
<div class="display-label">Age</div>
<div class="display-field">
#Html.DisplayFor(model => model.Age)
</div>
<div class="display-label">HireDate</div>
<div class="display-field">
#Html.DisplayFor(model => model.HireDate)
</div>
<div class="display-label">EmailAddress</div>
<div class="display-field">
#Html.DisplayFor(model => model.EmailAddress)
</div>
<div class="display-label">Salary</div>
<div class="display-field">
#Html.DisplayFor(model => model.Salary)
</div>
<div class="display-label">PersonalWebSite</div>
<div class="display-field">
#Html.DisplayFor(model => model.PersonalWebSite)
</div>
</fieldset>
<p>
#Html.ActionLink("Edit", "Edit", new { id=Model.Id }) |
#Html.ActionLink("Back to List", "Index")
</p>
Can anyone please tell me what is going wrong here?
DisplayFor is used for picking up templates from DisplayTemplates folder.
more at What is the #Html.DisplayFor syntax for?
Where as, LabelFor picks stuffs from data annotations
Ok I got the answer what is going wrong here. The problem is in the code generated by VS:
Visual Studio is generating the Details view using div tag as shown below
<div class="display-label">FullName</div>
Where as LabelFor is being used by Vistual Studio for Create view:
#Html.LabelFor(model => model.FullName)

View values not returned to the controller action

Can anyone tell whats wrong with my view? my model values is displayed correctly in my view but it is not returned to the controller action. public ActionResult DeleteConfirmed(ClientModel contacts) my ClientModel is returned null.
View:
#model ContactManager.Models.ClientModel
#{
ViewBag.Title = "Delete Information";
}
<h2>Delete Information</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>Details</legend>
<div class="display-label">ID</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.Client00130012)
</div>
<div class="display-label">Name</div>
<div class="display-field">
#Html.DisplayFor(model => model.Clientes0013.Nombre0013)
</div>
<div class="display-label">Contact Number</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.Direcc0012)
</div>
<div class="display-label">Country</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.F1Pais00200012)
</div>
</fieldset>
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
Controller Action:
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(ClientModel contacts)
{
contacts.Clientes0013.Client0013 = contacts.CanaClie0012.Client00130012;
contacts.Clientes0013.F1Pais00200013 = contacts.CanaClie0012.F1Pais00200012;
Clientes0013 clientes0013 = db.Clientes0013.Find(contacts.Clientes0013.Client0013, contacts.Clientes0013.F1Pais00200013);
db.Clientes0013.Remove(clientes0013);
db.SaveChanges();
return RedirectToAction("Index");
}
1. Keep the helpers inside the Form like below.
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
#Html.DisplayFor(i => i.PropertyName);
<input type="submit" name="Submit" value="Submit" />
}
2. In order to Submit the form and send the model in Post Action Method, you have to use a Hidden Field along with each DisplayFor Helper.
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
#Html.HiddenFor(i => i.PropertyName);
#Html.DisplayFor(i => i.PropertyName);
<input type="submit" name="Submit" value="Submit" />
}
3. You will not be able to see the Model values in Post Action Method when the View is like below..
#Html.HiddenFor(i => i.PropertyName);
#Html.DisplayFor(i => i.PropertyName);
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
<input type="submit" name="Submit" value="Submit" />
}
This is because you are using DisplayFor for all your properties. DisplayFor will not send anything in a POST, it doesn't create an input.
You could add #Html.HiddenFor(model => model.CanaClie0012.Client00130012) , etc. to each one of your properties in order for your view to send anything back.

How to make this into a checkbox?

So I have a view as such:
#model Tuple<LocApp.Models.Location, LocApp.Models.Service>
#{
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>Location</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Item1.name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item1.name)
#Html.ValidationMessageFor(model => model.Item1.name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Item1.active)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item1.active)
#Html.ValidationMessageFor(model => model.Item1.active)
</div>
#Html.CheckBoxFor(model => model.Item2.id)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
and the line: #Html.CheckBoxFor(model => model.Item2.id) IMO is suppose to go through and for every service in the database create a check box based on the id so I can then save the id. How ever, apparently I must pass in a boolean value? the only one I have is model.Item2.active
Ideas?
CheckBoxes can only have boolean states: Checked or Unchecked. Your model must set a checkbox state by using a boolean value of true or false. If you want your checkbox to act as the mechanism for saving an Id to your database, you need to write business logic for checking if the checkbox is checked on the controller action. If the checkbox is checked, write the Id, otherwise don't write the Id.
You should have a bool model.Item1.checked (or active, enabled, etc), then use that instead of model.Item1.id, it will keep track of whether the flag is on or off for that item.
Then when they POST the form you can loop through (or use LINQ) all the items and save the id for each one that has .checked = true.

MVC 3 Validation - Only show error messages after lost focus or submit?

I have setup the entities in my MVC 3 app with DataAnnotations (required, stringlength, etc) and the MVC page is showing validation error messages appropriately. But, the error messages are shown as soon as the page loads on a new form before the user has had the chance to enter an invalid value.
I had used JQuery validation a few years ago and was able to only show the error messages after the user lost focus on a field or attempted to submit the form. In fact, I think it was the default behavior.
Is there anyway to do the same in MVC 3 using DataAnnotations?
EDIT: Here is the HTML
<div class="form horizontal floated w50p">
<h3>Billing Information</h3>
#using(Html.BeginForm()){
<div class="item">
<div class="label">
<label>* First Name</label></div>
<div class="value">#Html.TextBoxFor(x => x.Name)</div>
<div class="value">#Html.ValidationMessageFor(x => x.Name)</div>
</div>
<div class="item">
<div class="label">
<label>* Address 1</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street1)</div>
<div class="value">#Html.ValidationMessageFor(x => x.Street1)</div>
</div>
<div class="item">
<div class="label">
<label>Address 2</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street2)</div>
</div>
<div class="item">
<div class="label">
<label>Address 3</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street3)</div>
</div>
<div class="item">
<div class="label">
<label>City</label></div>
<div class="value">#Html.TextBoxFor(x => x.City)</div>
<div class="value">#Html.ValidationMessageFor(x => x.City)</div>
</div>
<div class="item">
<div class="label">
<label>State/Province/Region</label></div>
<div class="value">#Html.TextBoxFor(x => x.StateProv)</div>
<div class="value">#Html.ValidationMessageFor(x => x.StateProv)</div>
</div>
<div class="item">
<div class="label">
<label>Zip / Postal Code</label></div>
<div class="value">#Html.TextBoxFor(x => x.PostalCode)</div>
<div class="value">#Html.ValidationMessageFor(x => x.PostalCode)</div>
</div>
<div class="item">
<div class="label">
<label>* Contact Phone</label></div>
<div class="value">#Html.TextBoxFor(x => x.ContactPhone)</div>
<div class="value">#Html.ValidationMessageFor(x => x.ContactPhone)</div>
</div> <input type="submit" value="Submit" />
}
The default behavior is exactly what you describe (errors should appear only after a field loses focus or form is submitted). So, there must be something wrong with your view or controller. Specifically, it sounds like the validator thinks the user is posting back even on the first view of the form. The first view of the form should be a GET not a POST. If you paste your controller code, that might help us diagnose it better.
You mean like enabling client validation? Sure, that's easy. Just:
create a view model
decorate it
create controller
create a view
include proper jquery scripts
So let's go ahead and follow those steps.
View model:
public class ProductViewModel
{
[Required] // <-- you could use any data annotation attributes you like
public string Name { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new ProductViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(ProductViewModel model)
{
return View(model);
}
}
View:
#model ProductViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
<input type="search" value="OK" />
}
Now leave the field blank and client validation will trigger assuming it is enabled in web.config (which it is by default when you create a new ASP.NET MVC 3 project using the default Visual Studio template):
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
If you want to handle custom validation attributes you could but it might be a little more painful. And once you get yourself confronted to real world applications and realize the weaknesses of declarative validation using attributes (data annotations) I would strongly recommend you checking out FluentValidation.NET.

Resources