I'm trying to pass a list of a few items to a view via the ViewData to create a drop down list. This shouldn't be too difficult, but I'm new to MVC, so I'm probably missing something obvious.
The controller assigns the list to the ViewData:
ViewData["ImageLocatons"] = new SelectList(gvr.ImageLocations);
and the view tries to render it into a drop down list:
<%= Html.DropDownList("Location", ViewData["ImageLocations"] as SelectList) %>
However, when I run it, I get this error:
There is no ViewData item of type 'IEnumerable' that has the key 'Location'.
Any ideas why this isn't working? Also, shouldn't it be looking for the key "ImageLocations" rather than location?
If you use:
ViewData["Location"] = new SelectList(gvr.ImageLocations);
and
<%= Html.DropDownList("Location") %>
Your life will be a lot easier.
Also check out the typo (missing i) when setting the ViewData in your example (ImageLocatons => ImageLocations). This causes the second parameter you pass to DropDownList to be null. This will cause the MVC engine to search for Location.
Is it possible that your ViewData was reset?
Try putting a break point in your View on the line where you emit the drop down list.
Then do a quick watch on ViewData["ImageLocations"].
Make sure that there is a value here when the view tries to use it.
Related
I'm creating my first app in rails.
Basically, I have a new customer form, normally when you enter a new customer you are redirected to the record you created.
However as I am loading all my pages via ajax I want to load the new record in rather than re-direct to it.
I already have the form firing via ajax, I just need to know how I can access the new record URL to load it into my container.
Hope that makes sense. Thanks in advance!
You can add an option :remote => true to your form_for helper method, so that instead of page redirect the form gets posted via ajax.
For Ex :
<%= form_for(#post, :remote => true) do |f| %>
...
<% end %>
Then create a new template named create.js.erb which will get rendered after create method has been executed.
In this template, you can display the details of the new record you created.
For Ex :
$('some_element').replaceWith('<%=#posts.name %>');
Edit: You mentioned using load in your javascript. I would generally avoid this as you're making two round trips to the server. 1) Create 2) Get html to load into a div. Additionally, if there is an error that happens, it will be more difficult to catch and do the "good thing", whatever that might be.
Once you've added the remote: true option to your form, you need to deal with what happens on the server side of things.
Assuming your using REST, you'll have a controller with a create action in it. Normally, this method is creating the item and then subsequently returning the HTML for the create page. Instead of this, we need to create a javascript view for the action. This will tell the calling page what to when this action is hit.
Let's assume your form has a div called "new_record_form" and that you have another div called "new_records". You'll want to blank out the form elements in the form, effectively resetting it. You'll also want to add the new record to the "new_records" div.
To add the record to the new records div, you might do something like this.
$("#new_records").append("#{#new_record.name}");
When you submit the form, you should see this added. One great way to debug issues is to use the inspector. If you're in chrome, right click anywhere, inspect element and select network. Do this prior to form submission. You'll be able to see the ajax call and the response. Your logs will also be helpful.
Don't forget to blank out the form as well.
Note: You mentioned all your pages are ajax, but I highly suggest you evaluate if this makes 100% sense due to the various issues that result. Not saying this is the best article on the subject but might be worth a read.
So normally I am doing something like this:
#Html.EditorFor(m => m.MyDateTime)
Then I have a custom template DateTime.cshtml that is used as the editor.
Whatever the date value of Model.MyDateTime is will be displayed as expected, and as expected the name of the field on the next POST will be MyDateTime.
My desire is to use the custom template in the Html.EditorFor WITHOUT binding in the model object, instead I wish to give it a form field name to be POSTed but have it start out blank.
However I can't find an override of Html.EditorFor() that will allow me to not specify a model object, so I can only specify the template to use and the html form field name so it starts empty.
Note: I tried #Html.EditorForModel("DateTime", "MyDateTime") but just got an error so I think that I misunderstood what that is for.
(I know I could just have MyDateTime be null coming back from the controller but that is not what I am asking here.)
Why would you want to use an EditFor that is going to edit nothing (no model passed)? Instead of going down that road, you should probably look at using a View or PartialView which do not require having a Strongly-Typed model.
I would like to pass the file name of a partial view as data retrieved from the viewbag as such:
<div id="Zone1">#Html.Partial(ViewBag.ZoneControl1)</div>
Where the "ZoneControl1" property of the ViewBag is the name of the desired partial view retrieved from elsewhere (i.e. database, web service, etc.). If I include the text as a literal string i.e.:
<div id="Zone1">#Html.Partial("Controls/MyPartial")</div>
It works just fine. If I pass that literal string as a property of the ViewBag from the controller, or even just create a variable in the consuming view, i.e.:
#{string zone1 = "Controls/MyPartial";}
<div id="Zone1">#Html.Partial(zone1)</div>
It doesn't work. The page appears to be loading but never displays anything in the browser. Again, this works fine if I hardcode the partial view name, but not if it is passed as data from a variable or property. Anyone know why this is happening? If this is intended or unavoidable behavior, is there a workaround?
You can't use dynamic in Html.Partial (which is what ViewBag is) because it accepts only strings. One quick way around this would be to cast your ViewBag.ZoneControl:
#Html.Partial((string)ViewBag.ZoneControl1)
As for the second part (zone1 = "Controls/MyPartial") I was unable to duplicate that.
The following code is what I wrote to test it and it works just fine.
#{ string zone1 = "Controls/MyPartial"; }
<div>#Html.Partial(zone1)</div>
I assume the answer with casting the ViewBag is what you're really looking for in this case.
Well, I have it working now and I'm not exactly sure what fixed it. I copied the razor code\markup and deleted that view and created a new view and pasted in the old code. The only difference was that when I created the new view, via the wizard, I specified to NOT use a master page and the resulting page had code to specify:
#{
Layout = null;
}
The original page was created using a master page and then I changed my mind and took out the layout directive entirely. Anyway, after making those changes, it WORKED! So, I initially surmised that the reason was that a view must specify "layout = null" if not using a master page. BUT, I then took out the "layout = null" code in this new page and it still worked! So... not sure what went wrong, but to sum up:
As #BuildStarted correctly noted, you can use a property of the ViewBag object as the partial view path, but you must cast it as a string for it to work properly. So, the premise for this question was incorrect and something else was mucking things up. Just not sure what.
I have the following partial view which renders a drop down list:
#model MartinDog.Core.Models.Section
#{
#Html.DropDownListFor(x=>x.Name
, new SelectList(Model.Dock.DockTemplate.Columns,
"Id", "FriendlyName",
Model.DockTemplateColumn.Id.ToString())
, new { #id = "ddlb_dockTemplateColumns" +
Model.Id.ToString()})
}
I render it on my page like so:
#{Html.RenderPartial("_Admin_Page_DockTemplateColumnDropDown", Model);}
The partial view is rendered once for every Section object. A Section object one I've created and is editable in a jquery dialog box (change the name, display order, dock template column, etc.)
On the test page I am using, this Section dialog box is rendered four times (as there are four of them in my parent object).
The problem:
*The SelectedValue in the SelectList for the drop down never gets set* - that is to say, the correct item in the drop down list is never selected when the dialog is displayed and I can't quite work out why.
I thought it might be because the drop down is rendered four times, so I tried rendering it for just one of the 'Sections' but still the same problem.
Anyone know what I can do?
***edit
Not sure if I'm doing it in a sucky way. I had thought of building the dialog just once with jquery and json but I'd prefer to do it this way as it just feels cleaner.
I do this:
In controller action (Edit for example):
ViewData["ProvinceID"] = new SelectList(dc.Provinces.OrderBy(p => p.NameAr), "ID", "NameAr", factory.ProvinceID);
and Markup:
<%: Html.DropDownList("ProvinceID") %>
See? so it is a list of factories and it has a Province field and what I want you to notice is the 4th parameter in the SelectList constructor, I passed factory.ProvinceID so the DropDownList knows which option to be set on. Otherwise the DropDownList will show the default value (the first one).
P.S: It is your job to change to Razor syntax; I don't use it.
Hope that helps.
Doh... fixed - totally my own fault.
I had set up my html.dropdownlistfor like so
#Html.DropDownListFor(x=>x.Name,
When it should've been like so:
#Html.DropDownListFor(x=>x.DockTemplateColumn.Id,
Setting the first argument to x=>x.DockTemplateColumn.Id (which uniquely identifies the items in my list) instead of x.Name fixed the issue straight away.
Just thought I'd post it here in case someone else makes the same mistake I did.
edit
Found the answer here:
C# mvc 3 using selectlist with selected value in view
I have what appears (to me anyway) to be a strange problem...
I created a simple editor template for a SelectListItem (SelectListItem.cshtml in the ~/Views/Shared/EditorTemplates folder), for example:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#Html.EditorFor(c => c.Categories)
</ul>
Where c.Categories is an IEnumerable
This worked fine, but I wanted another template to render the collection with slightly different markup, so I copied and renamed the editor template to, for example, 'CategoryIcons.cshtm' and invoked as follows:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#Html.EditorFor(c => c.Categories, "CategoryIcons")
</ul>
In short, the only difference is I'm specifying a named editor template.
When I open the page, I now get the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[System.Web.Mvc.SelectListItem]', but this dictionary requires a model item of type 'System.Web.Mvc.SelectListItem'
The template's model declaration, in both templates id:
#model System.Web.Mvc.SelectListItem
I don't understand why the default template works and the named template doesn't. Any help would be appreciated.
Thanks.
When you call #Html.EditorFor(c => c.Categories) it is falling back to the default template for IEnumerable. This default template is provided by the MVC framework, and its behaviour is to output Html.EditorFor() for each item in the enumeration. That in turn emits the appropriate editor template for each item in the list individually - in your case they're all instances of SelectListItem, so in the first case, the SelectListItem template is used for each item.
In the second case, by explicitly setting your EditorFor to use a particular editor template CategoryIcons, you are telling it to use that editor template for the whole enumeration, instead of allowing the enumerable to be templated by default, in turn using the template for each enumerated item.
I'm not sure of the best way around this just yet.
One approach would be to define a CategoryIcons template, whose model is an instance of IEnumerable<CategoryIcon>, which simply foreaches the Model enumeration, and performs Html.EditorFor for each item, with an explicit template reference of CategoryIcon. You then put your per-item editor template in that template (CategoryIcon not CategoryIcons). You would then call this by doing #Html.EditorFor(c => c.Categories, "CategoryIcons").
I'm going to have a look around to see if there are better ways to get this done, but I hope this might be useful for now. It would be great if templates could be parameterized, so you could write an IEnumerable template that takes as an argument the name of the template to use for each of its items.
Just an update, I stumbled on this question trying to address the same issue myself.
What I ended up doing was iterating through each instance of the collection and calling the EdtorFor individually, sort of like this:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#for (int i=0;i<Model.Categories.Count;i++) {
#Html.EditorFor(c => c.Categories[i], "CategoryIcons")
}
</ul>
Still not clean, but I like it better than inheriting a new class like you ended up doing.
(Sorry if my C# syntax is a little off, I write in vb.net)
If you're using EditorFor, I don't think the looping solution will work. It seems that using the IEnumerable template is the only way for the form inputs to be named correctly; if you just call EditorFor repeatedly, then your form <INPUT>s will have the same ID, rather than indexed IDs.
I experienced this issue and the solution was to emit the enumerable template and not loop through the list items.
Sorry to make this an answer not a comment -- don't have commenting rights.