So I have two separate models, one 'items' model, the second a 'sites'
model... I'm using KO to bind this data to two separate elements on
the DOM (and working as needed), but I've found a need to replace one
of my bound columns on one of my models, with data from the other.
On my 'items' model, I have a site ID column, that I'd love to swap
with the actual 'SiteName' property on that model (simple name, value-
pair - SiteName, SiteId)... Does any one know a way to do this within
KO?
I really want to keep the model data itself in tact on the server side,
verses just creating a custom model on the server side that does it for me.
I'm sure I could give those elements a special class, and loop
through them and replace them manually with jQuery, but I thought that
KO might have an easier way of doing this.
Thanks!
I have tried something like this, but it doesn't seem to work (yes, I know div tags within a table element are not standards based, I just wanted to see if it would work, and if it did, I'd transition from the table to another formatting option)
<tbody data-bind="foreach: items">
<tr data-bind="click: updateItem">
<td data-bind="text: ItemName"></td>
<div data-bind="foreach: sites">
<div data-bind="if: items.SiteId = sites.SiteId">
<td data-bind="text: sites.SiteName"></td>
</div>
</div>
The jQuery to do this using my returned model is:
$(function () {
$('#allItems tr .siteIdCell').each(function () {
for (i in allSites) {
if (allSites[i].SiteId == $(this).html()) {
$(this).html(allSites[i].SiteName);
}
}
});
});
Where .siteIdCell is the class I applied to the columns using this value, and allSites is the object array I'm receiving via JSON.
Not sure if I'll get many responses, but I just figured I'd update this if anyone else has the same issue, and there's no ability to do this in KO.
Related
I Wrote this code in view
#foreach (var item in Model.CDR)
{
<tr>
<th>
<a class="facebook" rel="nofollow" target="_blank" href="https://www.facebook.com/search/top/?q=#item.BNumber">Facebook</a>
<a class="facebook" rel="nofollow" target="_blank" href="https://www.truecaller.com/pk/#item.BNumber">True Caller</a>
</th>
When I click on this number,
it gets automatically send to url and
url automatically identify this number's country code ...
My Problem :
When I send the number Url is not identifying its country code.
Tell me how is it possible ?
The first few things to check would be the standards, how are you passing this modal. In the controller or do you have a separate ViewModel class?
Something you might want to add is a check
Assuming Model.CDR is a List or Enumerable
Another thing you should do is have the type of CDR.
Easiest way to do this if you are struggling is (If using Visual Studio) put a break point in at the #foreach line and in your watch, call Model.CDR to see what type it is passing back.
That will allow you to strongly type it in your foreach statement, makes the whole thing a heap easier.
#if (Model.CDR.Length != 0)
{
#foreach (object //CHANGE OBJECT TO YOUR TYPE// item in Model.CDR){
}
}
If you send in a little more info on what your Model looks like, how the CDR object is constructed I could help you a little more.
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?
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.
I have a collection of objects on my Model that I'm rendering in a View by using EditFor function, and I have an EditorTemplate which is responsible for actually rendering each object.
#Html.EditorFor(model => model.MyObjects)
This has worked well for a while now, and when you check the html, my text boxes are prefixed with the model property, followed by the index of the collection they came from.
<input class="text-box single-line" id="MyObjects_2__SomeProperty"
name="MyObjects[2].SomeProperty" type="Text" value="" />
However I've recently started using the ShowForEdit and ShowForDisplay properties in the model metadata for the collection, and in the first line of my editor template if the ShowForEdit is not true, I just skip it.
#if (!ViewData.ModelMetadata.ShowForEdit)
{
return;
}
But because these are all indexed in the html, when I try to save this collection back to the viewmodel via a postback, it fails because of a reliance on the indexing numbers. Every item in the collection after the missing index is missing from my view model when I check it's value.
In this case it's actually my first item in the collection that I'm skipping since I don't want it to be visible on the edit view, but because of this when I postback the first index in the html is 1 (instead of 0 like it normally would be), but this is a problem when you try to save the changes. This is also a problem when altering the DOM using javascript.
Has anyone else encountered a problem with the default model binder's ability to read data posted back when one or more indexes in the html represented collection are not present?
Are there model binders that handle this problem?
Ran into this issue recently and solved it by converting the List to a Dictionary<string, model> with GUIDs as the key.
#foreach (var index in Model.EmailAddresses.Keys)
{
<label asp-for="#Model.EmailAddresses[index].Email">Email</label>
<input asp-for="#Model.EmailAddresses[index].Email" type="text" />
}
This avoided having to include hidden inputs that map to the index value.
There are some very good blog posts that allow you to modelbind to a list without the need to provide zero based contiguous index. plz have a look at
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
http://zahidadeel.blogspot.com/2011/05/master-detail-form-in-aspnet-mvc-3-ii.html
Furthermore, if you are interested in MVVM pattern and knockout js you can check this great work by steve sanderson
For more reading put "editing varibale length list mvc style" in google and it will give u a dozen useful links
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.