I have a simple MVC3 app with an EF4 Model
Log
.Name
.CreatedDate
.LogTypeId
LogTypes
.Id
.Description
and a ViewModel
LogViewModel
Log MyLog
List<SelectListItem> Options
LogViewModel(){
Log = new Log();
}
This displays in my view correctly and I can edit/update the values, display the drop down list and set the name to "MyTestValue".
However, in my controller's HttpPost Create method the properties for logVm.Log are not set?
[HttpPost]
public ActionResult Create(LogViewModel logVm){
logVm.Log.Name == "MyTestvalue"; //false - in fact its null
}
What am I doing wrong?
That's probably because in your edit form you don't have corresponding values. So if yuor view is strongly typed to LogViewModel the form input names must be appropriately named:
#model LogViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Log.Name)
#Html.EditorFor(x => x.Log.Name)
</div>
<div>
#Html.LabelFor(x => x.Log.SomeOtherProperty)
#Html.EditorFor(x => x.Log.SomeOtherProperty)
</div>
...
<input type="submit" value="OK" />
}
sop that when the form is submitted the POSTed values look like this:
Log.Name=foo&Log.SomeOtherProperty=bar
Now the default model binder will be able to successfully bind your view model. Also make sure that the properties you are trying to assign are public and that have a setter.
The controller method should have a property named model
[HttpPost]
public ActionResult Create(LogViewModel **model**){
**model**.Log.Name == "MyTestvalue"; //true }
Related
I am very new to MVC, let me try to explain my scenario in plain simple English:
I have an strongly typed mvc form/page (Product.cshtml) with a model, say ProductViewModel.
This page has got two search buttons, one to search and bring the items to be added to the Product and other to bring in the location, most probably partial views.
Now, what I want is that these search results work in ajax form without complete post back, and then the results of these searches (items and location) should be posted back using model binding to the form when user clicks on the submit button.
What could be the best way of achieving this functionality?
Immediate responses will be well appreciated.
I thought, its good to share the complete code for clarity:
I have one form(Service1.chtml) that has a partial view to display users(_TestUser a partial view:read only), then another partial view(_PlotServiceRequestData) that should have a field to search the plot and bring back the details lke its owner name and landuser etc.
Then when I click on submit button of the main form, I should be able to read all data(main form) + new data from _PlotServiceRequestData partial view and save all data to database.
I was trying one more option, that is, to use #Ajax.ActionLink on Service1.cshtml to call the _GetPlotDetails method and then store partial view data in TempData, so that it is available to the form when users clicks on "Submit" button of Service1.cshtml, is this a right approach?, if I use ajax.BeginForm inside partial view then the data is posted to the
Service1 controller method which is actually to save the form data and not to update the partialview and in this method even I am not getting model data of the partial view.
Sevice1.cshtml:
#model ViewModels.TestViewModel
#{
ViewBag.Title =
"Service1";
}
#
using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.EditorFor(m => m.Title)
#Html.Partial(
"_TestUser", Model)
<div id="RequestPlotData">
#Html.Partial(
"_PlotServiceRequestData", Model.requestData)
</div>
<button type="submit">Save Form</button>
}
#section Scripts {
}
_PlotServiceRequestData.cshtml:
===============================
#model ViewModels.PlotServicesRequestDataViewModel
<
div id="RequestPlotData">
#
using (Ajax.BeginForm("_GetPlotDetails", "Test", new AjaxOptions { UpdateTargetId = "RequestPlotData", Url = Url.Action("_GetPlotDetails","Test") }))
{
<h1>Request Details</h1>
<div>
#Html.LabelFor(m => m.plotAddress)
#Html.EditorFor(m => m.plotAddress)
<input type="submit" name="submit" value="Ajax Post" />
</div>
<div>
#Html.LabelFor(m => m.LandUser)
#Html.EditorFor(m => m.LandUser)
</div>
<div>
#Html.LabelFor(m => m.OwnerName)
#Html.EditorFor(m => m.OwnerName)
</div>
}
</
div>
CONTROLLER:
==========
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.Mvc;
namespace
TestNameSpace
{
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Service1()
{
Injazat.AM.mServices.
LocalDBEntities context = new Injazat.AM.mServices.LocalDBEntities();
TestViewModel model =
new TestViewModel() { user = context.Users.First(), Title = "Land Setting Out",
requestData =
new PlotServicesRequestDataViewModel() { ServiceNumber ="122345", TransactionDate="10/10/2033" } };
return View(model);
}
[
HttpPost()]
public ActionResult Service1(TestViewModel model)
{
PlotServicesRequestDataViewModel s = (PlotServicesRequestDataViewModel)TempData[
"Data"];
TestViewModel vm =
new TestViewModel() { user = model.user, requestData = s, Title = model.Title };
return View(vm);
}
[
HttpGet()]
//public PartialViewResult _GetPlotDetails(string add)
public PartialViewResult _GetPlotDetails(PlotServicesRequestDataViewModel requestData)
{
//PlotServicesRequestDataViewModel requestData = new PlotServicesRequestDataViewModel() { plotAddress = add};
requestData.OwnerName =
"owner";
requestData.LandUser =
"landuser";
TempData[
"Data"] = requestData;
return PartialView("_PlotServiceRequestData", requestData);
}
}
}
You can probably use the jQuery Form plugin for this. This makes the process of posting the data from your form back to the server very easy. The form would post to an action that would return a partial view that you can then push into your UI.
To make this easier, jQuery form actually has a "target" option where it will automatically update with the server response (ie. the partial view returned from your search action).
View
<form id="searchForm" action="#(Url.Action("Search"))" method="POST">
<input name="query" type="text" /> <!-- order use Html.TextBoxFor() here -->
<input type="submit" />
</form>
<div id="result"><!--result here--></div>
Javascript
$('#searchForm').ajaxForm({
target: '#result'
});
Controller
public ActionResult Search(string query)
{
// Do something with query
var model = GetSearchResults(query);
return Partial("SearchResults", model)
}
This should hopefully help you to get on the right track. jQuery Form is a good plugin and is the main thing you should look into for ajaxifying your form posts back to the server. You might also want to look into using jQuery's $.post and $.ajax functions, but these require slightly more work.
I am a newbie to MVC3 technology and trying to workout my way get through a small problem.
I simply need to get checked checkbox values to be saved in database and on Edit view check them back.
<input type="checkbox" value="Photo" name="DocSub" /> Photograph<br />
<input type="checkbox" value="BirthCertificate" name="DocSub" /> Copy Of Birth Certificate<br />
<input type="checkbox" value="School Leaving Certificate" name="DocSub" /> School Leaving Certificate<br />
When the Submit button is clicked, the [HTTPPOST] Action method of the desired controller is called. There I receive the selected values in this form :
var selectedCheckBoxValues = Request.Form["DocSub"];
I am getting the all the checked checkbox values in comma separated form and able to store them to the database, but wondering if this is the right approach to go by.
Also I need to know to retrieve checkbox values from database on Edit view in already checked form.
the typical apporoach to these problems is to use a view with a model
ie, suppose this is view Documents.cshtml
#model DocumentViewModel
#Html.LabelFor(m => m.Photo)
#Html.CheckBoxFor( m => m.Photo )
#Html.LabelFor(m => m.BirthCertificate)
#Html.CheckBoxFor( m => m.BirthCertificate )
#Html.LabelFor(m => m.SchoolLeavingCertificate)
#Html.CheckBoxFor( m => m.SchoolLeavingCertificate )
and use a viewmodel to pass data to the view
the viewmodel is a class where you have the data your going to send to the view, ie.
public class DocumentViewModel{
public bool Photo {get;set;}
public bool BirthCertificate { get; set; }
public bool SchoolLeavingCertificate {get;set;}
}
and you'd have a controller that populates the viewmodel and calls the view
public ActionResult Documents()
{
var modelData = new DocumentViewModel();
//or retrieve from database at this point
// ie. modelData.Photo = some database value
return View(modelData);
}
[HttpPost]
public ActionResult Documents(DocumentViewModel documentsVM)
{
if (ModelState.IsValid)
{
//update the database record, save to database... (do stuff with documentsVM and the database)
return RedirectToAction("NextAction");
}
//else, if model is not valid redirect back to the view
return View(documentsVM);
}
look for tutorials out there on mvc basics. read code.
i have problem to pass data from view to controller , i have view that is strongly typed with my viewmodel "TimeLineModel", in the first i passed to this view my viewmodel from action on my controller
public ActionResult confirmation(long socialbuzzCompaignId)
{
return View(new TimeLineModel() { socialBuzzCompaignId = socialbuzzCompaignId, BuzzMessages = model });
}
with this i can get info from my action and display it on view , but i have other action POST which i won't get my view model to do some traitement
[HttpPost]
public ActionResult confirmation(TimeLineModel model)
{
}
i can get some propretie of the model but in others no , for example i can get the properti "socialBuzzCompaignId" of model , but other propertie like "IEnumerable BuzzMessages" i can't get it , i dont now why !!
this is the content of my view
#model Maya.Web.Models.TimeLineModel
#{
ViewBag.Title = "confirmation";
}
#using (Html.BeginForm())
{
<h2>confirmation</h2>
<fieldset>
#foreach (var msg in Model.BuzzMessages)
{
<div class="editor-label">
#msg.LongMessage
</div>
<br />
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
You need to include BuzzMessages properties within a form element. Since it's not editable, you'd probably want to use hiddens. There are two ways to do this. Easiest is instead of doing a foreach loop, do a for loop and insert them by index.
#for (int i =0; i<Model.BuzzMessages.Count(); i++v)
{
<div class="editor-label">
#Model.BuzzMessages[i].LongMessage
#Html.HiddenFor(m => m.BuzzMessages[i].LongMessage);
</div>
<br />
}
but to do this you'd need to use an IList instead of an IEnumerable in your view model to access by index.
Alternatively, you could create an Editor Template named after your BuzzMessages class (whatever its name is).
#model BuzzMessagesClass
#Html.HiddenFor(m => m.LongMessages)
<!-- Include other properties here if any -->
and then in your main page
#Html.EditorFor(m => m.BuzzMessages)
Check out http://coding-in.net/asp-net-mvc-3-how-to-use-editortemplates/ or search stack overflow if the details of editor templates confuse you.
Just like any HTML POST method, you have to get the data back to the Controller somehow. Just simply "showing" the data on the page doesn't rebind it.
You have to put the data in an input (or a control that will post back) to the appropriate model property name.
So, if you have a model property with name FirstName and you want this data to be rebound to the model on POST, you have to supply it back to the model by placing an "input hidden" (or similar control that postbacks) with the ID of FirstName will rebind that property to the model on POST.
Hope that explains it.
#foreach (var msg in Model.BuzzMessages)
{
<div class="editor-label">
#msg.LongMessage
<input type="hidden" name="BuzzMessages.LongMessage" value="#msg.LongMessage" />
</div>
}
It will post array of LongMessages. Get values like this:
[HttpPost]
public ActionResult confirmation(TimeLineModel model, FormCollection collection)
{
var longMessages = collection["BuzzMessages.LongMessage"];
}
anybody can teach me how to set default value in VIEW page?
the thing I want is something like :
#Html.HiddenFor(x => x.MemAd , value="M")
and
How can I pass dynamic value to another view page by using session?
You do this in the controller action rendering this view:
public ActionResult SomeAction()
{
MyViewModel model = ...
model.MemAd = "M";
return View(model);
}
and in the corresponding view:
#model MyViewModel
...
#Html.HiddenFor(x => x.MemAd)
will generate:
<input type="hidden" name="MemAd" id="MemAd" value="M" />
It has been intriguing that my MVC3 razor form renders duplicated values inside a foreach code block in spite of correctly receiving the data from the server. Here is my simple form in MVC3 Razor...
-- sample of my .cshtml page
#model List<Category>
#using (#Html.BeginForm("Save", "Categories", FormMethod.Post))
{
foreach (Category cat in Model)
{
<span>Test: #cat.CategoryName</span>
<span>Actual: #Html.TextBoxFor(model => cat.CategoryName)</span>
#Html.HiddenFor(model => cat.ID)
<p>---</p>
}
<input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
}
My controller action looks something like this -
[HttpPost]
public ActionResult Save(ViewModel.CategoryForm cat)
{
... save the data based on posted "cat" values (I correctly receive them here)
List<Category> cL = ... populate category list here
return View(cL);
}
The save action above returns the model with correct data.
After submitting the form above, I expect to see values for categories similar to the following upon completing the action...
Test: Category1, Actual:Category1
Test: Category2, Actual:Category2
Test: Category3, Actual:Category3
Test: Category4, Actual:Category4
However #Html.TextBoxFor duplicates the first value from the list. After posting the form, I see the response something like below. The "Actual" values are repeated even though I get the correct data from the server.
Test: Category1, Actual:Category1
Test: Category2, Actual:Category1
Test: Category3, Actual:Category1
Test: Category4, Actual:Category1
What am I doing wrong? Any help will be appreciated.
The helper methods like TextBoxFor are meant to be used with a ViewModel that represent the single object, not a collection of objects.
A normal use would be:
#Html.TextBoxFor(c => c.Name)
Where c gets mapped, inside the method, to ViewData.Model.
You are doing something different:
#Html.TextBoxFor(c => iterationItem.Name)
The method internall will still try to use the ViewData.Model as base object for the rendering, but you intend to use it on the iteration item. That syntax, while valid for the compiler, nets you this problem.
A workaround is to make a partial view that operates on a single item: inside that view you can use html helpers with correct syntax (first sample), and then call it inside the foreach, passing the iteration item as parameter. That should work correctly.
A better way to do this would be to use EditorTemplates.
In your form you would do this:
#model List<Category>
#using (#Html.BeginForm("Save", "Categories", FormMethod.Post))
{
#Html.EditorForModel()
<input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
}
Then, you would create a folder called EditorTemplates, either in the ~/Views/Shared folder or in your Controllers View folder (depending on whether you want to share the template with the whole app or just this controller), and in the EditorTemplates folder, create a Category.cshtml file which looks like this:
#model Category
<span>Test: #Model.CategoryName</span>
<span>Actual: #Html.TextBoxFor(model => model.CategoryName)</span>
#Html.HiddenFor(model => model.ID)
<p>---</p>
MVC will automatically iterate over the collection and call your template for each item in it.
I've noticed that using foreach loops within Views causes the name attributes of text boxes to be rendered the same for every item in the collection. For your example, every text box will be rendered with the following ID and Name attributes:
<input id="cat_CategoryName" name="cat.CategoryName" value="Category1" type="text">
When your controller receives the form data collection, it won't be able reconstruct the collection as different values.
The solution
A good pattern I've adopted is to bind your View to the same class you want to post back. In the example, model is being bound to List<Category> but the controller Save method receives a model ViewModel.CategoryForm. I would make them both the same.
Use a for loop instead of a foreach. The name/id attributes will be unique and the model binder will be able to distinguish the values.
My final code:
View
#model CategoryForm
#using TestMvc3.Models
#using (#Html.BeginForm("Save", "Categories", FormMethod.Post))
{
for (int i = 0; i < Model.Categories.Count; i++)
{
<span>Test: #Model.Categories[i].CategoryName</span>
<span>Actual: #Html.TextBoxFor(model => Model.Categories[i].CategoryName)</span>
#Html.HiddenFor(model => Model.Categories[i].ID)
<p>---</p>
}
<input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" />
}
Controller
public ActionResult Index()
{
// create the view model with some test data
CategoryForm form = new CategoryForm()
{
Categories = new List<Category>()
};
form.Categories.Add(new Category() { ID = 1, CategoryName = "Category1" });
form.Categories.Add(new Category() { ID = 2, CategoryName = "Category2" });
form.Categories.Add(new Category() { ID = 3, CategoryName = "Category3" });
form.Categories.Add(new Category() { ID = 4, CategoryName = "Category4" });
// pass the CategoryForm view model
return View(form);
}
[HttpPost]
public ActionResult Save(CategoryForm cat)
{
// the view model will now have the correct categories
List<Category> cl = new List<Category>(cat.Categories);
return View("Index", cat);
}