I am using Html.RenderAction in my View to call a method that is in my controller. The controller method returns a custom object, I want to know how would I be able to use this returned object in the view.
View
//at the top
#model ServiceCheckerUI.Models.DeleteDeliverableModel
#{ Html.RenderAction("retrieveDeliverableInfo","DeliverableManagement", new {uniqueId = element});}
//Trying to use the model property
Model.deliverableResponse.{properties}
Controller
public ActionResult retrieveDeliverableInfo(string uniqueId){
var response = _target.DoSomething();
return PartialView("DeleteDeliverable", new DeleteDeliverableModel {deliverableResponse = response});
}
Model
namespace ServiceCheckerUI.Models
{
public DeleteDeliverableModel
{
//omit
public GetDeliverableResponse deliverableResponse {get;set}
}
}
The GetDeliverableResponse object has fields like id, name etc which are strings and ints.
RenderAction is used to directly write response to the page and helps in caching the partial view. Your method should return partial view instead of GetDeliverableResponse. You can define the partial view and use GetDeliverableResponse as it's model.
public ActionResult RetrieveDeliverableInfo(string uniqueId)
{
var response = _target.DoSomething();
return PartialView("_Deliverable", response );
}
Here _Derliverable would be your partial view which will have GetDeliverableResponse as model.
To keep it more neat you can also Wrap the response object in a dedicated model class of _Derliverable like this:
class DerliverableModel
{
public GetDeliverableResponse Derliverables { get; set; }
}
now in your action method you need to pass the object of this model:
return PartialView("_Deliverable", new DerliverableModel { Derliveries = response });
Related
I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!
View Model looks like this:
public class AsmenysInfoViewModel2
{
public asmenys_info Asmenys_info { get; set; }
public List<miestai> Miestai { get; set; }
public string Test { get; set; }
}
And there are two actions. Get and Post.
public ActionResult Index(long? id)
{
var model = new AsmenysInfoViewModel2();
model.Test = "Test";
model.Asmenys_info = BllFactory.DalFactory.AsmenysInfoDal.GetById(id.Value);
return View(model);
}
[HttpPost]
public ActionResult Index(AsmenysInfoViewModel2 asmenys_info)
{
var model = asmenys_info;
return View(model);
}
And my view looks like this:
#model MODELS.AsmenysInfoViewModel2
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Index", "AsmenysInfo", FormMethod.Post))
{
#Html.ValidationSummary()
#Html.TextBoxFor(m => m.Asmenys_info.adresas)
<input type="submit" value="Išsaugoti" />
}
Doesn't matter if I use EditorFor or TextBoxFor - result is same. My model property "Asmenys_info" on posting is always null. If my class AsmenysInfoViewModel2 would not contain asmenys_info type property and would contain only "string, int etc" (no strongly typed) - it would work.
My question is :
How to post View Model which has strongly typed property which on posting would not be null?
Your model has a property named Asmenys_info and the parameter in your POST method is also named asmenys_info. Internally the DefaultModelBinder reads the values of the form data which includes a value for Asmenys_info and attempts to set property Asmenys_info to that value but it fails because there is no conversion from a string to a complex object.
Change the name of the parameter to anything other than a name of a property in your model and it will bind fine, for example
[HttpPost]
public ActionResult Index(AsmenysInfoViewModel2 model)
Change the below line with another object name
public ActionResult Index(AsmenysInfoViewModel2 asmenys_info)
in above method use any other name of object instead of asmenys_info.
because while mvc framework map your model with object there is confution in asmenys_info and Asmenys_info property of AsmenysInfoViewModel2 class.
I'm working with ASP.NET MVC5, but very new to it.
Is there a way to pass a specific instance of a model between views and the controller multiple times without using a form POST... for example...
When the controller is handling the request for the "FirstView" page request, the CarModel class is instantiated and an API call is made on the object to retrieve values from an API and set the object's properties.
The object is then passed to the "FirstView" page as the model and the properties are displayed for the user to view.
If the user is happy with the properties, they can then click the ActionLink to save the properties to a database.
I want to pass the entire model object back to the controller so that the work can be done to do the database insert... which I have shown in my controller as "SecondView".
If I am totally off track in terms of how this should be done, please could someone point me in the right direction and link me some reading materials... THANK YOU!
Here is my model...
public class CarModel
{
public string Make { get; set; }
public string Colour { get; set; }
public void GetModelDataFromAPI()
{
Make = "BMW";
Colour = "Black";
// many other properties, but ommitted for brevity
}
public int InsertPropertiesIntoDatabase()
{
// code to do the database insert anbd return an ID
return 1;
}
}
Here is my FirstView.cshtml...
#model CarsApp.Models.CarModel
<html>
<body>
<h1>First View</h1>
Make: #Model.Make, Colour: #Model.Colour
#Html.ActionLink("Add model properties to database", "SecondView", "Home")
</body>
</html>
Here is my controller...
namespace CarsApp.Controllers
{
public class HomeController : Controller
{
public ActionResult FirstView()
{
CarsApp.Models.CarModel objCarModel = new Models.CarModel();
objCarModel.GetModelDataFromAPI();
return View("FirstView", objCarModel);
}
public ActionResult SecondView()
{
// need to get a reference to the model from the first view
// then call the insert method of that model, with the properties
// holding the values from the API
return Content("ID:" + objCarModel.InsertPropertiesIntoDatabase());
}
}
}
I am developing an asp.net mvc web app in which I want to send 2 objects to View through Controller now I am sending only one object to view through controller
return View(repository.func(id));
and in view I am getting
<% var data = Model.First %>
But now I am confused how to send 2 objects and how to get it.
An excellent occasion to (learn to) use a ViewModel:
class MyViewModel { ... }
// and in the Action:
var view = new MyViewModel();
view.First = repository.func(id) ;
view.Second = ....;
return View(view);
You can use ViewBag (Personally I don't like this approach) or create class which will hold both values and use it for model for your view
I assume your view is strongly-typed to be of the same type as whatever you're returning from:
repository.func(id)
lets say object 'Foo'
Assuming you are using a strongly-typed view:
#model Foo
You can change this to be:
#model IEnumerable<Foo>
Then, your view will be strongly-typed to a collection (IEnumerable) of Foo
and in your view you can do:
foreach(var foo in Model)
{
//Do stuff
}
Naturally, your repository method will have to return a collection of objects (via something that implements IEnumerable - List<> for example)
Just a slight elaboration of Henk's answer
class MyViewModel {
TMyEntityType1 First { get; set; }
IQueryable<TMyEntityType2> Second { get; set; }
}
And then in the action you collect your 2 sets of data and house it in an instance of MyViewModel
var viewModel = new MyViewModel();
viewModel.First = repository.func(id);
viewModel.Second = repository.containing("?");
return View(viewModel);
An in your view you may want to change it to:
<% var dataFirst = Model.First;
var dataSecond = Model.Second;%>
Where Model is now of type MyViewModel and not the return type of repository.func(id)
I have a Settings Action on my Account controller that renders the Settings View.
On the Settings View, I recieve a ViewModel that includes ChangePasswordModel.
Here is the SettingsViewModel:
public class SettingsViewModel
{
public ChangePasswordModel ChangePasswordModel { get; set; }
}
The Settings View recieves
#model XBLTools.Models.SettingsViewModel
The ChangePassword View recieves
#model XBLTools.Models.ChangePasswordModel
The ChangePassword view works OK alone.
How to render the ChangePassword View passing the Model.ChangePasswordModel?
I've tried some combinations without success getting different errors:
#Html.RenderPartial("ChangePassword", (XBLTools.Models.ChangePasswordModel)(Model.ChangePasswordModel))
#Html.RenderPartial("ChangePassword", Model.ChangePasswordModel)
#Html.Partial("ChangePassword", (XBLTools.Models.ChangePasswordModel)(Model.ChangePasswordModel))
#Html.Partial("ChangePassword", Model.ChangePasswordModel)
Any ideas?
If it's null pass in a new instance of ChangePasswordModel:
#Html.RenderPartial("ChangePassword", new ChangePasswordModel())
Or instantiate it in the constructor of SettingsViewModel:
public class SettingsViewModel
{
public SetttingsViewModel()
{
ChangePasswordModel = new ChangePasswordModel();
}
public ChangePasswordModel ChangePasswordModel { get; set; }
}
You should initialise the ChangePasswordModel on the settings view model in the controller.
public ActionResult MyAction()
{
var model = new SettingsViewModel{
ChangePasswordModel = new ChangePasswordModel()
}
return View(model);
}
then use:
#Html.Partial("ChangePassword", Model.ChangePasswordModel)
You can just pass your model property:
#Html.Partial("ChangePassword", Model.ChangePasswordModel)
If the ChangePasswordModel proeprty is null, you'll get an error, since the partial view needs a model.
Make sure that you've set the ChangePasswordModel property to an instance.
Alternatively, you can just pass a new ChangePasswordModel instance:
#Html.Partial("ChangePassword", new ChangePasswordModel())