Displaying contents of a knockout observable array - javascript

Hi guys I'm new to java Script knockout framework, I would like to display contents of an array using knockout, in reality I want to retrieve these contents from a database using ajax but I decided to start with something simpler, which is like this:
$(document).ready(function() {
function requestViewModel() {
this.branchName = ko.observable();
this.allItems = ko.observableArray({items:[{orderItemId:1,
description:"Chocolate",
unitCost:8.50,
quantity:10,
total:84.0},
{orderItemId:2,
description:"Milk",
unitCost:5.0,
quantity:10,
total:50.0},
{orderItemId:3,
description:"Sugar",
unitCost:10.0,
quantity:20,
total:200.0}]});
};
ko.applyBindings(new requestViewModel());
});
......and here is my HTML
Branch Name: <input type="text" name = "branchName">
<br><br><br><br>
<table>
<thead>
<tr><th>Item id</th><th>Description</th><th>Unit Cost</th><th>Quantity</th><th>Total</th></tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: orderItemId"></td>
<td data-bind="text: description"></td>
<td data-bind="text: unitCost"></td>
<td data-bind="text: quantity"></td>
<td data-bind="text: total"></td>
</tr>
</tbody>
</table>
Please help where you can coz I get an error which is:
"Error: The argument passed when initializing an observable array must be an array, or null, or undefined."

The error says it all. You need to pass in an array instead of an object like so:
this.allItems = ko.observableArray([{orderItemId:1,
description:"Chocolate",
unitCost:8.50,
quantity:10,
total:84.0},
{orderItemId:2,
description:"Milk",
unitCost:5.0,
quantity:10,
total:50.0},
{orderItemId:3,
description:"Sugar",
unitCost:10.0,
quantity:20,
total:200.0}]);
Note the removal of {items: and }.

Related

From DB they are sending HTML, how to render it in the DOM?

I am receiving an object like this
[
{
"BET": 57630343,
"CUSTOMER": 181645,
"SPORT": "MLB",
"XX_FILL OPEN": "<button class=\"btn\" onclick=\"fillOpen(57630343)\">Fill Open</button>",
"XX_VIEW": null,
"XX_CANCEL": "<input type=\"checkbox\" name=\"sports\" value=\"soccer\" onchange=\"fillOpen(57630343)\"/>"
},...]
I am rendering that object in the DOM, but for now, looks like in the picture below
here is the HTML part for the dynamic table
<table>
<thead>
<tr>
<th ng-repeat="column in cols" ng-init="isXX = column.indexOf('XX') === 0">
<span ng-if="isXX">{{column.substring(3).replace('_', ' ')}}</span>
<span ng-if="!isXX">{{column}}</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in cols">
<span>{{row[column]}}</span>
</td>
</tr>
</tbody>
</table>
and here is the Angular part
ReportsFactory.pendingBets(reportParam).then(function(data) {
if (data.length) {
gridInfo = _.forEach(data, function(item) {return item;});
$scope.rows = gridInfo;
$scope.cols = Object.keys($scope.rows[0]);
}
}
The Database guys are sending me that data as you see it in the json I pasted above.
what I need to know, is, what should I do in order to render those elements in the DOM ?
You can use ngBindHtml for that.
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in cols" ng-bind-html="row[column]">
</td>
</tr>
</tbody>
Since you're getting the HTML from a $http call, you have to use $sce.trustAsHtml to correctly display this HTML on every single one of your row columns that require this:
$scope.rows.forEach(function(row) {
for (var key in row) {
if (key.indexOf('XX') === 0) {
var value = row[key];
if (value) {
row[key] = $sce.trustAsHtml(value);
}
}
}
});
Here's a working plunkr:http://plnkr.co/edit/7iFUhjg6Q0YIwRi47fDm?p=preview
You can do it by using ngSanitize.
Check it here. https://docs.angularjs.org/api/ngSanitize
Then, you can use ng-bind-html directive, which render HTML code to DOM element.
And, Javascript in the element can be applied with trustAsHtml method of $sce service.
I made an example here.
http://plnkr.co/edit/sTEUIWHwGXSAUSQ2rC8y?p=preview

knockout.js foreach $data AND $index()?

HTML
You can see in the html I am calling editUser with two parameters. $data being the first parameter and $index() being the second. It should send the current user object in the first parameter and the index of the foreach loop as the second.
<tbody data-bind="foreach: users">
<tr>
<td>
<div data-bind="click: $parent.editUser.bind($data, $index())" class="clickable icon black-icon big-icon icon-edit"></div>
</td>
<td data-bind="text: email"></td>
<td data-bind="text: username"></td>
<td data-bind="text: level"></td>
</tr>
</tbody>
Code
appViewModel.editUser = function( user, index ){
console.log(user);
console.log(index);
};
Output
0
Object{}
It completely reverses the parameters somehow. I am at a loss to explain this behavior.
The first argument of bind is the function context. So, if you want to pass 2 arguments you're gonna need this:
$parent.editUser.bind($data, $data, $index())
This way, the this in your editUser function would be the current item in your foreach iteration.
See MDN

Angular JS object as ng-model attribute name

I'm creating a dynamic angular js application wherein I want to use a textbox as a searchtext for the filter of a table. Here's a preview on what I am doing:
As of now, my code looks like this
<table>
<thead>
<tr class="filterrow">
<td data-ng-repeat="col in items.Properties">
<input id="{{col.DatabaseColumnName}}" type="text"
data-ng-model="search_{{col.DatabaseColumnName}}"/>
<!-- Above Here I want to dynamically assign the ng-model based on Databasecolumnname property-->
</td>
</tr>
<tr>
<th data-ng-repeat="col in items.Properties">{{col.ColumnTitle}}</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="content in items2.Contents | filter: search_{{col.DatabaseColumnName}}: search_{{col.DatabaseColumnName}}">
<td data-ng-repeat="col in items.Properties">
{{content[col.Databasecolumnname ]}}
</td>
</tr>
</tbody>
</table>
I already tried the approach of using $compile but I wasn't able to implement it. Any ideas in an approach? Thanks!
EDIT: Plnkr - plnkr.co/edit/5LaRYE?p=preview
You can do this by setting a base object for your ng-Models. So in your controller you will have:-
$scope.search = {};
and in your view do:-
<input ng-attr-id="{{col.DatabaseColumnName}}" type="text"
data-ng-model="search[col.DatabaseColumnName]"/>
With this your dynamic ng-model will be assigned to the search base object, ex:- if col.DatabaseColumnName is col1 then ngModel would be $scope.search.col1

Update Knockoutjs table

I have a table bound to knockoutjs using foreach.
<table>
<thead>
<tr>
<th>ID</th>
<th>BALANCE</th>
<th>GENDER</th>
<th>AGE</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: data -->
<tr>
<!-- ko foreach: Object.keys($data) -->
<td>
<label data-bind="text: $parent[$data]" />
</td>
<!-- /ko -->
</tr>
<!-- /ko -->
</tbody>
</table>
Table rows iterate an observableArray (about 2000 items).
After table is rendered, I need to edit a row but table do not render the row changed.
How can I do this whithout clear observableArray and reload it again?
Here JSFIDDLE
Thank you
You need to make your data array properties observable so knockout would observe the changes. I'd suggest you to use knockout mapping to do the job of creating observables for you, like this:
var foo = new MyVM();
var mapping = {
create: function(options) {
return ko.mapping.fromJS(options.data);
}
};
ko.mapping.fromJS(myJS, mapping, foo.data);
But you need to change your markup so it won't just iterate through object properties, but explicitly specify which property should be used:
<tbody>
<!-- ko foreach: data -->
<tr>
<td>
<label data-bind="text: _id" />
</td>
<td>
<label data-bind="text: balance" />
</td>
<td>
<label data-bind="text: gender" />
</td>
<td>
<label data-bind="text: age" />
</td>
</tr>
<!-- /ko -->
</tbody>
Here is working demo. Of course you can make observables yourself, then just rewrite your code, so every property of each item in data array would be an observable.
Edit:
Well, since we don't know the actual properties, I'd suggest the following code to make observables:
var foo = new MyVM();
for (var i=0, n = myJSON.length; i < n; i++) {
for (var prop in myJSON[i])
if (myJSON[i].hasOwnProperty(prop))
myJSON[i][prop] = ko.observable(myJSON[i][prop]);
}
foo.data(myJSON);
And in your view model function:
self.changeRow = function (){
if(typeof self.data() != "undefined"){
self.data()[0]["gender"]("male");
}
};
See updated demo.

Knockout js template, filter first element of observable array

Hi I'm trying to make the first element of my observableArray hidden, the following doesn't appear to be working, any ideas?
data-bind="ifnot: $root.typedData[0]===$data"
http://jsfiddle.net/Lx8jR/
<table border="1" style="width:90%">
<tr>
<td data-bind="text: typedData()[0].name"></td>
<td data-bind="text: typedData()[0].type"></td>
</tr>
<tr>
<td>
<table data-bind="foreach: typedData()">
<tr>
<td data-bind="text: name"></td>
</tr>
</table>
</td>
<td>
<table data-bind="foreach: typedData()">
<tr data-bind="ifnot: $root.typedData[0]===$data">
<td data-bind="text: type">
</td>
<td data-bind="text: $index">
</td>
</tr>
</table>
</td>
</tr>
</table>
var ViewModel = function() {
var self = this;
this.typedData = ko.observableArray([
{ name: "Bungle", type: "Bear" },
{ name: "George", type: "Hippo" },
{ name: "Zippy", type: "Unknown" }
]).indexed();
}
Looks like you missed a () on that line.
ifnot: $root.typedData[0]===$data
becomes
ifnot: $root.typedData()[0]===$data
http://jsfiddle.net/Lx8jR/1/
A simple mistake I've made a few dozen times.
If you get into the habit of using ko.utils.unwrapObservable this becomes less of an issue. If you use that function on a non-observable, it still succeeds.
... ko.utils.unwrapObservable($root.typedData)[0]
And for reference, there's an article on KnockMeOut which suggests a few other standards that help simplify our templates and bindings.
typedData is an observableArray, so in your comparison you would want to do (add ()):
$root.typedData()[0] === $data

Categories

Resources