I have a cshtml template and I'm using Razor to populate it. I'm passing an object that has several sub-lists inside it and I need to get the values from one of the items in one the sub-lists to use in the body of the text. So in the opening tag I have this:
#{
var myId = #Model.myId;
List<MyObject> newObj = #Model.MyList.Where(l => l.Id == myId).ToList();
}
But when I try to execute the template, it throws an error that '<'MyObject'>' was not closed, that all elements must have a matching self-closing tag or end tag. I understand that it seems to be reading this as an html tag, but why since its clearly inside the programming markup? Can I not call a list object in razor? If so, how do I get to this specific sub-list of items?
I've checked the rest of the page and the html has all its closing marks.
I think to fix your code you would have to do the following:
#{
var myId = Model.myId;
List<MyObject> newObj = Model.MyList.Where(l => l.Id == myId).ToList();
}
Which simply removes the # in front of the Model's.
However I feel that a better solution to your problem is to try and keep the logic code in your controller rather than your view.
As an example, if you are using a Partial View.
In your view you could call an action and pass in your model like this:
#Html.Action("MyAction", Model)
This would call a controller action that would do your select i.e.
[ChildActionOnly]
public ActionResult MyAction(MyModel model)
{
var newList = model.MyList.Where(l => l.Id == myId).ToList();
return PartialView("_MyPartial", newList);
}
Then use the #model attribute within your partial i.e.
#model List<MyObject>
Related
I am using MVC 5 and the latest version of Telerik (formerly Kendo) UI for ASP.NET MVC. I am working with hierarchical data. I am attempting to create the TreeView in the _Layout view and populate it with urls or action links.
My current code:
In the _Layout View:
#Html.Partial("_ProductTree")
"_ProductTree" Partial View:
#(Html.Kendo().TreeView().Name("ProductTree")
.DataSource(d => d
.Model(m => m
.Id("ProductId")
.HasChildren("Categories"))
.Read(r => r.Action("_ProductTree", "Home")))
.DataTextField("ProductName"))
Action Method:
[ActionName("_ProductTree")]
public JsonResult GetProductTree()
{
var products = _productBusinessService.GetProducts();
var result = products.Select(p => new
{
p.ProductID,
p.ProductName,
Categories= c.Categories.Any()
}).OrderBy(t => t.ProductName);
return Json(result, JsonRequestBehavior.AllowGet);
}
I am having a number of issues:
When I expand a parent node that has children, the TreeView is hitting the action method and appending the entire tree to the child, instead of just displaying the children.
I need to be able to nest the TreeView two-deep, for example Product > Category > Type.
I am trying to figure out how to aggregate or project the data using LINQ to do a two-deep higherarchy.
I tried turning off LoadOnDemand but that made the TreeView call the action method once for each record in the Product list.
I have tried inserting the TreeView Razor code directly into the _Layout view (not using a partial view). I realize that I may need to move the action method into a base controller class and inherit it in every controller to stop it from appending the list to the parent node. If I cant get this working soon, I may have to either use Kendo UI Professional or an open source alternative.
Some of you may be tempted to say that this question has been answered elsewhere. None of the posts I have found address the issues of populating and displaying nested (more than one deep) hierarchical data using the Telerik TreeView.
There is this post
Nested DataSources and TreeView with kendo ui
But it is for the JavaScript version of the TreeView (not the UI for MVC) version and it has not been answered.
Thank you in advance for your help!
The solution in code:
The _Layout view:
#Html.Partial("_ProductTree")
or
#RenderSection("productTree", false)
then in the content view
#section productTree
{
#Html.Partial("_ProductTree")
}
the _ProductTree partial view
#(Html.Kendo().TreeView().Name("ProductTree")
.DataSource(d => d
.Model(m => m
.Id("Id")
.HasChildren("HasChildren")
.Children("Children"))
.Read(r => r.Action("_ProductTree", "Home")))
.DataTextField("Name"))
I moved the action method to a BaseController abstract class that can be inherited by any controller that needs to display the ProductTree TreeView. The data was pulled from a ProductService and a CategoryService and aggregated using LINQ projection into anonymous objects:
[ActionName("_ProductTree")]
public JsonResult GetProductData()
{
var products = _productBusinessService.GetProducts();
foreach (var product in product)
{
foreach (var category in product.Categories)
{
category.ProductTypes =
_productService.GetProductTypes(category.CategoryId);
}
}
var productTreeData = products.Select(p => new
{
Id = p.ProductId,
Name = p.ProductName,
HasChildren = p.Categories.Any(),
Children = p.Categories.Select(c => new
{
Id = c.CategoryId,
Name = c.CategoryName,
HasChildren = c.ProductTypes.Any(),
Children = c.ProductTypes.Select(t => new
{
Id = t.ProductTypeId,
Name = t.ProductTypeName,
HasChildren = false
}).OrderBy(t => t.Name).ToList()
}).OrderBy(c => c.Name).ToList()
}).OrderBy(p => p.Name).ToList();
return Json(productTreeData, JsonRequestBehavior.AllowGet);
}
The result is a 3-deep, fully populated, sorted Telerik UI for ASP.NET Treeview containing the names and IDs of Product >> Category >> ProductType. Turning LoadOnDemand on or off did not seem to make a difference in this case. It should make a difference when using Bind in a TreeView.
I hope this helps!
I have a following function in a controller. It is called from index.cshtml. var relays works fine and I got the replays.count(). PRESETDETAILS has some columns from which I want to access specific columns in a loop (like I have mentioned in response.write).
Kindly tell me how I can get the specific columns in the for loop.
#region "Apply Preset Handlers"
public ActionResult btnApply_Click(int? id)
{
var relays = db.PRESETDETAILS.ToList().Where(t => (t.PRESETID).Equals(id));
for (int k = 0; k < relays.Count(); k++)
{
Response.Write(relays.Select(s => new relays{PRESETDETAILID = s.PRESETDETAILID }).ToString());
}
return View("Index");
}
#endregion
you need to loop through them, you're simply select the same things over and over...
var relays = db.PRESETDETAILS.Where(t => t.PRESETID == id).ToList();
foreach (var replay in replays)
{
Response.Write(string.Format("{0}", relay.ID));
}
NOW... looking at your code:
always use ToList() at the end of the query;
ToList() actually makes the call to the database, until then, it's just a promise
Don't use Response.Write in your Controller, send the data to the View instead
your code should be:
public ActionResult btnApply_Click(int? id)
{
var model = db.PRESETDETAILS.Where(t => t.PRESETID == id).ToList();
return View(model);
}
in your View you can loop through the records
#model List<YOUR_CLASS>
<ul>
#foreach(var replay in Model)
{
<li>#replay.ID</li>
}
</ul>
I can see that some MVC conventions are not yet in place with you, and I know it can be somewhat overwhelm from someone that comes from WebForms or a linear code approach, but why don't you take some time and check the FREE available course about ASP.MVC in the ASP.NET Website?
See the videos in the right side of this page: http://www.asp.net/mvc
#helper ShowTree(IEnumerable<CommentModel> comments) {
foreach (CommentModel c in comments)
{
// ...
I don't get how this works. Where does comments come from? Exactly what is this looping over?
I want to recursively display a List<CommentModel> from the model passed into the view. Each item in that list has its own list of CommentModels, and so on. Is this the right method to use to render those lists, and how do I use it?
in your view you can pass comment as parameter :
#model IEnumerable<CommentModel>
#ShowTree(Model)
#helper ShowTree(IEnumerable<CommentModel> comments)
{
//your code...
}
simply your action method can be something like this :
public ActionResult Comments()
{
var query = _commentsService.GetAll();
return View("comments",query);
}
This code obtains a listing of unique org names for display within my .cshtml page:
IEnumerable<dynamic> data = db.Query("Select * from provider
where submitter_element = '210300'");
//the 210300 could be any value passed to the query
var items = data.Select(i => new { i.org_name }).Distinct();
foreach(var name in items){
<text>#name.org_name<br></text>
The records in data are each unique themselves, but the data in each field contains the same values i.e. multiple providers have the same org_name.
I want to be able to reuse the data multiple times to create multiple unique lists. I was hoping to pass this to a #helper for display. To that end, I have the following:
#helper ListBoxDistinctDisplay(IEnumerable<dynamic> queryResult)
{
IEnumerable<dynamic> distinctItems = queryResult.Select(i => new { i.org_name }).Distinct();
foreach(var listItem in distinctItems){
<text>#listItem.org_name<br></text>
}
Then in my .cshtml page I do this:
#DisplayHelpers.ListBoxDistinctDisplay(data)
...and BINGO, I get my unique list on my "view" page.
The works perfectly, except as you see I am having to indicate .org_name within the helper.
My question is how can I pass the field name (org_name) into the helper so that my helper can be re-used no matter he field name?
OR...is there a totally different approach all together that I am unaware of?
THANKS!
Since you like to use dynamic, I'll stick with that.
You may want to pass selector:
#helper ListBoxDistinctDisplay(IEnumerable<dynamic> queryResult, Func<dynamic, dynamic> selector)
{
IEnumerable<dynamic> distinctItems = queryResult.Select(x => new {selectedField = selector(x)}).Distinct();
foreach (var listItem in distinctItems)
{
<text>#listItem.selectedField<br/></text>
}
}
Call it:
#DisplayHelpers.ListBoxDistinctDisplay(data, x => x.org_name)
I'm trying to set up a page where I display a list of items and the details of the selected item. I have it working but wonder whether I have followed the correct approach. I'll use customers as an example
I have set the aspx page to inherit from an IEnumerable of Customers. This seems to be the standard approach to display the list of items. For the Details I have added a Customer user control which inherits from customer.
I think i'm on the right track so far but I was a bit confused as to where I should store the id of the customer whose details I intend to display. I wanted to make the id optional in the controller action so that the page could be hit using "/customers" or "customers/1" so I made the arg optional and stored the id in the ViewData like this:
public ActionResult Customers(string id = "0")
{
Models.DBContext db = new Models.DBContext();
var cList = db.Customers.OrderByDescending(c => c.CustomerNumber);
if (id == "0")
{
ViewData["CustomerNumber"] = cList.First().CustomerNumber.ToString();
}
else
{
ViewData["CustomerNumber"] = id;
}
return View("Customers", cList);
}
I then rendered the User control using RenderPartial in the front end:
<%var CustomerList = from x in Model
where x.CustomerNumber == Convert.ToInt32(ViewData["CustomerNumber"])
select x;
Customer c = (Customer)CustomerList.First(); %>
<% Html.RenderPartial("Customer",c); %>
Then I just have an actionLink on each listed item:
<%: Html.ActionLink("Select", "Customers", new { id = item.CustomerNumber })%>
It all seems to work but as MVC is new to me I would just be interested in others thoughts on whether this is a good approach?
In regards to proper MVC and separations of concerns, you shouldn't be calling LINQ queries in your view. To get around that, change your controller action code from what you have to this:
if (id == "0")
{
ViewData["CustomerDetails"] = cList.First();
}
else
{
ViewData["CustomerDetails"] = From x in db.customers where x.id = cInt(id);
}
then your partial
<% Html.RenderPartial("Customer",ViewData["CustomerDetails"]); %>
Are you showing the customer information on the same screen that you have your list of customers and not a separate view?
In this case I would take the following approach.
Display a list of customer's, be it a listbox or drop down list.
Let's assume it's a drop down list, probably not very user friendly
You would have the text as the customer name and then the value as the customer id
<select title="Customers">
<option label="Pieter" value="001"></option>
</select>
and using JQuery's $.getJSON you could load the new data via a call to an asp.net MVC action.
Note that your Action should return JsonResult