Concatenate data in specific column - javascript

I have the following table:
<table class="table main-table" ng-if="linesArray && linesArray.length > 0">
<!-- HEADER-->
<thead class="table-head">
<tr>
<th ng-repeat="column in ::columns" width="{{::column.width}}">{{::column.label}}</th>
</tr>
</thead>
<!-- BODY -->
<tbody>
<tr ng-repeat="line in linesArray">
<td ng-repeat="column in ::columns" width="{{::column.width}}" ng-class="{
'center-text' : (!line[column.key] || line[column.key].length === 0)
}">{{line[column.key] !== undefined ? line[column.key] : '--'}}
</td>
</tr>
</tbody>
</table>
Which renders as shown:
WHAT I'M TRYING TO ACHIEVE:
To concatenate the data of two separate fields into one in the first column, which should appear something like this:
As you can see, the column shows the date and time with certain formatting.
The directive which operates the logic of the table:
function historicalSummaryTable($filter) {
return {
restrict: 'EA',
link: link,
templateUrl: 'jfrontalesru/app/components/historicalSummary/historicalSummaryTable.html',
scope: {
linesArray: "=",
columns: "=",
groupField: "#",
groupFieldFilter: "#",
groupFieldFilterFormat: "#"
},
};
function link(scope, element, attrs) {
var _groupField = 'groupField';
var _subgroupField = 'subgroupField';
scope.$watch('linesArray', function(value) {
scope.linesArray.forEach(function(line) {
// Applies the filter for every column if set
scope.columns.forEach(function(column, index) {
// Applies the filter
if (column.filter && column.filter.length > 0) {
line[column.key] = $filter(column.filter)(line[column.key], column.format);
}
});
});
});
}
}
In this directive the input date data is formatted, then it's passed through the controller like this.
vm.historicalColumns = [
{label: $translate('columnDateTime'), key: "timestamp",width:"18%", filter:"date",format:"mediumTime", group:false},
{label: $translate('columnDetails'), key: "detail",width:"50%", group:false},
{label: $translate('columnOrigin'), key: "origin",width:"17%", group:false},
{label: $translate('columnUser'), key: "user",width:"15%", group:false}
];
I'm in the dark here, as I'm not sure how to do this.

Could add a span that uses ng-if to check index
<td ng-repeat="column in ::columns" width="{{::column.width}}" ng-class="{
'center-text' : (!line[column.key] || line[column.key].length === 0)
}">
<span ng-if="$index==0">first column only</span>
{{line[column.key] !== undefined ? line[column.key] : '--'}}
</td>
Or map your data in controller and do the concatenation there

Related

ng-table number range filter is not working

what I am trying to do
I am trying to make one of my columns to take two number (in one column), so I can filter the numeric data by range just for this column. All other sort and pagination and filter by 'contain text' is working fine but I am just not sure how would I go about making just one particular column to have 'range' filter.
graphical representation of what I want
column_header1 columns_header2 column_header3
contain_filter1 contain_filter2 filter3_min_number
filter3_max_number
data data numeric data
. . .
. . .
. . .
What I have so far
I found one example from ng-table module website and I tried to implement their code to mine but I don't know how to approach it when I have to implement the range function inside my 'getData'.
Example that I found http://codepen.io/christianacca/pen/yNWeOP
The custom filter algorithm on 'age' data was what I was looking at.
my app.js
$http.get("http://hostname:port/variant/whole/1-10000", {cache: true})
.then(function (response) {
$scope.variants = response.data;
$scope.data = $scope.variants;
var self = this;
self.variantFilterDef = {
start: {
id: 'number',
placeholder: 'Start'
},
end: {
id: 'number',
placeholder: 'End'
}
};
self.variantsTable = new NgTableParams({
page:1,
count: 10
}, {
filterOptions: { filterFn: variantRangeFilter },
dataset: $scope.data,
filterLayout: "horizontal"
});
function variantRangeFilter(data, filterValues/*, comparator*/){
return data.filter(function(item){
var start = filterValues.start == null ? Number.MIN_VALUE : filterValues.start;
var end = filterValues.end == null ? Number.MAX_VALUE : filterValues.end;
return start <= item.pos && end >= item.pos;
});
}
/* from this part on, it is working code but no 'Range' function
$scope.variantsTable = new NgTableParams({
page: 1,
count: 10
}, {
total: $scope.variants.length,
getData: function (params) {
if (params.sorting()) {
$scope.data = $filter('orderBy')($scope.variants, params.orderBy());
} else {
$scope.data = $scope.variants;
}
if (params.filter()) {
$scope.data = $filter('filter')($scope.data, params.filter());
} else {
$scope.data = $scope.data;
}
$scope.data = $scope.data.slice((params.page() - 1) * params.count(), params.page() * params.count());
return $scope.data;
}
});
*/
});
});
my variant.html
<table ng-table="variantsTable" show-filter="true" class="table table-bordered table-striped table-condensed">
<tr ng-repeat="variant in $data">
<td data-title="'chrom'" sortable="'chrom'" filter="{ 'chrom': 'text' }" >
{{variant.chrom}}
</td>
<td data-title="'id'" sortable="'id'" filter="{ 'id': 'text' }" >
{{variant.id}}
</td>
<td data-title="'pos'" sortable = "'pos'" filter = "{ 'pos': 'text' }">
{{variant.pos}}
</td>
I would really appreciate any suggestion or any input, thanks!
The filter attribute of the ID table cell is not correct.
<td data-title="'id'" sortable="'id'" filter="{ 'id': 'text' }">
{{variant.id}}
</td>
Change it to:
<td data-title="'id'" sortable="'id'" filter="variantFilterDef">
{{variant.id}}
</td>
EDIT
After a bit of trial and error I have it working. I started from your code sample and made a number of changes. I used ControllerAs syntax. But essentially the fixes are:
<td data-title="'chrom'" sortable="'chrom'" filter="{ 'chrom': 'text' }">
to <td data-title="'chrom'" sortable="'chrom'" filter="{ 'name': 'text' }">
<td data-title="'pos'" sortable = "'pos'" filter = "{ 'pos': 'text' }">
to <td data-title="'pos'" sortable="'pos'" filter="variantCtrl.variantFilterDef">
if (params.filter()) {
self.data = $filter('filter')(self.data, {name: params.filter().name});
self.data = variantRangeFilter(self.data, params.filter());
} else {
self.data = self.data;
}
The main issue was the need to separate out the filters of the two columns in #3 by using {name: params.filter().name}) & then calling the custom Pos filter separately.
Codepen: http://codepen.io/anon/pen/QKBYOq?editors=1011

Rendering a table with dynamic headers

I have to render a table with dynamic headers, I mean, I don't want to do something like this in the HTML
<table>
<tr>
// THIS TABLE ROW IS WHAT I SAY
<th>here info</th>
<th>something here</th>
<th>another header</th>
</tr>
<tr ng-repeat="thing in things">
<td>{{thing.asfs}}</td>
<td>{{thing.asx}}</td>
<td>{{person.dsf}}</td>
</tr>
</table>
I want something like this
<table>
<tr ng-repeat="head in heads">
{{head}}
</tr>
<tr ng-repeat="bar in bars">
<td ng-repeat="foo in foos"></td>
</tr>
</table>
that is only an example, I need to do it with this data:
{
"55f6de98f0a50c25f7be4db0":{
"clicks":{
"total":144,
"real":1
},
"conversions":{
"total":4,
"amount":229
},
"cost":{
"cpc":0.1999999999999995,
"ecpc":1145.0000000000027,
"total":28.79999999999993
},
"revenue":{
"total":4,
"epc":0.027777777777777776
},
"net":{
"roi":-1.1612903225806457,
"total":4
},
"name":"Traffic Source #2",
},
"55f6de98f0a50c25f7be4dbOTHER":{
"clicks":{
"total":144,
"real":1
},
"conversions":{
"total":4,
"amount":229
},
"cost":{
"cpc":0.1999999999999995,
"ecpc":1145.0000000000027,
"total":28.79999999999993
},
"revenue":{
"total":4,
"epc":0.027777777777777776
},
"net":{
"roi":-1.1612903225806457,
"total":4
}
"name":"Traffic Source #3"
},
}
every key, like clicks, conversions, cost, etc, should be a td, it is just that I don't want static HTML.
Any suggestions?
EDIT
And also, sometimes that object will grow, could come up with some more keys like this one 55f6de98f0a50c25f7be4db0
I did this fiddle with the exact same data I am receiving
http://jsfiddle.net/wLkz45qj/
UPDATE:
What you need to do is first convert you inconvenient object to array of objects with simple structure, and then use my code , i.e.
{
a: {
b:{
c: 'x'
}
}
}
will turn into
[[ a, { 'b.c' : 'x' }], ...]
or just
[{ _id : a , 'b.c' :'x'}, ...]
easiest way to do that is to use lodash or underscore ( check map, flatMap, pairs etc)
#jperezov showed you core idea, little bit detailed example:
$scope.peopleKeys = Object.keys(people[0])
and
<table>
<tr>
<th></th>
<th ng-repeat="personKey in peopleKeys">
{{ personKey }}
</th>
</tr>
<tr ng-repeat='p in people'>
<th>{{ $index }}</th>
<td ng-repeat="personKey in peopleKeys">
{{ p[personKey] }}
</td>
</tr>
</table>
You may also have some dictionary with display names:
$scope.displayNames = {
id: 'ID',
firstName: 'First Name'
...
}
and then your header going to be:
<tr>
<th></th>
<th ng-repeat="personKey in peopleKeys">
{{ displayNames[personKey] }}
</th>
</tr>
PS: OR you can just use ui-grid
var app = angular.module('myApp', []);
function PeopleCtrl($scope, $http) {
$scope.headers=[];
$scope.data = [];
$scope.LoadMyJson = function() {
for (var s in myJson){
$scope.data.push(s);
if ($scope.headers.length < 1)
for (var prop in myJson[s]){
prop.data = [];
$scope.headers.push({th:prop, td: []});
}
}
for (var s in $scope.data){
for (var prop in $scope.headers){
var header = $scope.headers[prop].th;
var data = myJson[$scope.data[s]][header];
$scope.headers[prop].td.push(data);
}
}
};
}
What you're looking for is something like this, I think:
http://jsfiddle.net/wLkz45qj/8/
Maybe iterate another time over "inner" for formatting.

How to target the last column in the table body to replace its value with a fixed value for every row?

Using Vue, I have displayed table with dynamic data pulled from external JSON.
I want to target the last column in the table body to replace its value with a fixed value for every row.
How would I do this?
Note that my script uses the initial value from the JSON data for that column to determine which class to put on that td.
Here is my code:
var dataURL = 'inc/data.json.php'
Vue.component('demo-grid', {
template: '#grid-template',
replace: true,
props: ['data', 'columns', 'filter-key'],
data: function() {
return {
data: null,
columns: null,
sortKey: '',
filterKey: '',
reversed: {}
}
},
compiled: function() {
// initialize reverse state
var self = this
this.columns.forEach(function(key) {
self.reversed.$add(key, false)
})
},
methods: {
sortBy: function(key) {
this.sortKey = key
this.reversed[key] = !this.reversed[key]
}
}
})
var demo = new Vue({
el: '#app',
data: {
searchQuery: '',
gridColumns: [...],
gridData: []
},
ready: function() {
this.fetchData()
},
methods: {
fetchData: function() {
var xhr = new XMLHttpRequest(),
self = this
xhr.open('GET', programsURL)
xhr.onload = function() {
self.gridData = JSON.parse(xhr.responseText)
}
xhr.send()
}
}
})
<table>
<thead>
<tr>
<th v-repeat="key: columns" v-on="click:sortBy(key)" v-class="active: sortKey == key">
{{key | capitalize}}
<span class="arrow" v-class="reversed[key] ? 'dsc' : 'asc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-repeat="
entry: data
| filterBy filterKey
| orderBy sortKey reversed[sortKey]">
<!-- here is where I wish to target the 5th in this row to change its value -->
<td v-repeat="key: columns" v-class="lvl-1 : entry[key] === '1', lvl-2 : entry[key] === '2', lvl-3 : entry[key] === '3'>
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
Compare the special $index property with the length of the array (or computed property), and then use a template fragment so you can switch out the <td>
<template v-repeat="column in columns">
<td v-show="$index < columns.length-1">All other columns...</td>
<td v-show="$index === columns.length-1">Last Column</td>
</template>
Solved it with:
<div v-if="$index === 4">
...

Angular ng-repeat change values

I'm doing a table with angular using ng-repeat. And all it's work but in some cases the json return me some data like PA-AC-DE and i want to change this in the table in Pending, Active and deactivate. And i don't know how i can do it.
<table class="table table-bordered table-hover table-striped dataTable no-footer" data-sort-name="name" data-sort-order="desc">
<tr role="row" class="info text-center">
<th ng-click="order('msisdn')">Número Teléfono</th>
<th ng-click="order('icc')">ICC</th>
<!--th>IMEI</th-->
<th ng-click="order('ActivationStatus')">Estado</th>
<th ng-click="order('sitename')">Instalación</th>
<th ng-click="order('siteaddress')">Dirección</th>
<th ng-click="order('sitecity')">Ciudad</th>
<th ng-click="order('sitezip')">Código Postal</th>
<th ng-click="order('phonedesc')">Modelo Teléfono</th>
<th ng-click="order('ContractingMode')">VBP</th>
</tr>
<tr class=" text-center" ng-repeat-start="object in filteredsites = (objects | filter:searchText) | filter:tableFilter| orderBy:predicate:reverse" ng-click="showDetails = ! showDetails">
<td>{{object.msisdn}}</td>
<td>{{object.icc}}</td>
<td>{{object.ActivationStatus}}</td>
<td>{{object.sitename}}</td>
<td>{{object.siteaddress}}</td>
<td>{{object.sitecity}}</td>
<td>{{object.sitezip}}</td>
<td>{{object.phonedesc}}</td>
<td>{{ object.ContractingMode ? 'Yes': 'No'}}</td>
</tr>
</table>
You can use a filter
{{object.ActivationStatus | statusFilter}}
and statusFilter will be like:
angular.module('module', []).filter('statusFilter', function() {
return function(input) {
//switch-case
};});
You could use ng-show to show text depending on the value returned from your API like so:
<td><span ng-show="object.ActivationStatus=='AC'">Active</span><span ng-show="object.ActivationStatus=='PA'">Other Label</span></td>
and so on.
With a custom filter method it would look like in the demo below or here at jsfiddle.
But also a getter function with the same code would be OK.
angular.module('demoApp', [])
.controller('mainController', function() {
this.data = [
{status:'AC'},
{status:'AC'},
{status:'DE'},
{status:'PA'},
];
})
.filter('filterStatus', function() {
var labels = {
AC: 'active',
DE: 'deactive',
PA: 'pending'
};
return function(input) {
return labels[input];
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="mainController as ctrl">
<ul>
<li ng-repeat="row in ctrl.data">
status: {{row.status | filterStatus}}
</li>
</ul>
</div>
Based on AWolf answer with filter, here is using a function in the controller:
http://jsfiddle.net/f4bfzjct/
angular.module('demoApp', [])
.controller('mainController', function() {
var vm = this;
vm.data = [
{status:'AC'},
{status:'AC'},
{status:'DE'},
{status:'PA'},
];
vm.getFullStatus = function(value) {
var labels = {
AC: 'active',
DE: 'deactive',
PA: 'pending'
};
return labels[value];
}
});
<div ng-app="demoApp" ng-controller="mainController as ctrl">
<ul>
<li ng-repeat="row in ctrl.data">
status: {{ctrl.getFullStatus(row.status)}}
</li>
</ul>
</div>
I think you should create a filter in your module:
ngModule.filter('phoneNumberStatus', function() {
statuses = {
AC: 'Active'
DE: 'Unactive'
}
return function(value) {
return statuses[value] || "Unknown"
}
})
and then use it in your template:
<td>{{ object.ActivationStatus | phoneNumberStatus }}</td>
This way will enable you to reused this filter in any template, avoiding duplicated code.
You can create a javascript function that returns your desired value:
$scope.getFullActivationText = function(input) {
if (input === 'PA') {
return 'Pending';
}
else if (input === 'AC') {
return 'Active';
}
else if (input === 'DE') {
return 'Deactivate';
}
}
Now you can keep everything the same in your HTML but replace:
<td>{{object.ActivationStatus}}</td>
into
<td>getFullActivationText(object.ActivationStatus)</td>

AngularJS: How to parse JSON string to integer or float to display it in form number field

I am using AngularJS for my front-end and Laravel for the back-end. I am passing a variable to front-end screen called orderItems which contains the following data to my front-end.
[{
"item_description": "CAD Machine ",
"qty": "1",
"unit_price": "4000.00"
}, {
"item_description": "Lenovo laptop",
"qty": "1",
"unit_price": "3000.00"
}]
My front-end contains one text field and two number fields. Data is getting populated on text field and no data is showing up on number field.
Here is my code from the view:
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Cost</th>
<th>Total</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in order.items">
<td>
{{ Form::text("description[]",null, ['ng-model' => 'item.item_description', 'class' => 'input-small form-control', 'placeholder' => 'Item description', 'required']) }}
</td>
<td>
{{ Form::number("quantity[]", null, ['step' => 'any', 'ng-model' => 'item.qty', 'class' => 'input-mini form-control', 'placeholder' => 'Qty']) }}
</td>
<td>
{{ Form::number("cost[]", null, ['step' => 'any', 'ng-model' => 'item.unit_price', 'class' => 'input-mini form-control', 'placeholder' => 'Unit Price', 'required']) }}
</td>
<td>
<h4><% item.qty * item.cost | currency %></h4>
</td>
<td class="text-center">
<h5><a href ng-click="removeItem($index)"><i class="fa fa-times"></i> Remove</a></h5>
</td>
</tr>
</tbody>
</table>
Here is my controller where I assign orderItems to the scope element
app.controller('POEditFormController', function($scope) {
$scope.order = {
items: <?= $orderItems ?>
};
$scope.addItem = function() {
var len = $scope.order.items.length;
var prevItem = $scope.order.items[len - 1];
if (prevItem.item_description == "" || prevItem.unit_price == "") {
alert("Please enter the line details.");
} else {
$scope.order.items.push({
qty: 1,
item_description: '',
unit_price: ''
});
}
},
$scope.removeItem = function(index) {
$scope.order.items.splice(index, 1);
},
$scope.total = function() {
var total = 0;
angular.forEach($scope.order.items, function(item) {
total += item.qty * item.unit_price;
})
return total;
}
});
Because of my limited knowledge, I am not sure how to parse the qty and unit_price to float/integer.
You don't need to parse them in this case.
In general, Javascript is interpreted, so in most browsers if you do the following:
"3.5"*"2"
It will work.
However, if for some reasons you need to force the parsing, you can use:
parseFloat
parseInt
In
total += parseInt(item.qty) * parseFloat(item.unit_price);
These are two functions which respectively parse the string and return the parsed value.
These two are the most reliable functions. There are others possibilities, such as:
+ before the number (such as +"3.5")
Integer.parseInt(myNumber)
But these are not recognized in every browser
#wayne thanks for the suggestion, I managed to change the server response by setting up the accessors on the model class for qty and unit_price
public function getQtyAttribute($value)
{
return $this->attributes['qty'] = (float)$value;
}
public function getUnitPriceAttribute($value)
{
return $this->attributes['unit_price'] = (float)$value;
}
Here is the link to Laravel's Eloquent Accessors & Mutators

Categories

Resources