I'm totally new at mvc razor, so please excuse my unfamilarity with this subject. If I understood well, it is the controller which gets the url parameter, like the following:
// Get: /User/23
public ActionResult(int? id) {
return View("User", id);
}
So basically, this ActionResult gets the parameter in the url and sends it somewhere. But where?
Also, say I have a repository in which I return all the users in an IQueryable<> class. If the id is not null it gets only the relevant result, if not, it gets the whole list. Therefore, I'd like to return a list to the view and show the results. How can I do that? To ease the understanding I give the following example:
// I want to populate this list based on the id in the actionresult
#foreach(var u in MyList) {
name: #u.Name <br>
lastname: #u.LastName <hr>
}
return View invokes a helper method in the base class, Controller.
It will return a ViewResult, with the parameter being the model passed to the view.
So if you want to pass through a list of User's, you would do this:
public ActionResult Users()
{
var users = _repository.FindAllUsers();
return View(users);
}
Note how i left out the View name, so the default convention will be to look for a View based on the action name, in this case Users.cshtml. Always try and use convention over configuration - this is what makes MVC great.
#model IEnumerable<YourApp.Models.User>
#Html.DisplayForModel()
Shared\DisplayTemplates\User.cshtml
#Html.LabelFor(model => model.Name)
#Html.DisplayFor(model => model.Name)
#Html.LabelFor(model => model.LastName)
#Html.DisplayFor(model => model.LastName)
Note how i didn't use a foreach loop - i use i custom display template, again - convention over configuration.
Whatever you return as the second parameter is the model passed into the view. You could then inherit the razor page in WebViewPage, or better, use the #model keyword. The data passed in is available within the page via the "Model" variable.
Good example of usage after that is at http://weblogs.asp.net/scottgu/archive/2010/10/19/asp-net-mvc-3-new-model-directive-support-in-razor.aspx
Related
Let's say I am passing a List of Person as the model to a view page:
#model IEnumerable<Foo.Models.Person>
#foreach (var item in Model)
{
<p>
#Html.DisplayFor(ListOfPersons => item.Id) : #Html.DisplayFor(ListOfPersons =>item.Name)
</p>
}
I just don't get how this Lambda expression works. Why we are passing a IEnumerable and get a single value of on its objects?
As ListOfPersons is not a declared variable, and is just the parameter name for the expression, the expressions are valid.
To briefly touch on expressions, they are composed of both a parameter set and a body.
(parameter set) => (body)
The parameter set can be empty () or include 1 or many parameters (x) or (x,y) for example. The body then can use those parameters similar to how a method body would use parameters passed in.
#Html.DisplayFor(ListOfPersons => item.Id) when used in the scope shown is ignoring the parameter. It doesn't use it, and is similar to something like this
public int Id = 5;
public string DisplayFor(Person ListOfPersons)
{
return Id;
}
So you can see from this aspect that the parameter is not used and the value returned is actually a value from a different scope.
DisplayFor is scoped to use the page's model to bind to. So regardless of the parameter name, what is passed in to the parameter is going to be the model. As such, since the parameter is being completely ignored here, it doesn't particularly matter what it was named and could simply be () or _.
The returned value is then the value from the body, in this case item.Id and item.Name. However, as a result of there being no use of the parameter, the html rendered will be incorrect even though the value shown will be what looks to be accurate.
In order to remedy this, the model must be properly accessed or the rendered html will not be bound on post. This is typically done by iterating and using an index reference, as is shown in #Jonespolis' answer.
use a for loop in razor, so you maintain a direct reference to your model:
#for(int i =0; i < Model.Count(); i++)
{
<p>
#Html.DisplayFor(model => model[i].Id) : #Html.DisplayFor(model => model[i].Name)
</p>
}
#Html.DisplayFor(ListOfPersons => item.Id) : #Html.DisplayFor(ListOfPersons =>item.Name)
The view engine will examine the expression and determine that you want "display" controls for the Id and Name properties. It ignores the "input" variable name (ListOfPersons* in this case) and the item variable and just parses the expression on the right. You could just as well have done this:
#Html.DisplayFor(_ => item.Id) : #Html.DisplayFor(_ => item.Name)
*Note that ListOfPersons in your lambda does NOT reference a local variable - it just creates a new "variable" that you could reference in your lambda. The fact that you seem to have a property or local variable named ListOfPersons is irrelevant.
I have this in my controller:
string PONumber = Request.QueryString["PONumber"];
return View();
My question is how would I use the string in my view ?
I have tried:
#{
ViewBag.PONumber
}
but I get this error:
This operation will be resolved at runtime. Am I not doing this right ?
I have also tried:
<%= PONumber %>
and that also did not work
You need to put the result from your querystring into the ViewBag before you can pull it out of the ViewBag in your view:
// Assign directly to ViewBag
ViewBag.PONumber = Request.QueryString["PONumber"];
return View();
In your view, don't try to print the value by writing it in a code block (#{ }); simply use it where you'd like:
<h2>The PO Number is #ViewBag.PONumber.</h2>
You have a misnomer, when you have a Controller with:
[HttpGet]
public ActionResult GetExample(string PONumber)
{
}
The PONumber parameter, would represent said equivalent for your Query String. So you honestly wouldn't need to manually call Request.QueryString[...].
So you would assign parameter, directly to your ViewBag. However, you could add it directly to a model and display through your model also.
If your wanting to use a ViewBag, you would simply do:
// Front End:
#ViewBag.PONumber
// Back End:
ViewBag.PONumber = PONumber;
Also, make sure when you request your data that it isn't actually a null or empty value.
ViewBag.PONumber = Request.QueryString["PONumber"];
return View();
and then in view
#ViewBag.PONumber
I have a model that has a PersonId int, and also, a List<SelectListItems> People for people. The UI allows me to select a person from the drop down model, and save the value into PersonId.
That works, but if there is an error in my ModelState, caused by another field, I do this:
if (ModelState.IsValid == false)
{
return View(model);
}
Problem is, the object holding the list of people is NULL in the returned model. Do I really need to repopulate it from the database again, or can this somehow be 'stored', and only populated when I initially create the view?
Yes. You need to load the SelectView again.
Only values in your inputs are posted (hidden or normal ones)... so anything to be posted must be in those inputs.
If you want to avoid going to the database... you should cache that list.
Here you can see an example for the caching logic: https://stackoverflow.com/a/349111/7720
Yes, you need to repopulate it. Only the selected value of a dropdown list is sent via a form. It's perfectly normal to do this. In general, if you have data available on the server, it always makes sense to cache it/query for it again, rather than trusting any input from a user.
To conveniently build SelectLists, rather than using SelectListItem, I use a method on a base controller:
[NonAction]
public SelectList BuildSelectList<TSource>(IEnumerable<TSource> source,
Expression<Func<TSource, int>> valueKey, Expression<Func<TSource, string>> textKey,
object selectedValue = null)
{
var selectedValueKey = ((MemberExpression)(MemberExpression)valueKey.Body).Member.Name;
var selectedTextKey = ((MemberExpression)(MemberExpression)textKey.Body).Member.Name;
return new SelectList(source, selectedValueKey, selectedTextKey, selectedValue);
}
Note the use of NonActionAttribute, which is used to indicate that a public controller method is not an action method.
Then in the controller, it's easy to build any list again, without polluting the actions too much. For example:
[HttpPost]
public ActionResult Index(SomeViewModel model)
{
if (ModelState.IsValid)
return RedirectToAction("Success");
// The model wasn't valid, so repopulate the dropdown
model.People = BuildSelectList(db.People, m => m.Id,
m => m.Name, model.PersonId);
return View(model);
}
You could do something similar for SelectListItem, rather than manually rebuilding your lists each time.
I have a view that displays a table of data that I want to be able to sort by specifying a property on the row type.
My model (so far) is:
class Model
{
List<DataType> data;
Expression<Func<DataType, object>> SortProperty;
}
I've tried creating that in the view to be passed into my controller as follows:
<a href='<%= Url.Action("Index", "Approvals", new IndexModel() { Page = Model.Page, SortProperty = ((ApprovalModel m) => m.Id)}) %>'>Id</a>
which renders as:
<a href='/PartsLegislation/Approvals.aspx/Approvals?SortProperty=m%20%3D%3E%20Convert(m.Id)'>Id</a>
so it sort of looks like it'll work (all-be-it with a Convert expression in there), however in the controller SortProperty is always null.
TL;DR: How can I pass an expression pointing to a property from my view to the controller?
Edit: My controller action is as below:
[HttpGet]
public ActionResult Index(Model viewModel)
{
....
viewModel.Approvals = PartsDC.Repository<Approval>()
.Where(a => !a.Deleted)
.OrderBy(viewModel.SortExpression)
.Skip((viewModel.Page ?? 0) * RowsPerPage)
.Take(RowsPerPage)
.Select(a => Mapper.Map<Approval, ApprovalHeaderModel>(a))
.ToList();
...
}
As far as I know you can't pass complex objects via URL. You can pass for example string with name of property.
However if you want to just sort table maybe you should think about another approach to this problem. Check this http://www.kryogenix.org/code/browser/sorttable/ .
It allows you to simply sort your table by clicking on headers.
I'm somewhat new to the ASP.NET MVC architecture and I'm trying to sort out how I could return multiple sets of data to the view.
public ActionResult Index(string SortBy)
{
var actions = from a in dbActions.Actions
orderby a.action_name
ascending
select a;
return View(actions.ToList());
}
This code works very well for returning a single dataset. The situation I have is a that I've got a list of objects, each of which has a subset of objects that I want to display in a hierarchy on the output page. I'm not sure whether I'm looking for the quick way or the right way, thoughts on both avenues would be very much appreciated.
Thanks!
You could pass them through ViewData, an object that is passed from the controller to the view. The controller would look like this:
ViewData["ActionList"] = actions.ToList();
Retrieving it in the view:
<% foreach (var action in (List)ViewData["ActionList"]) %>
ViewData as described above is the quick way. But I beleieve it makes more sense to wrap the lists in a single object model which you then pass to the View. You will also get intellisense...
That subset of objects could/should be returned by a property on the Action (assuming db.Actions returns Action objects).
public class Action
{
//...
public IEnumerable<SubAction> SubActions
{
get { return do-what-ever; }
}
//...
}
Nothing special MVC'ish here...
In your view you just loop through both:
<%
foreach (Action a in ViewData.Model as IList<Action>)
{
foreach (SubAction sa in a.SubActions)
{
// do whatever
}
}
%>