Add new column to table using existing values - angularjs

I'm using angularjs and have a table which I build from two scope objects. It would be really nice have some kind of functionality where I can add columns by my self. The big problem is that I would like to use the existing values and try to use them in my new column. Is that possible? Or do I have to build up the columns on the server side and then return it?
PLUNKER
<!doctype html>
<html ng-app="plunker">
<head>
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng:controller="MainCtrl">
<table border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columnsTest" ng-if="column.checked" ng-bind="column.id"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in columnsTest" ng-if="column.checked">
{{ row[column.value] }}
</td>
</tr>
</tbody>
</table>
<br><br>
<input type="button" value="Add new column" ng-click="addColumn()" />
<br><br><br>
<p ng-repeat="c in columnsTest">Column {{$index}}: {{c}}</p>
</div>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $filter) {
$scope.addColumn = function() {
var newCol = { id: 'Value4', checked: true, value: 'Value1 + Value2 + Value3' }
$scope.columnsTest.push(newCol);
}
$scope.columnsTest = [{
id: 'Value1',
checked: true,
value: 'Value1'
}, {
id: 'Value2',
checked: true,
value: 'Value2'
}, {
id: 'Value3',
checked: true,
value: 'Value3'
}];
$scope.rows = [{
id: 1,
"Value1": 911,
"Value2": 20,
"Value3": 20
}, {
id: 2,
"Value1": 200,
"Value2": 20,
"Value3": 20
}];
});
</script>

You can use the $parse service.
Inject $parse into your controller and add a new method:
$scope.getCellValue = function(row, column) {
var getter = $parse(column.value);
return getter(row);
// Alternatively:
// return $parse(column.value)(row);
};
Use it like this:
<tr ng-repeat="row in rows">
<td ng-repeat="column in columnsTest" ng-if="column.checked">
{{ getCellValue(row, column) }}
</td>
</tr>
Demo: http://plnkr.co/edit/DVF2LXeZPqCL1Ik3EyVf?p=preview
Explanation:
The $parse service accepts a string expression to compile and returns a getter function. In your example we use the column.value:
var columnValue = 'Value1 + Value2 + Value3';
var getter = $parse(columnValue);
The returned getter function accepts a context object which the expression should be evaluated against. In your example we use the row object:
var row = { id: 1, "Value1": 911, "Value2": 20, "Value3": 20 };
var result = getter(row);
Basically the $parse service uses the string expression and the context object
and goes:
You want Value1 + Value2 + Value3, and you want to retrieve these values
from the row object.
Illustrated like this:
var result = row['Value1'] + row['Value2'] + row['Value2'];

With a 2D array you could structure your data like this:
$scope.rows = [
[911,20,30], // index 0 of 1st dim = 1st row; index 0,1,2 of 2nd dim = cells
[200,20,30] // index 1 of 1st dim = 2nd row
];
With this you can use two loops to get the cell and do your calculation, first loop for row and second for cell value.
Please have a look at the demo below or this plunkr.
In the demo I've created a function that does the calculation if you'd also pass an array like [0,1] it will tell the function to sum only col0 and col1 values.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $filter) {
function sumRows(data, values2sum) {
// e.g. data = [ [11, 12, 13], [21, 22, 23] ]
// new col = [ 32, 34, 46] // sum all
// new col = [ 32, 34, 0 ] // sum value1 & 2
// --> values2sum = [ 0, 1 ]
if ( angular.isUndefined(values2sum) ){
var all = true;
var value2sum = [];
}
angular.forEach(data, function(row, rowIndex) {
rowSum = 0;
angular.forEach(row, function(cell, colIndex) {
if ( all || values2sum.indexOf(colIndex) != -1 ) {
rowSum += cell;
}
});
row.push(rowSum);
})
}
$scope.addColumn = function() {
var rowSum, newRow = [], colId = $scope.columnsTest.length + 1;
$scope.columnsTest.push({
id: 'Value'+ colId,
checked: true,
value: 'Value'+colId
}); // rename columnsTest to tableHeading
//sumRows($scope.rows, [0,2]); // add value1 + value3
sumRows($scope.rows); // complete sum
//var newCol = { id: 'Value4', checked: true, value: 'Value1 + Value2 + Value3' }
//$scope.columnsTest.push(newCol);
}
$scope.columnsTest = [{
id: 'Value1',
checked: true,
value: 'Value1'
}, {
id: 'Value2',
checked: true,
value: 'Value2'
}, {
id: 'Value3',
checked: true,
value: 'Value3'
}];
/*$scope.rows = [{
id: 1,
"Value1": 911,
"Value2": 20,
"Value3": 20
}, {
id: 2,
"Value1": 200,
"Value2": 20,
"Value3": 20
}];*/
$scope.rows = [
[911,20,30],
[200,20,30]
]
});
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div ng-app="plunker" ng:controller="MainCtrl">
<table border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columnsTest" ng-if="column.checked" ng-bind="column.id"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="cell in row track by $index">
<!--ng-if="column.checked">-->
{{ cell }}
</td>
</tr>
</tbody>
</table>
<br>
<br>
<input type="button" value="Add new column" ng-click="addColumn()" />
<br>
<br>
<br>
<p ng-repeat="c in columnsTest">Column {{$index}}: {{c}}</p>
</div>

Related

Angular js - Multiple input per row edit & save

<div ng-controller="MyController">
<table border="1">
<tr data-ng-repeat="item in items">
<td data-ng-repeat="textValue in item.value">
<input type="text" data-ng-model="textValue" data-ng-readonly="isReadonly" />
</td>
</tr>
</table>
<input type="button" value="Edit" data-ng-click="enableEdit();" />
</div>
Will have multiple rows with multiple input type text for each row. Initially onload input type are set to readonly. Once user press the edit button all of the input box will become editable. User can change the text box values & save the updated values by clicking on save button.
var MyModule = angular.module('MyModule', []);
MyModule.controller("MyController", function($scope) {
$scope.isReadonly = true;
$scope.items = [{
"id": 1,
"value": {
"value1": 10,
"value2": 20,
"value3": 30,
"value4": 40
}
}, {
"id": 2,
"value": {
"value1": 50,
"value2": 60,
"value3": 70,
"value4": 80
}
}];
$scope.enableEdit = function() {
$scope.isReadonly = false;
}
});
Follow the below plnkr url
http://plnkr.co/edit/g0bpUg2AVjNhWAXG8PXc?p=preview
You should be using ng-if to toggle between the controls as below
<tr data-ng-repeat="item in items">
<td data-ng-repeat="textValue in item.value">
<label class="form-control" ng-if="isReadonly">{{textValue}} </label>
<input type="text" ng-if="!isReadonly" data-ng-model="textValue" />
</td>
</tr>
Controller Method
$scope.enableEdit = function() {
$scope.isReadonly = false;
}
$scope.saveValues=function(){
$scope.isReadonly = true;
}
LIVE DEMO(UPDATED PLUNK)
I assume the problem you are facing is related to changes not getting reflected back to the model.
The assumption was made because you haven't told us your problem in the first place.
Note: I've also added the saveValues() function for completeness.
The problem is due to how ngRepeat directive works and how you have used it. ng-repeat creates a child scope for each textValue and in these child scopes, ng-repeat does not create a 2-way binding for the textValue value. And so your model was never updated.
There are two different ways to solve this:
P.S: Example#2 is just for the demonstration of object technique and should be avoided for your use-case as it would further complicate the data structure of items.
Example#1) Using (key, value) like:
var MyModule = angular.module('MyModule', []);
MyModule.controller("MyController", function($scope) {
$scope.isReadonly = true;
$scope.items = [{
"id": 1,
"value": {
"value1": 10,
"value2": 20,
"value3": 30,
"value4": 40
}
}, {
"id": 2,
"value": {
"value1": 50,
"value2": 60,
"value3": 70,
"value4": 80
}
}];
$scope.enableEdit = function() {
$scope.isReadonly = false;
}
$scope.saveValues = function() {
$scope.isReadonly = true;
console.log($scope.items)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="MyModule">
<div ng-controller="MyController">
<table border="1">
<tr data-ng-repeat="item in items">
<td data-ng-repeat="(key, textValue) in item.value">
<input type="text" data-ng-model="item.value[key]" data-ng-readonly="isReadonly" />
</td>
</tr>
</table>
<input type="button" value="Edit" data-ng-click="enableEdit();" />
<input type="button" value="Save" data-ng-click="saveValues();" />
</div>
Example#2) Using objects like:
var MyModule = angular.module('MyModule', []);
MyModule.controller("MyController", function($scope) {
$scope.isReadonly = true;
$scope.items = [{
"id": 1,
"value": {
"value1": {
"value": 10
},
"value2": {
"value": 20
},
"value3": {
"value": 30
},
"value4": {
"value": 40
}
}
}, {
"id": 2,
"value": {
"value1": {
"value": 50
},
"value2": {
"value": 60
},
"value3": {
"value": 70
},
"value4": {
"value": 80
}
}
}];
$scope.enableEdit = function() {
$scope.isReadonly = false;
}
$scope.saveValues = function() {
$scope.isReadonly = true;
console.log($scope.items)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="MyModule">
<div ng-controller="MyController">
<table border="1">
<tr data-ng-repeat="item in items">
<td data-ng-repeat="textValue in item.value">
<input type="text" data-ng-model="textValue.value" data-ng-readonly="isReadonly" />
</td>
</tr>
</table>
<input type="button" value="Edit" data-ng-click="enableEdit();" />
<input type="button" value="Save" data-ng-click="saveValues();" />
</div>

Show null values always last when sorting several columns in AngularJS

I am working with a table that has several orderable columns. The columns can be ordered in both ASC and DESC ways. Some of the columns contain strings and some numbers. The problem is, that my data contains a lot of null values. Therefore, when sorting (ASC with strings or DESC with numbers) I first get a long list of nulls and then see the actual values at the bottom. This is why I'm looking for a way to put the null values always at the bottom.
I've already checked many stackoverflow questions like Angular Sorting with null objects, AngularJS orderby with empty field, and AngularJS sorting a list with nulls, but none of these seem to work in this case.
Here is a small example of what I'm working with:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.result = [ {
"value11": 17,
"value12": 34522342,
"value2": "a",
"value3": 4
},
{
"value11": 17,
"value12": 23453345,
"value2": "c",
"value3": null
},
{
"value11": 16,
"value12": 43553234,
"value2": null,
"value3": null
},
{
"value11": 17,
"value12": 23546324,
"value2": null,
"value3": 2
}
];
$scope.sortType = ['value11', 'value12'];
$scope.sortReverse = false;
$scope.sortableClass= function(sortColumn){
var thClass = ['sortable'];
var direction = '';
//if the parameter is name, the sortColumn parameter will be the value1.1 value1.2 array
if(sortColumn=="name"){
sortColumn = ['value11', 'value12'];
}
//toString is to account for ["value1.1", "value1.2"] array comparison
if(sortColumn.toString() == $scope.sortType.toString()){
direction = $scope.sortReverse ? 'asc' : 'desc';
thClass.push('selectedColumn');
thClass.push(direction);
}
return thClass;
}
$scope.sortTable = function(sortColumn) {
if(sortColumn == "name"){
$scope.sortType=['value11', 'value12'];
} else{
$scope.sortType=sortColumn;
}
$scope.sortReverse= !$scope.sortReverse;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<thead class="ng-scope">
<tr>
<th ng-class="sortableClass('name')">
Column1
</th>
<th ng-class="sortableClass('name')">
Column2
</th>
<th ng-class="sortableClass('name')">
Column3
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="n in result | orderBy:sortType:sortReverse">
<td data-title="Value1.1 and value1.2">{{n.value11}}:{{n.value12}}</td>
<td data-title="Value2">{{n.value2}}</td>
<td data-title="Value3">{{n.value3}}</td>
</tr>
</tbody>
</table>
</div>
Hope that you can help! Many thanks!
This is something I tried, but here at least one problem is that obj[$scope.sortType] is undefined. Hopefully this helps with solving the problem.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.result = [ {
"value11": 17,
"value12": 34522342,
"value2": "a",
"value3": 4
},
{
"value11": 17,
"value12": 23453345,
"value2": "c",
"value3": null
},
{
"value11": 16,
"value12": 43553234,
"value2": null,
"value3": null
},
{
"value11": 17,
"value12": 23546324,
"value2": null,
"value3": 2
}
];
$scope.sortType = ['value11', 'value12'];
$scope.sortReverse = false;
$scope.sortableClass= function(sortColumn){
var thClass = ['sortable'];
var direction = '';
//if the parameter is name, the sortColumn parameter will be the value1.1 value1.2 array
if(sortColumn=="name"){
sortColumn = ['value11', 'value12'];
}
//toString is to account for ["value1.1", "value1.2"] array comparison
if(sortColumn.toString() == $scope.sortType.toString()){
direction = $scope.sortReverse ? 'asc' : 'desc';
thClass.push('selectedColumn');
thClass.push(direction);
}
return thClass;
}
$scope.sortTable = function(sortColumn) {
if(sortColumn == "name"){
$scope.sortType=['value11', 'value12'];
} else{
$scope.sortType=sortColumn;
}
$scope.sortReverse= !$scope.sortReverse;
};
$scope.nullsToBottom = function(obj) {
return (angular.isDefined(obj[$scope.sortType]) ? -1 : 0);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<thead class="ng-scope">
<tr>
<th ng-class="sortableClass('name')">
Column1
</th>
<th ng-class="sortableClass('name')">
Column2
</th>
<th ng-class="sortableClass('name')">
Column3
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="n in result | orderBy:[nullsToBottom].concat(sortType):sortReverse">
<td data-title="Value1.1 and value1.2">{{n.value11}}:{{n.value12}}</td>
<td data-title="Value2">{{n.value2}}</td>
<td data-title="Value3">{{n.value3}}</td>
</tr>
</tbody>
</table>
</div>

ngTable execute a method with filter values

I want to take the value of the filter input, is that possible? I have this table
with that code
<td data-title="'Transaccion'" filter="{id_transaccion: 'text'}" sortable="'id_transaccion'">
<span editable-text="pago.id_transaccion" e-name="id_transaccion" e-form="rowform" e-required="">{{pago.id_transaccion}}</span>
</td>
I have the filter attr, and I tested everything trying to get the value of the field transacction of the filter, but nothing works, anyone knows how to get it?
You can access the filters object with the method filter() of the instance of NgTableParams. If you get your table data with 'getData' instead of 'dataset' (as in the example below), everytime you change a filter value the function 'getData' will be executed and you can access your updated filter object there and make any data manipulation you want. Look at the code of the snippet below:
(function() {
"use strict";
var app = angular.module("myApp", ["ngTable"]);
app.controller("demoController", demoController);
demoController.$inject = ["NgTableParams", "$filter"];
function demoController(NgTableParams, $filter) {
var self = this;
var simpleList = [{
name: 'ww',
age: 234,
money: 45,
country: 'pan'
}, {
name: 'xx',
age: 24,
money: 55,
country: 'col'
}, {
name: 'yy',
age: 454,
money: 82,
country: 'cr'
}, {
name: 'zz',
age: 345,
money: 34,
country: 'mex'
}];
self.tableParams = new NgTableParams({
// initial filter
filter: {
name: "w"
}
}, {
getData: getData
});
function getData(params) {
/*****************************
* LOOK HERE!!!!!
******************************/
// The filter object
var filterObj = params.filter();
self.ngTableFilters = filterObj;
// Filtering the list using the filter object...
var filteredList = $filter('filter')(simpleList, filterObj);
return filteredList;
}
}
})();
(function() {
"use strict";
angular.module("myApp").run(setRunPhaseDefaults);
setRunPhaseDefaults.$inject = ["ngTableDefaults"];
function setRunPhaseDefaults(ngTableDefaults) {
ngTableDefaults.params.count = 5;
ngTableDefaults.settings.counts = [];
}
})();
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://rawgit.com/esvit/ng-table/master/dist/ng-table.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://rawgit.com/esvit/ng-table/master/dist/ng-table.min.js"></script>
<div ng-app="myApp" class="container-fluid">
<div class="row" ng-controller="demoController as demo">
<div class="col-xs-12">
<h3>ngTable directive</h3>
<table ng-table="demo.tableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="row in $data">
<td data-title="'Name'" filter="{name: 'text'}">{{row.name}}</td>
<td data-title="'Age'" filter="{age: 'number'}">{{row.age}}</td>
<td data-title="'Money'" filter="{money: 'number'}">{{row.money}}</td>
</tr>
</table>
</div>
<div class="col-xs-12">
<h3>Filters:</h3>
<pre>{{ demo.ngTableFilters | json }}</pre>
</div>
</div>
</div>

Angular select element with complex object

Need some help with a select element in a Angular app I'm building.
Supposing I have the code below, what's the best way to change the property 'childId' of each item when selecting an option in the select element?
With the below code, when I select an element it will only set the 'child' property with the selected object and I can understand why. My only issue is that I also need to set the 'childId' property, so what's the right way to accomplish that?
<div ng-app="CustomApp" ng-controller="CustomCtrl">
<table class="table">
<thead>
<tr>
<th>Description</th>
<th>Child</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in dataItems">
<td>
<input name="Item[{{$index}}].Description"
value="{{item.description}}"
type="text"
class="form-control" />
</td>
<td>
<select name="Item[{{$index}}].Child"
ng-model="item.child"
ng-options="ichild as ichild.description for ichild in
$parent.childItems track by ichild.id">
<option value="">Select one option...</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
(function () {
"use strict";
var app = angular.module('CustomApp', []);
app.controller('CustomCtrl', ['$scope',
function($scope) {
$scope.dataItems = [
{ id: 1, description: 'foo one', childId: 1, child: { id: 1, description: 'bar01' }},
{ id: 2, description: 'foo two', childId: 0 },
{ id: 3, description: 'foo three, childId: 2, child: { id: 2, description: 'bar02' }}
];
$scope.childItems = [
{ id: 1, description: 'bar01' },
{ id: 2, description: 'bar02' }
];
}]);
})();
i think, this is what you want to do [actually i hope]:
<!doctype html>
<html>
<head>
</head>
<body>
<div ng-app="CustomApp" ng-controller="CustomCtrl">
<table class="table">
<thead>
<tr>
<th>Description</th>
<th>Child</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in dataItems">
<td>
<input name="Item[{{$index}}].description" ng-model="item.description" type="text" class="form-control" />{{item.description}}
</td>
<td>
<select name="Item[{{$index}}].child" ng-model="item.child" ng-options="ichild as ichild.description for ichild in $parent.childItems track by ichild.id"></select>
{{item.child}}
</td>
</tr>
</tbody>
</table>
</div>
<script src="/Scripts/angular.js"></script>
<script>
(function() {
"use strict";
var app = angular.module("CustomApp", []);
app.controller("CustomCtrl", ["$scope",
function($scope) {
$scope.dataItems = [{
id: 1,
description: "foo one",
childId: 1,
child: [{
id: 1,
description: "bar01"
}]
}, {
id: 2,
description: "foo two",
childId: 0
}, {
id: 1,
description: "foo three",
childId: 2,
child: [{
id: 2,
description: "bar02"
}]
}];
$scope.childItems = [{
id: 1,
description: "bar01"
}, {
id: 2,
description: "bar02"
}];
}
]);
})();
</script>
</body>
</html>

AngularJs ng-repeat 2D array in table, each subarray one column

I have an array and I need to put that array in table.
$scope.testArr=[
{'first':[
{ 'value':'1_1', 'rolle':'one1' },
{ 'value':'2_1', 'rolle':'two1' },
{ 'value':'3_1', 'rolle':'three1'}
]
},
{'second': [
{ 'value':'1_2', 'rolle':'one2' },
{ 'value':'2_2', 'rolle':'two2' },
{ 'value':'3_2', 'rolle':'three2' }
]
}
];
Resulting table should have 4 columns, each subarray should be one(or two) column(s). Like this:
one1 | 1_1 | one2 | 1-2
two1 | 2_1 | two2 | 2_2
three1|3_1 | three2|3_2
So far I got this. Its only the first subarray:
<table>
<tbody ng-repeat="test in testArr">
<tr ng-repeat="t1 in test.first">
<td> {{t1.rolle}} </td>
<td> {{t1.value}} </td>
</tr>
</tbody>
</table>
How can I add the second subarray as column? It's not necessary need to be a table.
var app = angular.module('app', []);
app.controller('mainCtrl', function ($scope) {
$scope.testArr = [{
'first': [{
'value': '1_1',
'rolle': 'one1'
}, {
'value': '2_1',
'rolle': 'two1'
}, {
'value': '3_1',
'rolle': 'three1'
}]
}, {
'second': [{
'value': '1_2',
'rolle': 'one2'
}, {
'value': '2_2',
'rolle': 'two2'
}, {
'value': '3_2',
'rolle': 'three2'
}]
}];
});
td {
border:solid 1px grey
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="mainCtrl">
<table>
<tbody ng-repeat="test in testArr">
<tr ng-repeat="t1 in test.first">
<td>{{t1.rolle}}</td>
<td>{{t1.value}}</td>
<td>{{testArr[1].second[$index].rolle}}</td>
<td>{{testArr[1].second[$index].value}}</td>
</tr>
</tbody>
</table>
</div>
</div>

Resources