I created a simple class to test this directly, on the Edit POST action I was under the impression if a property is not in the form it will use the value which exists in the database already, instead it is getting set to null and overwriting any values.
This is from the scaffold generation, lets say I comment out Password and Password Confirmation because I don't want them to edit it, so they will get left out of the POST values.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>User</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.Email, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
#*<div class="form-group">
#Html.LabelFor(model => model.Password, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PasswordConfirmation, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PasswordConfirmation)
#Html.ValidationMessageFor(model => model.PasswordConfirmation)
</div>
</div>*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
So the request would look like this:
__RequestVerificationToken=wcxt2SbPmLuI-FzGh7b0okDgfFIXacVKnvvuYpVkgJjpNEAgCbMzHTeMQv61xhbxch0kId6nh6mK-qoKML3CHpLOfk1SawIQIpdtVicWkys1&ID=1&Email=foo.bar%40test.com&FirstName=foo&LastName=bar
And here I simply want to save the values that were edited and any values which were not included use the original values instead.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Email,FirstName,LastName,Password,PasswordConfirmation")] User user)
{
if (ModelState.IsValid)
{
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
This is just an example to help me understand how to object gets populated when it's not in the form, if anyone could elaborate or point to resources which clearly show how the model is populated when the values aren't explicitly in the POST request that would be awesome.
I was under the impression if a property is not in the form it will
use the value which exists in the database already
That is not true. How does the form (just some HTML in the client browser) knows what value you have in the database ?
You have 2 solutions
1) In your HttpGet method, Send all the properties of your entity to the form, but don't display those to user. Keep in hidden fields inside the form and when you post the form, It will be available.
public ActionResult Edit(int id)
{
var vm = new EditUserVM();
var user = GetUserFromYourDb(id);
vm.Name = user.Name
vm.Email= user.Email
//The below property you don't want user to edit in form. But still sending.
vm.Token= user.Token
return View(vm);
}
in your view
#model EditUserVM
#using(Html.Beginform())
{
#Html.TextBoxfor(s=>s.Name)
#Html.TextBoxfor(s=>s.Email)
<!-- Hidden field here -->
#Html.HiddenFor(s=>s.Token)
#Html.HiddenFor(s=>s.UserId)
<input type="submit" />
}
2) If you do not wish to send all the fields to form, You may try this.(I recommend this). Have only fields which you want the user to edit in your GET form and In the HttpPost action method, Read the existing entity again from the database and update the fields with the values you received from the form. Your other field values stays same.
public ActionResult Edit(int id)
{
var vm = new EditUserVM();
var user = GetUserFromYourDb(id);
vm.Name = user.Name
vm.Email= user.Email
return View(vm);
}
in your view
#model EditUserVM
#using(Html.Beginform())
{
#Html.TextBoxfor(s=>s.Name)
#Html.TextBoxfor(s=>s.Email)
#Html.HiddenFor(s=>s.UserId)
<input type="submit" />
}
And in your HttpPost
[HttpPost]
public ActionResult Edit(EditUserVM model)
{
var existingUser = GetUserFromYourDb(model.UserId);
existingUser.Name = model.Name;
existingUser.Email = model.Email;
SaveUser(existingUser);
return RedirectToAction("UserSaved");
}
Related
I use the following code for the Editing Button, but clicking on the Edit button does not post it
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "PageID,GroupID,Title,ShortDescription,Text,Autor,Tags,Visit,ImageName,ShowInSlider,CreatDateTime")] Page page,HttpPostedFileBase imgUp)
{
if (ModelState.IsValid)
{
if (imgUp != null)
{
if (page.ImageName != null)
{
System.IO.File.Delete(Server.MapPath("/PageImages/" + page.ImageName));
}
page.ImageName = Guid.NewGuid() + Path.GetExtension(imgUp.FileName);
imgUp.SaveAs(Server.MapPath("/PageImages/" + page.ImageName));
}
pageRepository.UpdatePage(page);
pageRepository.save();
return RedirectToAction("Index");
}
....
I have separate data layer and repository and I use the following code for the Editing Pages Controller, but with clicking on the Edit button does not post form. Though it works well for creation and delete btn. my view code is:
<div class="form-horizontal">
<h4>Page</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.PageID)
#Html.HiddenFor(model => model.Visit)
#Html.HiddenFor(model => model.CreatDateTime)
#Html.HiddenFor(model => model.ImageName)
<div class="form-group">
#Html.LabelFor(model => model.GroupID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("GroupID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.GroupID, "", new { #class = "text-danger" })
</div>
</div>
....
<div class="form-group">
#Html.LabelFor(model => model.ImageName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="imgUp" id="imgUp"/>
#Html.ValidationMessageFor(model => model.ImageName, "", new { #class = "text-danger" })
#if (Model.ImageName != null)
{
<img src="/PageImages/#Model.ImageName" class="thumbnail" style="max-width:150px" />
}
.....
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
i trace my code and find this eror:
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Request","time":"2018-04-25T08:10:44.3663705Z","tags":{"ai.internal.sdkVersion":"web: 2.0.0.25000","ai.device.roleInstance":"Laptop-erfan","ai.operation.name":"GET PageGroup
above horizontal form tag is this code:
#model DataLayer.Page
#{
ViewBag.Title = "Edit";}<h2>Edit</h2>#using (Html.BeginForm("Edit", "Pages", FormMethod.Post, new { enctype = "multipart/form-data" })){ #Html.AntiForgeryToken()
As mentioned in the comments it doesn't look like you have a form in your view. You can add one using:
#using (Html.BeginForm()) {
<!-- form contentshere -->
}
BeingForm has several overloads as well that you can use to change where it goes to, form method (GET/PUT) and set html attributes.
You need to add the complete form page into a Form tag.
please Use
#using (Html.BeginForm()) {
//Put your complete code inside this form...
}
It will create the Form tag with specified actions and type.
You can also specific the custom action method and type of the method as well.
I have a form which pass a single data in hebrew, from the user (UI) to the SQL Server database.
For some reason the data stored in SQL Server like "???????".
If I go to the database and edit the value "???????" to the actual word in Hebrew, it saves it in Hebrew.
The field in the database is nvarchar
Why when the value pass trough the code to the database it stored like "??????" ?
cshtml:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Album</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AlbumName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AlbumName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.AlbumName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="צור אלבום" class="btn btn-default" />
</div>
</div>
</div>
}
Controller:
public ActionResult Create()
{
return View();
}
// POST: Albums/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "AlbumId,AlbumName")] Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(album);
}
In your SLQ Server database change the DataType of your column AlbumName:
var char(xxx) => nvarchar(xxx)
In Code First Apporach you should have Unicode set to True
I've noticed something in the mvc default projects that made me wonder how it works. When I create a ddefault MVC Project with Individual User Accounts authentication, visual Studio scaffolds an AccountController with two "ResetPassword" Actions. One that accepts a string parameter via GET request. The Action looks like this:
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
And the View looks like this:
#model SISGRAD_MVC.Models.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Reset" />
</div>
</div>
I access the Action with the code in the URL, GET-style, and the view knows to initialize the model property from the URL. One point of interest is that this only works if I use #Html.HiddenFor(). How does this work, and how does the view know when to pull data from the URL, and when not to?
Because you method is
public ActionResult ResetPassword(string code)
the DefaultModelBinder will add the value of code to ModelState
The HiddenFor(m => m.Code) method uses values from ModelState rather that the values from the model if they exist so it will render
<input type="hidden" name="Code" id="Code" value="###" />
where ### is the value you passed to the method.
Your statement that the "view knows to initialize the model property from the URL" is not correct. The model is not initialized and is in fact null which you can test using
<div>#Model.Code</div>
which will throw an "Object reference not set to an instance of an object." exception, whereas
<div>#ViewData.ModelState["Code"].Value.AttemptedValue</div>
will display the correct value.
Side note: From your comments, the reason that DisplayFor(m => m.Code) does not show the value is that its is using the value in the ViewData (which is null because the model is null). The default display template uses the following code (refer source code)
internal static string StringTemplate(HtmlHelper html)
{
return html.Encode(html.ViewContext.ViewData.TemplateInfo.FormattedModelValue);
}
as opposed to HiddenFor(m => m.Code) which uses the following code (refer source code
default:
string attemptedValue = (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(fullName, format) : valueParameter), isExplicitValue);
break;
Note also that if you define a route with url: "Account/ResetPassword/{code}" then you do not need to add the hidden input in your view. It will be added as a route value by default - the BeginForm() method will render
<form action="Account/ResetPassword/###" ... >
_getProductReviews.cshtml:
I'm calling my partial view like this:
<p>#Html.Partial("_CreateR");</p>
_CreateR.cshtml:
This code is auto generated by the controller:
#model Commerce.Domain.Entites.t_review
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>t_review</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.text, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.text)
#Html.ValidationMessageFor(model => model.text)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.title, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.title)
#Html.ValidationMessageFor(model => model.title)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.customer_id, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.customer_id)
#Html.ValidationMessageFor(model => model.customer_id)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.product_fk, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.product_fk)
#Html.ValidationMessageFor(model => model.product_fk)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
ProductController:
// GET: /Product/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Product/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,text,title,customer_id,product_fk")] t_review review)
{
if (ModelState.IsValid)
{
prose.CreateReview(review);
return RedirectToAction("Index");
}
return View(review);
}
When I use a simple view with actionlink it works but when I try to use partial View shows this msg
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Commerce.Domain.Entites.t_review]', but this dictionary requires a model item of type 'Commerce.Domain.Entites.t_review'.
Html.Partial renders a view without calling the Controller first.
Html.Action calls a Controller Action which can return like everything: a view, a partial view, json, etc.
As a general guideline, you should use Html.Partial only when you want to display static content or when you have access to the model required. For everything else, like wen you want to return more data from the server, use Html.Action.
I am having a issue passing a variable from a controller to a view. I have created an actionresult that allows users to add meals to a restaurant menu. I need the menuID in a html action link to access the menu details page. But when I run the page ViewData["MenuID"] returns as null even though menuID has a value in the controller action result. Is there another way to send data from a controller to a view?
Create action result
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(MealviewModel model, int? menuID)
{
if (menuID == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
else
{
ViewData["MenuID"] = menuID;
if (ModelState.IsValid)
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var currentUser = manager.FindById(User.Identity.GetUserId());
var currentrestaurant = (from r in db.Restaurants
where r.UserID == currentUser.Id
select r).First<Restaurant>().id;
var currentmenu = (from m in db.Menus
where m.Restaurantid == currentrestaurant
select m).First<Menu>().Id;
var meal = new Meal() { Name = model.Name, Description = model.Description, Price = model.Price, MenuID = menuID, Catergory = model.Catergory };
db.Meals.Add(meal);
db.SaveChanges();
return RedirectToAction("Create", new { MenudID = menuID });
}
}
return View();
}
Create CSHTML Page
#model BiteWebsite.Models.MealviewModel
#using BiteWebsite.Models;
#{
ViewBag.Title = "Add Items to Your Menu";
}
<h2>Add Items to Your Menu</h2>
#using (Html.BeginForm())
{
var ID = ViewData["MenuID"];
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Catergory, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Catergory", new SelectList(new[] {"Chef Specials","Deserts","Drinks","Main Courses","Sides",
"Starters" ,"Salads"}))
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { #cols = "80", #rows = "4" })
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Price, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Add Item" class="btn btn-default" />
<div class="btn btn-default">
#Html.ActionLink("View Menu", "Menu", "Details", new { Id = ID })
</div>
</div>
</div>
</div>
}
because you are returning a redirecttoaction. you should use tempdata or session variable to save the value in the menuid and that should hold the value when it's a redirecttoaction.
When you do a redirect you lose the contents of ViewData. TempData is preserved though, so you can use that if a redirect is a possibility. I don't know about ViewBag so you might test that.
You already are passing other values through a view model, why not make a new property in that viewmodel and pass it along to the view?