ASP.NET MVC 4.0 model binder not working with collection - asp.net

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="" />

Related

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

Addition of two numbers in MVC

I am trying to add two numbers in MVC.
My requirement is "I have 2 text boxes in View from which I have to retrieve data to controller"
View :
#using (Html.BeginForm("Addition", "Addition", FormMethod.Post))
{
<input id="Text1" type="text" value=#ViewBag.a name="firstNum" />
<input id="Text2" type="text" value=#ViewBag.b name="secondNum" />
<input id="Text3" type="text" value=#ViewBag.result />
<input type="submit" value="Submit" />
}
Controller Name : Addition
Action Name: Addition
[HttpPost]
public ActionResult Addition(FormCollection fc)
{
string[] keyss = fc.AllKeys;
ViewBag.a = fc.Keys[0];
ViewBag.b = fc.Keys[1];
ViewBag.total = ViewBag.a + ViewBag.b;
return View();
}
Now, from this form collection I want to retrieve values of textboxes.
Thanks.
One of the powers of MVC is the model binder - which you are completely ignoring here. Create a view model to match the expected content of your view
public class AdditionViewModel
{
public int A { get; set; }
public int B { get; set; }
public int Result { get; set; }
}
Use this as the expected parameter in your action
[HttpPost]
public ActionResult Addition(AdditionViewModel model)
{
model.Result = model.A + model.B;
return View(model);
}
Then finally in your view
#model AdditionViewModel
#using (Html.BeginForm("Addition", "Addition", FormMethod.Post))
{
#Html.EditorFor(x => x.A)
#Html.EditorFor(x => x.B)
#Html.DisplayFor(x => x.Result)
<input type="submit" value="Submit" />
}
Assuming you get the data in to ur controller , afterwards you just add Addition view and use
#ViewBag.total simple or you also can use viewdata or tempdata in case if u required .
The better way is
[HttpPost]
public ActionResult Addition(int firstNum, int secondNum )
{
ViewBag.Result=firstNum+secondNum;
return View();
}
Make sure you are doing a Numeric validation at client side

Behavior of default model binder with custom object while model binding

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.

Resources