referencing a list<Model> in a looping ParialView - asp.net-mvc

I have been trying to loop partial views, which share the same model as the main view. Basically the idea is that there is a registration page where a parent can register multiple children, the children info is in partial view so that i can add multiple instances of it on the main view.
I can get the partial view to display multiple times but it only saves the first student. if i replace the partial view (#Html.Partial...) with #Html.EditorFor(f => f.student[i].Person.FirstName) in the main view then it works fine and i can add multiple textboxes and save multiple students
how can i use the partial views and be able to pass in the ParentModel and correctly reference it?
hope all this makes sense... any help is appreciated. thanks!
Model:
public partial class Person()
{
public string FirstName { get; set; }
}
public partial class Student
{
public int Student_PersonID { get; set; }
public int Father_PersonID { get; set; }
public virtual Person Father { get; set; }
public virtual Person Person { get; set; }
}
public class ParentModel
{
public List<Student> student { get; set; }
}
Main View
#model WebPortal.Models.ParentModel
<div>
#for (int i = 0; i < cnt; i++)
{
#Html.Partial("_StudentPartial", Model, new ViewDataDictionary { { "loopIndex", i.ToString() } });
}
</div>
<div>
#Html.EditorFor(f => f.student[0].Father.FirstName)
</div>
Partial View (_StudentPartial)
#model WebPortal.Models.ParentModel
#{
int i = Convert.ToInt32(ViewData["loopIndex"].ToString());
}
#using (Html.BeginForm())
{
<div class="editor-field">
#Html.EditorFor(m => m.student[i].Person.FirstName)
</div>
}
Controller
public ActionResult Register(ParentModel pm)
{
using (var db = new SMEntities())
{
for (int i = 0; i < pm.student.Count; i++)
{
if (i != 0)
{
pm.student[i].Father = pm.student[0].Father;
}
db.Students.Add(pm.student[i]);
}
db.SaveChanges();
}
}

A better way to structure things will be like this :
Type the _StudentPartial View to Student like this -
#model WebPortal.Models.Student
#using (Html.BeginForm())
{
<div class="editor-field">
#Html.EditorFor(m => m.Person.FirstName)
</div>
}
Then model your main view like this :
#model WebPortal.Models.ParentModel
<div>
#for (int i = 0; i < cnt; i++)
{
#Html.Partial("_StudentPartial", Model.student[i]);
}
</div>
<div>
#Html.EditorFor(f => f.student[0].Father.FirstName)
</div>
Try to use this as a guideline : Give to the view only the information it needs, not more.

Related

How to post view model to controller

I am trying to create a view that takes a view model, simply has an int property and a list of child model, with int; string; string properties.
What i am trying to do is everytime user clicks an "add line" button, i am creating a new child model in my view model list, letting user edit the properties and then want to post back to my controller but no matter what, it is always empty.
I know this must be simple but has me stumped for hours today? Is there some magic that needs to happen?
Any help appreciated.
My Controller:
using System.Web.Mvc;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
public class EmployeeUploadController : Controller
{
// GET: EmployeeUpload
public ActionResult EmployeeUpload()
{
var vm = new EmployeeBulkUpload { UploadedBy = 12345 };
vm.Employees.Add(new EmployeeBulkUploadItem { Id = 1, JobTitle = "Labourer", Name = "Jimmy" });
return View(vm);
}
[HttpPost]
[ActionName("EmployeeUpload")]
public ActionResult EmployeeUploadPost(EmployeeBulkUpload vm)
{
return View(vm);
}
}
}
My Model:
using System.Collections.Generic;
namespace WebApplication1.Models
{
public class EmployeeBulkUpload
{
public EmployeeBulkUpload()
{
Employees = new List<EmployeeBulkUploadItem>();
}
public int UploadedBy { get; set; }
public List<EmployeeBulkUploadItem> Employees { get; set; }
}
public class EmployeeBulkUploadItem
{
public int Id { get; set; }
public string Name { get; set; }
public string JobTitle { get; set; }
}
}
My "Container" view:
#using WebApplication1.Models
#model WebApplication1.Models.EmployeeBulkUpload
#{
ViewBag.Title = "EmployeeUpload";
}<div>
#using (Html.BeginForm("EmployeeUpload", "EmployeeUpload", FormMethod.Post))
{
<h2>EmployeeUpload</h2>
<button>Add New</button>
foreach(EmployeeBulkUploadItem emp in Model.Employees)
{
Html.RenderPartial("_EmployeeUploadItem", emp);
}
<input type="submit" name="SaveButton" value="Save" />
}
</div>
My Item Partial view:
#model WebApplication1.Models.EmployeeBulkUploadItem
<li>
#Html.LabelFor(x => x.Id)
#Html.EditorFor(x => x.Id)
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.LabelFor(x => x.JobTitle)
#Html.EditorFor(x => x.JobTitle)
</li>
I can load with one record and post straight back but is always empty in my Controller EmployeeUploadPost method?

MVC Model Binding EF 6 Missing Parent Reference

I have an object with a collection. I am passing the model to the view and upon post the collection is there, however the reference (containing object Id) is not there and the navigation property is null.
Models
public class Order
{
public int OrderId { get; set;}
public List<OrderItem> OrderItems { get; set;}
}
public class OrderItem
{
public int OrderItemId { get; set; }
public int OrderId { get; set; }
public int SomeOtherValue { get; set; }
}
View:
#model Order
<h2>Edit Form</h2>
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.OrderId)
//Some other form fields for the order...
#for (int i = 0; i < Model.OrderItems.Count; i++)
{
#Html.TextBoxFor(x => Model.OrderItems[i].SomeOtherValue)
}
<input type="submit" value="Submit"/>
}
When I submit the form, the model gets bound correctly and the while debugging I can see the child OrderItems populated with updated information. However, the OrderId on each OrderItem is set to 0.
Controller
public ActionResult Edit(Order myOrder)
{
myOrder.OrderItems.Count(); // These are populated and the edited values are there.
myOrder.OrderItem[0].Order; // this is null (all order items)
myOrder.OrderItem[0].OrderId; // this is set to 0 (all order items)
//...more stuff and return view.
}
What is the best way to get around this problem? Is there some way to ensure that each OrderItem has a reference to the order when posting the form? I'm hoping I can avoid having to manually account for association in the controller when trying to save the object.
You should add hidden inputs for those fields. Check the code bolow:
..
#for (int i = 0; i < Model.OrderItems.Count; i++)
{
#Html.HiddenFor(x => Model.OrderItems[i].OrderId)
#Html.HiddenFor(x => Model.OrderItems[i].Order)
#Html.TextBoxFor(x => Model.OrderItems[i].SomeOtherValue)
}
..

mvc 4 complex list returning null

In other parts of my web site I'm using nested lists and am able to display and save without problem.
However, I can't seem to figure out why it is not working this time.
ViewModel
public class ActivityEditViewModel
{
//Activity
public int ActivityID { get; set; }
//... more properties
public List<ActivityService> ActivityServices { get; set; }
public class ActivityService
{
public int ServiceID { get; set; }
[DisplayName("Outcome Comment")]
public string OutcomeComment { get; set; }
//... more properties
}
}
View (Relevant part)
#for (int i = 0; i < Model.ActivityServices.Count; i++)
{
#Html.HiddenFor(x => x.ActivityServices[i].ServiceID)
#Html.LabelFor(x => Model.ActivityServices[i].OutcomeComment)
#Html.TextBoxFor(x => Model.ActivityServices[i].OutcomeComment)
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModels.ActivityEditViewModel vm)
{
//vm.ActivityServices <== is always null
Question
Why does vm.ActivityServices return null?
You can create a partial view for ActivityService and render this in foreach:
In your actual view:
#foreach(ActivityService item in Model.ActivityServices)
{
#Html.Partial("ActivityService", item)
}
In a new partial view ActivityService.cshtml:
#model Proyect.Models.ActivityEditViewModel.ActivityService
#Html.HiddenFor(m => m.ServiceID)
#Html.LabelFor(m => m.OutcomeComment)
#Html.TextBoxFor(m => m.OutcomeComment)
So your design Razor syntax must be as below:
You could first remove all your skipping elements by adding a line of code to remove and then use it normally to obtain desired record.
#
{
Model.ActivityServices.RemoveAll(o=> //your skip condition);
for (int i = 0; i < Model.ActivityServices.Count; i++)
{
#Html.HiddenFor(x => Model.ActivityServices[i].ServiceID)
#Html.LabelFor(x => Model.ActivityServices[i].OutcomeComment)
#Html.TextBoxFor(x => Model.ActivityServices[i].OutcomeComment)
}
}

Retrieve an array in viewmodel

I'm trying to create a page for editing my attachments.
The Attachment Model:
public class Attachment
{
...
private IList<JSONI18NText> titles = new List<JSONI18NText>();
private IList<JSONI18NText> descriptions= new List<JSONI18NText>();
...
public virtual IList<JSONI18NText> Titles
{
get { return titles; }
set { this.titles = value; }
}
public virtual IList<JSONI18NText> Descriptions
{
get { return descriptions; }
set { this.descriptions= value; }
}
The JSONI18NText Model:
public class JSONI18NText
{
public int LanguageId { get; set; }
public string Text { get; set; }
}
The Attachment ViewModel:
public class AttachmentModel
{
public AttachmentModel() { }
public AttachmentModel(Attachment at) {
...
this.Titles = new List<JSONI18NTextModel>();
this.Descriptions = new List<JSONI18NTextModel>();
foreach (JSONI18NText title in at.Titles)
{
this.Titles.Add(new JSONI18NTextModel(title, "Title"));
}
foreach (JSONI18NText description in at.Descriptions)
{
this.Descriptions.Add(new JSONI18NTextModel(description, "Description"));
}
}
[Display(Name = "Title", Description = "Title of the file")]
public IList<JSONI18NTextModel> Titles { get; set; }
[Display(Name = "Description", Description = "Description of the attachment file")]
[DataType(DataType.MultilineText)]
public IList<JSONI18NTextModel> Descriptions { get; set; }
The JSONI18NText ViewModel:
public class JSONI18NTextModel
{
public JSONI18NTextModel() { }
public JSONI18NTextModel(JSONI18NText jsonI18nText)
{
this.LanguageId = jsonI18nText.LanguageId;
this.Text = jsonI18nText.Text;
}
[HiddenInput(DisplayValue = false)]
public int LanguageId { get; set; }
public string Text { get; set; }
}
Now, what I'm trying to achieve is an edit form with a tabbed list for the languages: for example two tabs, one for English and one for Italian, if you click on each tab you read the input value of title and description for that particular language.
Everything works like a charm: I used a view script with two partial views, one for the lists and another for the JSONI18NTextModel:
Edit.cshtml:
...
#Html.EditorFor(model => model.Titles, "EditLabels")
#Html.EditorFor(model => model.Descriptions, "EditLabels")
...
EditLabels.cshtml:
#model List<CR2.Web.Areas.Admin.Models.JSONI18NTextModel>
#using CR2.Web.Infrastructure
#using CR2.Web.Areas.Admin.Models
#if(Model.Count() == 1)
{
#Html.EditorFor(model => model[0], "EditLabel");
}
else
{
for(int i = 0; i < Model.Count(); ++i)
{
<div>
<ul>
#Html.EditorFor(model => model[i], "EditLabel")
</ul>
</div>
}
}
EditLabel.cshtml:
#model CR2.Web.Areas.Admin.Models.JSONI18NTextModel
#using CR2.Web.Infrastructure
<li>
#Html.HiddenFor(model => model.LanguageId)
<div>
#Html.LabelWithTooltip(model => model.Text)
</div>
<div>
#Html.EditorFor(model => model.Text)
#Html.ValidationMessageFor(model => model.Text)
</div>
</li>
When I render it, it builds fields with names like "Titles.[0].Text", which is perfect I think...
The problem comes when I submit the form: "Titles" and "Descriptions" aren't populated in the AttachmentModel...(everything else is populated)
Any ideas? What am I doing wrong?
Thanks a lot!!!
Iterating over the list and populating each value should be something like:
#foreach (JSONI18NTextModel item in model)
{
Html.EditorFor(i => item.Titles, "EditLabels");
Html.EditorFor(i => item.Descriptions, "EditLabels");
}
Then I assume you are posting only one model to the controller action:
[HttpPost]
public ActionResult Edit(JSONI18NTextModel model)
{
// The only Model you are posting should be accessible here.
}
If you have a list of models on your page, then you may want to create a Details view where you access only one model at a time and you post only that model to the controller.

What is a better way to display data from multiple view models

I have two view models
public class DataImportViewModel
{
public WccrViewModel wccrVM { get; set; }
public string Status { get; set; }
public string ValidationResult { get; set; }
…
}
public class WccrViewModel
{
public double? WccrId { get; set; }
public string WccrDesc { get; set; }
}
and my view
#model List<Mass.ViewModels.DataImportViewModel>
#using (Html.BeginForm("Validation", "DataImport", FormMethod.Post))
{
<input id="ProcessReshop" type="submit" value="Reshop" />
for (int i = 0; i < 1; i++)
{
<div class="display-field">
#Html.DisplayFor(model=>model[i].wccrVM.WccrId)
</div>
<div class="display-field">
#Html.DisplayFor(model => model[i].wccrVM.WccrId)
</div>
}
A table (not shown) is populated with the DataImportViewModel. I want to have the WccrViewModel displayed on top of the Table. I want to be able to access the WccrViewModel without the for loop...but it shows as a good example. How can I use wccrVM.WccrId without the for loop? Thanks in advance...cheers
How about another viewmodel class that contains both a DataImportViewModel and a WccrViewModel?
Your viewmodel should be tailored to the view, and if the view calls for both those objects, why not create a view model with what it expects?

Resources