Asp.Net MVC validation & knockout foreach - asp.net-mvc

I have a MVC 4 validation & Knockout issue.
I tried this solution :
Translate knockout into razor to keep it's validation working
But i have the same problem as listed in comment of the valid answer => the validation works only for the first element.
After several searches, i found this article on model binding to a list :
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
I am wondering how to merge those two solutions.
Sample with validation working only for the first element :
Dim dummy As ViewModels.ObjectViewModel = Model.MyObjects.FirstOrDefault
#<tbody data-bind='foreach: MyObjects'>
<td>
#Html.TextBoxFor(Function(model) dummy.Label, New With {.data_bind = "value: Label"})
</td>
</tbody>
Sample with validation working, but without the knockout foreach, i can't add items dynamically
<tbody>
#Code
Dim i As Integer = 0
For Each object In Model.MyObjects
#<tr>
<td>
#Html.TextBoxFor(Function(m) Model.MyObjects(i).Label)
</td>
</tr>
i+= 1
Next
End Code
</tbody>
Maybe something with knockout variable : $index() ?

In the past I’ve tried to force combining razor and knockout. But more recently I just opt to go one way or the other. If I’m going to render something on the client side, then I’ll just go ahead and define everything in terms of HTML directly instead of going through razor.
Probably your best bet here is to just define the HTML elements directly. If you need to have validation on place, then just make sure of two things:
Set the corresponding jquery validate attributes (e.g. data-val-true) so that the form validates on the client side.
If you’re submitting data to an ASP.NET MVC Controller make sure the elements have the same name/id as needed by the controller so that the binding takes place on the controller method parameters.

So, after many searches and tests, i have found the solution :
1° Step is to put a correct name for the validation.
Razor View Code :
Dim dummy As ViewModels.ObjectViewModel = Model.MyObjects.FirstOrDefault
<tbody data-bind='foreach: MyObjects'>
<td>
#Html.TextBoxFor(Function(model) dummy.Label, New With {.data_bind = "value: Label, attr: { name: 'MyObjects[' + $index() + '].Label'}"})
</td>
</tbody>
You get this HTML for the first item : name="MyObjects[0].Label"
Which is cool and makes validation work.
But if you add an item dynamically, the validation won't work for the new item.
2° Step is to make the unobstrusive validation re parse your form.
JavaScript
viewModel.addObject = function () {
viewModel.MyObjects.push(new object())
$("form").data("validator", null);
$.validator.unobtrusive.parse($("form"));
};
Those two answers helped me a lot :
Knockout MVC with existing C# code - looping
MVC Model Validation on a dynamic form?

Related

Asp.net MVC4 #Html.EditorFor .Where() Attribute

In my view i am using editortemplate as below:
edit.cshtml
#model NetasCrm.Models.CRM_OPP_DETAILS
<table class="table table-hover">
<thead>
<tr>
<th>Çözüm</th>
<th>Üretici</th>
<th>Tarih</th>
<th>Tutar</th>
<th>Sil</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(model => model.CRM_SOLUTION_DISTRIBUTION, new { Solutions = ViewBag.Solutions, Vendors = ViewBag.Vendors })
#Html.HiddenFor(model => model.ID)
</tbody>
</table>
It is working but i am trying to do something(adding where clause.) as below to create an editor template for some of the items in my model.
#Html.EditorFor(model => model.CRM_SOLUTION_DISTRIBUTION.Where(p=>p.AMOUNT != 0), new { Solutions = ViewBag.Solutions, Vendors = ViewBag.Vendors })
I am getting the below error:
Templates can be used only with field access, property access,
single-dimension array index, or single-parameter custom indexer
expressions.
It's not how #Html.EditorFor() should be used, error message clear states about that.
To render what you want you may either use Html.Partial(), or create separate property in your model and move Where to it's getter.
Html.XxxFor expects a lambda expression that selects a property from the model. This is used to identify the property of the model which will be edited.
What you're trying to achieve is probably what you can do with an editor template.
Interesting articles on editor templates and their implementation:
Quick Tips About ASP.NET MVC – Editor Templates
Brad Wilson: ASP.NET MVC 2 Templates, Part 1: Introduction
In the second article you can see that if you have a custom class with the data you want to edit, you can create a template which is automatically used for your editor if you give it the right name and save it in the right place. You can make a class with a single field and include it in your model instead of the original property. This also allows to edit several properties at once (declaring a class with those properties)
Othe option would be a custom html helper:
Creating Custom HTML Helpers (NOTE: this technique will work perfectly with C#/Razor syntax)

Refactor a C# loop to Knockout Mapping Plugin

Where the server is sending the client a complex object and the goal is to transition from C#'s 'foreach' to KnockoutJS's 'data-bind="foreach: ' consider this code that populates a shopping cart with various pieces of info:
#{
foreach (var item in GetItems(Model))
{
<dt>
<input type="radio" id='mode_#(item.ID)' name="mode" value="#item.ID" />
#item.Label - $#item.PriceToAdd
</dt>
<dd>
#Html.Raw(item.Explanation) </dd>
}
}
}
Should the server's code be adjusted to flatten out the object before rendering the View or can KnockoutJS deal with unwrapping it? Would it get easier if the server sends JSON?
FOLLOWING UP:
It becomes clear the question boils down to mapping plugin and mfanto's first answer gets me part the way there:
self.items = ko.mapping.fromJS(#Html.Raw(JsonConvert.SerializeObject(Model.Items)));
firebug shows me output of:
self.items = ko.mapping.fromJS([{"ID":60},{"ID":62},{"ID":63},{"ID":64},{"ID":9}]);
Perhaps mapper fails because one of my Items (id=9) has different elements than the rest.
Probably I need to research one of the more advances usages of mapper?
FORMATTED OUTPUT COMPARES VALUES RETURNED BY JsonConvert vs. JavaScriptSerializer
...
self.itemsJSON = ko.mapping.fromJS(#Html.Raw(JsonConvert.SerializeObject(Model.Items)));
self.items = #Html.Raw(new JavaScriptSerializer().Serialize(Model.Items));
when the above code renders to a breakpoint in Firebug:
self.itemsJSON = ko.mapping.fromJS([{"ID":60},{"ID":62},{"ID":63},{"ID":64},{"ID":9}]);
self.items = [ //line breaks inserted for clarity
{"Explanation":"Item1's text.","Label":"Item1's Label","MsgConfirm":null,"PriceToAdd":1255,"TaxExempt":false,"PercentToAdd":0,"SortOrder":1,"ID":60},
{"Explanation":"Item2's text.","Label":"Item2's Label","MsgConfirm":null,"PriceToAdd":1255,"TaxExempt":false,"PercentToAdd":0,"SortOrder":2,"ID":62},
{"Explanation":"Item3's text.","Label":"Item3's Label","MsgConfirm":null,"PriceToAdd":295,"TaxExempt":false,"PercentToAdd":0,"SortOrder":3,"ID":63},
{"Explanation":"Item4's text.","Label":"Item4's Label","MsgConfirm":null,"PriceToAdd":395,"TaxExempt":false,"PercentToAdd":0,"SortOrder":4,"ID":64},
{"Explanation":null,"Label":"[foo]","MsgConfirm":null,"PriceToAdd":150,"TaxExempt":false,"PercentToAdd":0,"SortOrder":99,"ID":9}
];
thx
You don't need to flatten the object before you use Knockout. The ko.mapping plugin will create viewmodels with observable properties, and can handle complex nested objects.
To use it with an ASP.NET MVC model, use #Html.Raw() and a Json serializer (in this case Json.NET:
function AppViewModel() {
var self = this;
self.items = ko.mapping.fromJS(#Html.Raw(JsonConvert.SerializeObject(Model.Items)));
}
ko.applyBindings(new AppViewModel());
From there, you can use foreach:
<table>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: PriceToAdd()"></td>
</tr>
</tbody>
</table>
You can go either way with this. Render it on the server with Razor or render it on the client with knockout... The more fundamental question is where do you want to render it. There is no right or wrong answer here.
If you go with knockout, you need to deal with more than just having the server possibly flatten out your model. Knockout will require ajax requests to both read and then save your data and this is where the two solutions fundamentally differ I don't see any JavaScript as part of your solution and without that component, providing a ko solution is pretty impossible.
If you are thinking about using knockout simply as a client side templating engine, then something like jsrender is likely a better solution.

ASP.net MVC DisplayNameFor nested model in view

I have a view in my ASP.net MVC project which is associated to a controller (SubmissionController) and class (Submission).
In the view, I am displaying some values from the class using:
#Html.DisplayFor(Function(model) model.Created)
This works perfectly, but what I want to achieve now, is to be able to get the display name of a Collection nested within the model I reference above.
To make that clearer; I am viewing a single Submission of many Submissions in the view (using the Details function of the SubmissionController). Each submission has many Photos, and it is these photos I want to display in the Submission details view along with the submission details themselves.
To display the photos, I have the following piece of code creating a table of photos...
<tbody>
#For Each item In Model.Photos
Dim currentItem = item
#<tr>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_ID)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Status1.Value)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Size)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Taken)
</td>
</tr>
Next
</tbody>
Whilst untested, I believe the above code should work just fine.
With this in mind, How on earth do I get the DisplayNameFor values for Photo fields/properties?
This apparently, does not work!
#Html.DisplayNameFor(Function(model) model.Photos.Photo_Size)
By the way, I don't want to write in the header names manually, they must come from the model.
Answer
DigitalD's answer was almost there, here is the solution.
#Dim photoModel = New Photo()
#Html.DisplayNameFor(Function(photoModelx) photoModel.Photo_ID)
#Html.DisplayNameFor(Function(model) model.Photos.Photo_Size) won't work because model.Photos is likely an IEnumerable and doesn't have a Photo_Size property. You might get away with passing in an empty model item like so:
#Dim displayModel = new Photo()
#Html.DisplayNameFor(Function(displayModel) displayModel.Photo_Size)
etc.
If I am understanding correctly, you are trying to get the name of the field you are showing ("Photo Taken", "Photo Size", etc)? If this is the case, I would suggest using DataAnnotations and LabelFor(item=>item.Field). This will give you what you are looking for I believe.

How can a list of objects (a part of a bigger Model) with with Add and Delete buttons in MVC be updated with AJAX calls?

The situation is as follows:
I have a ViewModel that has a property that's a List<Product>, where Product is a class with let's say properties Property1, Property2 and Property3.
I have to render the ViewModel and I wish to render the List<Product> in an HTML table each row of which has a "Delete" button for that row.
Underneath the afore-mentioned HTML table, there should be 2 buttons:
3.1 "Add" - used to add a new empty Product to the list
3.2 "Use default product list" - the List has to be loaded via an AJAX call to the GetDefaultProduct() action of the controller
Clicking on a "Delete", "Add" or "Use default product list" button should not post the entire page
The model contains some other lists of items as well - for the sake of example: List<Sales>, List<Orders> and so on. I'm hoping I can re-use the solution for the List for those lists as well.
Is there a way to do this with ASP.NET MVC?
If yes, what is the best way to do it with ASP.NET MVC?
I did this with jQuery templating and I managed to implement this with simple operations, but I have to do it in an ASP MVC solution and I'm still trying to get the hang of the technology.
I've been reading about the Editor template, RenderAction, Async action and partial views and I'm trying to compose a solution with them and I'll post it if it works.
Thanks in advance for any suggestions and comments!
UPDATE
The solution lies (as Darin pointed out) in Steve Sanderson's blog post.
However, it assumes that the reader is aware of under-the-covers way of how a List of objects should be rendered in a CRUD-friendly, indexed manner.
So, in order to help anyone who wants to have an indexed list of omplex objects, I suggest reading mr. Haacked's blog post: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx.
After you are done with it, you can move on to Sanderson's blog. By the way, take a good look at the BeginCollectionItem custom HTML helper - its implementation isn't a trivial one as one might think.
The demo project is a sight for sore eyes - it's spot on and easy to understand.
The proposed solution DOES use some jQuery.ajax() calls (for the Add link), but just out of bare necessity.
PS: It's a bit frustrating that one has to read an explicit article from one of the developers of ASP.NET in order to find out that there's an implicit CoC (Convention-over-Configuration) in the default model binder - it just knows how to work with Lists, but no out-of-the-box HTML helper (or anything similar) doesn't let you in on this.
Personally I think that CRUD-friendly rendering of List<object> is a very common scenario, not an edge case so it should be simpler to use and a part of the ASP.NET MVC out-of-the-box machinery.
I would recommend you reading the following blog post. It would definitely put you on the right track for implementing an editing scenario of a variable length list.
This is a very easy way of doing it if you want to do using Jquery.
ASP.Net MVC 3 JQGrid
Save yourself some pain and download the Telerik Extensions for ASP.Net MVC. It's open source ( though there's a paid option).
The Grid control (possibly in conjunction with the Window control) will give you all the UI functionality you need and there's plenty of online examples.
I've been doing a lot of master-detail UI work recently and Telerik's been an immense help.
Maybe this is not the answer you are looking for, but hey, try to create a default typed edit view for a model using folowing settings:
this will generate the folowing code for view (assuming Razor engine):
#model IEnumerable<MvcApplication2.Models.User>
#{
View.Title = "GetData";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>GetData</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>
Name
</th>
<th>
Description
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
<td>
#item.Name
</td>
<td>
#item.Description
</td>
</tr>
}
</table>
The code wich will handle edit/update/details will accept Id of the entity as a parameter. These are paricualr other methods of your controller which will handle editting/adding/previewing of you item

Parsing Form Post Values From a Table in ASP.NET MVC?

Ok, I'm an MVC newbie coming from a webforms background, so please excuse any ignorance here. Here is my scenario. I've got a table consisting of a list of applications and associated permissions. Each table row consists of 3 pieces of information: a checkbox, some text describing the row, and a dropdown list allowing the user to select the appropriate permission for the application. I want to post this data and only work with the rows in the table which were checked (the id of the row is embedded as the checkbox name). From there, I want to grab the selected value from the DropDownList, and call the necessary code to update the DB. Here is my View page's code:
<%foreach (var app in newApps)
{ %>
<tr>
<td><input type="checkbox" name="AddApps" value="<%=app.ApplicationId %>" /></td>
<td><%=Html.Encode(app.ApplicationName)%></td>
<td><%=Html.DropDownList("AppRole", new SelectList(app.Roles, "RoleId", "RoleDescription"))%></td>
</tr>
<%} %>
How would I retrieve the appropriate values from the FormCollection when I get to the controller on form post? I have done this in the past when I only had checkbox values to retrieve by just calling Request.Form["CheckBoxName"] and parsing the string.
Or am I going about this entirely wrong?
You are halfway right in order to post your data that the controller can read the info it must be inside a form as so :
<% using(Html.BeginForm("Retrieve", "Home")) %>//Retrieve is the name of the action while Home is the name of the controller
<% { %>
<%foreach (var app in newApps) { %>
<tr>
<td><%=Html.CheckBox(""+app.ApplicationId )%></td>
<td><%=Html.Encode(app.ApplicationName)%></td>
<td><%=Html.DropDownList("AppRole", new SelectList(app.Roles, "RoleId", "RoleDescription"))%></td>
</tr>
<%} %>
<input type"submit"/>
<% } %>
and on your controller :
public ActionResult Retrieve()
{
//since all variables are dynamically bound you must load your DB into strings in a for loop as so:
List<app>=newApps;
for(int i=0; i<app.Count;i++)
{
var checkobx=Request.Form[""+app[i].ApplicationId];
// the reason you check for false because the Html checkbox helper does some kind of freaky thing for value true: it makes the string read "true, false"
if(checkbox!="false")
{
//etc...almost same for other parameters you want that are in thr form
}
}
//of course return your view
return View("Index");//this vaires by the name of your view ex: if Index.aspx
}
This site gives more details on how to handle the dropdownlist helper:
http://quickstarts.asp.net/previews/mvc/mvc_HowToRenderFormUsingHtmlHelpers.htm
Request.Form will still work, except that your checkboxes all have the same name.
So one way would be to give the checkboxes distinct names, e.g. "AddApps-app.id", and use Request.Form.
However, a more elegant and testable way is to use list binding.
In this model, you give your form elements a certain structured name, and the default model binder will wrap each set of form elements into a list of typed records in the controller. This is fully explained in this blog post.
The advantage here is that your controller deals only with instances of the application type, and hence has no implicit dependency on the way the view is structured. Therefore, it is very easy to unit test.

Resources