Asp.Net Core: How to properly pass data from view to controller? - asp.net-core-2.0

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.

Related

Blazor creating a generic drop-down

I'm trying to create a generic dropdown component for use across our system. However, I'm encountering issues when binding the EventCallback for when the selected item is changed.
This is my current musings for the generic drop down:
<div class="inputItem #(SizeClass) dropdown" style="min-width:#(Width);">
<SfDropDownList TItem="object" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="#(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="object" TValue="int" ValueChange="#OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="#(TextField)" Value="#(ValueField)" />
</SfDropDownList>
</div>
#code {
[Parameter]
public IEnumerable<object> DataSource { get; set; }
[Parameter]
public EventCallback<ChangeEventArgs<int, object>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
And here's one example component that would call it:
#page "/news/create"
#inject NavigationManager NavManager;
#using Microsoft.EntityFrameworkCore;
#inject IDbContextFactory<FIS2_DbContext> contextFactory;
#inject IFileService fileService;
#using FIS2withSyncfusion.Controls;
#using FIS2withSyncfusion.Models;
#using FIS2withSyncfusion.Utility;
#using Syncfusion.Blazor.RichTextEditor;
#using System.Collections.Generic;
#using System.Threading.Tasks;
#using Newtonsoft.Json;
<div class="dashWrapper">
<SfDashboardLayout AllowDragging="false" AllowFloating="false" AllowResizing="false" CellAspectRatio="2.5" CellSpacing="#(new double[]{20,20})" Columns="3">
<DashboardLayoutPanels>
<DashboardLayoutPanel Column="0" Row="0" SizeX="2" SizeY="2" Id="createNews">
<HeaderTemplate>
<h3>Create A News Item</h3>
</HeaderTemplate>
<ContentTemplate>
<div class="form-wrapper">
<div class="inputRow">
<TextBox AutoComplete="#(Syncfusion.Blazor.Inputs.AutoComplete.Off)" Placeholder="Title" Text="#(title)" HTMLAttributes="#textboxValidation" Width="450px" SizeClass="half-width"></TextBox>
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="#(itemModel.Category)" OnSelectedItemChanged="#(OnSelectedItemChanged)"></DropDownList>
#*<SfDropDownList TItem="spGetNewsCategoriesResult" TValue="int" Placeholder="Select a category" #ref="sfDropDown" DataSource="categories" CssClass="inputItem half-width" #bind-Value="#(itemModel.Category)">
<DropDownListFieldSettings Text="name" Value="id" />
</SfDropDownList>*#
</div>
<div class="inputRow">
<CheckBox Checked="isChecked" Label="Suggest Dates This Should Be Active?" OnCheckChange="#(OnCheckChange)" SizeClass="one-third" Width="300px"></CheckBox>
#if (isChecked)
{
<DateTimePicker Label="Active From:" SelectedDate="#activeFrom" Width="450px" SizeClass="one-third"></DateTimePicker>
<DateTimePicker Label="Active To:" SelectedDate="#activeTo" Width="450px" SizeClass="one-third"></DateTimePicker>
}
</div>
<div class="inputRow">
<FileUploader MaxSize="#(MaxSize)" OnClearFiles="OnClearFiles" OnFileRemove="OnFileRemove" OnFileUpload="OnFileUpload" SizeClass="full-width" Width="400px"></FileUploader>
</div>
<RichTextEditor DeniedAttributes="#DeniedAttributes" text=#(itemModel.Content) Height="400px" Width="1600px"></RichTextEditor>
</div>
</ContentTemplate>
</DashboardLayoutPanel>
</DashboardLayoutPanels>
</SfDashboardLayout>
</div>
#if (ShowDialog)
{
<Dialog Title="Create News Item" message="#Message" OKText="#OKText" cancelText="#CancelText" OnClose="OnDialogClose">
</Dialog>
}
#code {
[CascadingParameter]
Task<AuthenticationState> authenticationStateTask { get; set; }
public string userName { get; set; }
private int MaxSize { get; set; }
private string title { get; set; }
private int selectedCategory { get; set; }
private string content { get; set; }
int count { get; set; }
private bool ShowDialog { get; set; } = false;
private string Message { get; set; } = "";
private string OKText { get; set; } = "";
private string CancelText { get; set; } = "";
public DateTime activeTo { get; set; }
public DateTime activeFrom { get; set; }
private bool isChecked { get; set; }
SaveNewsItemModel itemModel = new SaveNewsItemModel();
List<string> DeniedAttributes = new List<string>() {
"id", "title", "style"
};
Dictionary<string, object> textboxValidation = new Dictionary<string, object>(){
{"maxlength", "100"}
};
List<spGetNewsCategoriesResult> categories = new List<spGetNewsCategoriesResult>();
private async Task OnCheckChange(bool check)
{
isChecked = check;
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var authState = await authenticationStateTask;
var user = authState.User;
userName = user.Identity.Name;
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
categories = await procedures.spGetNewsCategoriesAsync();
MaxSize = 15 * 1024 * 1024;
}
}
private List<ToolbarItemModel> Tools = new List<ToolbarItemModel>() {
new ToolbarItemModel()
{
Command = ToolbarCommand.Bold
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Italic
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Underline
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Undo
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Redo
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.OrderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.UnorderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.FontColor
},
new ToolbarItemModel()
{
Command = ToolbarCommand.CreateLink
},
new ToolbarItemModel()
{
Command = ToolbarCommand.RemoveLink
}
};
private async Task OnFileUpload(UploadChangeEventArgs args)
{
foreach (var file in args.Files)
{
var fileName = file.FileInfo.Name;
using (var ms = file.Stream)
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
int count = 1;
string tempFileName = fileName;
while (fileService.TempFileExists(tempFileName))
{
tempFileName = $"({count}) {fileName}";
count++;
}
var bytes = ms.ToArray();
await fileService.SaveFileToTempAsync(bytes, tempFileName);
var mimetype = fileInfo.Extension;
itemModel.AddFile(fileName, mimetype, tempFileName, contextFactory);
}
}
}
private async Task OnClearFiles(ClearingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnFileRemove(RemovingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnSelectedItemChanged(ChangeEventArgs<int, spGetNewsCategoriesResult> eventArgs)
{
itemModel.Category = eventArgs.Value;
StateHasChanged();
}
private async Task OnSave()
{
if (isChecked)
{
itemModel.RequestDates(activeFrom, activeTo);
}
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
var addedFiles = await procedures.spCreateNewsItemAsync(JsonConvert.SerializeObject(itemModel), userName);
if (addedFiles.Count > 0)
{
foreach (var file in addedFiles)
{
await fileService.MoveTempToNewsAsync(file.fileName, file.newsID, file.fileID);
}
}
Message = "This has been successfully saved and is now pending review; pressing OK will refresh the page.";
OKText = "OK";
ShowDialog = true;
}
private async Task OnDialogClose(bool r)
{
ShowDialog = false;
NavManager.NavigateTo(NavManager.Uri, true);
}
}
My issue is that I'm getting an error at this point: OnSelectedItemChanged="#(OnSelectedItemChanged)"
The error is:
Cannot convert from method group to EventCallback
The hunting I've done seems to imply that I need to explicitly pass the type in as a parameter, instead of using object and trying to infer it at runtime - I'm just a bit woolly on the details of how to do that?
TValue being an int is something that shouldn't change anywhere. But the TItem could be just about anything (in this particular scenario it's a spGetNewsCategoriesResult) - how do I cater for that?
After much hunting and tinkering, I've found the solution. By changing the component to this:
#typeparam T
<div class="inputItem #(SizeClass) dropdown" style="min-width:#(Width);">
<SfDropDownList TItem="T" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="#(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="T" TValue="int" ValueChange="#OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="#(TextField)" Value="#(ValueField)" />
</SfDropDownList>
</div>
#code {
[Parameter]
public IEnumerable<T> DataSource { get; set; }
[Parameter]
public EventCallback<Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, T>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
And referencing it as such:
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="#(itemModel.Category)" OnSelectedItemChanged="#(OnSelectedItemChanged)" T="spGetNewsCategoriesResult"></DropDownList>
The error is resolved. Decided to answer my own question as opposed to just deleting it because I figured it'll probably pop up for people on their own search.

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

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

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();
}

ASP.Net MVC: How to show checkboxes selection in webgrid column after postback data

I have developed a tabular UI with webgrid. i am showing student information through webgrid. i am showing multiple checkboxes for hobbies in each row of webgrid. when i select hobbies and click submit button then i saw hobbies selection is not going to action.
i guess there is my mistake in view model class design. please have a look at my code and tell me which area i need to change in code.
i want all hobbies should go to action when i click submit button and selected hobbies also should post to action for each student. a student may have multiple hobbies selected.
here is my viewcode
#model MVCCRUDPageList.Models.StudentListViewModel
#{
ViewBag.Title = "Index";
}
<h2>Student View Model</h2>
#using (Html.BeginForm("Index", "WebGridMoreControls", FormMethod.Post))
{
var grid = new WebGrid(Model.Students, canSort: false, canPage: false);
var rowNum = 0;
var SelectedHobbies = 0;
<div id="gridContent" style=" padding:20px; ">
#grid.GetHtml(
tableStyle: "table",
alternatingRowStyle: "alternate",
selectedRowStyle: "selected",
headerStyle: "header",
columns: grid.Columns
(
grid.Column(null, header: "Row No", format: item => rowNum = rowNum + 1),
grid.Column("ID", format: (item) => #Html.TextBoxFor(m => m.Students[rowNum - 1].ID, new { #class = "edit-mode" })),
grid.Column("Name", format: (item) => #Html.TextBoxFor(m => m.Students[rowNum - 1].Name, new { #class = "edit-mode" })),
grid.Column("Country", format: (item) =>
#Html.DropDownListFor(x => x.Students[rowNum - 1].CountryID,
new SelectList(Model.Country, "ID", "Name", item.CountryID),
"-- Select Countries--", new { id = "cboCountry", #class = "edit-mode" })),
grid.Column(header: "Hobbies",
format: #<text>
Hobbies
#foreach (var hobby in Model.Hobbies)
{
<div class="checkbox">
<label>
#Html.HiddenFor(e => e.Hobbies)
<input type="checkbox"
name="Hobbies"
value="#hobby.ID" /> #hobby.Name
</label>
</div>
}
</text>)
))
<input type="submit" value="Submit" />
</div>
}
Action code
public class WebGridMoreControlsController : Controller
{
// GET: WebGridMoreControls
public ActionResult Index()
{
StudentListViewModel osvm = new StudentListViewModel();
return View(osvm);
}
[HttpPost]
public ActionResult Index(StudentListViewModel oStudentListViewModel)
{
return View(oStudentListViewModel);
}
}
View model code
public class StudentListViewModel
{
public IList<Student> Students { get; set; }
public List<Country> Country { get; set; }
public IList<Hobby> SelectedHobbies { get; set; }
public IList<Hobby> Hobbies { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student{ID=1,Name="Keith",CountryID=0,Hobby=0},
new Student{ID=2,Name="Paul",CountryID=2,Hobby=0},
new Student{ID=3,Name="Sam",CountryID=3,Hobby=0}
};
Country = new List<Country>
{
new Country{ID=1,Name="India"},
new Country{ID=2,Name="UK"},
new Country{ID=3,Name="USA"}
};
Hobbies = new List<Hobby>
{
new Hobby{ID=1,Name="Football"},
new Hobby{ID=2,Name="Hocky"},
new Hobby{ID=3,Name="Cricket"}
};
}
}
Model code
public class Student
{
public int ID { get; set; }
[Required(ErrorMessage = "First Name Required")]
public string Name { get; set; }
public int CountryID { get; set; }
public int Hobby { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Hobby
{
public int ID { get; set; }
public string Name { get; set; }
}
please help me to rectify view, viewmodel and model class code. thanks
Add a viewmodel for Hobby:
public class HobbyViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
Modify your StudentListViewModel as following:
public class StudentListViewModel
{
public IList<Student> Students { get; set; }
public List<Country> Country { get; set; }
public IList<HobbyViewModel> Hobbies { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student{ID=1,Name="Keith",CountryID=0,Hobby=0},
new Student{ID=2,Name="Paul",CountryID=2,Hobby=0},
new Student{ID=3,Name="Sam",CountryID=3,Hobby=0}
};
Country = new List<Country>
{
new Country{ID=1,Name="India"},
new Country{ID=2,Name="UK"},
new Country{ID=3,Name="USA"}
};
Hobbies = new List<HobbyViewModel>
{
new HobbyViewModel{ID=1,Name="Football"},
new HobbyViewModel{ID=2,Name="Hocky"},
new HobbyViewModel{ID=3,Name="Cricket"}
};
}
Replace the foreach in the view as following:
#for (var i = 0; i < Model.Hobbies.Count; i++)
{
<div class="checkbox">
#Html.HiddenFor(m => m.Hobbies[i].ID)
#Html.CheckBoxFor(m => m.Hobbies[i].Checked)
#Html.LabelFor(m => m.Hobbies[i].Checked, Model.Hobbies[i].Name)
</div>
}

How to display email after login in he masterpage

My model is
public class Login
{
[Required]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
My Post method is
[HttpPost]
public ActionResult Login(Models.Login lu)
{
using (travelAndTourismEntities objentity = new travelAndTourismEntities())
{
if (ModelState.IsValid)
{
if (IsValid(lu.Email, lu.Password))
{
FormsAuthentication.SetAuthCookie(lu.Email, true);
return RedirectToAction("Home", "jettravel");
}
else
{
ModelState.AddModelError("", "Login details are wrong.");
}
}
return View(lu);
}
}
In view
#if (Request.IsAuthenticated)
{
<div class="col-md-3 col-sm-3 col-xs-12" style=" font-size:14px; ">
<div class="header_info">
<p class="login_username">Welcome <span>#Html.Encode(User.Identity.Name)</span></p>
}
I want to display customer email in view, but #if (Request.IsAuthenticated) is always getting false
Try replacing this line
<p class="login_username">Welcome <span>#Html.Encode(User.Identity.Name)</span></p>
with
<p class="login_username">Welcome <span>#HttpContext.Current.User.Identity.Name;</span></p>

Resources