Displaying json object details in knockout js - javascript

I have the following fiddle where I am trying to display the data in key:value pairs,
i.e., key as header and followed by the information as rows .
I have the data in this format:
self.data = ko.observableArray([{
1:
{
name: 'Name 1',
lastLogin: '8/5/2012'
}
}
, {
2:
{
name: 'Name 2',
lastLogin: '2/8/2013'
}
}
]);
I have fiddle as :
https://jsfiddle.net/1988/z7nnf0fh/1/
I am expecting as:
1
name Name 1 lastLogin 8/5/2012
2
name Name 2 lastLogin 2/8/2013

I'd personally move all logic to your viewmodel. Then you could either use ko.toJSON to stringify the contents of each object or if you really want to have the output like above, you could do:
function DataModel() {
var self = this;
self.data = ko.observableArray([{
1: {
name: 'Name 1',
lastLogin: '8/5/2012'
}
}, {
2: {
name: 'Name 2',
lastLogin: '2/8/2013'
}
}
]);
self.formattedValues = ko.observableArray([]);
self.formatData = function() {
var tempRow = [];
ko.utils.arrayForEach(self.data(), function(item) {
for (var i in item) {
for (var j in item[i]) {
tempRow.push({
key: j,
value: item[i][j]
});
}
self.formattedValues.push({
key: i,
rows: tempRow
});
tempRow = [];
}
})
};
self.formatData();
}
var dataModel = new DataModel();
ko.applyBindings(dataModel);
.name {
color: #bbb;
}
.value {
fot-weight: bold
}
th {
width: 25px;
}
p {
margin-right: 10px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="template: { name: 'template', data: formattedValues }"></div>
<script type="text/html" id="template">
<table>
<tbody data-bind="foreach: $data">
<tr>
<td data-bind="text: key"></td>
</tr>
<tr>
<td data-bind="foreach: rows">
<p>
<span class="name" data-bind="text: key + ': '"></span>
<span class="value" data-bind="text: value"></span>
</p>
</td>
</tr>
</tbody>
</table>
</script>
Hope that helps in some way

Related

Format JSON to specific table

Given this data:
[
{
'Column': 'A',
'Value': 10,
'Color': 'red'
},
{
'Column': 'B',
'Value': 25,
'Color': 'blue'
},
{
'Column': 'A',
'Value': 4,
'Color': 'blue'
}
]
I would like to create this table
<table>
<thead><td>A</td><td>B</td></thead>
<tr>
<td><span color='red'>10</span></td>
<td><span color='red'></span></td>
</tr>
<tr>
<td><span color='blue'>4</span></td>
<td><span color='blue'>25</span></td>
</tr>
</table>
using KnockoutJS such that the values are data-bound.
I modified an example here but can't seem to figure out how to do it: http://jsfiddle.net/ktqcvj4x/
I suspect it will involve pure computed functions to get distinct values
You could group the array based on Column and a nested group based on color.
const array = [
{ Column: "A", Value: 10, Color: "red" },
{ Column: "B", Value: 25, Color: "blue" },
{ Column: "A", Value: 4, Color: "blue" },
]
const group = {}
for (const { Column, Value, Color } of array) {
if (!group[Column])
group[Column] = {};
group[Column][Color] = Value;
}
console.log(group)
This creates an object like this:
{
"A": {
"red": 10,
"blue": 4
},
"B": {
"blue": 25
}
}
You can assign this to a computed property. You can also use 2 Sets to get the all the unique colors and columns.
HTML:
For the headers, loop through the columns and create td. Straightforward.
For the body, you need nested loops. For each item in colors, create a tr and for each item in columns, create a td. Use an alias in foreach so that it's easier to access properties from grouped computed property.
<tbody data-bind="foreach: { data: colors, as: 'color' }">
<tr data-bind="foreach: { data: $root.columns, as: 'column' }">
<td>
<span data-bind="style: { color: color },
text: $root.grouped()[column][color]"></span>
</td>
</tr>
</tbody>
Here's a working snippet. This works for any number of columns and colors in your array.
function viewModel(array) {
this.array = ko.observableArray(array);
this.columns = ko.observableArray([]);
this.colors = ko.observableArray([]);
this.grouped = ko.computed(_ => {
const group = {},
columns = new Set,
colors = new Set;
for (const { Column, Value, Color } of array) {
columns.add(Column)
colors.add(Color)
if (!group[Column])
group[Column] = {};
group[Column][Color] = Value;
}
this.columns(Array.from(columns))
this.colors(Array.from(colors))
return group
})
}
const v = new viewModel([
{ Column: "A", Value: 10, Color: "red" },
{ Column: "B", Value: 25, Color: "blue" },
{ Column: "A", Value: 4, Color: "blue" },
])
ko.applyBindings(v)
table, td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr data-bind="foreach: columns">
<td data-bind="text: $data"></td>
</tr>
</thead>
<tbody data-bind="foreach: { data: colors, as: 'color' }">
<tr data-bind="foreach: { data: $root.columns, as: 'column' }">
<td>
<span data-bind="style: { color: color },
text: $root.grouped()[column][color]"></span>
</td>
</tr>
</tbody>
</table>

Knockout Table : Highlight a Table Row

I have an Example Fiddle here. In this Table I wish to achieve Highlighting a Particular Row selected. If unselected Row should not be highlighted.
One of many sample I found Fiddle but I am unable to incorporate them inside my Example Fiddle Above.
Below is the HTML Code which shows basic Table.
<table id="devtable">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr data-bind=" click: $parent.select ">
<td data-bind="text: ID"></td>
<td data-bind="text: Name"></td>
<td data-bind="text: Status"></td>
</tr>
</tbody>
ID :
Name :
Status :
Here is the knockout function to do manipulations
<Script>
var rowModel = function (id, name, status) {
this.ID = ko.observable(id);
this.Name = ko.observable(name);
this.Status = ko.observable(status);
};
var myData = [{
id: "001",
name: "Jhon",
status: "Single"
}, {
id: "002",
name: "Mike",
status: "Married"
}, {
id: "003",
name: "Marrie",
status: "Complicated"
}];
function MyVM(data) {
var self = this;
self.items = ko.observableArray(data.map(function (i) {
return new rowModel(i.id, i.name, i.status);
}));
self.select = function(item) {
self.selected(item);
self.enableEdit(true);
};
self.flashCss = ko.computed(function () {
//just an example
return 'flash';
});
self.selected = ko.observable(self.items()[0]);
self.enableEdit = ko.observable(false);
self.changeTableData = function() {
// How do I change the Data here and it should also reflect on the Page.
// If I do binding depending on condition it gives me error
if(true){
var myData = [{
id: "001",
name: "Jhon",
status: "Single"
}, {
id: "002",
name: "Mike",
status: "Married"
}, {
id: "003",
name: "Marrie",
status: "Complicated"
}];
}
else{
myData = [{
id: "111",
name: "ABC",
status: "Single"
}, {
id: "222",
name: "XYZ",
status: "Married"
}, {
id: "3333",
name: "PQR",
status: "Complicated"
}];
}
}
}
ko.applyBindings(new MyVM(myData));
</script>
CSS code below
.flash { background-color: yellow; }
You can use the css binding to add the .flash class based on the currently selected value:
<tr data-bind="click: $parent.select,
css: { flash: $parent.selected() === $data }">
...
</tr>
If you don't like this logic being defined in the view, you can pass a reference to the selected observable and create a computed property inside your RowModel:
var RowModel = function( /* ... */ selectedRow) {
// ...
this.isSelected = ko.pureComputed(function() {
return selectedRow() === this;
}, this);
}
Here's the quick fix in your fiddle:
http://jsfiddle.net/wa78zoe4/
P.S. if you want toggle-behavior, update select to:
self.select = function(item) {
if (item === self.selected()) {
self.selected(null);
self.enableEdit(false);
} else {
self.selected(item);
self.enableEdit(true);
}
};

AngularJS: ng-repeat without similar key

I have list of objs:
[{
key:test1
name: name1
},
{
key:test1
name: name2
},
{
key:test2
name: name3
}]
And i use ng-repeat to display it:
<tr ng-repeat=item in list>
<td>{{item.key}}</td>
<td>{{item.name}}</td>
</tr>
Is it possible to combine values with similar keys without changing the structure? not to be displayed twice test1 in my case
now:
test1 : name1
test1 : name2
test2 : name3
desired result:
test1 : name1
_____ name2
test2 : name3
You can use groupBy filter:
angular.module('app', ['angular.filter']).controller('ctrl', function($scope){
$scope.list = [{
key:'test1',
name: 'name1'
}, {
key:'test1',
name: 'name2'
},{
key:'test1',
name: 'name3'
},{
key:'test2',
name: 'name4'
}];
})
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.js"></script>
<table ng-app='app' ng-controller='ctrl'>
<tbody>
<tr ng-repeat-start="(key, value) in list | groupBy: 'key'">
<td>{{key}}</td>
<td>{{value[0].name}}</td>
</tr>
<tr ng-repeat-end ng-repeat='item in value.splice(1)'>
<td></td>
<td>{{item.name}}</td>
</tr>
</tbody>
</table>
ng-repeat="item in list | unique:'key'"
Here is how you can achieve the common key value in a same place using angular-filter:
angular.module('app',['angular.filter']).controller('mainCtrl', function($scope){$scope.list = [{
key:'test1',
name: 'name1'
},
{
key:'test1',
name: 'name2'
},
{
key:'test2',
name: 'name3'
}]
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.min.js"></script>
<div ng-app='app' ng-controller='mainCtrl'>
<div ng-repeat="(key, value) in list | groupBy: 'key'">
<span ng-repeat='val in value'>{{val.name}} </span>
</div>
</div>
Before using ng-repeat update the list.
function rd(o, k, v) {
var n = [];
var l = {};
for(var i in o) {
if (l.hasOwnProperty(o[i][k])){
o[i][v] = l[o[i][k]][v]+ " " + o[i][k]
l[o[i][k]] = o[i]
} else{
l[o[i][k]] = o[i];
}
}
for(i in l) {
n.push(l[i]);
}
return n;
}
var list = rd(arr, "key", "name");
You can try this:
var app = angular.module('myApp', ['angular.filter']);
app.controller('myCtrl', function($scope) {
$scope.items = [{
"key":"test1",
"name": "name1"
},
{
"key":"test1",
"name": "name2"
},
{
"key":"test2",
"name": "name3"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="item in items | groupBy: 'key'">
<h3>Key : {{item[0].key}}</h3>
<p>Names : <span ng-repeat='i in item'>{{i.name}} </span></p>
</div>
</div>

How to count the number of rows containing a certain value?

I'm using AngularJS and I have a table that I populate using ng-repeat. Check this short example:
http://jsfiddle.net/sso3ktz4/
How do I check how many rows I have with a certain value? For example, in the fiddle above, how do I check how many rows I have with the word "second"? It should return "3".
AngularJS answers are preferred, although I also have JQuery available.
Thanks!
updated controller code is below, where $scope.findRowCount is required function
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
$scope.findRowCount=function(value){
var count=0;
angular.forEach($scope.items, function(item, i){
if(item.name==value){
count=count+1;
}
angular.forEach(item.examples, function(exp, j){
if(exp.name==value){
count=count+1;
}
})
});
console.log("count"+count);
return count;
}
var result=$scope.findRowCount("second");
console.log(result);
}
http://jsfiddle.net/p3g9vyud/
Try this way
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
//Get sum based on the label
$scope.getTotalByLabel = function(keyword)
{
$scope.totalSecond = 0;
angular.forEach($scope.items, function(value, key) {
if(value.name == keyword)
{
$scope.totalSecond += 1;
}
angular.forEach(value.examples, function(val, k) {
if(val.name == keyword)
{
$scope.totalSecond += 1;
}
});
});
return $scope.totalSecond;
}
}
th,
td {
padding: 7px;
text-align: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<table border="1">
<tbody ng:repeat="i in items">
<tr>
<td>{{i.name}}</td>
<td>{{$index}}</td>
</tr>
<tr ng:repeat="e in i.examples">
<td>{{e.name}}</td>
<td>{{$index}}</td>
</tr>
</tbody>
</table>
<b>Total of second</b>: {{getTotalByLabel('second')}}
</div>
</div>

Knockout group list into smaller lists with objects

I have daily data for multiple employees and depending on the start time and end time that could mean a lot of data.
So with the mapping plugin i mapped them into one big list, but i will need them grouped by employee into smaller lists so i can make a tables per employee (like smaller view models) that has filtering and sorting for that subset of data.
Here is a basic example i created with static data.
$(function () {
var data = {
Employees: [{
Id: 1,
Name: "Employee1",
Day: new Date(),
Price: 12.54
}, {
Id: 2,
Name: "Employee2",
Day: new Date(),
Price: 112.54
}, {
Id: 1,
Name: "Employee1",
Day: new Date(),
Price: 12.54
}, {
Id: 3,
Name: "Employee3",
Day: new Date(),
Price: 12.54
}]
};
// simulate the model to json conversion. from now on i work with the json
var jsonModel = JSON.stringify(data);
function employeeModel(data) {
var employeeMapping = {
'copy': ["Id", "Name", "Day", "Price"]
};
ko.mapping.fromJS(data, employeeMapping, this);
}
function employeeViewModel(data) {
var self = this;
var employeesMapping = {
'Employees': {
create: function (options) {
return new employeeModel(options.data);
}
}
};
ko.mapping.fromJSON(data, employeesMapping, self);
}
var productsModel = new employeeViewModel(jsonModel);
ko.applyBindings(productsModel);
});
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
tr:nth-child(even) {
background-color: white;
}
tr:nth-child(odd) {
background-color: #C1C0C0;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<table>
<tbody data-bind="foreach: Employees">
<tr>
<td><span data-bind="text:Id"></span>
</td>
<td><span data-bind="text:Name"></span>
</td>
<td><span data-bind="text:Day"></span>
</td>
<td><span data-bind="text:Price"></span>
</td>
</tr>
</tbody>
</table>
One possibility would be to use a computed value to group your data.
self.EmployeeGroups = ko.pureComputed(function () {
var employees = self.Employees(),
index = {},
group = [];
ko.utils.arrayForEach(employees, function(empl) {
var id = ko.unwrap(empl.Id);
if ( !index.hasOwnProperty(id) ) {
index[id] = {
grouping: {
Id: empl.Id,
Name: empl.Name
},
items: []
};
group.push(index[id]);
}
index[id].items.push(empl);
});
return group;
});
would turn your data from a flat array to this:
[{
grouping: {
Id: /* ... */,
Name: /* ... */
}
items: [/* references to all employee objects in this group */]
}, {
/* same */
}]
Expand the code snippet below to see it at work.
$(function () {
var data = {
Employees: [{
Id: 1,
Name: "Employee1",
Day: new Date(),
Price: 12.54
}, {
Id: 2,
Name: "Employee2",
Day: new Date(),
Price: 112.54
}, {
Id: 1,
Name: "Employee1",
Day: new Date(),
Price: 12.54
}, {
Id: 3,
Name: "Employee3",
Day: new Date(),
Price: 12.54
}]
};
var jsonModel = JSON.stringify(data);
function employeeModel(data) {
var employeeMapping = {
'copy': ["Id", "Name", "Day", "Price"]
};
ko.mapping.fromJS(data, employeeMapping, this);
}
function employeeViewModel(data) {
var self = this;
self.Employees = ko.observableArray();
self.EmployeeGroups = ko.pureComputed(function () {
var employees = self.Employees(),
index = {},
group = [];
ko.utils.arrayForEach(employees, function(empl) {
var id = ko.unwrap(empl.Id);
if ( !index.hasOwnProperty(id) ) {
index[id] = {
grouping: {
Id: empl.Id,
Name: empl.Name
},
items: []
};
group.push(index[id]);
}
index[id].items.push(empl);
});
return group;
});
// init
var employeesMapping = {
'Employees': {
create: function (options) {
return new employeeModel(options.data);
}
}
};
ko.mapping.fromJSON(data, employeesMapping, self);
}
var productsModel = new employeeViewModel(jsonModel);
ko.applyBindings(productsModel);
});
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
tr:nth-child(even) {
background-color: #efefef;
}
tr:nth-child(odd) {
background-color: #CCCCCC;
}
tr.subhead {
background-color: #D6E3FF;
font-weight: bold;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<table>
<!-- ko foreach: EmployeeGroups -->
<tbody>
<!-- ko with: grouping -->
<tr class="subhead">
<td colspan="2">
<span data-bind="text: Id"></span>
<span data-bind="text: Name"></span>
</td>
</tr>
<!-- /ko -->
<!-- ko foreach: items -->
<tr>
<td><span data-bind="text: Day"></span></td>
<td><span data-bind="text: Price"></span></td>
</tr>
<!-- /ko -->
</tbody>
<!-- /ko -->
</table>
<pre data-bind="text: ko.toJSON($root, null, 2)" style="font-size: smallest;"></pre>

Categories