I am reading an enum value from the db then bind it to the model. When i post the form with ajax, somehow the enum is unbound or the model property in null or zero but it display properly on the view. I have posted code below. Im using entityframework and mvc3
//model code constructor
public CarModel(Car car)
{
State=(CarState)car.State;
//car.State comes in as an int
//etc setting other variables
}
//CarState property
public CarState {get;set;}
//model code
#Html.DisplayFor(m=>m.CarState)
//Controller code()
Save(CarModel car)
{
//I have code that saves the changes
}
The minute i get to "car", CarState has no value.
It's not quite clear how you are passing this value to the controller action. You have only shown some #Html.DisplayFor(m=>m.CarState) but obviously this only displays a label in the view. It doesn't send anything back to the server. If you want to send some value back you will have to use an input field inside the form.
For example:
#Html.EditorFor(m => m.CarState)
or use a HiddenFor field if you don't want the user to edit it.
In any case you need to send that value to the server if you expect the model binder to be able to retrieve it. The model binder is not a magician. He cannot invent values. He binds values to your model from the Request.
Related
I have a form with few inputs, name, email, message and some checkboxes. I've created a model for these inputs and set all the validations i require.
But now I also want to pass my model (i.e. from #model MyModel) or rather some object property of my model together with those inputs.
Is populating a VewBag/viewData with my model a way to go?
#{
ViewBag.MyModel = Model;
// or ViewBag.ThatProperty = Model.ThatProperty
}
or do i still have a better way up my sleeve?
ViewBag and ViewData persist in one trip from server to client, and not the other way around.
There is no way to pass an object from the view to the controller. If it's a database object, you can pass the object Id using one of the two methods described below, then query the DB on post.
If you have no other way, you can encode the object as a JSON string (using the Newtonsoft package, for example) and pass it also using one of the two methods described below, but this isn't the best option.
To pass a property from the View to the Controller, you have two options:
Url Parameter
Hidden field
Url Parameter
<form ... asp-route-ThatProperty="#Model.ThatProperty">
...
</form>
Form Field
<form>
<input type="hidden" name="ThatProperty" value="#Model.ThatProperty" />
</form>
Controller Action
If 'ThatProperty' doesn't exist on your model, receive it as an extra parameter.
public IActionResult MyAction (MyModel model, string ThatProperty)
{
...
}
I am developing an application where I have a form with a model "StudentListModel".
I have a button in the same page which is not a submit button. I have written an ajax function which calls an action method in the controller specified.
Now my problem is there is a textbox for the studentname ,
[StringLength(160, MinimumLength = 3)]
[Display(Name = "First Name")]
[Required]
[Remote("CheckDuplicateNames", "AddStudent")]
public string StudentName { get; set; }
None of these validations are firing.However if I make the button as submit ,these will work.
Is there any way to do model validation other than using formsubmission?
Model validation is done automatically before your ActionMethod is executed and ModelState will be populated with information about that validation. You do not need to call ValidateModel as long as you are running your Controller and ActionMethods in the default MVC lifecycle.
An action method that has parameters will have the values for parameters populated using MVC Model Binding. This means that any values posted in a Form or QueryString (and a few other sources) will be name matched to simple parameters or properties in complex parameters. Using an HTML form and the MVC HtmlHelper methods for creating input types you get the desired behavior with very little work, but as you have noted it requires a form submit to send the data.
An ajax call will also populate the model using Model Binding but it requires the fields to be sent to the ActionMethod. Using jQuery it is as simple as performing a post or get request on the buttons click event passing a JavaScript object with your model's properties on it.
$('#yourButtonId').click(function() {
var student = {};
student.StudentName = $('#StudentName').val();
$.post('#Url.Action("ActionMethodName")', student).done(function (data) {
//handle returned result from ActionMethod}
});
});
You can call model validation manually in the controller method. The syntax is simply ValidateModel(model). This validates the model based on its current property values, and populates the ModelState dictionary with any errors.
If your model does not get populated with the values, but you have got them at hand, you can populate it using UpdateModel(model, values), another method inherited from the Controller class.
My MVC action method receives an entity object (Page) that the default model binder creates from form collection data. Some of the fields are wrong or null because they were not sent in the request to the server, for example I do not send "CreateDate" and the default model binder sets this property to some default value which I don't need.
Once the object is attached it of course tries to persist all the values (including invalid/not needed ones to the database). I could of course assign manually on a per property basis but was wondering if maybe I can somehow flag a property so it is not persisted when EntityState is set to modified and SaveChanges() is called..
public ActionResult SomeMethod(Page page)
{
page.ModifyDate = DateTime.Now;
_db.NewsPages.Attach(page);
_db.ObjectStateManager.ChangeObjectState(page, System.Data.EntityState.Modified);
_db.SaveChanges();
_db.Dispose();
}
The correct way to handle this is using different class for view model, attach empty entity to the context and assign real values per property (or let AutoMapper to handle this scenario) as #Darin suggested in the comment.
If you want to go your way you must not change state of the POCO entity but you must change state of every changed property:
public ActionResult SomeMethod(Page page)
{
page.ModifyDate = DateTime.Now;
_db.NewsPages.Attach(page);
ObjectStateEntry entry = _db.ObjectStateManager.GetObjectStateEntry(page);
entry.SetModifiedProperty("ChangedPropertyName");
// Do the same for all other changed properties
_db.SaveChanges();
_db.Dispose();
}
I have created a simple WCF service that is to be configured by an MVC3 UI.
When I call the index page from my controller, I want to display the values held in the configuration, which has been returned by the service. The user could then chose to edit these settings and then send them back to the service.
I want to do something like this in the index view ...
<div>
#Html.ActionLink("Edit", "Edit", model)
</div>
and then consume the model in the controller like this...
[HttpPost]
public ActionResult Edit( SettingsModel Config)
{
try
{
List<string> configErrors = null;
if (ModelState.IsValid)
{
// Set up a channel factory to use the webHTTPBinding
using (WebChannelFactory<IChangeService> serviceChannel = new WebChannelFactory<IChangeService>(new Uri(baseServiceUrl)))
{
IChangeService channel = serviceChannel.CreateChannel();
configErrors = channel.SetSysConfig(Config);
}
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
but this doesn't work.
Any suggestions???
When the form gets posted, all the input type fields data is collected and sent to the server. You can see this data using FireBug. The key point here is that, is the data that is being posted in a form, that MVC's default model binder can understand and map it to the model object, which is being passed as input parameter to the action method.
In your case, the model is of type "SettingsModel". You have to ensure that, the form data that is being posted is in format, that can be mapped to the "SettingsModel" object.
Same kind of question discussed in another thread : Can't figure out why model is null on postback?
Check Out this article : NerdDinner Step 6: ViewData and ViewModel
In the above article, carefully go through the "Using a ViewModel Pattern" section. My guess is that, this is what you are looking for.
You will need to post the values to populate the SettingsModel object on the Edit action. You can do this using hidden form fields if you don't want the user to see it. Otherwise you could have no parameters on the Edit action and make another call to the web service to populate the Settings model.
I have a struct that implements IValidatableObject, which I use for properties on a view model. The validation works fine with non-nullable properties, but if add a property of type Nullable<MyStruct> the IValidatableObject.Validate method is never called for that property, even if it has a value.
Is there any way that I trigger the validation on my nullable properties too, or is IValidatableObject not intended to be used with value types?
Thanks
Additional information:
I wanted to be able to specify units when entering data in my form, e.g. 10m, 100km, 1cm etc. I also wanted to validate that the entered data was within range and of a correct format. My struct converts the string with potentially different units (100cm/100m/1km) into a decimal property which always has the same unit (1/100/1000) and vice-versa.
I used IValidatableObject for validation because the validation will always be the same for any instance of MyStruct - I didn't want to add an attribute each time it is used.
My model would look something like this:
public class MyModel
{
public MyStruct Ab {get; set;}
public MyStruct? Cd {get; set;}
}
My view is a strongly typed view using the model, which renders my struct as a text box using a display template. When the form is posted, I have a custom model binder to convert the entered string back to my struct.
My controller action looks like:
public ActionResult MyAction(MyModel model)
{
//do stuff
}
The model is bound ok for properties of type MyStruct and Nullable<MyStruct>.
It seems that this is not possible.
I dug into the MVC source. In System.Web.Mvc.DataAnnotationsModelValidatorProvider there is the following code:
// Produce a validator if the type supports IValidatableObject
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) {
//stuff
}
According to this rejected bug report, Nullable<T> cannot be assigned to an interface that T implements:
"In short, a "DateTime?" value can be unboxed to a DateTime or an IFormattable value. But it cannot be assigned to a DateTime or IFormattable location."
Thus no validators are returned.
Looks like I will have to do this another way.