display nested arrays in knockout.js - javascript

I have a json object returned via jquery ajax from an asmx service. When I attempt to display it in a table, nothing displays. What might I be doing wrong?
Thanks in advance.
The returned json is ...
[{\"id\":1488,\"name\":\"Baby Books\",
\"categories\":
[{\"id\":152,\"name\":\"Activity Books\",\"value\":\"Ignore\"},
{\"id\":167,\"name\":\"Bedtime and Dreams\",\"value\":\"Ignore\"}]},
{\"id\":1489,\"name\":\"Story Books\",
\"categories\":
[{\"id\":1506,\"name\":\"Games - Floor Puzzles\",\"value\":\"Ignore\"}]}]
the js display code is (where gorupJson is returned from the call) ...
var viewModel = {
groups: ko.observableArray([]),
};
viewModel.groups(groupJson)
ko.applyBindings(viewModel);
the page content is ...
<table width="100%" >
<tbody data-bind="foreach: groups" style="width:100%">
<tr>
<td data-bind="text: name"></td>
<td>
<ul data-bind="foreach: categories">
<li>
<a><span data-bind="text: name"></span></a>
</li>
</ul>
</tbody>
</table>

Your json is not automatically converted into ko collection.
So you need to apply a mapping to this conversion, probably like this ko.mapping.fromJS(data). When you do it, your fields id, name, value will become in ko.observable
If you want to know more about mapping plugin in knockout, take a look at http://knockoutjs.com/documentation/plugins-mapping.html

First, your JSON is invalid.
Check out the fiddle: http://jsfiddle.net/YNdmh/
var groupJson = [{"id":1488,"name":"Baby Books","categories":
[{"id":152,"name":"Activity Books","value":"Ignore"},
{"id":167,"name":"Bedtime and Dreams","value":"Ignore"}]},
{"id":1489,"name":"Story Books","categories":
[{"id":1506,"name":"Games - Floor Puzzles","value":"Ignore"}]}]

You're missing closing tags for <td> and <tr>
The slashes in json have to go aswell.
http://jsfiddle.net/yhBvr/

Related

Knockout Foreach binding to html table not working

I have been working on this issue for a while. I am new to Knockout.
I have an html page with a table that I want displayed using Knockout. If I hardcode the JSON array, it works! When I use the $.getJSON function, it does not work.
A little background: I cannot use any server-side languages for this project- a CMS is being used that will not allow server-side stuff.
I get the JSON from a table of data I download, then pass it to this to make displaying, styling, etc. automated so that all the user has to do is upload the file with the html table and the rest is taken care of.
If there is an easier way, I am definitely open to suggestions. My question is: how do I make the foreach data display?
Here is my fiddle with some test data:
https://jsfiddle.net/xtw3nf8q/
HTML
<table>
<tbody data-bind="foreach: teststuff">
<tr>
<td data-bind="text: $data.testone"></td>
<td data-bind="text: $data.testtwo"></td>
<td data-bind="text: $data.testthree"></td>
<td data-bind="text: $data.testfour"></td>
<td data-bind="text: $data.testfive"></td>
<td data-bind="text: $data.testsix"></td>
<td data-bind="text: $data.testseven"></td>
</tr>
</tbody>
jQuery/Knockout
$.getJSON( "/echo/json/jsonfile.txt", function( data ) {
ko.applyBindings({
teststuff: data
});
});
Here is the jsonfile.txt contents:
[{"testone":"Number","testtwo":"49","testthree":"49","testfour":"49","testfive":"49","testsix":"49","testseven":""},{"testone":"Reporting","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"0.0 %"},{"testone":"Area","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":""},{"testone":"K. BACA","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"D. GRAY","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"J. ISA","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"Number","testtwo":"49","testthree":"49","testfour":"49","testfive":"49","testsix":"49","testseven":""},{"testone":"Reporting","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"0.0 %"},{"testone":"Area","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":""},{"testone":"T. BARK","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"H. LAND","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"F. JONES","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"},{"testone":"D. KNOWLES","testtwo":"0","testthree":"0","testfour":"0","testfive":"0","testsix":"0","testseven":"N/A"}]
I downloaded the KnockoutJS plugin/extension for Chrome to help debug and that got me to this point. There are now no errors in the developer console; when I console.log "data", it shows my data. I am at a loss!
You've asked the server for text! It's given you a string
$.getJSON( "/echo/json/jsonfile.txt", ....
You need to turn that string into a valid object (an array in this case):
$.getJSON( "/echo/json/jsonfile.txt", function( data ) {
ko.applyBindings({
teststuff: JSON.parse(data)
});
});
Live example (using string data): https://jsfiddle.net/xtw3nf8q/4/

How to sort data bind to table using knockoutjs mapping plugin?

I am able to bind data to a table and sort using the column headers.
However, I need to be able to edit the data so that it is updated and reflected in the corresponding table cell. To do this I need to make the data object an observable.
Instead of binding like this, using text binding:
<tbody data-bind="foreach: countries">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: capital"></td>
<td data-bind="text: population"></td>
</tr>
</tbody>
I need to bind using the value or textInput binding. Like so:
<tbody data-bind="foreach: countries">
<tr>
<td><input data-bind="value: name" /></td>
<td><input data-bind="value: capital" /></td>
<td><input data-bind="value: population" /></td>
</tr>
</tbody>
And instead of doing this:
$.getJSON('https://restcountries.eu/rest/v1/all', function(data){
self.countries(data);
});
I do this:
$.getJSON('https://restcountries.eu/rest/v1/all', function(data){
var mappedData = ko.mapping.fromJS(data);
var array = mappedData();
self.countries(array);
});
If I attempt to mapped the data using the plugin, the sort does not work.
How do I mapped the data as observables and still have the capability to sort them?
Here is a working JSFiddle example: Note: I removed the mapping since it breaks the code.
I checked the fiddle, the mapping plugin that you linked is wrong. The one in github is not supposed to be a cdn.
Remove the mapping plugin link you added and change it to this link.
I also forked the fiddle with the correct url and added the lines that you removed.
var mappedData = ko.mapping.fromJS(data);
var array = mappedData();
EDIT:
To fix the sorting, you need to change your sorting functions to call the observable values and not the observable function. Like from:
Excerpt from stringSort function:
var countryA = a[column.property].toLowerCase(), countryB = b[column.property].toLowerCase();
to:
var countryA = a[column.property]().toLowerCase(), countryB = b[column.property]().toLowerCase();
You also need to modify the numberSort, dateSort, and deepGet (which is used in objectSort) for other sorts to work. Please check updated fiddle for the sample changes.
JSFiddle link.

split a string (ng-model) created inside ng-repeat directive

I start to play with angularjs, and having this data model :
friends = [
{name:'John**amine**allo'},
{name:'Mary**aaa**mmm'},
{name:'Mike**bb**kkk'}
]
I would like to split each name by '**' to create a table of names.
I don't know if there is more straightforward way to do this ( maybe using some filter in angular-filter module) but here what I have so far tried:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.split('**')">
{{y}}
</td>
</tr>
thanks in advance for any help.
You need to split name property if the x elements:
<td ng-repeat="y in x.name.split('**')">{{y}}</td>
However this is probably not very good idea what you are trying to do. First of all, you need to take care of proper table structure (correct header colspan values). Secondary, it's better to render already processed data structure, rather then converting values inside templates. It just makes it overcomplicated, not to mention makes watch expression slower.
Demo: http://plnkr.co/edit/5PXTW1gjpKwg7e3S6hRM?p=preview
You can use a custom filter to specify the separator:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.name | mysplit:'**'">
{{y}}
</td>
</tr>
</table>
JS:
app.filter('mysplit', function() {
return function(data, sep) {
return data.split(sep);
}
});
Plunker

Adding Operations to Knockout Mapping Object

I am struggling with adding operations to my Knockout Mapping object.
Here is where I am at right now - and I am attempting to follow the Knockout: Mapping API information.
This is the HTML first:
+ Add Contact
<div id="ContactList">
<table>
<thead>
<tr>
<th>ID</th><th>Name</th><th></th>
</tr>
</thead>
<tbody data-bind="foreach: $root">
<tr>
<td data-bind="text: ContactID"></td>
<td data-bind="text: DisplayName"></td>
<td></td>
</tr>
</tbody>
</table>
</div>
And this is my first attempt at wiring up Knockout:
var baseModel = {
addContact: function() {
alert('Woo!');
}
};
contacts = ko.mapping.fromJS('[{"ContactID":6,"DisplayName":"Doe, John"},{"ContactID":7,"DisplayName":"Rogers, Mister"}]', { }, baseModel);
ko.applyBindings(contacts);
Now, if I remove the additional arguments ({} and baseModel) from the ko.mapping.fromJS, all my data is displayed in the table correctly. However (and as to be expected), I get a binding error with the "Add Contact" link. If I add baseModel back in, clicking the "Add Contact" link works, but no data displays. I get no errors one way or another, so I'm a bit stumped on what's going on there.
I'm new to KO, so I feel like I'm missing something pretty simple. I've searched across StackOverflow, but, at this point, I'm just plugging code in to see what works and not actually understanding what is happening here. Can someone provide some additional explanation and point me in the right direction? Thanks.
You're using ko.mapping.fromJS but you're passing a string, not an object/array. Either use fromJSON or pass in a real array.
Also, you should applyBindings on baseModel, and contacts should be a field of the VM. Then you can use foreach: contacts instead of your weird foreach: $root.
See fiddle with those changes: http://jsfiddle.net/antishok/kR4jc/3/
EDIT - Updating your answer with your code so the question can stand on its own.
JavaScript
var baseModel = {
addContact: function() {
alert('Woo!');
}
};
baseModel.contacts = ko.mapping.fromJSON('[{"ContactID":6,"DisplayName":"Doe, John"},{"ContactID":7,"DisplayName":"Rogers, Mister"}]');
ko.applyBindings(baseModel);​
HTML
+ Add Contact
<div id="ContactList">
<table>
<thead>
<tr>
<th>ID</th><th>Name</th><th></th>
</tr>
</thead>
<tbody data-bind="foreach: contacts">
<tr>
<td data-bind="text: ContactID"></td>
<td data-bind="text: DisplayName"></td>
<td></td>
</tr>
</tbody>
</table>
</div>​

Knockoutjs isn't updating my UI when I change an observableArray

This is my template:
<tr>
<td>
<table>
<thead>
<th>
<span>Option name:</span>
</th>
</thead>
<tbody data-bind="template: {name: 'optionChoiceTemplate', foreach: choices, templateOptions:{choiceArray: choices} }"></tbody>
</table>
<button data-bind="click: function(){choices.push('');}">Add new</button>
</td>
</tr>
But when I click the "Add new" button, my view doesn't update to include the new option with the empty string. I've checked in the debugger that the empty string is added to the choices, and I've made sure that choices is an observableArray, what else might be going wrong?
The issue is that when using the jQuery Templates plugin with the template binding with the foreach option, empty strings are treated as null values and are not rendered.
You could work around this by using an object {text: ''} and binding against text or by pushing something other than an empty string (like a single space).
Alternatively, if you are able to move to Knockout 2.0 and use the native templates, then your empty string items will be rendered properly.
I created a fiddle that uses your HTML to display a list of items, and allows the user to add a new item in two ways. The first way is using your click function you created. The second way is using a click binding.
This should answer your question.
http://jsfiddle.net/johnpapa/4PfUr/

Categories

Resources