Behavior of default model binder with custom object while model binding - asp.net

In the below code my 'tc' object has been initialized after posting, but values are null. What is the root cause of the issue. Why is the default model binder doesn't attempt to populate the properties with incoming posted values? What restriction the default model binder has to behave like this?
<form id="hello-world" action="/Home/Index">
<label for="user">User </label>
<input id="user" type="text" name="user" required="required">
<label for="city"> City </label>
<input id="city" type="text" name="city" required="required">
<label for="age"> Age </label>
<input id="age" type="text" name="age" required="required">
<input type="submit" value="Submit">
</form>
public ActionResult Index(TestClass tc)
{
ViewBag.TCName = tc.user; // its null
ViewBag.TCAge = tc.age; // its null
ViewBag.TCCity = tc.city; // its null
return View();
}
public class TestClass
{
public string user;
public string city;
public string age;
}

You should use properties and not fields on your view model:
public class TestClass
{
public string User { get; set; }
public string City { get; set; }
public string Age { get; set; }
}
The default model binder can only bind to properties with public getters/setters and not with fields. It simply ignores fields and they will never be initialized.

Related

How to get data from multiple elements

i have next ViewModel:
public class ChatCreationViewModel
{
public string ChatName { get; set; }
public List<User> Friends { get; set; }
public List<(string, bool)> Partcipants { get; set; }
}
and next view:
#model GalkaNet.ViewModels.ChatCreationViewModel
<form method="post" asp-action="NewChat" asp-controller="Chat" enctype="multipart/form-data">
<h2>Создание беседы</h2>
<label>Chat name: </label> <input type="text" name="ChatName" placeholder="Name" value=""></p>
<p><input type="submit" value="Create" /></p>
#for(int i = 0; i < Model.Friends.Count; i++)
{
<div>
<label>#Model.Friends[i].NickName</label>
<p> #Html.EditorFor(Model => Model.Partcipants[i].Item2)</p>
</div>
}
</form>
</div>
The thing is: i want to create a list of elements with user names and checkboxes. Once the form is submitted i want to place values from each checkbox combined with id of user (in front of whose name this checbox was) into Partcipants list of my view model that is going to server. How should i do this?
Create a Model Like this
public string ChatName { get; set; }
public List<UserModel > Friends { get; set; }
Your User Model Properties(ID,UserName,isSelected)
Fill these properties from your db
Id = friend.Id;
UserName = friend.Name,
isSelected = false
Your View
#for (int i = 0; i < Model.Friends.Count; i++)
{
<div class="form-check m-1">
<input type="hidden" asp-for="#Model.Friend[i].Id " />
<input type="hidden" asp-for="#Model.Friend[i].Username" />
<input asp-for="#Model.Friend[i].isSelected" class="form-check-
input" />
<label class="form-check-label" asp-
for="#Model.Friend[i].isSelected">
#Model.Friend[i].Username </label>
</div>
}

Use multiple forms in one view and submit them to several different tables

I have view that in this view, I want to pass some models to it. I'm passing models like this:
#Html.Partial("_Project", Model.AllProjects)
#Html.Partial("_Blog", Model.AllBlog)
#Html.Partial("_Comment", Model.AllComments)
this view feeds on a viewModel (HomeMultiViewModel) that contains some models like this:
public class HomeMultiViewModel
{
public List<Project> AllProjects { get; set; }
public List<Blog> AllBlog { get; set; }
public List<Blog> SearchInBlog { get; set; }
public List<Costumer> AllCostumers { get; set; }
public List<Comments> AllComments { get; set; }
}
Except these, I have 2 forms in this view that the values of one them should be sent to a model named ContactYou, and the values of another one should be send to a model names advise.
This is first form in my view:
<form class="UswithYouForm">
<input type="tel" class="UswithYouFormInput" placeholder="Phone number.." />
<button type="submit" class="UswithYouFormButtom">Submit</button>
</form>
This is the second for in that view:
<form class="adviseForm">
<input type="text" class="form-control" placeholder="Your Name" />
<input type="tel" class="form-control" placeholder="Phone number" />
<input type="text" class="form-control" placeholder="Subject" />
<textarea class="form-control" placeholder="Message..."></textarea>
<button class="slide__text-link">Send your request!</button>
</form>
And at last this is my controller for that view:
namespace Atawin.Controllers
{
public class HomeController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IServiceProvider _serviceProvider;
public HomeController(ApplicationDbContext context, IServiceProvider serviceProvider)
{
_context = context;
_serviceProvider = serviceProvider;
}
public IActionResult Index()
{
HomeMultiViewModel model = new HomeMultiViewModel();
model.AllProjects = (from p in _context.projects select p).ToList();
model.AllBlog = (from b in _context.blogs select b).ToList();
model.AllCostumers = (from c in _context.costumers select c).ToList();
model.AllComments = (from com in _context.comments select com).ToList();
ViewBag.RootPath = "/upload/Norm/";
return View(model);
}
public IActionResult Search(string searchedTxt)
{
HomeMultiViewModel model = new HomeMultiViewModel();
model.AllBlog = (from b in _context.blogs select b).ToList();
model.AllCostumers = (from c in _context.costumers select c).ToList();
model.SearchInBlog = (from sb in _context.blogs where sb.Title.Contains(searchedTxt) orderby sb.Id descending select sb).Take(15).ToList();
ViewBag.RootPath = "/upload/Norm/";
ViewBag.SearchedWords = searchedTxt;
return View(model);
}
// i want an action here to solve my problem
}
}
I actually reed the questions like this on the whole websites, but I can't solve my problem. If you need more to answer this question tell me so I'll send theme to you.

How to build object within a form in razor view

public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public MyType MyType { get; set; }
public IMyConfig MyConfig { get; set; }
}
public enum MyType
{
FirstConfig,
SecondConfig,
ThirdConfig
}
public interface IMyConfig
{
string BuildFirstJson();
string BuildSecondJson();
}
public class FirstConfig : IMyConfig
{
public string cat { get; set; }
public string dog { get; set; }
public string lion { get; set; }
public string BuildFirstJson()
{
...
}
public string BuildSecondJson()
{
...
}
}
public class SecondConfig : IMyConfig
{
public string bear { get; set; }
public int pig { get; set; }
public string fish { get; set; }
public string shark { get; set; }
public string dolphin { get; set; }
public string BuildFirstJson()
{
...
}
public string BuildSecondJson()
{
...
}
}
//ThirdConfig built similarly
So what I am trying to do is build a form in a razor view that can handle the MyModel class and switches the displayed IMyConfig bindings based on the selected MyType for example, if FirstConfig is selected from the enum list, then FirstProp, SecondProp, ThirdProp text boxes are displayed and when the form is submitted, those properties are correctly built into a FirstConfig object and passed into MyModel as a IMyConfig interface. I have no idea how to accomplish this part, I am plan on using jquery .change() to listen for a switch in the MyType dropdown. But I am not sure how to handle the seperate displaying and automatic model building(if that makes sense).
If that doesn't make sense, the quick version is I am trying to build a razor view form for MyModel and don't how to approach the MyConfig property which is based on the MyType property.
<form asp-action="ActivityCreateSubmit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group" style="display: none;">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" value="#ViewBag.AppId" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MyType" class="control-label"></label>
<select id="selection" asp-for="MyType" asp-items="Html.GetEnumSelectList<MyType>()">
<option value="">Select...</option>
</select>
<span asp-validation-for="MyType" class="text-danger"></span>
</div>
#switch(Model.MyType)
{
case FirstConfig:
<input name="first-input" />
<input name="second-input" />
<input name="third-input" />
break;
case SecondConfig:
... Do something ...
break;
}
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
Controller -
//Ignore ActivityTable, it is a dependency injection.
public ActionResult ActivityCreateSubmit(ActivityTable at, MyModel a)
{
at.AddActivity(a)
return View("../Home/Index");
}
//This is the page I am working on creating
public ActionResult ActivityCreate()
{
return (View());
}
I could not get exactly what your problem is, but I guess you need the following (I am glad to edit the answer if that is not what you need).
you can check the selected value in that enum inside your Razor page:
<form>
#switch(Model.MyType)
{
case FirstConfig:
<input name="cat"/>
<input name="dog" />
<input name="lion" />
break;
case SecondConfig:
... Do something ...
break;
}
</form>
UPDATE:
The action ActivityCreateSubmit should have a parameter of some class type the class you require (FirstConfig). The name you gave to each input MUST be identical to the properties in your class FirstConfig, i.e., like this:
<input name="cat"/>
<input name="dog"/>
<input name="lion"/>
Now try to submit, it should work.

Nullable in Model Binding

I am using ASP.NET Core with VS 2017. I want to allow the users to create an employee record where they can leave the address input field empty i.e. as optional field input.
Using [Bind("Name","Address")] always validates the inputs and the request will always fail when the address field is empty.
Here is the data model.
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
and the Create method reading the parameters from Post request has a model binding as follows
public async Task<IActionResult> Create([Bind("Name,Address")] Employee emp)
and the view has a default asp form using Employee Model.
<div class="col-md-10">
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
Is there any way to make the address input field as an optional input for Model Binding?
I'm very new to Asp.net core, but from what I've learned so far, this is how I would do it:
<form method="post" asp-controller="YourController" asp-action="Create">
<div>
<input asp-for="Name" placeholder="Name"/>
<div class="stylized-error">
<span asp-validation-for="Name"></span>
</div>
<input asp-for="Address" placeholder="Address"/>
<div class="stylized-error">
<span asp-validation-for="Address"></span>
</div>
<input type="submit" value="Submit" />
<div class="error-list" asp-validation-summary="ModelOnly"></div>
</div>
</form>
You want Address to be optional which is by default true, so you actually want to make Name required:
public class Employee
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
}
And your controller:
public async Task<IActionResult> Create(Employee emp)
{
if (ModelState.IsValid)
{
// Do whatever you want here...
}
}
A property is considered optional if it is valid for it to contain
null. If null is not a valid value to be assigned to a property then
it is considered to be a required property.
Later
You can use Data Annotations to indicate that a property is required.
From docs.microsoft

ASP.NET MVC 4.0 model binder not working with collection

The default model binder is not mapping the collections in my model. Here is my code:
Model:
public class Company
{
public string Name;
public List<CompanyActivity> Activities
}
public class CompanyActivity
{
public string Code;
public string Description
}
Controller:
[HttpPost]
public ActionResult Index(Company company) {}
View/HTML:
<input name="Name" type="text" value="some name" />
<input name="Activities[0].Code" type="text" value="1" />
<input name="Activities[0].Description" type="text" value="a" />
<input name="Activities[1].Code" type="text" value="2" />
<input name="Activities[1].Description" type="text" value="b" />
The Name input is mapped but the Activities list is empty.
You have defined your model with fields, but you have to use properties. You only have to change your model for this:
public class Company
{
public string Name { get; set; }
public List<CompanyActivity> Activities { get; set; }
}
public class CompanyActivity
{
public string Code { get; set; }
public string Description { get; set; }
}
Differences between fields and properties: Difference between Property and Field in C# 3.0+ and ASP.net MVC - Model binding excludes class fields?
The names need to be identical for it to build them as a collection/array:
I would create a ViewModel to handle this case, this is why I almost always build a ViewModel to handle view rendering and the idiosyncrasies of MVC.
Model
public class Company
{
public string Name;
public List<CompanyActivity> Activities
}
ViewModel
public class CompanyViewModel
{
public string Name;
//View specific
public List<int> CompanyActivityCodes
}
View
<input name="Name" type="text" value="" />
<input name="CompanyActivityCodes" type="text" value="" />
<input name="CompanyActivityCodes" type="text" value="" />
This will then bind the CompanyActivityCodes property, you can then reassign these to build up the Activities property in the controller.
public ActionResult Index(CompanyViewModel companyViewModel)
{
var company = new Company { Name = companyViewModel };
company.Activities = companyViewModel.CompanyActivityCodes.Select(x => new CompanyActivity { Code = x });
}
it shoould be
<input name="company.Activities[0].Code" type="text" value="" />

Resources