ASP .NET MVC4 Adding new items to view and model binding - asp.net-mvc

I create a website for my wife. She's a teacher and she would like to have a possibility to create exercises for their students. The case is that she would like to create for instance the following exercise:
Exercise 1: Fill the sentence using a correct word:
My wife is 30 ............. old
I live in this city for 30 .........
I have the following model:
public class Exercise
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ExerciseType Type { get; set; }
public DifficulityLevel DifficulityLevel { get; set; }
public List<ExerciseItem> Items { get; set; }
public DateTime TimeOfCreation { get; set; }
public DateTime TimeOfModification { get; set; }
}
public class ExerciseItem
{
[Key]
public Guid Id { get; set; }
public string Content { get; set; }
public List<ExerciseItemOption> Options { get; set; }
public ExerciseItemOption CorrectSelection { get; set; }
}
I creates a View for my Exercise. I can fill in the basic properties like Name, Description, Difficulity Level and Type. Then I would like to create a button "Add exercise item". When clicked, a partial view (or something else) should be added dynamically where new ExerciseItem can be provided.
I've tried to following:
I've added a button
#Ajax.ActionLink("Add exercise item",
"AddExerciseItem",
"Exercise", new AjaxOptions() { HttpMethod="GET", InsertionMode = InsertionMode.InsertBefore, UpdateTargetId="ExerciseItems"})
and the appropriate div:
<div id="ExerciseItems"></div>
My action method looks as follows:
public ActionResult AddExerciseItem()
{
return PartialView("ExerciseItem", new ExerciseItem());
}
and the partial view:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.DisplayNameFor(model => model.Content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content, new { style = "width:200px" })
</div>
</fieldset>
It works fine. However when I click button for creating a whole exercise, I do not have ExerciseItem collection in my model:
public ActionResult Create(Exercise exercise)
{
using (PersistanceManager pm = new PersistanceManager())
{
exercise.Id = Guid.NewGuid();
exercise.TimeOfCreation = DateTime.Now;
exercise.TimeOfModification = DateTime.Now;
pm.ExcerciseRepository.Add(exercise);
}
return RedirectToAction("Index");
}
How should I change the code in order to bind my list of added ExerciseItem objects to my model Exercise?

Check out this article about model binding. You basically need to create special names for the exercise items so that they get bound correctly.
e.g. partial:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
<label>content</label>
<input type="hidden" name="ExcersiseItem.Index" value="SomeUniqueValueForThisItem" />
<input type="text" name="ExcersiseItem[SomeUniqueValueForThisItem].Name" value="#Model.Content" />
</fieldset>
You can also look at my answer to this question MVC3 Non-Sequential Indices and DefaultModelBinder. Thanks Yarx for finding it, I was actually trying to find it :)

Related

How To Validate DropDown List in Asp .net MVC

The List of the month are Already added In Databases. For Adding the salary (Create operation), i have to select the month type from the Drop Down List, if the month is not selected the program should not have to redirect to create action. How Can i validate the Drop Down, Before Routing to Create Action ?
#using(Html.BeginForm("Create","Talab",FormMethod.Post))
{
<div class="row">
<div class="form-group">
<div class="col-md-2">
Add New
</div>
</div>
<div class="form-group">
<div class="col-md-2">
#Html.DropDownListFor(model => model.Month.id, (IEnumerable<SelectListItem>)ViewData["monthType"], "--Select a Month--")
#Html.ValidationMessageFor(model => model.Month.id)
</div>
</div>
</div>
}
My View Model has following Property
public class Salary
{
public int id { get; set; }
public Nullable<int> month_id { get; set; }
[Required]
public virtual Month Month { get; set; }
public IEnumerable<Month> GetMonths()
{
IEnumerable<Month> mat = null;
mat = this.db.Months.ToList();
return mat;
}
}
Public Class Month
{
public int id { get; set; }
public string month { get; set; }
public virtual ICollection<Salary> Salary { get; set; }
}
My Controller Action Index
public ActionResult Index()
{
Salary salary = new Salary();
ViewData["monthType"] = salary .GetMonths().ToList().Select(
s => new SelectListItem
{
Text = s.month,
Value = s.id.ToString()
});
return View(salary);
}
Your dropdownlist should bind to property month_id which is Nullable<int>, but you have not decorated it with the [Required] attribute. It needs to be
[Required(ErrorMessage="Please select a month")]
public Nullable<int> month_id { get; set; }
and in the view
#Html.DropDownListFor(m => m.month_id, ....)
#Html.ValidationMessageFor(m => m.month_id)
Side note: You claim your using a view model, but in fact your not. What you have shown is a data model. A view model contains only properties relevant to your view and should never contain method that access your database. Refer What is ViewModel in MVC?
A view model in your case would be
public class SalaryViewModel
{
public int id { get; set; }
[Required(ErrorMessage="Please select a month")]
[Display(Name = "Month")] // for use in #Html.LabelFor()
public Nullable<int> month_id { get; set; }
public SelectList MonthList { get; set; } // or IEnumerable<SelectListItem> MonthList
}
Where you populate the MonthList property in the controller and use it in the view as
#Html.DropDownListFor(m => m.month_id, Model.MonthList, "--Select a Month--")
Make sure you use required on the model.
[Required]
public int id { get; set; }
Validating required selection in DropDownList
https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute(v=vs.110).aspx
Specify the range attribute
[Required]
[Range(1, 12, ErrorMessage="Please select a value")]
public int Id { get; set; }
Source : MVC3 validation with data-annotation?

MVC dropdown select (blank) option

I initially set up my dropdown with submit button which was fine but now I wanted to have it just work without the button (I added onchange). However now I find another difficulty that initially when page is displayed, if I "select" the first option, nothing happens (obviously) so I though to add "please select" option. I found couple of solutions such as writing my custom list of SelectListOptions but this seems like it could be over the top for my case. Could anyone shed some light here and let me know what would be the easiest option here? Sorry if it is simple answer I am really stuck. Here is my code:
Model
public class SurveyDropdownModel
{
public SelectList selectSurveys { get; set; }
public string selectedId { get; set; }
public IEnumerable<RespondentModel> respondents { get; set; }
public SurveyDropdownModel(List<SurveyModel> surveys)
{
selectSurveys = new SelectList(surveys, "SurveyID", "SurveyTitle");
respondents = null;
}
}
public class SurveyModel
{
[Required]
[Display(Name = "Survey ID")]
public int SurveyID { get; set; }
[Display(Name = "Title")]
public string SurveyTitle { get; set; }
[Display(Name = "Updated")]
public DateTime SurveyUpdatedDate { get; set; }
[Display(Name = "Active")]
bool IsActive { get; set; }
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
HealthCheckDataLayer.HealthCheckRepository repo = new HealthCheckRepository(connectionString);
List<SurveyModel> surveyList = repo.ReturnSurveys<SurveyModel>();
var model = new SurveyDropdownModel(surveyList);
return View(model);
}
[HttpPost]
public ActionResult Index(SurveyDropdownModel model)
{
//not important here
}
}
View
#model HealthCheckWebApp.Models.SurveyDropdownModel
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div class="col-md-4">
<h4>Select product:</h4>
#using (Html.BeginForm("Index", "Home"))
{
#Html.DropDownList("selectedId", Model.selectSurveys, new { onchange = "this.form.submit()" })
}
</div>
</div>
<br />
<br />
#if(Model.respondents!=null)
{
#* not relevant here*#
}
I guess now that I didn't include how do I pull my list , I am calling a stored procedure from my repository there (It's required to do it with SP).
Thanks.
Use #Html.DropDownListFor. Here is a description.
Usage:
#Html.DropDownListFor(x=> x.selectedId, Model.selectSurveys, "Select something", new { onchange = "this.form.submit()" )

'OnlineShoppingSystemMVC.Models.User', but this dictionary requires a model item of type 'OnlineShoppingSystemMVC.Models.Repository'

I may be asking a question that a lot of programmers might have asked but still here it goes..
I have the a model User with following properties
public string firstName { get; set; }
public string lastName { get; set; }
public string userName { get; set; }
public int userTypeID { get; set; }
public string email { get; set; }
public long phoneNumber1 { get; set; }
public long phoneNumber2 { get; set; }
public string address { get; set; }
In the action UserProfile() I load an object named user with a user's details as shown above and send it to the view as
View(user);
And I have created a strongly typed view as
#model OnlineShoppingSystemMVC.Models.User
#{
ViewBag.Title = "UserProfile";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>UserProfile</h2>
<fieldset>
<legend>User</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.firstName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.firstName)
</div>
.
.
</fieldset>
<p>
#Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) |
#Html.ActionLink("Back to List", "Index")
</p>
When I execute the application and redirect to the view it showing the following error
The model item passed into the dictionary is of type 'OnlineShoppingSystemMVC.Models.User', but this dictionary requires a model item of type 'OnlineShoppingSystemMVC.Models.Repository'.
Can any one help out on where I went wrong?
Thanks for any support in advance.
Whatever controller action you are redirecting to accepts a Respository model but not a User model as its parameter. Is that enough information to fix the problem?

How to Creating View And View Model For Two Related Entity

i have two entity:
1) student
and 2) address.
public class Student
{
Public Int StudentId { get; set; }
Public String FullName { get; set; }
Public virtual IList<Address> Addresses { get; set; }
}
public class Address
{
Public Int AddressId { get; set; }
Public Int StudentId { get; set; }
Public String FullAddress { get; set; }
Public virtual Student Student { get; set; }
}
each student may have zero or more address.
i want to create single view for this two entity. i know that must create a view model. this is view model.
public class StudentViewModel
{
Public Int StudentId { get; set; }
Public String FullName { get; set; }
public Address AddAddressModel { get; set; }
Public virtual IList<Address> Addresses { get; set; }
}
and i create a view for StudentViewModel. this is StudentViewModel:
#model MyProject.Models.StudentViewModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm ())
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.FullName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FullName)
#Html.ValidationMessageFor(model => model.FullName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AddAddressModel.FullAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AddAddressModel.FullAddres)
#Html.ValidationMessageFor(model => model.AddAddressModel.FullAddres)
</div>
<button id="add">add address to list</button>
<input type="submit" value="save in database" />
</fieldset>
please tell me how i can add one or more address on by one in Addresses Property of StudentViewModel and show after this operation to user. finally when i click on the "save in database" button student and his addresses must be inserted in database.
I have in the past submitted nested child entities, but that was always done with an API call and the form was serialized to a JSON object before submission.
Because you only really need multiple FullAddress values, you could change your model and view accordingly (not tested):
Model:
public class StudentViewModel
{
public int StudentId { get; set; }
public string FullName { get; set; }
public string[] Addresses { get; set; }
}
View:
In your view, when you click the 'Add' button, make sure that (through JavaScript) you end up with something like this:
<textarea name="Addresses[]">Some Address 1</textarea>
<textarea name="Addresses[]">Some Address 2</textarea>
etc...
you can try using:
#EditorFor(m => model.Addresses)
this will create template for your address model

How to pass an entire ViewModel back to the controller

I have a ViewModel that contains two objects:
public class LookUpViewModel
{
public Searchable Searchable { get; set; }
public AddToSearchable AddToSearchable { get; set; }
}
The two contained models look something like this:
public class Searchable
{
[Key]
public int SearchableId { get; set; }
public virtual ICollection<AddToSearchable> AddedData { get; set; }
}
public class AddToSearchable
{
[Key]
public int AddToSearchableId { get; set;}
[Required]
public int SearchableId { get; set; }
[Required]
public String Data { get; set; }
[Required]
public virtual Searchable Searchable { get; set; }
}
I have a view that uses my LookUpViewModel and receives input to search for a SearchableId. If the Searchable object is found, a LookUpViewModel object is created and passed to the View. The view then displays editor fields for AddToSearchable.Data. Once submitted, I want the LookUpViewModel to be passed to an action method to handle all the back-end code. The only problem is, the LookUpViewModel passed to my action method contains a null reference to Searchable and a valid reference to AddToSearchable.. i.e. I'm missing half of my data.
Here's an example of what my view looks like:
#model HearingAidTrackingSystem.ViewModels.LookUpViewModel
#using (Html.BeginForm("LookUp", "Controller", "idStr", FormMethod.Post))
{
<input type="text" name="idStr" id="idStr"/>
<input type="submit" value="Search" />
}
#if (Model.Searchable != null && Model.AddToSearchable != null)
{
using (Html.BeginForm("AddMyStuff", "Controller"))
{
Html.HiddenFor(model => model.Searchable.SearchableId);
Html.HiddenFor(model => model.Searchable.AddedData);
Html.HiddenFor(model => model.AddToSearchable.AddToSearchableId);
Html.HiddenFor(model => model.AddToSearchable.SearchableId);
Html.HiddenFor(model => model.AddToSearchable.Searchable);
<div class="editor-field">
#Html.EditorFor(model => model.AddToSearchable.Data)
#Html.ValidationMessageFor(model => model.AddToSearchable.Data);
</div>
<input type="submit" value="Submit" />
}
}
and here are my action methods:
public ActionResult LookUp(LookUpViewModel vm)
{
return View(vm);
}
[HttpPost]
public ActionResult LookUp(string idStr)
{
int id = /*code to parse string to int goes here*/;
Searchable searchable = dal.GetById(id);
LookUpViewModel vm = new LookUpViewModel { Searchable = searchable,
AddToSearchable = new AddToSearchable() };
//When breakpoint is set, vm contains valid references
return View(vm);
}
[HttpPost]
public ActionResult AddMyStuff(LookUpViewModel vm)
{
//**Problem lies here**
//Do backend stuff
}
Sorry for the lengthy post. I tried my best to keep it simple. Any suggestions you may have.. fire away.
Two methods to fix it:
You can add to do HiddenFor() for all properties of Model.Searchable.
You can use serialization to transfer your Model.Searchable into text presentation and repair it from serialized form in controller.
Update: The problem is: You need to use #Html.HiddenFor(), not Html.HiddenFor();.

Resources