I think there is a simple answer to this, but for some reason I am hitting a writers block today. I have an app which people can search (like any app :) ) and generate a list view. From that list view, you can view details, which will take you to a details view. No JS, just another page. But, what I would like to do is to have a back/next button within the details view so that you can parse the result set without having to go back to the list and then select the next record.
I have had two ideas on accomplishing this. Idea 1) Have a next/previous record field in my custom class. However, this wouldn't work as the search query will not necessarily return consecutive results and I would have to query the DB each time to get those next/prev listings. Idea 2) Have a list of IDs or some custom object passed in the view state for each detials view corresponding to the search results. On this one, I am not sure of the 'overhead' associated with this method.
Any suggestions from the SO community?
Thanks!
Construct the link that opens the details view so that it has next and previous parameters that contain a list of the previous/following elements as well as the id of the current element. Probably easiest to do if you iterate through the list model data by index rather than using foreach -- that way you just reference the previous/next element relative to the current element when finding their ids. Make sure to handle the first and last elements correctly.
In your controller action the next/previous parameters should be nullable -- if you don't get there from the list, you don't render the next/previous buttons. You could "post" the details view action and put the extra parameters in the form rather than the URL so the user can still bookmark the url. Note that this has it's own issues though with regards to the browser's back/forward button and refreshing.
<% for (int i = 0, len = Model.Count(); i < len; ++i)
{
Foo model = Model.ElementAt(i);
string prev = i == 0 ? null : string.Join( ",", Model.Take(i).Select( m => m.ID.ToString() ).ToArray() );
string next = i == (len - 1) ? null : string.Join( ",", Model.Skip(i).Select( m => m.ID.ToString() ).ToArray() )
%>
<% using (Html.BeginForm("Details","Bar", new { id = model.ID } )) { %>
<%= Html.AntiForgeryToken() %>
<%= Html.Hidden( "Prev", prev ) %>
<%= Html.Hidden( "Next", next ) %>
<input type="submit" value="Details" />
<% } %>
<% } %>
I would probably also use some jQuery to change the input button to a link that submits the enclosing form with a click event handler, but that's just my preference for the UI.
Related
I have a view with a bunch of elements in it.One of them (a div) is shown depending of a value that changes inside a select_tag (also within the same page).
I'm getting the selected ID from the select_tag element
$('#some_id').on('change',function(){
//$(this).val()
})
but then just don't know how to fetch the object and check for one of its properties and that way know if I should show the div or not?.
I thought of sending that id to the server...do whatever I need over there and then come back to the view and try something like this
<% if some_condition %>
<div>
...
</div>
<% end %>
This (of course) might not be the way.I'd be glad to understand how this happens
You way of doing it after getting some data from the server onChange of the select would work. You might also consider this approach.
But if you could pre-populate the divs and add a class or some other attribute it with which you could relate to the chosen option in the select field, then you can prevent the server call entirely.
<div class="opt1Val selectOptsDiv"> </div>
<div class="opt2Val selectOptsDiv"> </div>
<div class="opt3Val selectOptsDiv"> </div>
hide all these divs initially with css display: none;.
$('#some_id').on('change',function(){
var selectedOptionVal = $(this).find("option:selected).val();
$("." + selectedOptionVal).show();
})
But I guess you would be able to do this if the showing and hiding of the div's depend only on the selected option value and do not have to go through some other processing at the server end.
I have the following form:
<li>
<% using (Html.BeginForm("TestMethod", "MyController", FormMethod.Post, new {id = "TestMethod"}))
{%>
<%= Html.Hidden("model", Model.MyListOfObjects) %>
<%}%>
Test
</li>
And the javascript function for the onclick is as follows:
function SubmitForm() {
document.forms["TestMethod"].submit();
}
I am trying to pass the list of objects from the view into the controller, but i have yet managed to get this to work. My Controller function is:
[Authorize]
[HttpPost]
public ActionResult TestMethod(List<Objects> model)
{
dynamic Expando = new ExpandoObject();
Expando.test = model;
return View(Expando );
}
When I view the List of objects in the debugger it always displays "System.Collections.Generic.List`1[]" with no actual objects inside.
So my question is what should I be doing to pass a List of objects into a controller?
I have also tried:
<% using (Html.BeginForm("TestMethod", "MyWork", FormMethod.Post, new {id = "TestMethod"}))
{%>
<% int itemx = 0; %>
<% foreach (var x in Model.MyListOfObjects)
{%>
<%= Html.Hidden("model"+"["+itemx+"]", x) %>
<%itemx++; %>
<% } %>
<%}%>
You cannot just put List<object> as action parameter and expect the model binder to be able to automagically guess what object types you want to put there. You will need to write a custom model binder if you wanted to handle multiple sub-types as illustrated in this post.
And if you want to use a single type for the list such as List<MyViewModel> then simply loop through each element of the list (respecting the convention) and for each element build a hidden field for each property that you want to bind.
But since those are hidden fields, I guess that the user is not supposed to modify them. In this case those hidden fields have nothing to do in your view. Let's not reinvent the ViewState that we were all so happy to get rid of when we moved to ASP.NET MVC from classic WebForms. Simply put a hidden field containing an unique id that will allow you to refetch the corresponding list elements in the POST action given this unique id from wherever you fetched them initially (your database or something I suppose).
You need to have one hidden element for each object in the list, and named model[0], model[1], etc.
I have a search page that display a search result. The search result is a list of persons that matched the specific search. I'm iterating through this list displaying them in a table. As headers for this table I want the DisplayName from the model. If I don't inherit IEnumerable I wouldn't be able to iterate through the list. I'm new at this MVC thing =)
I iterate through the result like this:
<% foreach (var item in Person) { %>
<%: item.surname %>
<% } %>
But how do I print the "DisplayName" of an attribute without iterating through the whole list? I would just like to do:
<%: Html.LabelFor(m => m.surname) %>
If it's any help I inherit an IEnumerable at the top of the page:
<%# Page Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>" %>
Edit
I want to display "Your surname" but I don't know how to access it from the view.
[DisplayName("Your surname")]
public object surname { get; set; }
Here's a very similar question that hasn't been answered either: Can I use LabelFor() when the page inherits from IEnumerable<T>?
If you only need to display specifics of one person; you should consider sending only one person to the view instead of a complete list of persons. In that case
Model.Surname
would work just like that. So instead of:
Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>"
do
Inherits="System.Web.Mvc.ViewPage<RegistryTest.Models.Person>"
In this case a single person is loaded into your Model and Model.Property works fine. If you want an IENumerable<>, think about what that means. You are sending a list of persons, so the only thing in your "Model" is a IENumerable<> of persons. There is no way that the view can know what you want if you call Model.Property, since in the Model there are multiple Objects and the view doesn't know which Object you want to get the Property from.
Bottom line, if you want to call Model.Property (Model.surname) but also want to send an IENumerable you are having a design flaw. If you send a list you should want to do something with the complete list (iterate through and do something with the contents). If you just want to do something with one person in that list, re-design your view/controller and send that single person; then you can use Model.Property.
//EDIT BASED UPON COMMENTS
As I see it now you either want to do one of those two things (I do not know which):
Show the records of an item in your list in a table and put the DisplayName of the current object shown in the table in the header.
Show all items of the list in your table and put some sort of DisplayName in the header. This makes less sence but it could be that you mean to name your list.
Situation 1
This is working as the rest of your code? The following would work just fine.
<% foreach (var item in Model) { %>
<table>
<th>item.DisplayName</th>
<tr>
<td>item.Property1</td>
<td>item.Property2</td>
</tr>
</table>
<% } %>
Situation 2
If you want a DisplayName of the list (??) you should create a ViewModel containing the IENumerable of Persons and a public string ListName. Now you can do something like:
<table>
<th>Model.ListName</th>
<% foreach (var item in Model) { %>
<tr>
<td>item.Property1</td>
<td>item.Property2</td>
</tr>
<% } %>
</table>
this would create a table with the name of your List (given in the ViewModel) as header and as items in the table you have your persons.
Design problem?
However, I would love to see you write some more information in your question above. Give us some more information on what you want to do. Do you want to show records of each Person in the list one-by-one? In that case I would recommend you create a Partial View where you put your table. Then you would get something like:
<% foreach (var item in Model) { %>
<% Html.RenderPartial("TablePerson",item); %>
<% } %>
tableperson.ascx:
...
Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>"
...
<table>
<th>Model.DisplayName</th>
<tr>
<td>Model.Property1</td>
<td>Model.Property2</td>
</tr>
</table>
So, we need more information I'm afraid :)
If it's a collection with every entry having the same surname then try model[0].surname or model.ToList().First().surname
You don't need ToList() if its a List<T> already. Then it would be just model.First()
You are specifying that the page model is IEnumerable, and you say you would like to print a property of an element. Since you have a list of elements, you need to specify which of the elements you would like to retrieve the property from.
I you want a specific index in the list you will need to convert the IEnumerable collection to IList (ToList()), depending on the criteria, you may also be able to find the required element using something like a LINQ Single() operation.
Otherwise you could select the property from all the element in the list using Model.Select(m => m.PropertyName) which will give you a list of just this property, and then concatenate this list to a single string.
I've seen plenty of examples (NerdDinner, Sanderson's Sports Store, etc.) where a view is bound to a collection of objects. The syntax in the view is usually something like this...
<%# Page... Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyViewModel>>" %>
...
<% foreach (var myViewModel in Model) { %>
I've also seen plenty of examples of inserts or updates where the controller automatically binds the model parameter to the form elements in the view.
I'm looking for a mix of the two techniques where my view has form elements pertaining to a collection of myViewModels where each myViewModel has 3-4 properties. The intent is to allow the user to enter a set of these in one take.
Assuming this is possible, can anyone help me with the syntax? I can't figure out how to label the form elements to make the binding work.
This is possible through the built-in model binder, but you have to do a little bit of convention-based naming of your form objects. First, your action needs to take a collection:
[HttpPost]
public ActionResult CreateFoos(List<Foo> foos)
{
// I have a list of the foo objects that were posted
}
And then, in the view, let's say you wanted to make a form for each object:
<% for (int i = 0; i < Model.Count; i++) { %>
<%: Html.TextBoxFor(x => x[i].Property1) %>
<%: Html.TextBoxFor(x => x[i].Property2) %>
<%: Html.TextBoxFor(x => x[i].Property3) %>
<% } %>
Pay attention to how the controls are rendered in the HTML, because in your "create" view, you might want to have a javascript button that allows the user to add another record, and you'll have to increase the index for each additional control. It's not too hard, but I just wanted to warn you to pay attention to the source it actually generates.
The definitive answer is here: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
You need to name each field as if it were part of an array: "PropertyName[i]"
I'm with a problem, I have a ajax link that pass a parameter, but, the page that it opens does not need that parameter. The page only load 2 partial views, one of those need that parameter passed to the page to load the data correctly, and the other just need to load a form, so, don't need that parameter. How can i acheive this?
In order to do what you want, you will need to add the id to the ViewData construct.
var sysfunctions= UnisegurancaService.FunctionsRepository.All();
ViewData["NeededID"] = id
return View(sysfunctions);
then in your view where you render the partial
<%= Html.RenderPartial("GridFunction", (int)ViewData["NeededID"]) %>
Cast as required of course.
Whatever gets pushed in as the second param becomes the .Model in the partial. I would suggest also strongly typing your partials.
Try this:
<% Html.RenderPartial("GridFunction", new ViewDataDictionary {{"Id", ViewData["Id"]}}); %>
UPDATED:
And add this in your controller action:
ViewData["Id"] = Id;
UPDATED:
And in your GridFunction partial View you can access Id as:
<%= ViewData["Id"] %>
//Controller
public ActionResult EditFunctions(int id)
{
var sysfunctions= UnisegurancaService.FunctionsRepository.All();
return View(sysfunctions);
}
// This is the controller (it does no need the parameter "ID")
//This is the view "EditFunctions"
<div id="formFunction">
<% Html.RenderPartial("FormFunction"); %>
</div>
<div id="gridFunction">
<% Html.RenderPartial("GridFunction"); %> // The grid needs the ID to work correctly but its in the parent page not in the partial call....and the call is an ajax call
</div>
If some dependency of the page needs the parameter, then the page needs to know enough to pass the data in, so the page should be able to provide the data. Or, more simply, just add the parameter to the Page's viewdata and be done with it.