Cannot use indexing in view because of IEnumerable in Model? - asp.net-mvc

In my Model I have IEnumerable, and due to this I cannot use for loop in view. If I use foreach the Html generated doesn't have indexing, which is what I need. How do I solve this problem.
I'm trying to use same view model to create and edit and I'm having problem in the edit part.
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
public long Id { get; set; }
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
public string KindName { get; set; }
public string TypeName { get; set; }
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public IEnumerable<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is my view where I have used both for and foreach, but I have commented for as I cannot use indexing in it currently
#*
#for (int i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Value</label>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Properties[i].Value, new { #value = Model.Properties[i].Value })</div>
}
*#
#foreach (var properties in Model.Properties)
{
<div class="label">#properties.Name</div>
<div class="input-block-level">#Html.TextBoxFor(model => properties.Value, new { #value = properties.Value })</div>
<br/>
}
Currently Html.Textbox for generates following two names which doesn't have indexing in it
properties.value
because of this the values that are submitted during the post are Null. If I use for loop then I think my properties.value will change to properties[0].value and properties[1].value??? I'm not sure, but that is what I'm trying to achieve.
If I change my model from
public IEnumerable<Property> Properties { get; set; }
to
public List<Property> Properties { get; set; }
then my following method in repository would not work as it reutrns IQueryable for value properties, and casting from IQueryable to List would not be possible.
public CreateModule GetModuleDetails(long id)
{
var module = (_dbSis.Modules.Where(t => t.Id == id).Select(m => new CreateModule
{
Id = id,
ModuleId = m.ModuleId,
TypeName = m.ModuleType.TypeName,
KindName = m.ModuleType.ModuleKind.KindName,
Properties = m.PropertyConfiguration.PropertyInstances.Select(
x => new Property { Name = x.Property.Name, Value = x.Value })
}));
return (module.FirstOrDefault());
}

Create a partial view like below, name it Property.cshtml, and put it under Views/Shared/EditorTemplates.
#model MyApp.Models.Property
<div class="label">#Model.Name</div>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Value)</div>
<br/>
Then, replace the foreach loop in your View with this:
#EditorFor(model => model.Properties)

Related

How to query an array of integers ​in an integer collection in ASP.NET Core 3.1?

I would like to query an integer collection. For now, I can only filter by the first value in the array of integers. How can I query that includes all values ​​in the integer array for a blog tag id collection?
//list --> my blog posts variable
....
if (f.blogTagIds != null)
{
list = list.Where(p => p.BlogTagRelation.Select(p => p.BlogTagId).Contains(f.blogTagIds[0]));
}
....
SearchFilterClass
public class SearchFilterType
{
...
public int[] blogTagIds { get; set; }
...
}
ViewBag BlogTags
var BlogTags = new List<SelectListItem>();
foreach (var item in _uow.BlogTag.GetAllByEnabledDate(null, _uow.Cookie.GetAdminLangId, _uow.Cookie.GetAdminWebSiteId))
{
BlogTags.Add(new SelectListItem
{
Text = item.Title,
Value = item.Id.ToString()
});
}
ViewBag.BlogTags = BlogTags;
Filter Form BlogTagIds Select Control
<select asp-for="f.blogTagIds" class="form-control" multiple="multiple" asp-items="ViewBag.BlogTags"></select>
BlogTagRelation.cs
public partial class BlogTagRelation
{
public int Id { get; set; }
public int BlogId { get; set; }
public int BlogTagId { get; set; }
public virtual Blog Blog { get; set; }
public virtual BlogTag BlogTag { get; set; }
}
Example route
/Blog/list?f.blogTagIds=8&f.blogTagIds=6
Try:
var list =list.Where(x => blogTagIds.All(r => x.BlogTagRelation.Any(y => y.BlogTag.BlogTagId== r)));
make sure that All of the blogTagIds are contained in BlogTagRelation
result:
[I select user with special roleId]

Populate DropDownList in ASP.NET MVC from database table using Entity Framework 6 and ViewModel

I have been scratching my head for a whole night on an issue I can do quickly using ajax/jquery and stored procedures. I want to
1) Populate a drop down list from values obtained from a database table using Entity Framework and view model. I DO NOT WANT TO USE VIEWBAG OR VIEWDATA. Any help appreciated.
2) How can I generate a Create View using the View Model with the all the default fields ? The scaffholding works on a model but not on a view model ?
MY MODELS
public class Employee
{
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string City { get; set; }
public string Level { get; set; }
public int DepartmentId { get; set; }
}
public class Grade
{
public int ID { get; set; }
public string Level { get; set; }
}
View Model
public class GradeSelectListViewModel
{
public Employee Employee { get; set; }
public IEnumerable<SelectListItem> Grades { get; set; }
public GradeSelectListViewModel(Employee employee, IEnumerable grades)
{
Employee = employee;
Grades = new SelectList(grades, "Grade", "Name", employee.Level);
}
}
MY CONTEXT CLASS
public class EmployeeContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Grade> Grades { get; set; }
}
MY CONTROLLER
public ActionResult Edit (int? id)
{
using (var db = new EmployeeContext())
{
var model = new GradeSelectListViewModel(db.Employees.Find(id), db.Grades);
//model.Employee = db.Employees.Single(x => x.EmployeeID == id);
model.Grades = db.Grades.ToList().Select(x => new SelectListItem
{
Value = x.ID.ToString(),
Text = x.Level
});
return View(model);
}
}
MY RAZOR PAGE CSHTML
#model MVCDemo.ViewModels.GradeSelectListViewModel
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
....
#Html.DropDownListFor(x => Model.Employee.Level,
new SelectList(Model.Grades, "ID", "Level"),
"Select Level")
....
<input type="submit" value="Create" class="btn btn-default" />
}
The main issue is that in the view you have new SelectList(Model.Grades, "ID", "Level") but Grades is IEnumerable<SelectListItem> and SelectListItem does not contain properties named ID and Level.
However there are a a few other issues with your code. First a view model should not contain a data model, and instead your view model should be
public class GradeSelectListViewModel
{
public int? ID { get; set; } // make this ID so you do not need an input for it
public string Name { get; set; }
.... // other properties of Employee that your editing
[Required(ErrorMessage = "..")]
public int? Level { get; set; } // make value types nullable to protect against under-posting attacks
public IEnumerable<SelectListItem> Grades { get; set; }
}
and add display and validation attributes as required. Note that I deleted the constructor (you don't seem to be using it, but if you did, then you also need to include a parameter-less constructor, otherwise an exception will be thrown when submitting to the POST method. I also assume that Level should be typeof int since you binding to the int ID property of Grade.
The the code in your GET method should be
Employee employee = db.Employees.Find(id);
var model = new GradeSelectListViewModel()
{
ID = employee.EmployeeID,
Name = employee.Name,
Level = employee.Level, // convert to int?
....
Grades = db.Grades.Select(x => new SelectListItem
{
Value = x.ID.ToString(),
Text = x.Level
})
};
return View(model);
and in the view
#Html.DropDownListFor(x => Model.Level, Model.Grades, "Select Level")
Note also that in the POST method, your need to reassign the SelectList if you return the view because ModelState is invalid.
You can use the following approach that populates three DropDownListFor at the same View:
ViewModel:
public class GroupViewModel
{
public IEnumerable<SelectListItem> Schedules { get; set; }
public int ScheduleId { get; set; }
public IEnumerable<SelectListItem> Labs { get; set; }
public int LabId { get; set; }
public IEnumerable<SelectListItem> Terms { get; set; }
public int TermId { get; set; }
}
Controller:
public ActionResult Create()
{
//Populate DropDownList binding values
var model = new GroupViewModel
{
//Preselect the Lab with id 2
//LabId = 2,
Labs = repository.Labs.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
}),
Terms = repository.Terms.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
}),
Schedules = repository.Schedules.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
})
};
return View("Create", model);
}
View:
#Html.DropDownListFor(m => m.LabId, new SelectList(Model.Labs, "Value", "Text"),
"Select", new { #class = "selectpicker" })
#Html.DropDownListFor(m => m.ScheduleId, new SelectList(Model.Schedules, "Value", "Text"),
"Select", new { #class = "selectpicker" })
#Html.DropDownListFor(m => m.TermId, new SelectList(Model.Terms, "Value", "Text"),
"Select", new { #class = "selectpicker" })

#Html.Editor Model binding Issue

I am trying to generate Two sets of List of checkboxes on a view. It all working apart from Post action. On submit,
ParentViewModel is not binding the ChildViewModel properly
Model. FirstCheckboxList
Model. SecondCheckboxList
Above both are coming as null.
I am not sure what I am missing. Any help on this would be great.
Thanks in advance.
CheckboxItems.cshtml
#model List<CheckboxItem>
#{
for (int i = 0; i < Model.Count(); i++)
{
<div>
#Html.CheckBoxFor(x => x.ElementAt(i).Checked, new { #id = Model.ElementAt(i).Id, onclick = "GetValue()" })
<span id="Padded">#Model.ElementAt(i).Text</span>
</div>
}
}
MainView.cshtml
#Html.BeginForm(){
#Html.EditorFor(m=> m.FirstCheckboxList,"CheckboxItems")
#Html.EditorFor(m=> m.SecondCheckboxList, "CheckboxItems")
}
#Html.TextBoxFor(m => m.FSelected, new Dictionary<string,object>() {{"readonly",true}})
#Html.TextBoxFor(m => m.FUniverse,new Dictionary<string,object>() {{"readonly",true}})
<input type="submit" name="nextBtn" value ="Next" />
}
ParentViewModel
public class ParentViewModel
{
public int PId { get; set; }
public IEnumerable<CheckboxItem> FirstCheckboxList{ get; set; }
public IEnumerable<CheckboxItem> SecondCheckboxList{ get; set; }
public Int64 FSelected { get; set; }
public Int64 FUniverse { get; set; }
}
CheckboxItem : child view model
public class CheckboxItem
{
public int Id { get; set; }
public string Text { get; set; }
public bool Checked { get; set; }
}
controller action
[HttpPost]
public ActionResult MyCheckboxView(int planid, ParentViewModel model, string nextBtn)
{
// do something
return View(Model);
}
Try changing your viewmodel for the ParentViewModel to use a List<CheckboxItem> instead of an IEnumerable<CheckboxItem>:
public class ParentViewModel
{
public int PlanId { get; set; }
public List<CheckboxItem> FirstCheckboxList{ get; set; }
public List<CheckboxItem> SecondCheckboxList{ get; set; }
public Int64 FSelected { get; set; }
public Int64 FUniverse { get; set; }
}
The model binder needs a data structure like a List or an Array so that it can correctly bind elements at specified indexes. IEnumerable is just an interface and doesn't support indexes like this.
Edit
Also, as a side-note, you don't have to bother with the for loop in your EditorTemplate because MVC can do all this for you. Just change the model type to be #model CheckboxItem, remove the loop and get rid of the id attribute so it looks like this:
#model CheckboxItem
#{
<div>
#Html.CheckBoxFor(x => x.Checked, new { onclick = "GetSelectedFrame()" })
<span id="Padded">#Model.Text</span>
</div>
}
}
Also, make sure your EditorFor call doesn't supply the EditorTemplate's name, as this messes up the "MVC Magic" (see this question which explains that it automatically iterates the list without the template name and doesn't with the template name):
#Html.BeginForm(){
#Html.EditorFor(m=> m.FirstCheckboxList)
#Html.EditorFor(m=> m.SecondCheckboxList)
}

[ask]asp.net mvc update database error

I have two model classes in one-to-one relationship:
class Person
{
public int PersonID { get; set; }
public int DetailPersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DetailPerson DetailPerson { get; set; }
}
class DetailPerson
{
public int DetailPersonID { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
and the code for the edit page view:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.PersonID)
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)
#Html.LabelFor(m => m.DetailPerson.Address)
#Html.TextBoxFor(m => m.DetailPerson.Address)
#Html.LabelFor(m => m.DetailPerson.PhoneNumber)
#Html.TextBoxFor(m => m.DetailPerson.PhoneNumber)
<input type="submit" value="Edit">
}
The EF scaffold uses this code to update data:
db.Entry(person).State = System.Data.EntityState.Modified;
db.saveChanges();
When I submit the edit form, I got an error like this:
A foreign key value cannot be inserted because a corresponding primary key value does not exist. [ Foreign key constraint name = FK_dbo.People_dbo.DetailPersons_DetailPersonID ]
But if I do this:
Person p = db.Persons.Find(person.PersonID);
p.DetailPerson = person.DetailPerson;
p.FirstName = person.FirstName;
p.LastName = person.LastName;
db.saveChanges();
update data success without error
I want to know why the first way causse an error,
when I set the breakpoint at the line containing EntityState.Modified,
but the foreign key value ( DetailPersonID ) is 0.
Then, I added #Html.HiddenFor(m => m.DetailPersonID) on the edit form.
I got another error:
A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
I still update database on the other way,
I am just curious why the first way which is EF standart to update data got an error.
You shouldn't use two classes if there is a one-to-one relationship since EF will normalize the db into the corresponding form anyway. Combine your classes like this.
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
if you must have separate classes (not recommended), do it like this:
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DetailPersonID { get; set; }
public virtual DetailPerson DetailPerson { get; set; }
}
public class DetailPerson
{
public int PersonID { get; set; }
public virtual Person Person { get; set; }
public int DetailPersonID { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
Then when you retrieve your object, do it like this:
// include the detail when retrieving the parent
Person person = db.People.Include(p=>p.DetailPerson).Single(p=>p.PersonId == whateverIdYou Need);
// modify whatever you like
person.DetailPerson.Address = "my new address";
// then your previous statement will work
db.Entry(person).State = System.Data.EntityState.Modified;
db.saveChanges();

using nested class within class doesnt works in mvc 3

Hi I have included nested class within a class to use it in a view but it doesnt show up the properties of that nested class below is the class, I want to use sku in the view :
View:
#model Nop.Web.Models.Catalog.CategoryModel
<div class="product-item">
<h2 class="product-title">
#Model.Name
</h2>
<div class="description">
**#Model.ProductVariantModels.Select(x => x.Sku)//doesnt works** // partial class productvariant
</div>
<div class="add-info">
#Model.Name <br/> #Model.FullDescription //values from class CategoryModel
</div>
</div>
Model:
public class CategoryModel : BaseEntityModel
{
public CategoryModel()
{
ProductVariantModels = new List<ProductVariantModel>();
}
public string Name { get; set; }
public string FullDescription { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public string MetaTitle { get; set; }
public IList<ProductVariantModel> ProductVariantModels { get; set; }
public class ProductVariantModel : BaseNopEntityModel
{
public string Name { get; set; }
public bool ShowSku { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
}
}
ProductVariantModels is a List. You have to enumerate the List.
#foreach (var pvModel in Model.ProductVariantModels) {
#pvModel.Sku
}
If you're using HtmlHelpers, you have to use a for loop with an index rather than a foreach loop.
As explained really well here, the automatic model binding expects field input names to have a 'dot' notation like Property.SubProperty.SubSub... to match the instance properties when assigning -- but if you render them in a foreach loop they won't have the full expression, and thus won't output the full 'dot' notation.
Also see MVC 4 binding nested list of lists result

Resources