not sure how to word this correctly..
so i have a view which has a strongly typed viewmodel as:
class MyViewModel
{
public string MyName get; set;
public string DateOfBirth get; set;
public Address MyAddress get; set;
}
class Address
{
public string Street get; set;
etc...
}
i'm loading the inital view with MyViewModel and using the following to display a partial view to load the address (this way so the address input can be reused).
the view contains a dropdown list with a list of user names in, selecting a value in the drop down calls a ajax .change function which does the following:
$.get('/User/DisplayAddress/', {'selection': selection }, function (html) {
$('#addressBlock').html(html);
});
that all works great..and the html is loaded in.. however, the viewmodel and the address is now disconnected. so when i submit my page, the MyAddress from the ViewModel now contains a null.
How should i be correctly doing this in mvc2 / ajax?
another approach i used was to use the '<% RenderPartial("viewname", Model.MyAddress); %>
which works but i still have to return the data in json and add the values to the fields manually in a java function - this works well.. but its very messy when MyAddress field my contain several fields and to hardcode adding the value into the input fields just looks horrible.
the problem was the binding got screwed when insert the HTML from the partial.
my partial view didnt follow the same namespace so the input fields lost the binding to the parentview model.
i.e. my textbox now has the naming convention MyViewModel.Address.Street etc.
i used the jquery approach to replace the div container with the HTML. the problem was the binding had got screwed when displaying the html.
i set up the 'name' element of the textboxes to use the same as the inputmodel and this kept the binding when inserting the partial.
ideally, i would use a 'flat' viewmodel instead of this 'inheritance' approach but i wasnt able to in this instance.
Related
I have an asp.net MVC 5 site.
When I scaffold a view it always generates EditorFor on each property.
What data annotation can I use to get it to generate DisplayFor on some fields (as they only need to be displayed not edited)?
Weirdly I can find nothing on Google about this.
You could apply a little trick using the UIHintAttribute.
First, create a new EditorTemplate in your Views\Shared\EditorTemplates folder, with the name DisplayOnly.cshtml, the only content is the line
#Html.DisplayForModel()
Then, mark your Attributes which are never edited but displayed, with the UIHintAttribute:
public class MyViewModel
{
[UIHint("DisplayOnly")]
public string OnylyDisplayed { get; set; }
public string Editable { get; set; }
}
Even if EditoFor(...) is scaffolded, the Template engine will route you to the Display Template.
EDIT
I was sure this would work this way, but I had issued getting it working the first time; interestingly, something like #Html.LabelForModel() in the Template works. Maybe I'm missing something. What will work is, if you need a simple output, to just have #Model in the template. You could use multiple different Editor Templates which act as read only, for different types.
I have a table with a dropdown list in each row, like this:
#Html.DropDownListFor(m => m.Transactions[i].CategoryID, ...)
and everything mostly works. I can select items, submit the form, and the model has my updated selections. So far so good.
The problem is that this isn't very reliable. Since the name of each dropdown is based on an index rather than an ID that means the match-up between the post values and the actual items living in the database are based on indices. Most of the time that works fine, but what if the list of items changes between the time the page loads and the time a user does a postback? The indices have changed, which means the post data won't match up correctly, and bad things happen. OR I've seen the browser incorrectly try to preserve selections in dropdowns between posts, but because the list of items is changing (what may be item #2 now may be item #3 by the time the page is refreshed) and everything is based on indices, the wrong dropdowns get the wrong values.
So basically, how can I force the dropdowns to generate a name and ID that looks more like this:
Transactions_CategoryID_12385652 // 12385652 is the CategoryID
rather than this:
Transactions_4_CategoryID // 4 is the array index
and still have the benefits of automatic binding?
Edit: The second issue I mentioned (input values being restored incorrectly after a refresh) seems to only happen with Firefox. https://bugzilla.mozilla.org/show_bug.cgi?id=46845
You'd have to write your own custom model binder as well as a Html extension to generate the element names in this way, or you could generate the markup manually using Razor.
Is there a particular reason you want need to do it this way? You're almost always following the conventions of the framework unless there's a good reason not to.
You can pass the exact collection you want to the view and bind that to the DropDownFor html helper.
Say you have a Person.
public class Person
{
public int Id { get; set; }
public int Name { get; set; }
}
And you want to add a new person. Create a view model. Create a property in this view model of type SelectList. This select list will hold the collection of the model you want to populate the dropdown list with.
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public SelectList MySelectList { get; set; }
}
Let's say you want the drop down list to hold the Person's Id as the selected value and the Person's name as the text value. You may want to create a second view model to represent that or use an anonymous object. Let's call the collection myCollection. And let's say that the collection is made up of objects that have two properties (and Id and Name property). Now simply pass this view model with a value for MySelectList.
var viewModel = new MyViewModel
{
MySelectList = new SelectList(myCollection, "Id", "Name")
};
In your razor view you can set up the #Html.DropDownListFor like so:
#Html.DropDownListFor(m => m.Id, Model.MySelectList)
In order to pass a select value to the view for the dropdownlist simply use the SelectList constructor overload that allows this value to be passed:
new SelectList(myCollection, "Id", "Name", selectedValue)
I am working with a ASP.NET MVC4 application. I have created a view model which contains menu items and I can switch languages in page by Resources file.
#region Properties
[Display(Name = "MenuText", ResourceType = typeof(App.App_Resources.Menu))]
public string menuText { get; set; }
public List<MenuItem> menuItems { get; set; }
#endregion
However, I want to get this resource string in my .cshtml file, then I try as following
#model App.Models.MenuViewModel
#Html.LabelFor(model => model.menuText) <- Success
#Html.DisplayForModel("menuText") <- Success
#Model.menuText <- Fail
I inserted a break point and found out that Model contains a property which name is menuText but value is null. And I checked that Html also contains a property Model and its menuText also is null.
However, menuItems has items since I assign objects in constructor.
Why the menuText cannot be initialized and assigned value to it?
Why I can succeed to show the resource string with first two but Model.menuText is null and fail to show anything? What is different between the models in #Html.XXX and #Model?
#Model.menuText retrieves the raw string value stored within the menuText property. Attributes are ignored.
Using LabelFor causes the Display attribute of the property to be examined. The localised string is stored in the attribute, not the property.
Note that I think the Model object/class should not be used to store information for display (that's what ViewData is for), but rather only for round-trip data that is sent from the client to the server.
I have the following editor template called 'DropDown.cshtml'. The list part works fine, and the template uses some voodoo I did to get the required SelectList from ViewData. The controller places all select lists in the view model into ViewData, and there is nothing wrong with the list side of things.
#{
var list = this.GetModelSelectList();
}
#Html.DropDownListFor(m => Model, list)
I use this template on foreign key view model properties like this one:
[Required]
[UIHint("DropDown", "MVC", "SelectListName", "JobLevelSelectList")]
[Display(Name = "Job Level")]
public Guid? JobLevelId { get; set; }
public SelectList JobLevelSelectList { get; set; }
In the controller, JobLevelId has the correct value immediately before executing the view, yet no item it selected in the rendered select element. or rather, the first item in the select list is always selected.
Why does DropDownListFor ignore the property value when used in my editor template and yet work fine when invoked directly?
This is unfortunately a known bug in MVC3 (I haven't tried it in MVC 4 Beta to see if it fixed).
The work around that I have used is to manually set the Selected property accordingly in collection that the DropDownListFor is bound to, it is not ideal but it worked.
I have a controller with typical create methods (one for GET, one for POST). the POST takes a strongly-typed parameter:
[HttpPost] public ActionResult Create(Quiz entity)
however, when the callback is made, the properties of my entity are null... if I redefine it like this:
[HttpPost] public ActionResult Create(Quiz entity, FormCollection form)
I can see that the values are there e.g. form["Component"] contains "1". I've not had this problem in the past and I can't figure out why this class would be different.
thoughts anyone?
The easiest way to get the default model binder to instantiate Quiz for you on postback is to use the Html form helpers in you view. So, for example, if your Quiz class looked like this:
public class Quiz
{
public int Id { get; set; }
public string Name { get; set; }
}
The following code in your view would ensure the values are present on postback:
#Html.HiddenFor(mod => mod.Id)
#Html.TextBoxFor(mod => mod.Name)
Keep in mind that values which need to be posted back but not shown in the view (like identifiers) need to be added to the view with Html.HiddenFor.
Here's a more comprehensive list of Html form helper functions.
I FIGURED IT OUT!!
so, in my model (see comments on #ataddeini's thread below) you can see I have a Component... to represent components I used a couple of listboxes, the second (Components) dependent on the contents of the first (Products). In generating the second list I used
#Html.DropDownListFor(x => x.Component, ...)
which (as shown in one of the above links) generates a form field called "Component"... and therein lies the problem. What I needed to have done is bind it to the the Id of the component instead!
#Html.DropDowListFor(x => x.Component.Id, ...)
hurray!