BeginCollectionItem not binding - asp.net-mvc-5

I've previously used the Html.BeginCollectionItem() extension and had no issues with binding from view to controller. In my current project however I'm having issues.
The model
public class EditPathViewModel : TradeContractorEscalationPath
{
public Guid ContractorId { get; set; }
public List<ContactDisplayModel> Contacts { get; set; }
public List<TradeContractorEscalationPathContact> AssignedContacts { get; set; }
public int maxOrder { get; set; }
}
public partial class TradeContractorEscalationPath
{
public System.Guid Id { get; set; }
public System.Guid TradeContractorId { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<System.DateTime> UpdatedDate { get; set; }
}
I am trying to bind AssignedContacts back to the Controller, which looks like...
[HttpPost]
public async Task<ActionResult> EditPath([Bind(Include = "Id,TradeContractorId,Description,Notes,ContractorId,AssignedContacts,Contacts")] Models.EditPathViewModel model)
{
...
}
However, it constantly returns null. The BeginCollectionItem is configured like so:
Editor Template for TradeContractorEscalationPathContact
#model Telecare.POCO.TradeContractorEscalationPathContact
#Html.EditorFor(model => model.Order)
#Html.EditorFor(model => model.TradeContractorEscalationPathId)
#Html.EditorFor(model => model.ContactId)
The view where Assigned Contacts collection item is called
...
<div class="col-lg-6">
<div class="ibox-title">
<h5>Contacts</h5>
</div>
<div class="ibox-content">
#Html.EditorFor(model => model.AssignedContacts)
</div>
</div>
...
Example HTML from DOM when POST is called
...
<input name="AssignedContacts.index" class="ui-sortable-handle" type="hidden" value="a5895b40-2de5-4ed5-a3ed-137c047963e8" autocomplete="off">
<input name="AssignedContacts[a5895b40-2de5-4ed5-a3ed-137c047963e8].Order" class="text-box single-line ui-sortable-handle" id="AssignedContacts_a5895b40-2de5-4ed5-a3ed-137c047963e8__Order" type="number" value="1" data-val-required="The Order field is required." data-val="true" data-val-number="The field Order must be a number.">
<input name="AssignedContacts[a5895b40-2de5-4ed5-a3ed-137c047963e8].TradeContractorEscalationPathId" class="text-box single-line ui-sortable-handle" id="AssignedContacts_a5895b40-2de5-4ed5-a3ed-137c047963e8__TradeContractorEscalationPathId" type="text" value="0807186a-3bad-e811-9c42-8cae4cf36195" data-val-required="The Trade Contractor Path field is required." data-val="true">
<input name="AssignedContacts[a5895b40-2de5-4ed5-a3ed-137c047963e8].ContactId" class="text-box single-line ui-sortable-handle" id="AssignedContacts_a5895b40-2de5-4ed5-a3ed-137c047963e8__ContactId" type="text" value="1e09c3ae-42ac-e811-9c42-8cae4cf36195" data-val-required="The Contact field is required." data-val="true">
<input name="AssignedContacts.index" class="ui-sortable-handle" type="hidden" value="974a0c38-642b-40f1-a5bd-dbc4b880eaa2" autocomplete="off">
<input name="AssignedContacts[974a0c38-642b-40f1-a5bd-dbc4b880eaa2].Order" class="text-box single-line ui-sortable-handle" id="AssignedContacts_974a0c38-642b-40f1-a5bd-dbc4b880eaa2__Order" type="number" value="2" data-val-required="The Order field is required." data-val="true" data-val-number="The field Order must be a number.">
<input name="AssignedContacts[974a0c38-642b-40f1-a5bd-dbc4b880eaa2].TradeContractorEscalationPathId" class="text-box single-line ui-sortable-handle" id="AssignedContacts_974a0c38-642b-40f1-a5bd-dbc4b880eaa2__TradeContractorEscalationPathId" type="text" value="0807186a-3bad-e811-9c42-8cae4cf36195" data-val-required="The Trade Contractor Path field is required." data-val="true">
<input name="AssignedContacts[974a0c38-642b-40f1-a5bd-dbc4b880eaa2].ContactId" class="text-box single-line ui-sortable-handle" id="AssignedContacts_974a0c38-642b-40f1-a5bd-dbc4b880eaa2__ContactId" type="text" value="f9d8afdd-42ac-e811-9c42-8cae4cf36195" data-val-required="The Contact field is required." data-val="true">
<input name="AssignedContacts.index" type="hidden" value="75a5e8e5-82e1-4f58-b185-ca6abefc5d9c" autocomplete="off">
<input name="AssignedContacts[75a5e8e5-82e1-4f58-b185-ca6abefc5d9c].Order" class="text-box single-line" id="AssignedContacts_75a5e8e5-82e1-4f58-b185-ca6abefc5d9c__Order" type="number" value="3" data-val-required="The Order field is required." data-val="true" data-val-number="The field Order must be a number.">
<input name="AssignedContacts[75a5e8e5-82e1-4f58-b185-ca6abefc5d9c].TradeContractorEscalationPathId" class="text-box single-line" id="AssignedContacts_75a5e8e5-82e1-4f58-b185-ca6abefc5d9c__TradeContractorEscalationPathId" type="text" value="0807186a-3bad-e811-9c42-8cae4cf36195" data-val-required="The Trade Contractor Path field is required." data-val="true">
<input name="AssignedContacts[75a5e8e5-82e1-4f58-b185-ca6abefc5d9c].ContactId" class="text-box single-line" id="AssignedContacts_75a5e8e5-82e1-4f58-b185-ca6abefc5d9c__ContactId" type="text" value="9ea0dcf8-42ac-e811-9c42-8cae4cf36195" data-val-required="The Contact field is required." data-val="true">
<input name="AssignedContacts.index" type="hidden" value="965b81be-f52c-4055-8b4b-bccf916f14a4" autocomplete="off">
<input name="AssignedContacts[965b81be-f52c-4055-8b4b-bccf916f14a4].Order" class="text-box single-line" id="AssignedContacts_965b81be-f52c-4055-8b4b-bccf916f14a4__Order" type="number" value="4" data-val-required="The Order field is required." data-val="true" data-val-number="The field Order must be a number.">
<input name="AssignedContacts[965b81be-f52c-4055-8b4b-bccf916f14a4].TradeContractorEscalationPathId" class="text-box single-line" id="AssignedContacts_965b81be-f52c-4055-8b4b-bccf916f14a4__TradeContractorEscalationPathId" type="text" value="0807186a-3bad-e811-9c42-8cae4cf36195" data-val-required="The Trade Contractor Path field is required." data-val="true">
<input name="AssignedContacts[965b81be-f52c-4055-8b4b-bccf916f14a4].ContactId" class="text-box single-line" id="AssignedContacts_965b81be-f52c-4055-8b4b-bccf916f14a4__ContactId" type="text" value="fc7a9669-43ac-e811-9c42-8cae4cf36195" data-val-required="The Contact field is required." data-val="true">
...
From all the threads I've read - the HTML looks like it should correctly bind so I must be missing something or doing something wrong elsewhere? Does anyone have any ideas?
Thanks in advance

OK - turns out I'm an idiot. The code above does indeed work but it wasn't binding because it wasn't within the #Html.BeginForm()) section. Sigh.

Related

Angular 13 - Data is accessible inside component, but does not show on the html page

So, I am learning Angular right now, and tried doing a CRUD Application. Almost everything works, except when trying to get data using ID, I can access all the data in the component.ts file, but the same is not rendered into html file. Here is what I have tried :-
edit-to.component.ts
export class EditTodoComponent implements OnInit {
private tid: number = 0;
public Todo:TodoModel= null ;
public desc:string='';
public title:string='';
public id:number;
constructor(private route: ActivatedRoute, private http:HttpClient) { }
ngOnInit(): void {
this.route.paramMap.subscribe(all_params =>{
this.tid = parseInt(all_params.get('id'))
//console.log( all_params.get('id'))
//console.log( this.tid );
});
this.getTodoFromId(this.tid)
}
getTodoFromId(id){
//console.log("id="+id)
this.http.get<{data: any}>('http://localhost:3000/api/todos/get_todo_from_id/'+id).subscribe((data) =>{
console.log("response in angular= \n");
this.Todo = data[0]
console.log(this.Todo.todo_title) //-> This one works
}
editOnSubmit(form:NgForm){
//something here
}
edit-todo.component.html
<div class="col-md-12">
<form (ngSubmit)="editOnSubmit(todoForm)" #todoForm="ngForm">
<input type="text" class="form-control" id="todo_id" [(ngModel)]="id" name="todo_id" value="1">
<div class="mb-3">
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title" value="{{ Todo.todo_title}}"> //-> This one does not work.
</div>
<div class="mb-3">
<label for="label" class="form-label">Description</label>
<textarea class="form-control" id="todo_description" [(ngModel)]="desc" name="desc" ></textarea>
</div>
<button type="submit" class="btn btn-success">Edit To Do</button>
</form>
</div>
</div>
Separating out,
from edit-to.component.ts :-
console.log(this.Todo.todo_title) works
but from edit-todo.component.html
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title" value="{{ Todo.todo_title}}"> does not work
it's because you have a ngModel and a value configured on same input field
in this situation the ngModel have priority on value attribute
to fix it you can initialize title in ngOnInit
this.title = this.Todo.todo_title;
in your case you wait reply from http request so the initialization after hte http reply :
getTodoFromId(id){
this.http.get<{data: any}>('http://localhost:3000/api/todos/get_todo_from_id/'+id).subscribe((data) =>{
console.log("response in angular= \n");
this.Todo = data[0]
this.title = this.Todo.todo_title;
console.log(this.Todo.todo_title);
}
Define a property of type BehaviorSubject.
isAvailableData = new BehaviorSubject<boolean>(true);
Then where you subscribe the data set it to true.
getTodoFromId(id){
//console.log("id="+id)
this.http.get<{data: any}>('http://localhost:3000/api/todos/get_todo_from_id/'+id).subscribe((data) =>{
console.log("response in angular= \n");
this.Todo = data[0];
this.isAvailableData.next(true);
console.log(this.Todo.todo_title) //-> This one works
}
after that in your HTML add ngIf to col-md-12 div:
<div class="col-md-12" *ngIf="(isAvailableData | async)">
<form (ngSubmit)="editOnSubmit(todoForm)" #todoForm="ngForm">
<input type="text" class="form-control" id="todo_id" [(ngModel)]="id" name="todo_id" value="1">
<div class="mb-3">
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title" value="{{ Todo.todo_title}}"> //-> This one does not work.
</div>
<div class="mb-3">
<label for="label" class="form-label">Description</label>
<textarea class="form-control" id="todo_description" [(ngModel)]="desc" name="desc" ></textarea>
</div>
<button type="submit" class="btn btn-success">Edit To Do</button>
</form>
Also you made a mistake in your form the input should be like below:
And there is no need to define a property for id, title, ...
These are the properties of todo object
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="Todo.todo_title" name="todo_title" #todo_title="ngModel">

Identifier 'fName' is not defined. The component declaration, template variable declarations, and element references do not contain such a member

How to solve this problem. I am using angular 8.
Error in Line number 85.
"addProduct(fName.value, lName.value, email.value,Password.value)"
I want to add fName, lName , email, Password on the MongoDB Atlas.
This problem is" Identifier 'fName' is not defined. The component declaration, template variable declarations, and element references do not contain such a member,
The identifier 'lName' is not defined. The component declaration, template variable declarations, and element references do not contain such a member,
The identifier 'email' is not defined. The component declaration, template variable declarations, and element references do not contain such a member,
Identifier 'password' is not defined. The component declaration, template variable declarations, and element references do not contain such a member.
" Generated
<body>
<div class="container p-3">
<div class="offset-3 col-6">
<div class="card">
<div class="card-header p-3 ">
<h3> Add User Form</h3>
</div>
<div class="card card-body">
<form [formGroup]="frmSignup" (submit)="addProduct()">
<div class="row ">
<div class="form-group col-md-6">
<label class="FontBold">First Name </label>
<!--Use class binding for validation-->
<input [class.is-invalid]="frmSignup.get('fName').invalid" type="text" class="form-control font-weight-bold"
formControlName="fName">
<label [class.d-none]="frmSignup.get('fName').valid" class="text-danger">First is
required</label>
</div>
<div class="form-group col-md-6">
<label class="FontBold">Last Name </label>
<!--Use class binding for validation-->
<input [class.is-invalid]="frmSignup.get('lName').invalid" type="text" class="form-control font-weight-bold"
formControlName="lName">
<label [class.d-none]="frmSignup.get('lName').valid" class="text-danger">Last is required</label>
</div>
</div>
<div class="form-group">
<label for="email" [ngClass]="frmSignup.controls['email']" class="FontBold">email Address</label>
<input id="email" formControlName="email" type="email" class="form-control font-weight-bold"
[ngClass]="frmSignup.controls['email'].invalid ? 'is-invalid' : ''">
<label class="text-danger" *ngIf="frmSignup.controls['email'].hasError('required')">
email is Required!
</label>
<label class="text-danger" *ngIf="frmSignup.controls['email'].hasError('email')">
Enter a valid email address!
</label>
</div>
<div class="form-group">
<label for="password" [ngClass]="frmSignup.controls['password']" class="FontBold">Password:</label>
<input id="password" formControlName="password" type="password" class="form-control font-weight-bold"
[ngClass]="frmSignup.controls['password'].invalid ? 'is-invalid' : ''">
<label class="text-danger " *ngIf="frmSignup.controls['password'].hasError('required')">
Password is Required!
</label>
<label class="FontBold">
(Note:- Password contain at least 8 characters,It Contain 1 number,1 Capital Case,1 Small Case,1 Special Character. )
</label>
</div>
<div class="form-group">
<label for="confirmPassword" [ngClass]="frmSignup.controls['confirmPassword']" class="FontBold">Confirm Password:</label>
<input id="confirmPassword" formControlName="confirmPassword" type="password" class="form-control font-weight-bold"
[ngClass]="frmSignup.controls['confirmPassword'].invalid ? 'is-invalid' : ''">
<label class="text-danger" *ngIf="frmSignup.controls['confirmPassword'].hasError('required')">
Password is Required!
</label>
<label class="text-danger" *ngIf="frmSignup.controls['confirmPassword'].hasError('NoPassswordMatch')">
Password do not match
</label>
</div>
<div class="form-group">
<button [disabled]="frmSignup.invalid"
(onClick)="addProduct(fName.value, lName.value, email.value,Password.value)"
type="submit" class="btn btn-primary btn-block font-weight-bold">Register</button>
</div>
<div class="form-group">
<button type="submit" class="btn btn-danger btn-block font-weight-bold" routerLinkActive="list-item-active" routerLink="/">Cancle</button>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
And This is the .ts file.
import { Component } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { CustomValidators } from 'src/app/modules/custom-validators';
#Component({
selector: 'app-add-user',
templateUrl: './add-user.component.html',
styleUrls: ['./add-user.component.css']
})
export class AddUserComponent {
public frmSignup: FormGroup;
constructor(private fb: FormBuilder) {
this.frmSignup = this.createSignupForm();
}
createSignupForm(): FormGroup {
return this.fb.group(
{
fName :['', [Validators.required, Validators.minLength(5)] ],
lName :['', [Validators.required, Validators.minLength(5)] ],
email: [
null,
Validators.compose([Validators.email, Validators.required])
],
password: [
null,
Validators.compose([
Validators.required,
// check whether the entered password has a number
CustomValidators.patternValidator(/\d/, {
hasNumber: true
}),
// check whether the entered password has upper case letter
CustomValidators.patternValidator(/[A-Z]/, {
hasCapitalCase: true
}),
// check whether the entered password has a lower case letter
CustomValidators.patternValidator(/[a-z]/, {
hasSmallCase: true
}),
// check whether the entered password has a special character
CustomValidators.patternValidator(
/[ !##$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/,
{
hasSpecialCharacters: true
}
),
Validators.minLength(8)
])
],
confirmPassword: [null, Validators.compose([Validators.required])]
},
{
// check whether our password and confirm password match
validator: CustomValidators.passwordMatchValidator
}
);
}
addProduct(fName, lName, email, Password) {
const obj = {
fName,
lName,
email,
Password
};
console.log(obj);
}
}
How about try to use getter and setter ?
In your case,,
get fName() { return this.frmSignup.get('fName'); }
then maybe can access form data in template.

Liferay 7: hidden aui input won't set value based on parameter

I have an entity with a primary key and two other fields.
I am able to display them in a Search Container in my primary View JSP, and I want to implement an edit/update function, so I created a different JSP for that. I pass the properties of the entity I wish to edit in portlet:renderURL portlet:param tags just like this:
<portlet:renderURL var="editEntity">
<portlet:param name="jspPage" value="/update-page.jsp" />
<portlet:param name="primaryKey" value="<%= entityId %>" />
<portlet:param name="name" value="<%= entityName%>" />
<portlet:param name="description" value="<%= entityDesc%>" />
</portlet:renderURL>
In the update-page JSP if I set any input field hidden, the parameter based values disappear, so the controller cannot process the fields' values.
i.e.:
<aui:input name="primaryKey" type="hidden" value="${primaryKey}" />
<aui:input name="primaryKey" type="hidden" value="${name}" />
<aui:input name="primaryKey" type="hidden" value="${description}" />
Note: I only want to hide the primary key field, the controller servlet should be able to process it and update my entity based on the primary key, like this:
<aui:input name="primaryKey" type="text" value="${name}" />
<aui:input name="primaryKey" type="text" value="${description}" />
The funny thing is, that everything just works when I set the input fields text type, but I wouldn't want the users to enter the primary key, duh...
Any ideas how could I fix this?
It's work for me
view.jsp
<%# include file="init.jsp" %>
<portlet:actionURL name="testURL" var="testURL" />
<aui:form name="fm" method="post" action="<%= testURL.toString()%>">
<aui:input name="primaryKey" type="hidden" value="123" />
<aui:button-row>
<aui:button name="submit" type="submit" value="OK" />
</aui:button-row>
</aui:form>
TestmvcportletPortlet.java
package com.example.portlet;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;
import com.liferay.portal.kernel.util.ParamUtil;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.Portlet;
import javax.portlet.ProcessAction;
import org.osgi.service.component.annotations.Component;
#Component(
immediate = true,
property = {
"com.liferay.portlet.display-category=category.sample",
"com.liferay.portlet.instanceable=true",
"javax.portlet.display-name=Test Portlet",
"javax.portlet.init-param.template-path=/",
"javax.portlet.init-param.view-template=/view.jsp",
"javax.portlet.resource-bundle=content.Language",
"javax.portlet.security-role-ref=power-user,user"
},
service = Portlet.class
)
public class TestmvcportletPortlet extends MVCPortlet {
#ProcessAction(name = "testURL")
public void addBook(ActionRequest actionRequest,ActionResponse actionResponse) throws SystemException {
String a = ParamUtil.getString(actionRequest, "primaryKey");
System.out.println("Value is "+a);
}
}
have you found anything that you missed code?
I found a solution to the problem.
So, after long hours of testing I found out that I just could not get the values stored in parameters just as ${paramName} anywhere in a simple HTML tag, but I still don't know why.
What I did was request for the needed values stored within the parameters inside a JSP scriptlet just like this:
<%
String primaryKey = request.getParameter("primaryKey");
String name = request.getParameter("name");
String description = request.getParameter("description");
%>
Then I was good to go with my form:
<aui:form action="<%= updateAbbreviationURL %>" method="post">
<aui:input name="primaryKey" type="hidden" value="<%= primaryKey %>" />
<aui:input name="entityName" label="Name" type="text" value="<%= name %>" />
<aui:input name="entityDesc" label="Description" type="text" value="<%= description %>" />
<aui:button name="submit" value="submit" type="submit" />
<aui:button name="cancel" value="cancel" type="button" onClick="<%= viewURL %>" />
</aui:form>
I'd be really grateful if someone told me why my initial implementation didn't work, I mean referring to the parameter values as mentioned above, ${paramName}
Thanks in advance!

ASP.Net validation not fireing for hidden fileds

I have 3 required fields on my page and 2 of them are hidden. The hidden fields values are filled by jQuery based off the selection of dropdown lists. If I just leave everything blank so that none of the fields are every filled in and hit the submit button the #Html.ValidationSummery() only lists an error for the 1 visible field. How can I get it to show errors when the hidden fields don't have a value as well?
The code for my hidden fields:
<li>
<label for="SelectedProjects">Selected Projects:</label>
<select size="1" id="SelectedProjects" name="SelectedProjects" multiple="multiple"></select> <button class="removeButton" data-codetype="Project" type="button">-</button>
#Html.EditorFor(m => m.SelectedProjectCodes)
</li>
<li>
<label for="SelectedTasks">Selected Tasks:</label>
<select size="1" multiple="multiple" id="SelectedTasks" name="SelectedTasks"></select> <button class="removeButton" data-codetype="Task" type="button">-</button>
#Html.EditorFor(m => m.SelectedTaskCodes)
</li>
The HTML rendered:
<li>
<label for="SelectedProjects">Selected Projects:</label>
<select size="1" id="SelectedProjects" name="SelectedProjects" multiple="multiple"></select> <button class="removeButton" data-codetype="Project" type="button">-</button>
<input data-val="true" data-val-required="The SelectedProjectCodes field is required." id="SelectedProjectCodes" name="SelectedProjectCodes" type="hidden" value="" />
</li>
<li>
<label for="SelectedTasks">Selected Tasks:</label>
<select size="1" multiple="multiple" id="SelectedTasks" name="SelectedTasks"></select> <button class="removeButton" data-codetype="Task" type="button">-</button>
<input data-val="true" data-val-required="The SelectedTaskCodes field is required." id="SelectedTaskCodes" name="SelectedTaskCodes" type="hidden" value="" />
</li>
The properties in the ViewModel backing it:
[HiddenInput(DisplayValue = false), Required]
public string SelectedProjectCodes { get; set; }
[HiddenInput(DisplayValue = false), Required]
public string SelectedTaskCodes { get; set; }
By default jQuery validation (which is what MVC uses) ignores hidden fields. You can adjust this by changing the default setting when the page loads. One way might be to add an class to the hidden fields to force them to be validated.
<li>
<label for="SelectedProjects">Selected Projects:</label>
<select size="1" id="SelectedProjects" name="SelectedProjects" multiple="multiple"></select> <button class="removeButton" data-codetype="Project" type="button">-</button>
<input class="force-validation" data-val="true" data-val-required="The SelectedProjectCodes field is required." id="SelectedProjectCodes" name="SelectedProjectCodes" type="hidden" value=""/>
</li>
the the javacript to change the defaults would look like this
jQuery.validator.setDefaults({
ignore: ":hidden:not(.force-validation)"
});

Fill model before calling "POST" method

I have the following model:
public class ProjectMemberUserRolesViewModel
{
public List<ProjectMemberUserRolesElementViewModel> Elements { get; set; }
}
public class ProjectMemberUserRolesElementViewModel
{
public string AccessType { get; set; }
public bool Delete { get; set; }
public bool Create { get; set; }
public bool Edit { get; set; }
public bool Read { get; set; }
}
following GET method:
public ActionResult UserRoles(int id)
{
ProjectMemberUserRolesViewModel model = new ProjectMemberUserRolesViewModel();
model.Elements = (from i in db.ProjectAccessTypes
select new ProjectMemberUserRolesElementViewModel()
{
AccessType = i.Type,
Create = i.ProjectMemberAccess.Where(p => p.ProjectMemberID == id).Select(p => p.Create).FirstOrDefault(),
Delete = i.ProjectMemberAccess.Where(p => p.ProjectMemberID == id).Select(p => p.Delete).FirstOrDefault(),
Edit = i.ProjectMemberAccess.Where(p => p.ProjectMemberID == id).Select(p => p.Edit).FirstOrDefault(),
Read = i.ProjectMemberAccess.Where(p => p.ProjectMemberID == id).Select(p => p.Read).FirstOrDefault()
}).ToList();
return View(model);
}
View model:
#model ProjectMemberUserRolesViewModel
#{
ViewBag.Title = "UserRoles";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>UserRoles</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
foreach (var item in Model.Elements)
{
<div>
#Html.DisplayFor(modelItem => item.AccessType)
</div>
<div>
#Html.CheckBoxFor(modelItem => item.Delete) #Html.DisplayNameFor(modelItem => item.Delete)
#Html.CheckBoxFor(modelItem => item.Create) #Html.DisplayNameFor(modelItem => item.Create)
#Html.CheckBoxFor(modelItem => item.Edit) #Html.DisplayNameFor(modelItem => item.Edit)
#Html.CheckBoxFor(modelItem => item.Read) #Html.DisplayNameFor(modelItem => item.Read)
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
and POST method
[HttpPost]
public ActionResult UserRoles(ProjectMemberUserRolesViewModel model)
{
//........
return View(model);
}
problem is ProjectMemberUserRolesViewModel model parameter is null for POST method. If I look at page source I see, that no prefix for foreach elements:
<form action="/ProjectMember/UserRoles/1" method="post"><input name="__RequestVerificationToken" type="hidden" value="toGniflvXh6n56oubtdZZazRAAsCxGID9X9XoQaj8ItViXZiIx3ouYNbGZfi6QPdRQAU1tU8wRwdeojxs2dtpy-NlKZ1ClgxicV0nSSwHsIclXYA8FHYY6QDmku3Wd4ptIPaJ_amu9FmKV5TwI3Ijg2" /><input id="item_AccessType" name="item.AccessType" type="hidden" value="Child Projects" /> <div>
Child Projects
</div>
<div>
<input data-val="true" data-val-required="The Delete field is required." id="item_Delete" name="item.Delete" type="checkbox" value="true" /><input name="item.Delete" type="hidden" value="false" /> Delete
<input data-val="true" data-val-required="The Create field is required." id="item_Create" name="item.Create" type="checkbox" value="true" /><input name="item.Create" type="hidden" value="false" /> Create
<input data-val="true" data-val-required="The Edit field is required." id="item_Edit" name="item.Edit" type="checkbox" value="true" /><input name="item.Edit" type="hidden" value="false" /> Edit
<input data-val="true" data-val-required="The Read field is required." id="item_Read" name="item.Read" type="checkbox" value="true" /><input name="item.Read" type="hidden" value="false" /> Read
</div>
<input id="item_AccessType" name="item.AccessType" type="hidden" value="File Archive" /> <div>
File Archive
</div>
<div>
<input id="item_Delete" name="item.Delete" type="checkbox" value="true" /><input name="item.Delete" type="hidden" value="false" /> Delete
<input id="item_Create" name="item.Create" type="checkbox" value="true" /><input name="item.Create" type="hidden" value="false" /> Create
<input id="item_Edit" name="item.Edit" type="checkbox" value="true" /><input name="item.Edit" type="hidden" value="false" /> Edit
<input id="item_Read" name="item.Read" type="checkbox" value="true" /><input name="item.Read" type="hidden" value="false" /> Read
</div>
<input id="item_AccessType" name="item.AccessType" type="hidden" value="Project Member" /> <div>
Project Member
</div>
<div>
<input id="item_Delete" name="item.Delete" type="checkbox" value="true" /><input name="item.Delete" type="hidden" value="false" /> Delete
<input id="item_Create" name="item.Create" type="checkbox" value="true" /><input name="item.Create" type="hidden" value="false" /> Create
<input id="item_Edit" name="item.Edit" type="checkbox" value="true" /><input name="item.Edit" type="hidden" value="false" /> Edit
<input id="item_Read" name="item.Read" type="checkbox" value="true" /><input name="item.Read" type="hidden" value="false" /> Read
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</form>
How to do a correct View code?
How to do a correct View code?
By replacing the foreach loop with for:
for (var i = 0; i < Model.Elements.Count; i++)
{
<div>
#Html.DisplayFor(x => x.Elements[i].AccessType)
</div>
<div>
#Html.CheckBoxFor(x => x.Elements[i].Delete)
#Html.DisplayNameFor(x => x.Elements[i].Delete)
#Html.CheckBoxFor(x => x.Elements[i].Create)
#Html.DisplayNameFor(x => x.Elements[i].Create)
#Html.CheckBoxFor(x => x.Elements[i].Edit)
#Html.DisplayNameFor(x => x.Elements[i].Edit)
#Html.CheckBoxFor(x => x.Elements[i].Read)
#Html.DisplayNameFor(x => x.Elements[i].Read)
</div>
}
or by using an editor template:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.EditorFor(x => x.Elements)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
and then inside the corresponding editor template which will be automatically rendered by ASP.NET MVC for each element of the collection ~/Views/Shared/EditorTemplates/ProjectMemberUserRolesElementViewModel.cshtml:
#model ProjectMemberUserRolesElementViewModel
<div>
#Html.DisplayFor(x => x.AccessType)
</div>
<div>
#Html.CheckBoxFor(x => x.Delete)
#Html.DisplayNameFor(x => x.Delete)
#Html.CheckBoxFor(x => x.Create)
#Html.DisplayNameFor(x => x.Create)
#Html.CheckBoxFor(x => x.Edit)
#Html.DisplayNameFor(x => x.Edit)
#Html.CheckBoxFor(x => x.Read)
#Html.DisplayNameFor(x => x.Read)
</div>

Resources