I'm almost loosing my head trying to use the default asp.net MVC features to acomplish this mission. First of all i'll introduce what i've so far and what is my goal.
I have those two models.
public class ClassifiedViewModel
{
public int? ClassifiedId { get; set; }
public List<ClassifiedWishListViewModel> wishListModel {get; set;}
[Required]
public String UserId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
}
public class ClassifiedWishListViewModel
{
public string KeyWord { get; set; }
public int CategoryId { get; set; }
public int SubCategoryId { get; set; }
}
Added more information
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ClassifiedId, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.UserId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-3">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Description, new { #class = "form-control", rows = "10", cols = "60" })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="wishList">
</div>
<button type="button" class="btn" id="btnAddWish">
<span>Add item</span>
</button>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default" id="btnSave">Save</button>
</div>
</div>
</div>
}
script
$("#btnAddWish").on("click", function () {
$.get('/Classified/ClassifiedWishList', function (template) {
$("#wishList").append(template);
});
});
Controller - method to get partial view
[HttpGet]
public ActionResult ClassifiedWishList()
{
var model = new ClassifiedWishListViewModel();
var categoriesBO = new CategoriesBO();
var categories = categoriesBO.GetAllCategories();
model.Categories =
from c in categories
select new SelectListItem
{
Text = c.Name,
Value = c.CategoryId.ToString()
};
return PartialView("ClassifiedWishList", model);
}
And i'm trying to create a view to edit the Classified. But the classifiedwishList is a variable lenght property. So, i've created a partial view to that generates the ClassigiedWishListViewModel editor many times in the same form. But when i try to submit the entire form the ClassifiedWishListViewModel never gets binded to my original ClassifiedViewModel property.
Is there a way that i can acomplish that by simple using Asp.net MVC features? Every place i look suggests me to use a javascript framework and send all the information to the Controller through serialized jsons, but by using that i will loose all the validation that the Annotations performs (like required, maxlenght, others...) in the MVC, correct?
It's really frustrating that if you need a more dynamic page on Asp.net MVC you have to forget all the tools the framework offers you and start to just send and retrieve data through JSON communication.
Any kind of help is welcome! Thanks guys!
Related
I am trying to create a golf score app. I have created the database that has the following tables Round, Course and Hole.
The hole table has a foreign key to the courseId as when I create each hole I want to link it to a course.
When clicking on the create new I would like to have a dropdown list to select the courseId from when inputting all the hole details.
But when I click on the create new I get the following error in the Hole view model.
System.ArguementNullException: Value can not be Null. Parameter name items. in my Create action code within the HoleViewModelController.
This is my HoleViewModel code
public class HoleViewModel
{
[Key]
public int HoleId { get; set; }
public int HoleNumber { get; set; }
public int Par { get; set; }
public int Length { get; set; }
public int StrokeIndex { get; set; }
[ForeignKey("Course")]
public int? CourseId { get; set; }
public IEnumerable<SelectListItem> CourseNamesDropdownList { get; set; }
public virtual CourseViewModel Course { get; set; }
}
}
This is my CourseViewModel code
public class CourseViewModel
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
This is my HoleViewModelController Create action
public ActionResult Create()
{
ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View();
}
This is my MVC Create view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>HoleViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.HoleNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.HoleNumber, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.HoleNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Par, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Par, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Par, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Length, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Length, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Length, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StrokeIndex, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StrokeIndex, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StrokeIndex, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CourseId, "CourseId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Course.CourseId, Model.CourseNamesDropdownList, "Please select from List", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CourseId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
Ok so i resolved this issue by ammending the ActionResult Create code from
public ActionResult Create()
{
ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View();
}
To
public ActionResult Create()
{
var dbcourse = db.Course.ToList();
//Make selectlist, which is IEnumerable<SelectListItem>
var courseNameDropdownList = new SelectList(db.Course.Select(item => new SelectListItem()
{
Text = item.CourseName.ToString(),
Value = item.CourseId.ToString()
}).ToList(), "Value", "Text");
// Assign the Selectlist to the View Model
var viewCourse = new HoleViewModel()
{
Course = dbcourse.FirstOrDefault(),
// The Dropdownlist values
CourseNamesDropdownList = courseNameDropdownList,
};
//ViewBag.CourseId = new SelectList(db.CourseViewModels, "CourseId", "CourseName");
return View(viewCourse);
}
This has allowed the dropdown to be populated and be added to the view.
The class Unidade:
public class Unidade
{
public int UnidadeId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
}
Is used twice in the class Insumo, as Unidade and UnidadeConsumo
public class Insumo
{
public int InsumoId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
public int UnidadeId { get; set; }
public Unidade Unidade { get; set; }
public int UnidadeConsumoId { get; set; }
public Unidade UnidadeConsumo { get; set; }
}
To edit Insumo there are two actions EDIT in the controller:
public ActionResult Edit(int? id)
{
Insumo insumo = db.Insumos.Find(id);
if (insumo == null) return HttpNotFound();
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
And the POST EDIT:
[HttpPost]
public ActionResult Edit([Bind(Include = "InsumoId,Apelido,Descricao,UnidadeId,UnidadeConsumoId")] Insumo insumo)
{
if (ModelState.IsValid)
{
db.Entry(insumo).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
The view to display the fields to edition, comprised of two dropdown lists to select both units is:
#model Gestor.Models.Insumo
#{
ViewBag.Title = "Alterar";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Alterar</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Insumo</h4>
<hr />
#Html.Partial("CopyEdit")
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Gravar" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Retornar a lista", "Index")
</div>
And the partial view CopyEdit in the center:
#model Gestor.Models.Insumo
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.InsumoId)
<div class="form-group">
#Html.LabelFor(model => model.Apelido, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Apelido, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Apelido, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Descricao, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Descricao, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Descricao, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeId, "Unidade", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UnddId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeConsumoId, "Unidade de Consumo", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UndConsId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeConsumoId, "", new { #class = "text-danger" })
</div>
</div>
The problem is thta whrn returned to the POST Edit action, all fields are ok, but UnidadeConsumoId is always 0, what doesn't even exists in the database?
Can someone tell me why is it not returning the expected value, i.e. the selected value in the dropdown list reflecting the id of if?
Because your current code is rendering a SELECT element with name attribute value UndConsId.
<select class="form-control" name="UndConsId">
<!-- options -->
</select>
For model binding to work, the input element name attribute value should match with the parameter/property name used in the http post action method. Your parameter name is UnidadeConsumoId, not UndConsId
To fix this issue, pass UnidadeConsumoId as the first parameter of the DropDownList method call so that it will render the SELECT element with name UnidadeConsumoId. You can pass the ViewBag.UndConsId as the second parameter to explicitly specify the collection to be used to build the SELECT element.
This should work
#Html.DropDownList("UnidadeConsumoId", ViewBag.UndConsId as SelectList,
new { #class = "form-control" })
I want make a form using a model that contains a list of another entity, but I don't know how to do it. Note: without Entity Framework.
I have use only a model that contains only its fields, but not another entity.
My code is:
Tarea.cs
public class Tarea
{
public int ID { get; set; }
public string NombreTarea { get; set; }
public int TiempoTarea { get; set; }
}
Employee.cs
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime JoiningDate { get; set; }
public int Age { get; set; }
public List<Tarea> Tareas { get; set; }
}
And my view is:
Create.cshtml
#model MVCSimpleApp.Models.Employee
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Employee</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JoiningDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JoiningDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JoiningDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Age, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Age, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Age, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
I don't know how to put the List<Tarea> in the form. Thanks in advance.
For Example, if we want to collect two sets of Tareas model,
View Code:
#for (var item = 0; item < 2; item++)
{
#Html.HiddenFor(model => model.Tareas[item].ID)
#Html.TextBoxFor(model => model.Tareas[item].NombreTarea, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Tareas[item].TiempoTarea, new { #class = "form-control" })
}
And in the Post Create Action Method..
[HttpPost]
public ActionResult Create(Employee model)
{
// here we we get the all values given from the view ...
string example = model.Name;
List<Tarea> list = model.Tareas; // and iterate through model.Tareas to get List model values
}
I'm new to MVC, so apologies for the noob question!
I have the following model (TimeEntry), used for entering timesheet details:
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
Task type is based on the following model:
namespace MVCLogin.Models
{
public class TaskType
{
public int TaskTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
I use the following partial view which is used for entering the timesheet details:
<div class="col-md-1">
#Html.EditorFor(model => model.TaskType)
#Html.ValidationMessageFor(model => model.TaskType)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.MonHours , new { style = "width:100%", #class = "hours mon" })
#Html.ValidationMessageFor(model => model.MonHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.TueHours, new { style = "width:100%", #class = "hours tue" })
#Html.ValidationMessageFor(model => model.TueHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.WedHours, new { style = "width:100%", #class = "hours wed" })
#Html.ValidationMessageFor(model => model.WedHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.ThuHours, new { style = "width:100%", #class = "hours thu" })
#Html.ValidationMessageFor(model => model.ThuHours)
</div>
<div class="col-sm-1">
#Html.TextBoxFor(model => model.FriHours, new { style = "width:100%", #class = "hours fri" })
#Html.ValidationMessageFor(model => model.FriHours)
</div>
I want the task type field to be a drop down, but I can't figure out how to wire it up. The classes and data are right, as if I scaffold a controller (using Visual Studio's Add Controller tool) for the TimeEntry class it works fine:
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TaskTypeID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
How can I get the dropdownlist to work in the partial view?
The null parameter in your Html.DropDownList is the data of the Dropdown and it's a Ienumerable<SelectList>.
In order to pass the data. You need to transform your TaskType List into SelectListItem List (You can do this in the controller or somewhere else just before sending Model from Controller to View) and put it in your Model like this
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public IEnumerable<SelectListItem> TaskTypeSelectListItems { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
And the dropdown code will changed to
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TaskTypeID", model => model.TaskTypeSelectListItems, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
Notice that the selected value will bind to "TaskTypeID" on your model when you post/get to the server.
Per your request: So the ViewBag is a property that enables you to dynamically share values from your controller to the view. Since it is dynamic, keep in mind that you will need to explicitly cast objects so the razor engine knows what they are at runtime. Note that upon reading about it (further reading at bottom), ViewBag is short lived and only lives within the current request so in the event that you have a model state error in your controller, you will need to reload the data into your viewbag.
This will help keep your models/view models very clean.
So on to the code, Test setup: I have my main page (Index)and I have a partial view (AddSomething) that is loaded in my main Index page. Given your models, here is what my controller looks like. Please Note that I am loading up the viewbag in my parent page initially but if an error occurs on my submit then I need to reload my viewbag since the dynamic object is short lived.
DALHelper dalHelp = new DALHelper();
public ActionResult Index()
{
//Get a list of task types from the data access layer
//Cast them into a SelectItemList and pass them into viewBag
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View();
}
public ActionResult AddSomething()
{
TimeEntry te = new TimeEntry();
return View(te);
}
[HttpPost]
public ActionResult AddSomething(TimeEntry te)
{
if (ModelState.IsValid)
{
//call my Data Access Layer to add new entry and redirect to wherver on success
if (dalHelp.CreateTimeEntry(te))
{
return RedirectToAction("Index");
}
else
{
//something happened during adding entry into DB, write whatever code to handle it gracefully
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
ModelState.AddModelError("TimeEntryID", "An Error was encountered...");
return View(te);
}
}
else
{
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View(te);
}
}
In my partial View I need to cast my viewbag item since it is a dynamic type so the razor engine can process: Also not that I am using Dropdownlistfor unstead of just dropdownlist:
#model deletemeweb2.Models.TimeEntry
#using (Html.BeginForm("AddSomething", "Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TimeEntry</h4>
#Html.ValidationMessageFor(model => model.TimeEntryID, "", new { #class = "text-danger" })
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.TaskTypeID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TaskTypeID, (IEnumerable<SelectListItem>)ViewBag.TaskDD, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TaskTypeID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MonHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MonHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.MonHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TueHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TueHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TueHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WedHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WedHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WedHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ThuHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ThuHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ThuHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FriHours, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FriHours, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FriHours, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Further reading about ViewBag and like properties when dealing with different controller needs: http://www.c-sharpcorner.com/blogs/viewdata-vs-viewbag-vs-tempdata-in-mvc1
I am developing an web application (Food blog) in ASP.NET MVC where I want to allow the user to type in a Recipe. A recipe does of course contains a lot of ingredients.
I have the following classes:
public class Recipe
{
public string Name { get; set; }
public int ID { get; set; }
public string Auhtor { get; set; }
public string Category { get; set; }
public string Text { get; set; }
public List<Ingredient> Ingredients { get; set; }
public byte[] Picture { get; set; }
}
public class Ingredient
{
public int ID { get; set; }
public double Amount { get; set; }
public string Unit { get; set; }
public string Name { get; set; }
}
And in html i would like to make a page where the user can type in a recipe with a lot of ingredients.
The create-view so far looks like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Recipe</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Category, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Category, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Category, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Text, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Text, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Text, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="Ingredient">
#Html.LabelFor(model => model.Ingredients, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10" id="IngredientDiv">
<label>Ingredient</label>
</div>
</div>
<div class="AddEl">
<p><a>Tilføj endnu en ingredients</a></p>
</div>
<div class="RemoveEl">
<p><a>Fjern en ingredients</a></p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Picture, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Picture, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Picture, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
<script type="text/javascript" src="/Scripts/jquery-1.10.2.js"></script>
<script>
//Ajax to navigate between days
$(document)
.ready(function () {
var counter = 1;
$('.AddEl').click(function () {
var el = $('#IngredientDiv').clone().attr('id', 'element_' + ++counter).appendTo('#Ingredient');
});
$('.RemoveEl').click(function () {
var el = $(this).parents('.elem');
if (counter > 1) {
el.remove();
counter--;
}
});
});
</script>
</div>
}
How can I link the Ingredients to this view?
AS you can see I have already written the code to dynamically add and remove elements of the ingredient-div.
Thanks for your help!
Not sure how you want to show List<Ingredient> in your view but you can have List<Ingredient> by having view.bag in your controller:
ViewBag.Ingredients= new SelectList(db.GetIngredient(), "Value", "Text");
and in your view you could have :
#Html.DropDownListFor(model => model.Ingredients, new SelectList(ViewBag.Ingredients, "Value", "Text")
Probably you need more that one #Html.DropDownListFor and you can keep the first DropDownListFor visibale and the rest invisible. Then as soon as user adds one Ingredient you can make the next DropDownListFor visible to let user add another Ingredient.
First of all you need to make user there is relationship between tables. Add new attribute in Recipe Class:
public int IngredientsID;
Then need to encapsulate both of your entity classes in a View Model. You can accomplish same thing with view bag but using view model is the best practice. Create view model like this:
public class FoodViewModel {
public Recipe Recipe {get; set;}
public IEnumerable<Ingredient> Ingredients {get; set;}
}
Retrieve the list of Ingredients from context and supply it to your view model and pass your view model to your view .cshtml
public ActionResult New()
{
var _list = _context.Ingredients.ToList();
var viewModel = new FoodViewMode
{
Ingredients = _list
};
return View(viewModel);
}
Now, strongly type your view .cshtml with FoodViewModel.
#model Models.FoodViewModel
You will need to make some changes in your view .cshtml since your view model contains two entity models. You will need to use fully qualified name here. e.g.
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
Will turn into:
#Html.LabelFor(model => model.Receipe.Name, htmlAttributes: new { #class = "control-label col-md-2" })
Instead of "model.Name", we are using "model.Receipe.Name".
Now to get drop down list you can do:
#Html.DropDownListFor(x => x.Recipe.IngredientsID, new SelectList(Model.Ingredients, "ID", "Name"));
I hope it will solve your problem.