Is there any way to pass a whole model via html.actionlink in ASP.NET MVC 3? - asp.net-mvc-3

How do I pass a whole model via html.actionlink or using any other method except form submission? Is there any way or tips for it?

Though it's not advisable in complex cases, you can still do that!
public class QueryViewModel
{
public string Search { get; set; }
public string Category { get; set; }
public int Page { get; set; }
}
// just for testing
#{
var queryViewModel = new QueryViewModel
{
Search = "routing",
Category = "mvc",
Page = 23
};
}
#Html.ActionLink("Looking for something", "SearchAction", "SearchController"
queryViewModel, null);
This will generate an action link with href like this,
/SearchController/SearchAction?Search=routing&Category=mvc&Page=23
Here will be your action,
public ViewResult SearchAction(QueryViewModel query)
{
...
}

No, you cannot pass entire complex objects with links or forms. You have a couple of possible approaches that you could take:
Include each individual property of the object as query string parameters (or input fields if you are using a form) so that the default model binder is able to reconstruct the object back in the controller action
Pass only an id as query string parameter (or input field if you are using a form) and have the controller action use this id to retrieve the actual object from some data store
Use session

You could use javascript to detect a click on the link, serialize the form (or whatever data you want to pass) and append it to your request parameters. This should achieve what you're looking to achieve...

Related

how to pass in a reference variable into a RenderPartial?

I am trying to pass a modelclass store to a RenderPartial. The goal for the renderpartial is to change/set values on this (store)model. I have been trying like this:
#{ Html.RenderPartial("test", new store(){Output=""}); }
#{ Html.RenderPartial("test", new store(){Output2=""}); }
public class store
{
public string Output { get; set; }
public string Output2 { get; set; }
}
the Partial 'test' has to change the Output properties. Is it uberhaupt possible and if yes how to do this? The renderpartial contains a javascript to calculate the value of the properties.
RenderPartial is meant to get either none of the data from the parent or a part of the model.
The overloads at:
http://msdn.microsoft.com/en-us/library/system.web.mvc.html.renderpartialextensions.renderpartial(v=vs.108).aspx
Tell you object isn't a custom object but meant to be part of the Model, ex Model.Customers
Pass the required value from your model to the partial and let the partial create it's own objects.
If you really want to pass it to the partial then create a new view model for your parent view and set the Output property in your viewmodel and pass it to the partial.
Note also that the partial gets it's own copy of the data and cannot update the parents copy so that may defeat what you actually want here.
If you need some other calculate data do it in your controller prior to passing it to the view if possible.
#store st = new store(){Output="", Output2=""};
#{ Html.RenderPartial("test", new RouteValueDictionary {{"output", st.Output}, {"output2", st.Output2}}); }

mvc3 composing page and form element dynamically

I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic #Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form.
Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.
Question is, how can I handle all this extra fields in a controller?
Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?
I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)
[HttpPost]
public ActionResult Save(dynamic doc)
{
int docType = doc.type;
switch (docType)
{
case 1:
var invoice = new Invoice(doc);
invoice.amount = Request.Form["amount_field"];
invoice.code = Request.Form["code_field"];
//and so forth for every specific property of Invoice
Repository.Save(invoice);
break;
case 2:
var contract = new Contract(doc);
contract.fromDate = Request.Form["fromDate_field"];
contract.toDate = Request.Form["toDate_field"];
//and so forth for every specific property of Contract
Repository.Save(contract);
break;
..... // and so forth for any document types
default:
break;
}
}
But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.
Update
A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like
public IEnumerable<Field> Tipologie { get; set; }
where
public class Field
{
public int IdField { get; set; }
public String Label { get; set; }
public String Value { get; set; }
public FieldType ValueType { get; set; }
public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}
public enum FieldType
{
STRING, INT, DECIMAL, DATE, ENUMERATION
}
Is this a better approach? In this case I can have just an action method like
[HttpPost]
public ActionResult Save(Document doc)
But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model?
Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?
To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel
The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:
if (model is Contract) ...
I'm not sure if you will be able to set up different actions each with a different signature, eg,:
public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...
Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.
In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.

How to Post Partial View Data?

Any input much appreciated :)
I want to know one thing whether I can post multiple partial views data in MVC?(means i want to update partial views data to DATABASE)
Here is the Example:
Model:-
public class PassengerViewModel
{
public List<PassengerModel> Passengers { get; set; }
public ContactModel Contact { get; set; }
}
Controller:-
[RequiredAuthentication]
public ActionResult Passenger()
{
var passengrViewMdl = new PassengerViewModel()
{
Contact = new ContactModel(),
Passengers = psngrService.LoadPassengers(Convert.ToInt32(Session["LPORefNO"]))
};
return View(passengrViewMdl);
}
[HttpPost]
public ActionResult Passenger(PassengerViewModel passengerViewModel)
{
Here i want to update Passengers & Contact information
}
View:-
#model QR.LPO.Core.Models.PassengerViewModel
#{
ViewBag.Title = "Add Passengers";
}
#using (Html.BeginForm())
{
#Html.Partial("_Passenger", Model.Passengers);
#Html.Partial("_PassengerContact", Model.Contact);
<input type="submit" value="Submit" />
}
Thanks.
Yes, indeed you can, but, controller usually works only with one model per request, so either your model should have declared within it properties of both partial submodels, or submodels themselves.
This is possible due to HTML specifications, all data on form, which has submit buttom is send to submit action url.
This will almost work as you have it - there's nothing inherent to partials that would prevent this, in the end the html that's output is all that's important.
The problem with your code is that presumably the model of your _Passenger view is of type Passengers and the model of your _PassangerContact view is of type Contact. What this means is that if you standard HtmlHelper extensions (like Html.Textbox(...) or Html.TextboxFor(...) the fields they generate will not have full names like Contact.Name, but instead just names relative to their model, like Name. This will cause modelbinding to fail in your post action.
You can solve this in a number of ways.
Simply use the same model type (PassengerViewModel) in your sub-views, and write code like #Html.TextboxFor(m => m.Contact.Name).
Instead of using Html.Partial, use Html.EditorFor(...). This passes the proper prefix information into the child view so the field names are generated properly.
Explicitly set the prefix yourself
Like this:
#{
var childViewData = new ViewDataDictionary(this.ViewData);
childView.TemplateInfo.HtmlFieldPrefix = "Contact";
}
#Html.Partial("_PassengerContact", Model.Contact, childViewData)
You could also look at creating a Html.PartialFor overload yourself, as described in this stackoverflow question: ASP.NET MVC partial views: input name prefixes

TryUpdateModel for a model containing a list removes information from model

I have a big object that I resorted to serializing using #Html.Serialize():
[Serializable]
public class ModelB
{
public List<ModelA> ListOfModelA { get; set; }
// more stuff
}
This object contains a list of objects from a class that contains several properties. Some of them I include them in my view, while other I do not even bother to put them as hidden fields, as I have them in my serialized model.
[Serializable]
public class ModelA
{
public string StringA { get; set; }
public string StringB { get; set; }
// more stuff
public string HiddenStringA { get; set; }
public string HiddenStringB { get; set; }
// more stuff
}
Now, when I post back the form with my changes I reconstract my model and then I update it using the dictionary of values obtained from the form.
[HttpPost]
public ActionResult Edit([Deserialize] ModelTwo model,
FormCollection form)
{
TryUpdateModel(model, form.ToValueProvider());
// more stuff
}
I step in my code and just before I do the update, I see that my deserialized model contains a list ListOfModelA that in turn contains all the elements that should be there, and within them I can see all the HiddenStringA and HiddenStringB properties. Then I peek inside the form and I see a dictionary with keys like these:
ListOfModelA[0].StringA
ListOfModelA[0].StringB
ListOfModelA[1].StringB
ListOfModelA[1].StringB
while there are NO keys for the rest of the properties like this one:
ListOfModelA[0].HiddenStringA
Next, I move one step further and I let the code do the TryUpdateModel. Now, looking inside the ListOfModelA property, all the elements have been replaced with new ones that have all the hidden values null. It is as if the update reconstructed whole elements (with the limited information it had), rather than updating only the properties for which it had information.
Is this the expected behaviour? Is there a way to keep my model, and update only the properties that have keys in the dictionary?
Thanks,
Panos
My problem is more complex than the one described above, but the solultion that I found for the specific design is along the following lines. I serialize the part of the model that is a list, and bind the whole model through an argument of the action. This way I avoid using the TryUpdateModel, although this is not what causes me the trouble. Then, I inject the non-null values into the deserialized model from the model created from the form, and lastly I point the corresponding part of the latter to the former.
[HttpPost]
public ActionResult Edit(ModelTwo model,
[Deserialize] List<ModelA> serializedList)
{
serializedList.InjectFrom<NonNullLoopValueInjection>(model.ListOfModelA);
model.ListOfModelA = serializedList;
// more stuff
}
For the injection, I use Value Injecter where NonNullLoopValueInection is defined below
public class NonNullLoopValueInjection : LoopValueInjection
{
protected override bool AllowSetValue(object value)
{
return value != null;
}
}
Maybe not the best design, but it is working, and it might lead me to something better. Any feedback is more than welcome.

ASP.NET MVC AllowHtml bug or something I didn't use correctly

My model contains a string field called "longdescription" which gets the value of the tinymce editor's content
Public class ArticleModel:BaseModel{
[StringLength(8000, ErrorMessage = "Long description must be in 8000 characters or less"), AllowHtml]
public string LongDescription { get; set; }
}
Here is my controller code
[HttpPost]
public ActionResult AddEdit(ArticleModel model)
{
string buttonName = Request.Form["Button"];
if (buttonName == "Cancel")
return RedirectToAction("Index");
// something failed
if (!ModelState.IsValid)
{
}
// Update the articles
}
My problem is when I use Request.Form to access the post value, it's working fine without throwing "A potentially dangerous...." error, but when I use Request.Params["Button"], it threw that errors. Is something I am missing?
Thanks
Updated
Sorry the answer Adam gave doesn't really answer my question. Can anyone give more suggestion?
Ideally you shouldn't really be using either. Those are more Web Forms centric values even though they 'can' be used.
Either pass in a FormsCollection item and check it there using collection["Button"] or even better - your cancel button itself should probably just do the redirect. Why post when you do nothing but redirect?
In your view you can emit the url via Url.Action() and put that into your button's click handler (client side)
It is the HttpRequest.Params getter that is throwing this exception. This getter basically builds and returns a key/value pair collection which is the aggregation of the QueryString, Form, Cookies and ServerVariables collections in that order. Now what is important is that when you use this getter it will always perform request validation and this no matter whether you used the [AllowHtml] attribute on some model property or if you decorated the controller action with the [ValidateInput(false)] attribute and disabled all input validation.
So this is not really a bug in the AllowHtml attribute. It is how the Params property is designed.
As #Adam mentioned in his answer you should avoid accessing request values manually. You should use value providers which take into account things such as disabled request validation for some fields.
So simply add another property to your view model:
public class ArticleModel: BaseModel
{
[StringLength(8000, ErrorMessage = "Long description must be in 8000 characters or less")]
[AllowHtml]
public string LongDescription { get; set; }
public string Button { get; set; }
}
and then in your controller action:
[HttpPost]
public ActionResult AddEdit(ArticleModel model)
{
string buttonName = model.Button;
if (buttonName == "Cancel")
{
return RedirectToAction("Index");
}
// something failed
if (!ModelState.IsValid)
{
}
// Update the articles
}

Resources