how to bind data to a user defined class asp.net core 2 - asp.net-core-2.0

I defined a model look like this
public class X {
int a;
MyClass b;
}
and I have an action look like this
public IActionResult Test(X x){}
now when user submit data I want to manipulate that data and then assign it to b.
how can I do it?

You didn't provide the detailed explanation such as how you want to submit data , suppose you have a book model class that contains a child Author property :
public class Book
{
public int id { get; set; }
public string name { get; set; }
public Author author { get; set; }
}
public class Author {
public int authorId { get; set; }
public string authorName { get; set; }
}
In your view , using Tag Helpers in forms , you can bind to Author.authorName :
#model Book
<form asp-controller="Home" asp-action="RegisterInput" method="post">
BookID: <input asp-for="id" /> <br />
BookName: <input asp-for="name" /><br />
AuthorID: <input asp-for="author.authorId" /> <br />
AuthorName: <input asp-for="author.authorName" /><br />
<button type="submit">Register</button>
</form>
In controller , you will find the input values will automatically bind to related properties :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RegisterInput(Book book)
{
return View();
}

Related

Asp.Net Core Razor Pages - Binding and saving a list of objects

I have to get the photographs for a list of people. How do I generate the <input type="file" asp-for="#person.Photo"> controls in the cshtml page? Right now all the controls have the same name ("person_Photo"). Also how do I access this property in OnPost?
Sample code below for clarity:
Person class
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public IFormFile Photo { get; set; }
}
Create.cshtml.cs
public class CreateModel : PageModel
{
[BindProperty]
public List<Person> People {get; set;}
public async Task<IActionResult> OnGetAsync(int applicationId)
{
var applicants = repo.GetApplicants(applicationId);
People = applicants.Select(a => new Person(){Id=a.Id,Name=a.Name,Photo=null}).ToList();
}
public async Task<IActionResult> OnPostAsync()
{
//People is null below. For example OnGet fills People with 3 Person.
foreach (var person in People)
{
//I need to save the Photo.
//How do I access the IFormFile Photo?
//All the file inputs have the same name when I inspect the HTML.
}
}
}
Create.cshtml
All the file controls have the same name ie. person_Photo
<form method="post" enctype="multipart/form-data">
#foreach (var person in Model.People)
{
<input type="hidden" asp-for="#person.Id" />
<input type="hidden" asp-for="#person.Name" />
<input type="file" asp-for="#person.Photo"/>
}
<button type="submit"/>
</form>
In your OnPost method, you can access the uploaded files by using the
IFormFile property of each person.
The name attribute in the HTML should match the property name of the Person class.
You can use the Request.Form.Files collection to access the uploaded files:
public async Task<IActionResult> OnPostAsync()
{
for (int i = 0; i < People.Count; i++)
{
People[i].Photo = Request.Form.Files[$"People[{i}].Photo"];
// Save the photo
}
}

Asp.Net Core: How to properly pass data from view to controller?

I am trying to pass some data from a view to a controller, using a view model.
In view I have something like:
#model LoginRegisterViewModel
<input type="text" name="Email" id="Email" placeholder="Email">
<input type="password" class="form-control" name="Password" id="Password" placeholder="Password">
View Model:
public class LoginModel
{
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember Me")]
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
public class RegisterModel
{
public string Username { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
public string ReturnUrl { get; set; }
}
public class ResetModel
{
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public string ReturnUrl { get; set; }
}
public class LoginRegisterViewModel
{
public LoginModel Login { get; set; }
public RegisterModel Register { get; set; }
public ResetModel Reset { get; set; }
}
And in controller:
public async Task<IActionResult> Login(LoginRegisterViewModel model, string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var userOrEmail = model.Login.Email;
SignInResult result;
if (userOrEmail.IndexOf('#') > 0)
result = await _signInManager.PasswordEmailSignInAsync(userOrEmail, model.Login.Password, model.Login.RememberMe, false);
else
result = await _signInManager.PasswordSignInAsync(userOrEmail, model.Login.Password, model.Login.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return LocalRedirect(returnUrl);
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
return View(model);
}
I am doing something wrong because I get the following error: NullReferenceException: Object reference not set to an instance of an object.
referencing this line: var userOrEmail = model.Login.Email;
you are binding to the wrong properties.
The controller action expect an object of type LoginRegisterViewModel, and it's the type of the model your view is expecting , but your controls are binding to wrong property .
for example <input type="text" name="Email" id="Email" placeholder="Email"> this maps to model.Email (where model is an object of LoginRegisterViewModel) , and LoginRegisterViewModel doesn't have an Email property but it has a LoginModel property which has the Email property , so you should bind your input to model.Login.Email .
Ps : you can use the MVC core tag helpers to bind properties to model
<input asp-for="model.Login.Email" name="anyothername" class="form-control" />
or even razor syntax
#Html.EditorFor(model => model.Login.Email)
Just got it. I should have used name="Login.Email" and name="Login.Password" in the view.

View not passing data to the controller on submission of form

When I use ViewModel in Create action the data traveled from view to controller but when I use Customer class instead of ViewModel my obj receive no data.
My Customer Model:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsSubscribedToNewsletter { get; set; }
public MembershipType MembershipTypes { get; set; }
[Display(Name = "Membership Type")]
public byte MembershiptypeId { get; set; }
public DateTime BirthDate { get; set; }
}
My MembershipType Model:
public class MembershipType
{
public byte Id { get; set; }
public short SignUpFee { get; set; }
public byte DurationInMonths { get; set; }
public byte DiscountRate { get; set; }
public string Name { get; set; }
}
My ViewModel:
public class CustomerViewModel
{
public IEnumerable<MembershipType> MembershipTypes { get; set; }
public Customer Customerss { get; set; }
}
Controller:
[HttpPost]
public ActionResult Create(Customer c)
{
return View();
}
View:
#using (#Html.BeginForm("Create", "Customers"))
{ <div class= "form-group">
#Html.LabelFor(m=> m.Customerss.Name )
#Html.TextBoxFor(m => m.Customerss.Name, new {#class = "form-control"
</div>
<div class="checkbox">
<label>
#Html.CheckBoxFor(m=>m.Customerss.IsSubscribedToNewsletter ) Subscribed To NewsLetter
</label>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Customerss.MembershiptypeId )
#Html.DropDownListFor(m => m.Customerss.MembershiptypeId ,new SelectList(Model.MembershipTypes ,"Id","Name"),"Select Membership Type", new { #class = "form-control" })
</div>
#Html.HiddenFor(m => m.Customerss.Id)
<button type="submit" class="btn btn-primary"> Save </button>}
Page Source

ValidationResult and ViewModels

I have recently created a new application that is using MVC4 and EF5. The tables already existed, so we are using Database first. This is all new to me, it is my first time in MVC or EF.
I have some models in my EMDX file, and in one of them I inherited IValidatableObject and put some code in the Validate function. This was working fine, then I changed my view to use a ViewModel, now I get an error from validate, and I am stumped. It is still calling my validate function, but no longer posting it back to the screen, i just get a yellow screen.
Error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Model:
public partial class Names : IValidatableObject {
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}
ViewModel:
public class NamesVM {
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
}
Controller: Edit Function:
[HttpPost]
public ActionResult Edit(NamesVM nvm) {
if (ModelState.IsValid) {
dbCommon.Entry(nvm.Name).State = EntityState.Modified;
dbCommon.SaveChanges();
return RedirectToAction("Index");
}
return View(nvm);
}
View:
#model NamesVM
<div class="editor-label">
#Html.LabelFor(model => model.Name.Id)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Id)
#Html.ValidationMessageFor(model => model.Name.Id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Name)
#Html.ValidationMessageFor(model => model.Name.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Active)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Active)
#Html.ValidationMessageFor(model => model.Name.Active)
</div>
<input type="submit" value="Save" />
The codes works fine if everything is correct on the screen, but when validation fails, i don't get a nice error, I get a yellow screen. I am sure I am missing something, I just don't know what.
finally solved it. The IValidatableObject needs to be moved to the ViewModel, as well as the logic for it.
Model
public partial class Names { //: IValidatableObject { //Remove the IValidateableObject from here
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
// this whole method can be commented out
//public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
//}
}
View Model
public class NamesVM : IValidatableObject { // add the IValidatableObject to your view model
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
// and move your validation logic to here
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}

Populating a select box in a form using related ID in MVC3

I have a very simple data structure with two models. The first containing UserName, UserQuestion and userLocationID and another with LocationName and LocationID, the locationID in the first table is related to the LocationName the second table. However I've not specified any relationship. I've set up the data structure using the code first method in used here .
I would like to create a form which has two text inputs for a user to enter their name and question and a select box that is populated with all the locationNames from the second table. However I can't seem to create the model that allows me to do so. Do I need to make a separate ViewModel?
Does anyone know of a simple tutorial that will explain how to do this?
I'm quite new at MVC, and the dot net framework. . And I've had a look at this answer but I can't seem to modify it to fit my needs. So Apologies if I'm asking for something really basic.
I can give an example in one controller, one view and three C# classes. To use this code, create an empty MVC2 project in visual studio and add a reference to Entity Framework dll version 4.1. If you need help as to where to put these files I recommend Steve Sanderson's MVC2 book.
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public string Question { get; set; }
public virtual Location Category { get; set; }
}
public class Location
{
public int ID { get; set; }
public string LocationName { get; set; }
}
Repository
using System.Data.Entity;
using System.Collections.Generic;
using System.Linq;
public class Repository : System.Data.Entity.DbContext
{
public DbSet<User> User { get; set; }
public DbSet<Location> Locations { get; set; }
public Repository()
{
this.Database.Connection.ConnectionString =
#"Server=.;Database=Test;Integrated Security=SSPI";
if (!this.Database.Exists())
{
this.Database.Create();
this.Locations.Add(new Location { LocationName = "Queensway" });
this.Locations.Add(new Location { LocationName = "Shepherds Bush" });
this.SaveChanges();
}
}
public IEnumerable<Location> GetLocations()
{
return this.Locations.Where(x => x.ID > -1);
}
public Location GetLocation(int id)
{
return this.Locations.First(x => x.ID == id);
}
public void SaveUser(User user)
{
this.User.Add(user);
this.SaveChanges();
}
}
Controllers\HomeContoller.cs:
using System.Web.Mvc;
public class HomeController : Controller
{
Repository repo = new Repository();
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(User user, int categoryId)
{
user.Category = repo.GetLocation(categoryId);
repo.SaveUser(user);
return View();
}
}
Views\Home\Index.aspx
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<User>" %>
<html>
<body>
<% using (Html.BeginForm())
{%>
Username: <%: Html.TextBoxFor(model => model.UserName) %><br />
Question: <%: Html.TextBoxFor(model => model.Question) %><br />
Location: <select name="categoryId">
<% foreach (var location in new Repository().GetLocations())
{%>
<option value="<%= location.ID %>">
<%= location.LocationName %></option>
<%} %>
<br />
</select>
<p>
<input type="submit" value="Create" />
</p>
<% } %>
</body>
</html>

Resources