I need some help with this, please!
I have this Model:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
I have also a EditorTemplate
#model MyProject.Models.MyModel
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The Edit View
#using Microsoft.AspNetCore.Mvc.ViewFeatures
#model List<MyProject.Models.MyModel>
#{
ViewData["Title"] = "Edit";
}
<div class="spaceUnderLogo">
<div class="row">
<div class="col-md-12 ">
<h2>Edit</h2>
<form id="manageMyModel" asp-controler="MyController" asp-action="Edit" method="post" class="">
<div class="form-horizontal">
<hr />
#Html.EditorForModel()
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
And MyController has this two methods
[HttpGet]
public async Task<IActionResult> Edit()
{
List<MyModel> model = await GetCurrentInfoAsync();
return View(model);
}
[HttpPost]
public async Task<ActionResult> Edit(List<MyModel> model)
{
// here is where I have the issue
}
As you see I'm having an issue with the post method. When I get the view I send 2 objects: {Id = 1, Name = "A", Value = "9999"} and {Id = 2, Name = "B", Value = null}
Edit B Value property to "8888" and in the Post I'm getting the List only with the Value property like this:
{Id = 0, Name = null, Value = "9999"} and {Id = 0, Name = null, Value = "8888"}
Any idea?
You can use:
#model MyProject.Models.MyModel
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Name)
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The trick here is the:
#Html.HiddenFor(model => model.Id)
This will ensure that the form contains the value when posted, but there is no editor visible on the form. You will need one of these hidden input for any field that is readonly for the model retrieved by a GET in any form submit.
Related
Is my ViewModel flow right? The 'SubObjectViewModel' class usage is a bad or a good practice? Or should I try other option, like creating a ViewModel only to this class?
And also, how should I return the ViewModel 'ObjectViewModel' to the controller with all its values, change them and refresh values from page view?
My ViewModel
Public class ObjectViewModel{
public string name { get; set; }
public int value { get; set; }
public bool IsCameraPermited { get; set; }
public List<SubObjectViewModel> choosenSubObjects{ get; set; } // need to get it back on controller;
public class SubObjectViewModel
{
public int IdObject { get; set; }
public string Name { get; set; }
public string Config { get; set; }
}
public List<Object> listSub{ get; set; } //list that will be filled on 'Create' Controller
}
My Controller
public IActionResult Create(int id)
{
List<Object> listSubObject = new List<Object>();
listSubObject = _getAllSubObjectsDAO.ListByID(id);
List<Object> choosenObjects= new List<Object>();
choosenObjects = _getChoosenObjectsDAO.ListByID(id);
List<SubObjectViewModel> listSubObject = new List<SubObjectViewModel>();
foreach (Object item in choosenObjects )
{
string config = _configurationDAO.GetConfigurationById(item.configId);
ObjectViewModel .SubObjectViewModel SubObject = new ObjectViewModel .SubObjectViewModel { IdObject = item.Id, Name = item.Name , Config = config };
listSubObject.Add(setorVM);
}
ObjectViewModel objVM = ObjectViewModel{
name ="test",
value = 2,
IsCameraPermited =true,
listSub = listSubObject,
choosenSubObjects = listSubObject
};
return View(objVM);
}
My View
#model Project.Models.ViewModels.ObjectViewModel
... more code
<form asp-action="Create">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#foreach (var item in listSub)
{
<div class="txt">
#item
</div>
}
</div>
</div>
</div>
</form>
Thanks in advance.
how should I return the ViewModel 'ObjectViewModel' to the controller with all its values
To achieve above requirement, you can refer to the following code snippet.
#model ObjectViewModel
#{
ViewData["Title"] = "Create";
var choosenSubObjects = Model.choosenSubObjects;
}
<form method="post">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#for (var i = 0; i < Model.choosenSubObjects.Count; i++)
{
<div class="txt">
<div class="form-group">
<label asp-for="#choosenSubObjects[i].IdObject" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].IdObject" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Name" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Name" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Config" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Config" type="text" />
</div>
</div>
}
</div>
</div>
</div>
</form>
Controller Actions
[HttpGet]
public IActionResult Create(int id)
{
//code logic here
//...
return View(objVM);
}
[HttpPost]
public IActionResult Create(ObjectViewModel objectViewModel)
{
//code logic here
//...
Test Result
I have been trying to validate an input which is not properly in the model. I have the following code:
#using (Html.BeginForm("Address", "Locations", FormMethod.Post, new { id =
"mainForm" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="card shadow-sm">
<div class="card-header">
<h3 class="card-title">
Step 1
</h3>
<label>
Search for service location(s)
</label>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
#Html.ValidationMessage("ZipCode")
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">House number:</label>
<input type="text" class="form-control" id="HouseNumber" name="HouseNumber" />
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">City:</label>
<input type="text" class="form-control" id="City" name="City" />
</div>
</div>
</div>
</div>
<div class="card-footer">
<div class="row">
<div class="col-md-12">
<button class="btn btn-primary" type="submit">
<i class="fas fa-search"></i>
Search for location(s)
</button>
</div>
</div>
</div>
</div>
}
The mode is
#model PagedList.IPagedList<iCRM.Models.Address>
But the name which I gave to the input is not in the model. However the validation is not working at all. And the POST is ignoring my validation.
Can somebody help me out, what I am doing wrong?
Thanks in advance.
You have 2 options.
add ZipCode to the model class iCRM.Models.Address.
Write custom validation with Request.Forms["ZipCode"] and Javascript handler on client side. (#Html.ValidationMessage("ZipCode") you can not call)
This is idea based on option 2 but not a definite answer.
Controller
[HttpGet]
public ActionResult Address()
{
return View();
}
[HttpPost]
public ActionResult Address(AddressModel model)
{
if (String.IsNullOrEmpty(Request.Form["ZipCode"]))
{
ViewBag.ValidationForZipCode = "Problem with ZipCode";
}
return View();
}
View
#model WebApplication1.Models.AddressModel
#{
ViewBag.Title = "Address";
string strValidationForZipCode = "";
if (ViewBag.ValidationForZipCode != null)
{
strValidationForZipCode = ViewBag.ValidationForZipCode.ToString();
}
}
<h2>Address</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken();
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="pull-right">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
<table>
<tr>
<td>
<div class="form-group">
#Html.LabelFor(model => model.Address1, htmlAttributes: new {
#class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
<input type="hidden" id="hdValidationForZipCode" name="hdValidationForZipCode" value="#strValidationForZipCode" />
</div>
</td>
</tr>
<tr>
<td>
</td>
</tr>
</table>
</div>
}
<script type="text/javascript">
var str1 = document.getElementById("hdValidationForZipCode").value; //$('#hdValidationForZipCode').val();
if (str1 != "")
alert(str1);
</script>
Model
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class AddressModel
{
[Required]
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
}
I know this question has been asked so many times but checked all the answers with my code and I have no naming conflict and I pass the ID as a hidden field and yet the ViewModel passes to the controller as null.
This question is the link of my project idea and snippet of the code. What might I be missing?
I created a new project and copied your code into the project. I, too, recieved a null model when trying to create. It took a few changes to fix it for me. I had to add 'Model.AccountOrOU' as the passed in model to the partial. I had to initialize the model in the controller for the Create Index. I also had to change the name attribute on the properties of the AccountOrOU model in the view. Here is my code ->
Index.cshtml
#model AccountVM
#{
ViewData["Title"] = "Home Page";
}
<h2>Create</h2>
#using (Html.BeginForm("SaveAccount", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div>
#{await Html.RenderPartialAsync("_Account.cshtml", Model.AccountOrOU);}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" />
</div>
</div>
</div>
}
_Account.cshtml
#model AccountOrOU
<div class="form-horizontal" id="ViewData">
<h4>Account Partial</h4>
<hr />
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="AccountOrOU.Name" class="form-control" />
</div>
</div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test2.Models
{
public class AccountVM
{
public AccountVM()
{
AccountOrOU = new AccountOrOU();
}
public AccountOrOU AccountOrOU { get; set; }
}
public class AccountOrOU
{
public string Name { get; set; }
}
}
Controller
public IActionResult Index()
{
AccountVM blah = new AccountVM();
return View(blah);
}
public IActionResult SaveAccount(AccountVM input)
{
return View("Index", input);
}
See if this helps:
#{await Html.RenderPartialAsync("_Account.cshtml"}
Then change this line:
<input type="text" name="AccountOrOU.Name" class="form-control" />
to this:
#Html.TextBoxFor(x => x.AccountOrOU.Name, new { #class="form-control" })
How can I save into my db this the selected option, Right now It's saving all the data but the ProfileText that is what I need...
I think I need to add an asp-for but I dont know where to be honest, or if it's a different way please tell me.
Here is my view:
#model HCCBPOHR.Data.Candidate
#*#model HCCBPOHR.DomainModel.CandidateModel*#
#*#model HCCBPOHR.Services.CandidateService.PostBoth*#
#{
ViewData["Title"] = "CandidateCreate";
}
<h2>CandidateCreate</h2>
<h4>Candidate</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post" enctype="multipart/form-data" asp-action="CandidateCreate">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Number" class="control-label"></label>
<input asp-for="Number" class="form-control" maxlength="9" />
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group">
#Html.DropDownList("ProfileText", "Select Profile")
</div>
<div class="form-group">
<label asp-for="CV" type="file" class="control-label"></label>
<input asp-for="CV" type="file" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" onclick="this.disabled=true;this.form.submit();" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Here is my Model:
public class Candidate : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string ProfileText { get; set; }
public Byte[] CV { get; set; }
public string CVNAME { get; set; }
public List<Profile> ProfileList { get; set; }
}
This is how I'm sending the List to the View:
public IActionResult CandidateCreate(int postId)
{
using (var applicationcontext = new ApplicationContext())
{
IEnumerable<SelectListItem> items = applicationcontext.Profile.Select(c => new SelectListItem{Value = c.ProfileText,Text = c.ProfileText});
ViewBag.ProfileText = items.ToList();
return View();
}
}
The error that I'm having right now is
NullReferenceException: Object reference not set to an instance of an object.
In your view change the code to:
<div class="form-group">
<select asp-for="ProfileText" asp-items="#Model.ProfileList"></select>
</div>
Then in your model you'll have a property which we can call ProfileText which will then get post to the server when the form is submitted.
Change your model by introducing a new prop as follows:
public SelectList ProfileList { get; set; }
Now in your action your will need to do:
var model = new Candidate();
...
model.ProfileList = new SelectList(YourProfileListFromDbOrSomeWhereElse);
return View(model);
Please note you can also use SelectList(IEnumerable, string dataValueField, string dataTextField)Constructor if you want to set the dataValueField and dataTextField. I do not know how you get your ProfileList and what it contains so hence why I've only made use of the SelectList(IEnumerable items); Constructor.
Further reading here
As I can see, you are not populating the dropdownlist. I think it's better practice to have the values that can be selected(in a SelectList or string list) and a variable that will hold the selected value in your model and use DropDownListFor. the syntax would be:
#Html.DropDownListFor(model => model.ProfileText, new SelectList(Model.ProfileList, "name"), new {(here html attributes)#class = "form-control"})
After doing that you will get the selected value when posting the model
I'm having trouble binding the selected value of a drop down list to the correct property in my view model. I can't see what I am doing wrong here. I've put the code that should help show what I'm doing below. I've omitted some things such as the population of the 'AllFolders' property of the view model, as it's just a simple List with an object called ImageGalleryFolder.
Every time the form posts back, the ParentFolderId property is null without fail. This is driving me crazy and I've wasted a lot of time trying to work it out.
Can anyone see something I'm doing wrong?
This is the view model
public class ImageGalleryFolderViewModel
{
[Required]
public string Title { get; set; }
public int Id { get; set; }
public string CoverImageFileName { get; set; }
public HttpPostedFileBase UploadedFile { get; set; }
public string ParentFolderId { get; set; }
public IList<ImageGalleryFolder> AllFolders { get; set; }
}
Here is the view code
#using Payntbrush.Presentation.Demo.MVC3.Areas.Admin
#model Payntbrush.Presentation.Demo.MVC3.Areas.Admin.Models.ImageGalleryFolderViewModel
#{
ViewBag.Title = "Create A New Gallery Folder";
}
<h2>#ViewBag.Title</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm((string)ViewBag.Action + "Folder", "Portfolio", FormMethod.Post, new { Id = "CreateFolder", enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
if(((string)ViewBag.Action).ToLower() == FormConstants.Edit.ToLower())
{
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.CoverImageFileName)
#Html.HiddenFor(m => m.ParentFolderId)
}
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UploadedFile)
</div>
<div class="editor-field">
<input type="file" name="UploadedFile"/>
#Html.ValidationMessageFor(model => model.UploadedFile)
</div>
{
// Count > 1 is important here. If there is only 1 folder, then we still don't show the drop down
// as a child folder can't have itself as it's own parent.
}
if(#Model.AllFolders.Count > 1)
{
<div class="editor-label">
Choose a parent folder (optional)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.ParentFolderId, new SelectList(Model.AllFolders, "Id", "Title"))
</div>
}
<p>
<input type="submit" value="Save" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I've ommitted my view, but this is what my form looks like when rendered in the browser. The form looks good from what I can see?
<form Id="CreateFolder" action="/SlapDaBass/Portfolio/EditFolder/1" enctype="multipart/form-data" method="post">
<input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="1" />
<input id="CoverImageFileName" name="CoverImageFileName" type="hidden" value="" />
<input id="ParentFolderId" name="ParentFolderId" type="hidden" value="" />
<div class="editor-label">
<label for="Title">Title</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="The Title field is required." id="Title" name="Title" type="text" value="Test" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="UploadedFile">UploadedFile</label>
</div>
<div class="editor-field">
<input type="file" name="UploadedFile"/>
<span class="field-validation-valid" data-valmsg-for="UploadedFile" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
Choose a parent folder (optional)
</div>
<div class="editor-field">
<select id="ParentFolderId" name="ParentFolderId">
<option value="1">Test</option>
<option value="2">Test 2</option>
</select>
</div>
<p>
<input type="submit" value="Save" />
</p>
</form>
And this is the controller action:
[HttpPost]
public ActionResult EditFolder(int id, ImageGalleryFolderViewModel model)
{
if (ModelState.IsValid)
{
Services.PortfolioService.UpdateFolder(model.MapToDomainModel(), model.UploadedFile);
return Home;
}
return View();
}
change the data type of the ParentFolderId
public class ImageGalleryFolderViewModel
{
[Required]
public string Title { get; set; }
public int Id { get; set; }
public string CoverImageFileName { get; set; }
public HttpPostedFileBase UploadedFile { get; set; }
public int ParentFolderId { get; set; }
public IList<ImageGalleryFolder> AllFolders { get; set; }
}
also use the Html helper for the dropdownlist
<%:
Html.DropDownListFor(
model => model.ParentFolderId ,
new SelectList(
new List<Object>{
new { value = 1 , text = "Test" },
new { value = 2 , text = "Test2" },
new { value = 3 , text = "Test3"}
},
"value",
"text"
)
)
%>
i hope you are strongly typing your view like
public ActionResult EditFolder()
{
return View(new ImageGalleryFolderViewModel());
}
Please refer below link for the bind drop down list. It will be very helpful to you.
ASP.NET MVC - drop down list selection - partial views and model binding
Here if you do not want to create property in model for the List of items, than you can also store it in a ViewData or ViewBag. Please find sample code below.
<%= Html.DropDownList("Category.CategoryId", new SelectList((
IEnumerable<ProductManagement.Models.Category>)ViewData["CategoryList"],
"CategoryId", "CategoryName"))%>
You're creating a Hidden input for ParentFolderId with an empty value. This is probably overriding the value that the DropDownList is trying to post. Remove this line:
#Html.HiddenFor(m => m.ParentFolderId)
you have 2 element for ParentFolderId
one of them is hidden field
#Html.HiddenFor(m => m.ParentFolderId)
second is select element
#Html.DropDownListFor(m => m.ParentFolderId, new SelectList(Model.AllFolders, "Id", "Title"))
and modelbinder bind the first matched element value to model.
You have to remove hidden field