How to bind child array in array by using Knockout.js? - javascript

I'm using Knockout to bind data to a table:
I have obj:
obj=[{id:"1",productName:"laptop",tag:[promotion,blackfriday]},{id:"2",productName:"Samsung galaxy note III",tag:[samsung,galaxy]}]
HTML:
<table data-bind="foreach:list" id="listProduct">
<tr>
<td data-bind="text:productName"></td>
<td data-bind="foreach:obj.tag">
<p data-bind="text:tag"></p>
</td>
</tr>
</table>
JavaScript (to bind data):
ko.applyBindings({ list: obj }, document.getElementById('listProduct'));
But it only displays the product name and does not bind "tag" obj; I want Knockout to bind data as:
Product Name Tag
1 Laptop promotion
blackfriday
2 Samsung galaxy note III samsung
galaxy

based on the code you gave, there are a couple simple things that will fix this for you:
productName is misspelled
the foreach:obj.tag should be foreach:tag
the text:tag should be text:$data (see the docs on this)
your tags in the data given are not strings - so it won't work if they aren't defined elsewhere
JS:
var obj = [{
id: "1",
productName: "laptop",
tag: ["promotion", "blackfriday"]
}, {
id: "2",
productName: "Samsung galaxy note III",
tag: ["samsung", "galaxy"]
}];
ko.applyBindings({ list: obj }, document.getElementById('listProduct'));
HTML:
<table id="listProduct">
<tr data-bind="foreach:list">
<td data-bind="text:productName">test</td>
<td data-bind="foreach:tag">
<p data-bind="text:$data">test</p>
</td>
</tr>
</table>

The implementation that are you using is too odd, and I think is not correct. For example I don't know why are you retrieving the listProduct element and sending as argument to ko.applyBidning, this is not how knockout works.
You also have mistakes using inside object in the foreach binding, check out documentation to see the correct usage. Even you have a misspelled in the HTML with the productName value.
You have syntax errors inside both tag arrays, the values are string so you need to add quotes to each element.
I will show a better and clean way to do it fixing the issues mentioned above:
JS
function VM(){ //declare your VM constructor.
this.list = ko.observableArray([
{id: '1', productName: 'laptop', tag: ['promotion', 'blackfriday']},
{id: '2', productName: 'Samsung galaxy note III', tag: ['samsung', 'galaxy']}
]); //Add and observable array an set your data. If you don't need that this array be an observable just use a normal JS array.
}
ko.applyBindings(new VM()); //apply binding
HTML
<table data-bind="foreach: list">
<tr>
<td data-bind="text: productName"></td>
<td data-bind="foreach: tag">
<p data-bind="text: $data"></p>
</td>
</tr>
</table>
Check out this fiddle to see it working.

Related

NG-Table not working with nested data structure

I am trying to use the NG-Table library to display data to users in a web-app I'm creating, but it seems that the built in functions are not able to sort data structured like this:
var data = [
{
generalInfo:{
name: "Morty",
age: 20
}
},
{
generalInfo:{
name: "Ricky",
age: 20
}}];
The actual database I am using requires the data to be structured like this, so I can not change the structure. I tried changing my HTML to match this format, but the filter element does not allow dot notation.
My html looks like this:
<div ng-controller="appController as vm">
<table ng-table="vm.tableParams" class="table table-bordered table- striped table-condensed" show-filter="true">
<tr ng-repeat="user in $data">
<td title="'Name'" filter="{ generalFeatures.name: 'text'}" sortable="'generalFeatures.name'">{{user.generalFeatures.name}}</td>
<td title="'Age'" filter="{ generalFeatures.age: 'number'}" sortable="'generalFeatures.age'"> {{user.generalFeatures.age}}</td>
</tr>
</table>
</div>
Is there any way for me to sort data structured this way?
I found a temporary solution to this problem. I could create new properties in the parent object by looping through all of the properties in the nested object and adding them to the parent.
angular.forEach(data, function (user) {
if(user.generalInfo != null){
user.age = user.generalInfo.age;
user.name = user.generalInfo.name;
}
});

How knockout know about data-binded object if it has only its name?

I have something like:
<form action='/someServerSideHandler'>
<p>You have asked for <span data-bind='text: gifts().length'> </span> gift(s)</p>
<table data-bind='visible: gifts().length > 0'>
<thead>
<tr>
<th>Gift name</th>
<th>Price</th>
<th />
</tr>
</thead>
<tbody data-bind='foreach: gifts'>
<tr>
<td><input class='required' data-bind='value: name, uniqueName: true' /></td>
<td><input class='required number' data-bind='value: price, uniqueName: true' /></td>
<td><a href='#' data-bind='click: $root.removeGift'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: addGift'>Add Gift</button>
<button data-bind='enable: gifts().length > 0' type='submit'>Submit</button>
</form>
and
var GiftModel = function(gifts) {
var self = this;
self.gifts = ko.observableArray(gifts);
self.addGift = function() {
self.gifts.push({
name: "",
price: ""
});
};
self.removeGift = function(gift) {
self.gifts.remove(gift);
};
self.save = function(form) {
alert("Could now transmit to server: " + ko.utils.stringifyJson(self.gifts));
// To actually transmit to server as a regular form post, write this: ko.utils.postJson($("form")[0], self.gifts);
};
};
var viewModel = new GiftModel([
{ name: "Tall Hat", price: "39.95"},
{ name: "Long Cloak", price: "120.00"}
]);
ko.applyBindings(viewModel);
// Activate jQuery Validation
$("form").validate({ submitHandler: viewModel.save });
how it is happening that ko.applyBindings(viewModel); is magically making biding by name of variable? Is knockout searching it somehow by name? How the template know that this is his array/data set? I am basically .net developer so in my mind taking something "by name" is not clear. Or maybe am I wrong that this is taken by name? I read documentation but I still do not get the way how knockout connect template gifts() with array named gifts from model?
but the way this is sample from knockout main page.
http://knockoutjs.com/examples/gridEditor.html
how it is happening that ko.applyBindings(viewModel); is magically making biding by name of variable? Is knockout searching it somehow by name?
Cutting some corners here, but two things in which Javascript (not so much KO) differs from .NET, as related to your question:
All members (e.g. self.gifts) can also be accessed as if self had a string based indexer to get them (e.g. self['gifts']);
Javascript is dynamically typed, so self['gifts'] can at run time contain an array, string, observable: whatever.
So, Knockout can take your string "gifts" use it to get to the variable self["gifts"] and at run time check for its type to see if it's an array, observable y/n, etc, and choose the appropriate code path accordingly.
As for your other question:
How the template know that this is his array/data set?
Knockout is open source (though perhaps not easy to read when starting out with JS), and if you dive in to it you'll find that foreach assumes it's passed an array.

ng repeat array in array

I am trying to repeat a child array of a multidimensional array with ng repeat in Angular.
My json object is this:
$scope.items = [{ "id":1,
"BasisA":"1",
"Basis":true,
"personSex":"m",
"isCollapsed":false,
"name":"Mark Polos",
"age":"1955",
"results":[{"1000":{"company_name":"***","model":"***","modelname":"***","pr":222,"rating":4.5,"priority":9,"matching":1},
"1001":{"company_name":"***","model":"***","modelname":"***","pr":228.7,"rating":5.7,"priority":7,"matching":2},
"1002":{"company_name":"***","model":"***","modelname":"***","pr":241.7,"rating":1.9,"priority":4,"matching":3}
}]
}]
Itried somthing like this:
... data-ng-repeat="item in items">
And then in the table of this child:
<tr data-ng-repeat="i in item | orderBy:'insItem.pr'">
It doesn't look like that results property is actually an "array." If that's just a typo in your example, then disregard. If not ... read on.
It looks like an array with a single item, and that Item is a set of properties which are, in turn, objects. In other words, you would reference the property "pr" for the result named "1000" by with code that looks like item.results[0]["1000"].pr NOT with code that looks the way your ng-repeat is expecting(item.results[0].pr).
Can you transform your items when you get them so that results is a true array?
OR - can you use a function inside of your controller that returns the array you are looking for?
View Code:
<... data-ng-repeat="result in resultsFromItem(item)" >
Controller Code:
$scope.resultsFromItem = function (item) {
if(item==undefined || item.results==undefined || item.results.length==0) {
return [];
}
var myResults = [];
for (var key in item.results[0]) {
if(item.results[0].hasOwnProperty(key)) {
myResults.push(item.results[0][key]);
}
}
return myResults;
}
You might even decide to hang that "transformed" results object off each item object (so you only have to go through the transform one time) if you wanted to.
You should access to the results field:
... data-ng-repeat="item in items">
<tr data-ng-repeat="i in item.results">
Since the nested array is in the results property of the main object.
I used three nested ng-repeat directives to get this rolling :-) The third ng-repeat uses ng-repeat="(key, value) in result" functionality to display all result object keys and values, which I got working with the help of this answer on how to iterate over keys and values in ng-repeat. The orderBy: part isn't yet working (if someone knows how to implement that then any help is welcomed).
<ul>
<li ng-repeat="item in items">
id: {{item.id}}, name: {{item.name}}, age: {{item.age}}, results:
<table>
<tr ng-repeat="result in item.results">
<td>
<table style="border: 1px solid black;">
<tr ng-repeat="(key, value) in result | orderBy: value.pr">
<td> {{key}} </td> <td> {{ value }} </td>
</tr>
</table>
</td>
</table>
</li>
</ul>
Plunker

Assignment of property values in AngularJS

In AngularJS I have a problem that a variable is assigned a value of another object.
I have ng-table and this work fine
<table ng-table="tableParams" data-ng-init="getMeasureUnits()" class="table" >
<tr ng-repeat="measUnit in measureUnits" ng-click="oneEntity(measUnit)">
<td data-title="'Id'" >{{measUnit.idUnit}}</td>
<td data-title="'Name'" >{{measUnit.name}}</td>
<td data-title="'abb'" >{{measUnit.abb}}</td>
</tr>
in Controller I have function oneEntity and I got one entity from table in function but can't be assigned to another entity that which I present it in another table.
$scope.measureUnitOne = {};
$scope.oneEntity = function(entity){
$scope.measureUnitOne = entity;
$location.path("/administration/edit/measureUnit.id");
};
After function I get fine location url I everything OK byt did't display the values in table :
...
<input type="text" class="form-control" id="mesaureUnitName" data-ng-model="measureUnitOne.name" placeholder="Name">
...
Object {name: "Litar", abb: "l", last_update: 1441538332000, id_user: 123, idUnit: 2}
$$hashKey: "object:46"
abb: "l"
idUnit: 4
id_user: 123
last_update: 1441538363000
name: "Litar"
proto: Object
In first case when i Write values , table show values , in second case, that is what I got after function and noting show ... Can someone telm me where is problem ??

Iterate properties of an object knockoutjs

From what I've been able to find online I don't think it's possible to use the foreach data-bind to iterate through the properties of an observable object in knockout at this time.
If someone could help me with a solution to what I'm trying to do I'd be very thankful.
Let's say I have an array of movies objects:
var movies = [{
title: 'My First Movie',
genre: 'comedy',
year: '1984'
},
{
title: 'My Next Movie',
genre: 'horror',
year: '1988'
},
];
And what I would like to do is display this data in a table, but a different table for each genre of movie.
So I attempted something like this:
<div data-bind="foreach: movieGenre">
<table>
<tr>
<td data-bind="year"></td>
<td data-bind="title"></td>
<td data-bind="genre"></td>
</tr>
</table>
</div>
and my data source changed to look like this:
for (var i = 0; i < movies.length; ++i) {
if (typeof moviesGenres[movies.genre] === 'undefined')
moviesGenres[movies.genre] = [];
moviesGenres[movies.genre].push(movie);
}
I've tried about a dozen other solutions, and I'm starting to wonder if it's my lack of knowledge of knockout(I'm pretty green on it still), or it's just not possible the way I'd like it to be.
You can make your array "movies" an KO observable array and the array "movieGenre" a KO computed property. Have a look at this fiddle.
The code in the fiddle is given below for reader convenience;
KO View Model
function MoviesViewModel() {
var self = this;
self.movies = ko.observableArray([
{
title: 'My First Movie',
genre: 'comedy',
year: '1984'
},
{
title: 'My Next Movie',
genre: 'horror',
year: '1988'
},
{
title: 'My Other Movie',
genre: 'horror',
year: '1986'
}
]);
self.movieGenre = ko.computed(function() {
var genres = new Array();
var moviesArray = self.movies();
for (var i = 0; i < moviesArray.length; i++) {
if (genres.indexOf(moviesArray[i].genre) < 0) {
genres.push(moviesArray[i].genre);
}
}
return genres;
});
};
HTML
<div data-bind="foreach: movieGenre()">
<h3 data-bind="text: 'Genere : ' + $data"></h3>
<table border="solid">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Year</th>
</tr>
</thead>
<tbody data-bind="foreach: $parent.movies">
<!-- ko if: $data.genre === $parent -->
<tr>
<td data-bind="text: $data.title"></td>
<td data-bind="text: $data.genre"></td>
<td data-bind="text: $data.year"></td>
</tr>
<!-- /ko -->
</tbody>
</table>
</div>
As you can see "movieGenre" is made a computed property. Whenever the observable array "movies" changes, the moveGenre is calculated and cached. However, since this is not declared as a writable computed property, you cannot bind this to your view. Hence, it's value is used in the data binding.
The approach for rendering is simply looping through the calculated "movieGenre", and nest another loop for movies. Before adding a row to the table, for the corresponding table, the movie object is evaluated with the current movieGenre. Here, the container-less control flow syntax is used. We can use the "if" binding, but that would leave an empty table row per each movie object where the genre is otherwise.
The $parent binding context is used to access the parent contexts in the nested loop.
Hope this helps.

Categories