ASP.NET MVC 3: Using Enumerable extension methods in the view - asp.net-mvc-3

Given the following Razor Partial View and understanding that Product is an NHibernate mapped object so the calls to IEnumerable here will fire database queries (when not cached).
Is this bad practice? Should I be providing a flatter view of my data for this view so that I can make these calls in my controller/business logic?
#model IEnumerable<MyProject.Data.Models.Product>
<table>
<tr>
<th></th>
<th>Total Orders</th>
<th>Fulfilled</th>
<th>Returned</th>
<th>In stock</th>
</tr>
#foreach (var product in Model) {
<tr>
<td>
#Html.ActionLink(product .Name, "Detail", "Product", new { id = product.Id }, null)
</td>
<td>
#product.Orders.Count
</td>
<td>
#product.Orders.Where(x=>x.Fulfilled).Count()
</td>
<td>
#product.Orders.Where(x=>x.Returned).Count()
</td>
<td>
#(product.Stock.Count - product.Orders.Count)
</td>
</tr>
}
</table>

Is this bad practice?
Yes.. In fact it's breaking the MVC pattern - the View's should not call back through the model, only receive in order to do it's sole job: rendering HTML.
If you need additional information than just the one entity, populate a ViewModel with all the information you need, then pass that to your View.
Also, don't loop through the IEnumerable in the model, use a Display Template:
#Html.DisplayForModel()
The advantage of this is no explicit loop, taking advantage of MVC conventions, and adhering to model hierachy when model binding.

Related

How to transfer parameters in th:href in spring boot Thymeleaf?

These are my simple Thymeleaf table HTML file and Spring MVC controller codes. First below is my table image.
I try to make some html codes to transfer the post id value to view codes when the 'Edit' or 'Delete' link are clicked, but I have no idea how to do. These are my Spring MVC Controller codes and view.html codes.
#Controller
public class PostController {
#Autowired
private PostService postService;
#RequestMapping("/posts/view/{id}")
public String view(#PathVariable("id") Long id, Model model) {
Post post = postService.findById(id);
model.addAttribute("post", post);
return "posts/view";
}
And,
<table id="blogTable" border="1" width ="1000" height="400" align = "center">
<thead>
<tr>
<th>Post ID</th>
<th>Post Title</th>
<th>Post Content</th>
<th>Date</th>
<th>Author</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="post : ${posts}">
<td th:text="${post.id}">Post ID</td>
<td th:text="${post.title}">Post Title</td>
<td th:text="${post.body}">Post Content</td>
<td th:text="${post.date}">Date</td>
<!-- <td th:text="${post.auther.userName()}">Author</td> -->
<td>
Edit<br/> ==> How to transfer the post.id parameter to th:href?
Delete ==> How to transfer the post.id parameter to th:href?
</td>
</tr>
</tbody>
</table>
I am a beginner in HTML and Spring. How can I put post.id value into view mvc controller through th:href tag?
Use th:href like described in the documentation
th:href is an attribute modifier attribute: once processed, it will compute the link URL to be used and set the href attribute of the tag to this URL.
We are allowed to use expressions for URL parameters (as you can see in orderId=${o.id}). The required URL-encoding operations will also be automatically performed.
If several parameters are needed, these will be separated by commas like #{/order/process(execId=${execId},execType='FAST')}
Variable templates are also allowed in URL paths, like #{/order/{orderId}/details(orderId=${orderId})}
For example (notice the th:href and the parameter postId that receives the value from your variable post):
<td>
Edit<br/> ==> How to transfer the post.id parameter to th:href?
</td>
You can add parameters in parentheses:
<a th:href="#{/index(param1='value1',param2='value2')}">
Thymeleaf evaluates the above to:
<a href="/index?param1=value1,m2=value2">
For example, suppose we set default value to two parameters; name="Dracula" and age = 25.
<a th:href=#{/person(name=${name},age=${age})}></a>
I used in this format and work fine.
<a th:href="#{${urlBase} + '/#!/pedido' + ${other.value}" target="_blank">Link</a>
You can something try like following:
Edit<br/>
Delete

When using AjaxHelper to retrieve a partial view, the embedded data is always the same

We use ASP.NET MVC 5's AjaxHelper and Ajax.BeginForm to request a partial view. That request also needs some JSON data in order to update a map control.
The view rendering part of the process works great (a table body is replaced with the strongly-typed partial view), but the JSON data (embedded into the data-json attribute of the div element as described in this answer and retrieved in my OnSuccess function) always has the same value.
To eliminate the controller code or ViewBag as culprit, I replaced the JSON data (originally retrieved from the ViewBag) with a direct call to DateTime.Now. Sure enough, the same DateTime is printed each time in updateMap() (e.g., 2/11/2016+5:24:42+PM)
I've tried disabling caching, and changing the HTML method to Post, in my AjaxOptions.
In the Parent View (changing the ListBox selection submits the form):
#model string
#{
ViewBag.Title = "Project List";
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "tableBody",
OnSuccess = "updateMap",
HttpMethod = "Post",
AllowCache = false
};
}
#using (Ajax.BeginForm("GetProjectsData", ajaxOpts))
{
<fieldset>
<legend>Project State</legend>
<div class="editor-field">
#Html.ListBox("selectedStates", ViewBag.StatesList as MultiSelectList,
new { #class = "chzn-select", data_placeholder = "Choose States...", style = "width:350px;", onchange = "$(this.form).submit();" })
</div>
</fieldset>
}
<table class="table">
<thead>
<tr>
<th>
Project Name
</th>
<th>
Project Firm
</th>
<th>
Project Location
</th>
<th>
Building Type
</th>
<th>
Project Budget
</th>
<th></th>
</tr>
</thead>
<tbody id="tableBody">
#Html.Action("GetProjectsData", new { selectedStates = Model })
</tbody>
</table>
<script>
function updateMap() {
var jsonData = $("#geoJsonData").attr("data-json");
var decoded = decodeURIComponent(jsonData);
console.log(decoded); // always prints same value
}
</script>
The partial view:
#model IEnumerable<OurModel>
<div id="geoJsonData" data-json="#Url.Encode(DateTime.Now.ToString())"></div>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.COMPANY_NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.STATE)
</td>
<td>
#Html.DisplayFor(modelItem => item.BUILDING_TYPE)
</td>
<td>
#Html.DisplayFor(modelItem => item.BUDGET_AMT)
</td>
</tr>
}
I'm hesitant to jettison the MVC helper classes' pattern of returning a partial view and instead manually render a view into a JSON object. Why is the updated tablebody visible on screen, but when jQuery requests the div element it always has the same data?
Interesting...replacing the div with a good old hidden input element worked. Now fresh data is retrieved each time.
This
<div id="geoJsonData" data-json="#Url.Encode(DateTime.Now.ToString())"></div>
Became this
<input id="geoJsonData" type="hidden" value="#Url.Encode(DateTime.Now.ToString())" />
I wonder why the data-json in the div remained "stale" while the value of the input field did the trick?

append two IEnumerable items

IEnumerable<Addresses> AddressSet1=myServices.GetAddresses(LocationId1);
IEnumerable<Addresses> AddressSet2=myServices.GetAddresses(LocationId2);
I want to combine the above two AddressSets
I tried IEnumerable<Addresses> AllAddresses=AddressSet1.Concat(AddressSet2)
But after this when I try to access items from IEnumerable AllAddresses by on my razor view
#if(!myHelper.IsNullorEmpty(model.AllAddresses )
{
#Html.EditorFor(model => model.AllAddresses )
}
and I am getting errors -- Illegal characters in path .Any suggestions to identify cause of this error ?
If I am trying to run my page with out the Concat I am able see the records in AddressSet1 /AddressSet2 displayed on the page .But when I try to combine the two to form I Enumerable AllAddresses ,it is throwing errors please help
pasted below is my Editor Template
#model MyServiceRole.Models.Addresses
#{
ViewBag.Title = "All addresses Items";
}
<table>
<tr>
<td>
<span>Index</span>
</td>
<td>
</tr>
<tr>
<td>Address XID</td>
<td>
#Html.EditorFor(model => model.AddressID)
</td>
</tr>
<tr>
<td>Title</td>
<td>
#Html.EditorFor(model => model.Title)
</td>
</tr>
<tr>
<td>Description</td>
<td>
#Html.EditorFor(model => model.Description)
</td>
</tr>
<tr>
<td>Image URL</td>
<td>
#Html.EditorFor(model => model.Photo.URL)
</td>
</tr>
</table>
I tested your issue and ran into the same problem.
List<string> a = new List<string>{ "a" };
List<string> b = new List<string>{ "b" };
IEnumerable<string> concat = a.Concat<string>(b);
foreach(string s in concat) { } // this works
return View(concat);
In view:
#model IEnumerable<string>
#foreach(string s in Model) //This blows up
{
}
#Html.EditorFor(m => Model) //Also blows up
It looks like you honestly can't use templates with or enumerate over the
System.Linq.Enumerable.ConcatIterator<T>
class that Concat creates within a View. This seems like a bug.
Anyway adding .ToList() fixes your issue.
return View(concat.ToList());
If you want to use editor templates why are you writing foreach loops? You don't need this loop at all. Simply write the following and get rid of the foreach:
#Html.EditorFor(x => x.AllAddresses)
and then you will obviously have a corresponding editor template that ASP.NET MVC will automatically render for each element of the AllAddresses collection so that you don't need to write any foreach loops in your view (~/Views/Shared/EditorTemplates/Address.cshtml):
#model Address
...

getting all values in list without iterating through them

In my controller, I'm returning returning View with a list of products.
return View(ProductList)
In my view I want to get all the values of the product list WITHOUT iterating through them either with a for each loop or any other way.
I need to do this as I'm going to design each Product differently in the view and I can't use a for each loop
The first productId is available using Model.Firstordefault().ProductId. Similarly I can get productName, productDescription and so on.
But how can I get the second productId to nth productId in the list?
Thanks
Arnab
.
That's a great candidate for a display template. In your strongly typed view simply:
#model IEnumerable<ProductViewModel>
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
and then define a display template which will automatically be rendered for each element of the collection (~/Views/Shared/DisplayTemplates/ProductViewModel.cshtml):
#model ProductViewModel
<tr>
<td>#Html.DisplayFor(x => x.Id)</td>
<td>#Html.DisplayFor(x => x.Name)</td>
<td>#Html.DisplayFor(x => x.Description)</td>
</tr>
Templated helpers work by conventions and you never have to write any loops in your views.

MVC Razor Rendering controls dynamically

This is one way I've found to render controls dynamically with ASP.NET MVC 3 Razor. This is giving me correct data, but I'm curious if anyone sees any red flags with this method, or a painfully more obvious way to do this.
#using (Html.BeginForm())
{
foreach (var item in Model)
{
<tr>
<td>
#item.app_name
</td>
<td>
#item.setting_name
</td>
<td>
#item.setting_description
</td>
<td>
#if (item.data_type == "Bit")
{
#Html.CheckBox("setting_value", item.setting_value == "1" ? true : false)
}
else
{
#Html.TextBox("setting_value", item.setting_value)
}
</td>
<td>
#item.setting_value
</td>
</tr>
}
}
You could use Editor and Display Templates instead...
Check out this link:
http://blogs.msdn.com/b/nunos/archive/2010/02/08/quick-tips-about-asp-net-mvc-editor-templates.aspx
What do editor templates have to do with dynamically creating controls?
What if you need to drive a UI/View from settings in a database, for example?

Resources