Knockout.js, how to change a value of mapped two dimensional array? - javascript

I have a controller that returns two dimensional array of objects as json. I make a call from my javascript:
$.getJSON("/Game/GetBoard", function (json) {
data = json;
board = ko.mapping.fromJS({ board: data });
ko.applyBindings(board, $('.board')[0]);
});
I also have the following html:
<table>
<tbody data-bind="foreach: board">
<tr data-bind="foreach: $data">
<td data-bind="attr: { class: Color }"></td>
</tr>
</tbody>
</table>
It generates a nice 2 dimensional html table with nicely colored cells (based on a class that comes from a Color property). How can I now change this color to something else?
I tried: board[1][1]({Color: 'red'});, but I get an error saying that board[1] does not exist...
And another question, how can I add more than one class to the binding? I tried:
...
<td data-bind="attr: { class: Color + ' some-other-class' }"></td>
...
But then I get:
class="function b() { if (0 < arguments.length) { if (!b.equalityComparer || !b.equalityComparer(d, arguments[0])) { b.H(), d = arguments[0], b.G(); } return this; } r.T.Ha(b); return d; } some-other-class"
Is it a bug or am I doing something wrong?

The mapping plugin will turn your array into an observableArray and your properties into observables.
For your first case, you would need to unwrap the observable array by doing: board.board()[1][1]
For the other question, Color is an observable. If you are using it in an expression and want to get its value, then you need to do Color(). So, it would look like:
<td data-bind="attr: { class: Color() + ' some-other-class' }"></td>

Related

Highlight cell with highest value in ng-repeat with AngularJS 1.4.14

I am working on a dashboard and I'd like some help '_'.
I get a JSON from an API and I get an array of 5 element and each contain this structure (I simplified it but it is close to that).
{
"app_id": "id",
"app_name": "name",
"users_percentiles": {
"users_percentile_1": "3408",
"users_percentile_2": "2356",
"users_percentile_3": "988",
"users_percentile_4": "1099",
}
}
Then, I use a table to organize those elements in my dashboard.
<tbody ng-repeat="dash in dashboard">
<tr>
<td>{{dash.app_id}}</td>
<td>{{dash.app_name}}</td>
<td ng-repeat="percentile in dash.users_percentiles">
{{(percentile}}%
</td>
</tr>
</tbody>
I'd like for each ng-repeat highlight the highest value of percentile (if two are equals, then both should be highlighted).
I think i should add something like :
ng-class="{max : percentile == maxPercentile}"
and a function :
$scope.maxPercentile = -1;
angular.forEach(percentiles, function (percentile) {
if (percentile > $scope.maxPercentile) {
$scope.maxPercentile = percentile;
}
});
But I don't know really where to use this method.
I tried with a $watch but no matter how I tried I didn't get it to work...
<tbody ng-repeat="dash in dashboard">
<tr>
<td>{{dash.app_id}}</td>
<td ng-init = "maxpercentile = getMaxPercentile(dash.users_percentiles)">{{dash.app_name}}</td>
<td ng-class="{max : percentile === maxpercentile}"
ng-repeat="percentile in dash.users_percentiles">
{{percentile}}%
</td>
</tr>
</tbody>
$scope.getMaxPercentile = function(percentiles){
var maxPercentile = -1;
angular.forEach(percentiles, function (percentile) {
if (percentile > $scope.maxPercentile) {
maxPercentile = percentile;
}
});
return maxPercentile;
}

Object not printing/returning information to HTML(using jquery .hover())?

I have the following table in which I have two ids, mPoint and bPoint. I want to insert data here using html() when I hover over elements with id hydrogen or helium.
<div class="info" id="hydrogen">
<em>1</em>
H
<p>Hydrogen</p>
</div>
<div class="info" id="helium">
<em>2</em>
Li
<p>Helium</p>
</div>
<table class="propTable">
<tr>
<td>Melting Point</td>
<td id="mPoint"></td>
</tr>
<tr>
<td>Boiling Point</td>
<td id="bPoint"></td>
</tr>
</table>
Here is the JS Function
function PropElements() {
var propHydrogen = {
"m_point": "14.01",
"b_point": "20.28"
};
var propHelium = {
"m_point": "0",
"b_point": "4.22"
};
$('.info').hover(function() {
var getId = this.id;
var getPropName = "prop" + getId.charAt(0).toUpperCase() + getId.slice(1);
console.log(getPropName);
$("#mPoint").html(getPropName.m_point + " K");
$("#bPoint").html(getPropName.b_point + " K");
}, function() {
$("#mPoint").html("unknown");
$("#bPoint").html("unknown");
});
}
Here are two objects for Hydrogen and Helium. getPropName will change the name of the hovered element such that it becomes prop[hoveredElementID]. Now If I hover over div with #hydrogen, getPropName will change name to propHydrogen, which is also the name of the object. And then using html(), it should print the value of m_point and b_point at the position of specified IDs.
But its showing the value of getPropName.m_point and getPropName.b_point as unknown. I have tried putting the objects inside the function but it still returns unknown even though console is returning the exact propName that should work. I tried using innerHTML, but that returns undefined instead.
Though, If I use the name directly(propHydrogen.m_point), it prints the value correctly. What could be the problem here?
The issue is because you cannot dynamically use the value of a variable to point to another defined variable in the manner you are attempting. A better method would be to use a single object to store all information required and then access the properties of that object dynamically. Try this:
function propElements() {
var elements = {
hydrogen: {
"m_point": "14.01",
"b_point": "20.28"
},
helium: {
"m_point": "0",
"b_point": "4.22"
}
};
$('.info').hover(function() {
$("#mPoint").html(elements[this.id].m_point + " K");
$("#bPoint").html(elements[this.id].b_point + " K");
}, function() {
$("#mPoint").html("unknown");
$("#bPoint").html("unknown");
});
}
Working example
Note that by making the property names in the object match the id of the .info elements it saves a lot of unnecessary string manipulation.

AngularJS and XML, how to render it?

I am working along DB guys, they are sending me the data thru XML, and depending the kind of element they specify is what I need to display in the view.
The code you will see is a dynamic table
<table>
<thead>
<tr>
<th ng-repeat="column in cols">
<span>{{column}}</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in cols"
ng-init="isXX = column.indexOf('XX') === 0">
<span ng-if="!isXX">{{row[column]}}</span>
<button ng-if="isXX" class="btn btn-xs btn-blue"
ng-click="fillOpen()">
{{column.substring(3).replace('_', ' ')}}
</button>
</td>
</tr>
</tbody>
</table>
and here is what I have in the controller
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]);
}
}
as you can see here I have this ng-init
ng-init="isXX = column.indexOf('XX') === 0" where I am telling the app, if the property I am receiving comes with XX at the index, then display a button <button ng-if="isXX" ng-click="fillOpen()">...</button> but so far, I have some more props coming with XX at the beginning, so I need to do it more dynamic.
This is how my view looks so far
what I need to know, is how to read that XML, this is the XML printed in the Nodejs terminal
[{ BET: 57635034,
CUSTOMER: 181645,
SPORT: 'NFL',
'XX_FILL OPEN': '<element><element_type>WAGER_ACTION_BUTTON</element_type><element_call>fillOpen(57635034)</element_call><element_content/></element>',
XX_VIEW: '<element><element_type>BASIC_DROPDOWN</element_type><element_call>callThisFunction()</element_call><element_content><li>1</li><li>2</li><li>3</li><li>4</li></element_content></element>',
XX_CANCEL: '<element><element_type>BASIC_CHECKBOX</element_type><element_call/><element_content>1</element_content></element>'
}]
so, the first says
'XX_FILL OPEN': '<element><element_type>WAGER_ACTION_BUTTON</element_type><element_call>fillOpen(57635034)</element_call><element_content/></element>'
WAGER_ACTION_BUTTON should be a button
the second one says
BASIC_DROPDOWN that should be a dropdown and so on, so, how should I do in order to display the proper HTML element depending on what the XML says ?
Any suggestions ?
if I understood you correctly you want to dynamically render the xml or html content to your view... I assume that element and element type are directive you have or something.
use
ngBindHtml
e.g:
<div class="col-xs-offset-1 m-r-offset-8 p-t-offset-2 font-l-16">
<span mathjax-bind ng-bind-html="question.question.body"></span>
</div>
or you might need to use the trustAsHtml function
<div class="col-xs-offset-1 m-r-offset-8 p-t-offset-2 font-l-16">
<span mathjax-bind ng-bind-html="trustAsHtml(question.question.body)"></span>
</div>
$scope.trustAsHtml = function (val) {
return $sce.trustAsHtml(val);
};
this will take your string xml (html) code and render it...
you could always build a personalize directive and use $compile as well like:
app.directive('ngHtmlCompile',function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.ngHtmlCompile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
});
and in the code just call the ng-html-compile... no need for $sce

How to ng-repeat array of an object in object?

I have this data:
{
"order":[
{
"id":1,
"table":1,
"foods":"{'foods':[{'id':2, 'name':'Nasi Minyak', 'qty':1}]}",
"drinks":"{'drinks':[{'id':1,'name':'Teh O Ais','qty':1}]}",
"waiter":"ali",
"foods_status":0,
"drinks_status":0,
"created_at":"2015-07-12T00:30:52.637Z",
"updated_at":"2015-07-12T00:30:52.637Z"
},
{
"id":2,
"table":2,
"foods":"{'foods':[{'id':2, 'name':'Nasi Goreng', 'qty':1}]}",
"drinks":"{'drinks':[{'id':1,'name':'Milo Ais','qty':1}]}",
"waiter":"abu",
"foods_status":0,
"drinks_status":0,
"created_at":"2015-07-12T00:51:43.552Z",
"updated_at":"2015-07-12T00:51:43.552Z"
}
]
}
I try to grab all foods name inside table like this:
<table class="table-bordered table table-striped">
<thead>
<tr>
<td>#</td>
<td>Name</td>
<td>Action</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="order in orders">
<td>{{order.id}}</td>
<td>{{order.foods.name}}</td>
<td>
<button class="btn btn-danger" ng-click="">Delete</button>
</td>
</tr>
</tbody>
</table>
And this is my $http.get to get the data:
$http.get("../api/orders")
.success(function(data) {
var order = data.order;
$scope.orders = order;
});
I managed to bind the id but I could't bind the name inside the foods array.
How to get the name inside the foods array of this data?
Plunker:
http://plnkr.co/edit/2oiOc06cZph4en8DJ18n
You need another ng-reapeat. Something like this:
<tr ng-repeat="order in orders">
<td>{{order.id}}</td>
<td>
<span ng-repeat="item in order.foods.foods">{{item.name}}/</span>
</td>
<td>
<button class="btn btn-danger" ng-click="">Delete</button>
</td>
</tr>
Another consideration is about the format of your JSON. this line:
"foods":"{'foods':[{'id':2, 'name':'Nasi Minyak', 'qty':1}]}"
the way it is, "foods" is holding a String, and not a Object. To make the ng-reapeat work, you will need to force JSON from string using
JSON.parse(jsonString);
or change your JSON to:
"foods":{"foods":[{"id":2, "name":"Nasi Minyak", "qty":1}]}
Side note, why repeat the keys "foods" and "drinks"? Doesn't seem logic to me. Change your data structure to:
"order":[
{
"id":1,
"table":1,
"foods":[{"id":1, "name":"Nasi Kerabu", "qty":1},{"id":2, "name":"Nasi Minyak", "qty":1}],
"drinks":[{"id":1,"name":"Sirap Ais","qty":1},{"id":2, "name":"Milo Ais", "qty":1}],
"waiter":"ali",
"foods_status":0,
"drinks_status":0,
"created_at":"2015-07-12T00:30:52.637Z",
"updated_at":"2015-07-12T03:30:35.684Z"
},
...
]
and use:
<td> <span ng-repeat="item in order.foods">{{item.name}}</span> </td>
Here is a plunker with these modifications:
http://plnkr.co/edit/UVvCVzh4hbsEwolyWpDs?p=preview
Here is a plunker that works http://plnkr.co/edit/snE9Em0tCKh0nUHIlTFn?p=preview.
Consider modifying your JSON file. Use double quotation marks instead of single quotation marks.
In your modified JSON file in your new plunker remove double quotations marks here "[{'id':1,
I have made some changes in your data and it fix the issue you are facing.
Here is plunker link
`http://plnkr.co/edit/nxBGMMyuNIzUOvQAu7YY?p=preview`
Each order would be an object like this:
{
"id":2,
"table":2,
"foods":"{'foods':[{'id':2, 'name':'Nasi Goreng', 'qty':1}]}",
"drinks":"{'drinks':[{'id':1,'name':'Milo Ais','qty':1}]}",
"waiter":"abu",
"foods_status":0,
"drinks_status":0,
"created_at":"2015-07-12T00:51:43.552Z",
"updated_at":"2015-07-12T00:51:43.552Z"
}
Note that foods points to an object, whose only key 'foods' points to an array... whose first component should be an object. However, if you read more closely:
"foods":"{'foods':[{'id':2, 'name':'Nasi Goreng', 'qty':1}]}",
Notice the double quotes surrounding foods's value? They mean that it points to a String instead of an object.
First, you need to delete the double quotes surrounding the values of both foods and drinks:
"foods":{'foods':[{'id':2, 'name':'Nasi Goreng', 'qty':1}]},
"drinks":{'drinks':[{'id':1,'name':'Milo Ais','qty':1}]},
And then replace all the single quotes with double ones, to make the object comply with the JSON object definition:
"foods":{"foods":[{"id":2, "name":"Nasi Goreng", "qty":1}]},
"drinks":{"drinks":[{"id":1,"name":"Milo Ais","qty":1}]},
Now, to get 'name', you need to access order.foods.foods[0].name instead of order.foods.name.

Accessing object fields in observable array of objects. KnockoutJS

I have a table, that is filled through data-binds with data from observable array of objects (persons). When i click a certain cell of table, index of a line, and index of a cell is written into variables "self.currentLine" and "self.currentCell", while input appears above with 100% width and 100% height, covering that data with itself.
Is there a possibility to get access to certain field of certain object in observable array, using only indexes of fields instead of using field names? (ex. not self.persons[0]'name', but self.persons[0][0])
Here is a code(JS):
function person(fullname, age, sex, married)
{
this.name = ko.observable(fullname); //string, only observable field, while i'm trying to get this working properly.
this.age = age; //Data
this.sex = sex; //string
this.married = married; //bool
};
function personViewModel()
{
var self = this;
self.currentLine = ko.observable();
self.currentCell = ko.observable();
self.columnNames = ko.observableArray([
'Name',
'Age',
'Sex',
'Married'
]);
self.persons = ko.observableArray([...]);
};
self.setLine = function(index)
{
self.currentLine(index);
};
self.setCell= function(cellIndex)
{
self.currentCell(cellIndex);
};
};
ko.applyBindings(new personViewModel());
And HTML code i use:
<table>
<thead data-bind="template: { name: 'tableHeader', data: columnNames }" />
<tbody data-bind="template: { name: 'tableContent', foreach: persons }" />
</table>
<script id="tableHeader" type="text/html">
<tr data-bind="foreach: $data">
<td data-bind="text: $data,
css: { 'active': $root.currentItem() == $data }">
</td>
</tr>
</script>
<script id="tableContent" type="text/html">
<tr data-bind="click: $root.setLine.bind($data, $index())">
<td data-bind="click: $root.setCell.bind($data, $element.cellIndex)">
<span data-bind="text: name"></span>
<input type="text" data-bind="visible: $root.currentCell() == 0 && $index() == $root.currentLine(),
value: name"/> <!--fixed-->
</td>
</tr>
</script>
In html i set input visible according to cell clicked in the table. So now i need to pass a value of a cell to an input, so i could edit this data.
UPDATE: as usual, i've forgot to put round brackets '()' after value: name() in input. But here comes second question. As i know value must be automaticly changed while input loses his focus. But mine doesn't change...
Use the input value binding to to pass a value of a cell:
AFAIK, there is no way to access a field with its supposed index, to read a field from an object in observableArray you may use this syntax :
persons()[index].fieldName(), given that the field is observable also.
hope it help.

Categories

Resources