I am using MVC 3 w/ Razor and using the new dynamic ViewBag property. I would like to use the ViewBag property with the EditorFor/LabelFor Html helpers but can't figure out the syntax.
The View does have a #model set, but the object I am trying to use is not part of that model. I am aware I can create a ViewModel but that is not what I am after.
Can anyone help?
Controller:
var myModel= _repo.GetModel(id);
var newComment = new Comment();
ViewBag.NewComment = newComment;
return View(myModel);
View:
#model Models.MyModel
#(Html.EditorFor(ViewBag.NewComment.Comment))
I haven't tried it, but this should work I think.
#(EditorFor(m => ViewBag.NewComment)
It is possible to use a Linq-to-SQL syntax, but use a completely different object on the right side.
Not knowing what your Comment Model looks like, my gut reaction would be to just do:
#Html.EditorFor(ViewBag.NewComment)
However, because ViewBag is dynamic, you may need to cast NewComment before you use it, in order to get the EditorFor magic.
#Html.EditorFor(ViewBag.NewComment as Comment)
Update
Strike that, EditorFor can only accept an Expression as a parameter, and that Expression must return a property of the page model. I don't think EditorFor or EditorForModel are going to be of any use to you if you don't want to use a ViewModel. Have you considered switching the roles of whatever it is you're using the Model for, with that of the ViewBag?
If for some reason I need to use ViewData to pass the model into my view I do the following to allow for the Html.DisplayFor() helpers.
In the views code block I cast the ViewData model object to its underlying type
var newCommentModel = ( NewComment )ViewBag.NewComment;
Then I assign the following expression to the helper using the strong-typed reference
#Html.DisplayFor( model => newCommentModel )
The expression tree now contains a strongly-typed model and the DisplayTemplate is correctly displayed.
Related
I'm using
ViewBag.Something = session.Query<Something>().ToList();
To pass information from class Something to View and use it in selectList
#Html.DropDownListFor(model => model.model, new
SelectList(ViewBag.Something, "Id", "name"), "--Smthing--")
Is that bad? and how can i change it to be better?
The practice is not at all bad but it is recommonded to have a List property in our mode it self. in your case something like
public ActinResult YourActionMethod()
{
YourModelObject.Something = session.Query<Something>().ToList();
// And return your view after further code statements
return View(YourModelObject);
}
Infact instead of the original list object you cancreate a SelectList object where you can easily bind key and value and send it to view. By this way you can add all your business and build your model at once place and can populate it from the location you want to. Later View will just use that model rather than applying some more intelligence to it. It helps alot as all your values resides under same object rather than some in Model and some in ViewBag.
Secondly this practice is also fine if you don't have this list at more than one place and you are not reusing this model in any views. Also if you want to access this property outside your view e.g. in Layout or in some parent view which is consuming your view.
You can take a look at following post which explains how to bind a list to your model.
Setting default selected value of selectlist inside an editor template
I prefer to add all my properties to the model of the view, but the ViewBag can be useful to add things that don't quite fit in the model. It can also be useful when binding to elements in the layout since it doesn't have access to the view's model.
I Have a List like
IEnumerable<SelectListItem> Users;
I can populate the Users Items in Dropdownlist by 3 way
1-Use ViewModel
Public class myViewModel
{
IEnumerable<SelectListItem> UserList;
}
and fill it like
viewmodel.UserList=GetUsers();
2-Use from ViewBag
ViewBag.UserList=GetUsers();
3-Use from ViewData
ViewData["Users"]=GetUsers();
What is Difference between my ways and which one is better
There's a fourth way, which I think is the best way to go.
Since you only have one object (of type IEnumerable<SelectListItem>) you could just pass it to your view as the model (no need for an intermediate ViewModel).
In terms of the possibilities, there's no real difference. The difference is that your first method and the method I just described are strongly typed, meaning you get Intellisense and compile-time validation whereas your second and third method are weakly typed and you get no intellisense and no compile-time validation.
In your case, better to use ViewModel because it's clean MVC and you get strongly-type benefits.
ViewBag and ViewData are better, for example, if you have a lot of partial views in your view, or difficult layout which need passed data. But as I understood, you need only to show dropdownlist, so use ViewModel.
In my controller I have the following:
ViewBag.VendorName = vendorname;
In my View I have the following:
#Html.TextBox("VendorName")
It appears that the TextBox VendorName is being populated with the content of ViewBag.VendorName.
This is what I want but didn't know that from a ViewBag you can directly populate a TextBox.
Is this expected and why does this happen?
According to Steve Sanderson in Pro MVC:
HTML helper methods populate their value from the following places (in this order):
ViewData.ModelState["VendorName"].Value.RawValue
For string based helpers, the value parameter passed to the helper method, or if you didn't supply one, then ViewData.Eval["VendorName"] (and ViewBag.VendorName as you're seeing here)
For strongly typed helpers, the corresponding property value on your Model object
ScottGu in this post link text shows how one can utilize EditorTemplates for things such as a Country DropDownList. My question is how can one pass a dynamic list of Countries to the EditorTemplate?
Even better you make the partial view strongly typed and pass the model to the EditorFor helper
#Html.EditorFor(m=>m.SelectedCountry, Model.AvailableCountries)
Probably the most elegant solution is using a Custom Attribute, you can later access Model metadata using: ViewData.ModelMetadata.
e.g:
[Foreign(Type="DropDown", TableName="Countries")]
public int IdCountry { get; set; }
where ForeignAttribute is a class you must declare, and later use it to build your editor template.
You can pass it in ViewData and feed ViewData from and ActionFilter if the data is required very often (although arguable it is an anti-pattern).
Similar to #Benja's answer
You can also use the [AdditionaMetaData(key,value)] attribute in a similar fashion without having to define your own attribute. Key and value have to be strings.
The extra data can be retrieved in the view with: #ViewData.ModelMetadata.AdditionalValues["DropDownData"]
If i got view which inherits from:
System.Web.Mvc.ViewPage<Foo>
Where Foo has a property Bar with a type string
And view wants to render strongly typed partial view which inherits from:
System.Web.Mvc.ViewUserControl<string>
like this:
Html.RenderPartial("_Bar", Model.Bar);%>
Then why it will throw this:
The model item passed into the dictionary is of type 'Foo'
but this dictionary requires a model item of type 'System.String'.
when bar is not initialized?
More specific: why it passes Foo, where it should pass null?
As #Dennis points out, if the model value is null, it will use the existing model from the view. The reason for this is to support the ability to call a partial view using a signature that contains only the partial view name and have it reuse the existing model. Internally, all of the RenderPartial helpers defer to a single RenderPartialInternal method. The way you get that method to reuse the existing model is to pass in a null value for the model (which the signature that takes only a view name does). When you pass a null value to the signature containing both a view name and a model object, you are essentially replicating the behavior of the method that takes only the view name.
This should fix your issue:
<% Html.RenderPartial( "_Bar", Model.Bar ?? string.Empty ) %>
Look at ASP.NET MVC source (HtmlHelper.cs -> RenderPartialInternal method -> line 258):
...
if (model == null) {
if (viewData == null) {
newViewData = new ViewDataDictionary(ViewData);
}
...
this is exactly your case. ASP.NET MVC uses the ViewData from your ViewContext
UPDATED:
Try this instead:
<% Html.RenderPartial("_Bar", Model.Bar ?? "Default" ); %>
If you pass null as the model to RenderPartial, then it will look at the original model, which is why the error says foo.
You'll need to make sure that bar is initialized to be an empty string instead of null.
Edit: #Arnis, look at the source code. It doesn't lie. You are passing null to the overload of RenderPartial. You are not passing Foo. Internally, the system uses the Model from your page's ViewContext (which is Foo) when you pass a null Bar to RenderPartial.
Though this has been answered, I ran across this and decided I wanted to solve this issue for my project instead of working around it with 'new ViewDataDictionary()'.
I created a set of extension methods:
https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
I also added some methods that don't call the partial if the model is null, this will save a lot of if statements.
I created them for Razor, but a couple of them should also work with aspx style views (the ones that use HelperResult probably aren't compatible).
The extension methods look like this:
#* calls the partial with Model = null *#
#Html.PartialOrNull("PartialName", null)
#* does not call the partial if the model is null *#
#Html.PartialOrDiscard("PartialName", null)
There are also methods for IEnumerable models and the discard ones can also be called with a Razor lambda that allow you to wrap the partial result with some html.
Feel free to use them if you like.