In partial view: "The model item passed into the dictionary is of type" - asp.net-mvc-3

I lack understanding of some basic MVC concepts, despite all my searching.
I created an MVC project in Visual Studio, which contains the partial view _LogOnPartial.shtml. I just want to access information within the view pertaining to the user, to put in a user dropdown menu. When I try to put this at the top of the partial view cshtml page I get the above error:
#model MyProject_MVC.Models.UserRepository
When I try this I also get an error:
#Html.Partial("_LogOnPartial", MyProject_MVC.Models.UserRepository)
'MyProject_MVC.Models.UserRepository' is a 'type', which is not valid in the given context

You have to provide an instance of MyProject_MVC.Models.UserRepository to the partial view. _LogOnPartial is strongly-typed to that type. It lets you access its public members at compile time and decide how you can display it in your view.
If you want to use your own type in that view, first you have to change the type that is strongly-typed to it.
#model MyProject_MVC.Models.UserRepository
Then you have to create an instance of that type in your action method and pass it to the view as the model or a property of the model.
public ActionResult LogOn()
{
return View(new UserRepository());
}
Now you can access this instance as Model object in your view.
#Html.Partial("_LogOnPartial", Model);

#model _LogOnPartial.Models.UserRepository should probably be: #model MyProject_MVC.Models.UserRepository
and for the last part, you have to provide and instance of the type UserRepository as the second parameter, not the type itself.

Related

Use NavigateAsync in Prism.Forms with an already existant ViewModel

Scenario: I have view, view model and model for PickList and PickLine. The PickListViewModel contains an ObservableCollection<PickLineViewModel> and the PickList model contains a List<PickLine>. My PickList page contains a ListView which is bound to the ObservableCollection<PickLineViewModel> and if a line is tapped NavigateAsync is called to navigate to the tapped PickLine.
Normally, when I call NavigateAsync Prism navigates to the page, locates the view model, creates an instance of it and binds this instance to the view. But in this case the view model instance that should be bound to the page is already existant (as an element of my ObservableCollection) and I don't want the Prism ViewModelLocator to create a new instance, due to the fact that it then would have to get data from web service and I try to keep the number of web service calls as low as possible.
Also I can't use models in the ObservableCollection because the view model contains properties which are only used for UI purposes so these properties should definitely not be part of the model, but the UI properties I'm talking about are needed in the PickList page and in the PickLine page.
Tl;dr: Is there any way in Prism.Forms to provide a view model instance
on navigating to a page that will than be bound to it?
Your problem is that you are confusing what a Model is and what a ViewModel is. In this case what you should have is:
PickLine (Model)
PickLineView (View)
PickLineViewModel (ViewModel)
PickLineListView (View)
PickLineListViewModel (ViewModel)
Containing your ObservableCollection of PickLine not PickLineViewModel
Without seeing your precise code, I'm going to take a guess from experience here that your code probably looks something like the following in principal:
public ObservableCollection<PickLineViewModel> Items { get; set; }
public DelegateCommand<PickLineViewModel> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
var picks = _dataService.GetPickLines();
Items = new ObservableCollection<PickLineViewModel>(
picks.Select(x => new PickLineViewModel
{
Model = x,
ItemTappedCommand = ItemTappedCommand
});
}
I see code like the above a lot from people who confuse the difference between a Model and ViewModel, and often from people who don't understand how to properly bind back to the ViewModel from something like a Cell in a ListView.
What you should have instead is code that is more like:
public ObservableCollection<PickLine> Items { get; set; }
public DelegateCommand<PickLine> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
var picks = _dataService.GetPickLines();
Items = new ObservableCollection<PickLine>(picks);
}
Is there any way in Prism.Forms to provide a view model instance on navigating to a page that will than be bound to it?
No, as far as I know, Prism Forms does not provide view model-first navigation (as opposed to Prism on WPF which does).
You should be able to work around this, though, by passing your existing view model as parameter and making the ViewModelLocator-created view model an "empty shell" that redirects everything to the actual view model.

Model view controller: shall the view know custom data types?

Very simple question: in a strict MVC design pattern we want to keep Model, View and Controller can the View layer know which custom data classes are defined in the model?
As example:
I got a CarViewController in the view layer and a Car object in the model layer. Whenever the model layer changes the controller object that "sits" between the model and the view notifies the CarViewController and in my current implementation passes a copy of the updated car data as an instance of the Car class. Is this correct?
My gut instinct would have said no because I would not want the view layer to know the details of the model objects. It is not strict decoupling. However if I pass specific values instead of passing the custom data model I would need to stick to standard/primitive values (E.g. int as number of wheels) and it may require more coding.
Have I understood MVC correctly? Is there any reason why the view should not know the custom classes of the model layer?
If I am understanding your question correctly, I would say that your view needs to know the details of your Car object in most of the cases. You can utilize metadata in this way like this:
Model:
public class Car
{
[Display(Name = "Number of wheels")]
public int Wheels { get; set; }
}
View:
#model Namespace.Models.Car
#* This will display whatever your [Display(Name="Value")] decorator defines
as a display name, also the editor will respect the data type decorator. *#
#Html.LabelFor(m => m.Wheels)
#Html.EditorFor(m => m.Wheels)
In this case if you basically pass down a primitive then all metadata for your model is lost.

MVC3 System.Collections.Generic.List pass the wrong model item

Hello everyone I have checked my controller and view it seems there is no problem but I get System Colletion error.
Here is my controller
public ViewResult Index()
{
return View(db.banner.ToList());
}
Here is My View
{
#model IEnumerable<icerik.Models.banner>
}
And I get this error
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[icerik.Models.banner]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[icerik.Models.contents]'.
Maybe you have a partial in your main view:
#Html.Partial("SomePartial")
and this partial is strongly typed to IEnumerable<contents>:
#model IEnumerable<icerik.Models.contents>
So make sure that you are passing the correct model to this partial. If you do not specify anything to the Partial helper (as in my example) the main model will be passed to this partial.
So always specify the correct model:
#Html.Partial("SomePartial", SomeModelInstance)

How do you exclude properties from binding when calling UpdateModel()?

I have a view model sent to the edit action of my controller. The ViewModel contains references to EntityObjects. (yea i'm fine with it and don't need to want to duplicate all the entities properties in the viewmodel).
I instantiate the view model and then call UpdateModel. I get an error that a property is "null" which is fine since it is a related model. I am trying to exclude the property from being bound during model binding. On debugging it I see in the entity where the model binder is trying to set the value of the property to null.
Here is my edit action:
var model = new SimplifiedCompanyViewModel(id);
var excludeProperties = new string[] {
"Entity.RetainedEarningsAccount.AccountNo"
,"Property.DiscountEarnedAccount.ExpenseCodeValue"
,"Entity.EntityAlternate.EntityID"
,"Property.BankAccount.BankAccountID"
,"Entity.PLSummaryAccount.AccountNo"
,"Property.RefundBank.BankAccountID"
,"Company.Transmitter.TCC"
};
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
I have looked at a few other issues about specifying a "prefix" but I don't think that is the issue since I am telling it to bind to the viewmodel instance not just the entity object.
Am I excluding the properties correctly? Strange thing is is only seems to happen on this item. I suspect it may be an issue with the fact that there is actually no refund bank related to my entity. But I have other related items that don't exist and don't see the same issue.
More info... since I'm told me model isn't designed well.
The Company is related to a BankAccount. The Company view shows the currently related BankAccount.BankAccountId and there is a hidden field with the BankAccount.Key. I use jQueryUI autocomplete feature to provide a dropdown of bank account displaying the BankAccount.BankAccountId and when one is selected the jQuery code changes the hidden field to have the correct Key value. So, when this is posted I don't want the current bankaccounts BankAccountID modified, hence I want it to skip binding that field.
If I exclude BankAccountId in the model then on the BankAccount edit view the user would never be able to change the BankAccountId since it won't be bound. I'm not sure how this indicates a poor model design.
Use the Exclude property of the Bind attribute:
[Bind(Exclude="Id,SomeOtherProperty")]
public class SimplifiedCompanyViewModel
{
public int Id { get; set; }
// ...
}
This is part of the System.Web.Mvc namespace. It takes a comma-separated list of property names to exclude when binding.
Also you should consider using TryUpdateModel instead of UpdateModel. You can also just have the default model binder figure it out by passing it as an argument to the constructor:
public ActionResult Create([Bind(Exclude="Id")]SimplifiedCompanyViewModel model)
{
// ...
}
A very simple solution that I figured out.
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
ModelState.Remove("Entity.RetainedEarningsAccount.AccountNo");
ModelState.Remove("Property.DiscountEarnedAccount.ExpenseCodeValue");
ModelState.Remove("Entity.EntityAlternate.EntityID");
ModelState.Remove("Property.BankAccount.BankAccountID");
ModelState.Remove("Entity.PLSummaryAccount.AccountNo");
ModelState.Remove("Property.RefundBank.BankAccountID");
ModelState.Remove("ompany.Transmitter.TCC");
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
Another option here is simply don't include this attribute in your view and it won't be bound. Yes - you are still open to model injection then if someone creates it on the page but it is another alternative. The default templates in MVC will create your EditorFor, etc as separate items so you can just remove them. This prevents you from using a single line view editor with EditorForModel, but the templates don't generate it that way for you anyways.
EDIT (adding above comment)
DRY generally applies to logic, not to view models. One view = one view model. Use automapper to easily map between them. Jimmy Bogard has a great attribute for this that makes it almost automatic - ie you create the view model, load up your Customer entity for example, and return it in the action method. The AutpMap attribute will then convert it to a ViewModel. See lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models
Try the Exclude attribute.
I admit that I haven't ever used it.
[Exclude]
public Entity Name {get; set;}

asp.net mvc 2 multiple selection listbox. how to read values in controller

I'm having a hard time getting a fairly simple binding to work.
When editing an employee I want to display a listbox containing all the available roles in the system, (the employee's roles selected), and when saving the employee I want the selected roles to get populated by MVC into the Employee object which comes as an input param to the controller.
I know I can read the comma-separated values from Request.Form, but I'd rather not touch the Request object directly in my controllers as this makes them harder to test.
What is the best way to get MVC to supply me with the list of roles as an input param?
You have two options. The first is to get the values in the controller and put them into the ViewData dictionary, like so:
var list = EmployeeRoles.GetAll();
ViewData["EmployeeRoles"] = list;
Then you access it in the View using:
<%= (List<EmployeeRoles>)ViewData["EmployeeRoles"] %>
This way is kind of ugly because the view then has to know that it can use a 'magic string' to get an object out of the ViewData dictionary, which then has to be casted back into its original type.
The second way is more elegant, but introduces a little more complexity into your code. You create a ViewModel class, or basically a class that encapsulates all the data you want:
public class EmployeeViewModel
{
public Employee Employee { get; set; }
public ICollection<EmployeeRoles> EmployeeRoles { get; set; }
public EmployeeViewModel(Employee employee, ICollection<EmployeeRoles> roles)
{
Employee = employee;
EmployeeRoles = roles;
}
}
Then you would pass it from the controller to the view like so:
return View(new EmployeeViewModel (employee, roles);
On the view side, you'd access it like any other model:
<%= Model.Employee.Name %>
<%= Model.EmployeeRoles.First() %>
This method is more testable, but then you'd have to make a new ViewModel class for anything that requires data from more than one source.
As for returning the data from the view to the controller, the default model binder is actually quite good. You just have to use a HtmlHelper on the View to let it know what it should send the value back as. I don't have my book in front of me right now, but it should be something like this for a textbox:
<% Html.TextBox("Name", Model.Employee.Name) %>
The HtmlHelper will figure out what it needs to send back in order for the model binder to bind it correctly. I don't know what it is for a DropDownBox off the top of my head though.

Resources