In the readme of Angular module "md-data-table" it says "Assume we have a $nutrition service". I tried adding a service (makerService), with a return of data. But I can't see the data represented on the screen. I tried different JSON formats, as an error I get:
$makerService.makers.get is not a function
This is my Javascript code:
(function() {
'use strict';
angular.module('BowbleCRM', [
'ngMaterial',
'md.data.table'
])
.service('$makerService', function () {
var makers = {
"makers" : [
{"full_name": "Jan", "email": "jan#test.nl"},
{"full_name": "Pieter", "email": "pieter#test.nl"}
]
};
return makers;
})
.controller('BowbleCRMController', ['$makerService', '$scope', BowbleCRMController]);
function BowbleCRMController($makerService, $scope) {
'use strict';
$scope.currentNav = 'importeren';
$scope.selected = [];
$scope.query = {
order: 'full_name',
limit: 5,
page: 1
};
function success(makers) {
console.log(makers);
$scope.makers = makers;
}
$scope.getMakers = function () {
$scope.promise = $makerService.makers.get($scope.query, success).$promise;
};
}
})();
And this is a part of my HTML.
<md-toolbar class="md-table-toolbar md-default">
<div class="md-toolbar-tools">
<span>Alle makers</span>
</div>
</md-toolbar>
<!-- exact table from live demo -->
<md-table-container>
<table md-table md-row-select multiple ng-model="selected" md-progress="promise">
<thead md-head md-order="query.order" md-on-reorder="getMakers">
<tr md-row>
<th md-column><span>Volledige naam</span></th>
<th md-column><span>E-Mail adres</span></th>
</tr>
</thead>
<tbody md-body>
<tr md-row md-select="maker" md-select-id="full_name" md-auto-select ng-repeat="maker in makers.data">
<td md-cell>{{maker.full_name}}</td>
<td md-cell>{{maker.email}}</td>
</tr>
</tbody>
</table>
</md-table-container>
<md-table-pagination md-limit="query.limit" md-limit-options="[5, 10, 15]" md-page="query.page" md-total="{{makers.length}}" md-on-paginate="getMakers" md-page-select></md-table-pagination>
How can I make a correct service, and showing the data with the Angular module "md-data-table"?
You don't have a function get on $makerService, you just return makers which is an object.
If you want to call a get promise you must implement it on makerService. Something like
.service('$makerService', function () {
var makers = {
"makers" : [
{"full_name": "Jan", "email": "jan#test.nl"},
{"full_name": "Pieter", "email": "pieter#test.nl"}
]
};
function get(params){
//..implement your logic here (for your controller code, this should return a promise)
}
return {
makers: makers,
get: get
};
})
And in your controller you can call it like this:
$scope.getMakers = function () {
$scope.promise = $makerService.get($scope.query, success).$promise;
};
Related
I get a nested JSON object back from an API call that looks something along the lines of this:
{
"name": “Main “Folder”,
"children": [
{
"name": “Child Folder 1”,
"children": []
},
{
"name": “Child Folder 2”,
"children": [
{
"name": “Sub Folder 1”,
"children": [
{
“name”: “Sub Sub Folder 1”,
“children”: []
}
]
},
{
"name": “Sub Folder 2” ,
"children": []
}
]
}
]
}
There is no limit on how far the JSON object can be nested so that is unknown to me. I need to have all of the children of the folders to be indented under the parent in the table. I'm not really even sure how to go about doing this. The first thing I tried was doing something like this in my HTML file, but I quickly realized it wasn't going to work.
folders.html
<table>
<thead>
<tr><strong>{{ this.tableData.name }}</strong></tr>
</thead>
<tbody ng-repeat="b in this.tableData.children">
<tr>
<td>{{ b.name }}</td>
<td ng-repeat="c in b.children">{{ c.name }}</td>
</tr>
</tbody>
</table>
folders.js
export default class FoldersController {
constructor($rootScope, $scope, $uibModal) {
this.tableData = {Example Data from top}
}
}
Is there a not too complicated way to go about doing this? Thanks!
You should create a component with a template that contains a table, then you can nest your component inside itself to follow the tree structure logical path:
Your root controller should contain your table data:
angular.module('app').controller('RootCtrl', ['$scope', function($scope) {
// assigning the data to $scope to make it available in the view
$scope.tableData = {Example Data from top};
}]);
Your tree component could be something on this lines:
angular.module('app').component('treeComponent', {
controller: 'TreeCtrl',
bindings: {
tree: '<',
},
templateUrl: 'tree-view.html'
});
your root template should load the first instance of the component:
<div>
<tree-component tree="tableData"></tree-component>
</div>
then the component template should take care of the the recursion when required;
tree-view.html:
<table class="record-table">
<thead>
<tr>
<th>
<strong>{{ $ctrl.tableData.name }}</strong>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="node in $ctrl.tableData.children">
<td>{{node.name}}</td>
<td ng-if="node.children.length > 0">
<tree-component tree="node.children"></tree-component>
</td>
</tr>
</tbody>
</table>
creating indentation then becomes easy using basic css:
.record-table .record-table {
padding-left: 20px
}
I was able to figure out a solution of my own using recursion in the js file. I implemented mindthefrequency's answer as well and it seems to be working just fine. I'm marking it as the best answer because it seems to be the cleaner solution, but I'm posting what I have in case someone wants to take a more js oriented approach.
First, in the js file, use recursion to add all of the nodes and how far each needs to be indented to the table data variable.
folders.js
export default class FoldersController {
constructor($rootScope, $scope, $uibModal) {
this.resp = {Example Data from top}
this.tableData = []
this.createTable(this.resp.children, 0, [
{
name: this.resp.name,
indent: '0px',
},
]);
}
createTable(children, count, data) {
count += 1;
// base case
if (!children || children.length === 0) {
return;
}
for (const child of children) {
const { name } = child;
const returnData = data;
returnData.push({
name: name,
indent: `${count * 25}px`,
});
this.tableData = returnData;
this.createTable(child.children, count, returnData);
}
}
}
Then, in the html file, use angularjs to properly indent each node
folders.html
<table>
<thead>
<tr><strong>Table Header</strong></tr>
</thead>
<tbody ng-repeat="b in vm.tableData">
<tr>
<td ng-style="{'padding-left': b.indent}">{{ b.name }}</td>
</tr>
</tbody>
</table>
In my AngularJS app, I use ng-repeat to display rows of records. When the user removes the last record in the array, it removes the custom properties on the form tag. This is breaking all of the custom validations set to those properties. If the user removes all other records, the properties are still intact. Any help is much appreciated. Please see the plunker for code.
http://plnkr.co/edit/8s5brh7Hj9cu0gdpNpxt?p=preview
<body ng-controller="MainCtrl as vm">
<form name="vm.cptForm" role="form" ng-submit="vm.submit()" novalidate="">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>DATE OF SERVICE</th>
<th>REMOVE</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in vm.items">
<td>{{item.id}}</td>
<td type = "text"
name="name"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.name.$valid}"
ng-model="item.name">{{item.name}}</td>
<td type = "text"
name="dos"
class = "form-control ng-class: 'error': vm.showErrors && !vm.cptForm.dos.$valid}"
ng-model="item.dos">{{item.dos}}</td>
<td>
<button class="btn btn-xs btn-danger" type="button" ng-click="vm.remove(item)">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="row">
<span class="error" ng-show="vm.showErrors && vm.cptForm.dos.$error.termedMember">
</form>
</body>
and here's the js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var vm = this;
vm.cptForm = {};
vm.items = [];
vm.items = [
{
"id":0,
"name": "Jane",
"dos":"05/05/2017"
},
{
"id":1,
"name": "Janet",
"dos":"05/05/2017"
},
{
"id":2,
"name": "John",
"dos":"05/05/2017"
},
{
"id":3,
"name": "Johnathan",
"dos":"05/05/2017"
},
{
"id":4,
"name": "Joanne",
"dos":"05/05/2017"
}
];
vm.remove = function(item){
console.log(item);
console.log(vm.cptForm); //before splice vm.cptForm contains dos and name properties
var index = vm.items.indexOf(item);
debugger;
vm.items.splice(index,1);
// console.log(vm.items);
//console.log(vm.cptForm); //after splice vm.cptForm no longer contains dos and name properties
vm.validate(vm.items);
};
vm.validate = function(items){
angular.forEach(items,function(item){
if(item.dos < getDate()){ //compared to today for code simplicity
vm.cptForm.dos.$setValidity("termedMember", false);
}
});
};
});
Edit
I tried creating a copy of the form prior to splicing it and then assigning it back to the original form, but that did not work. How can I retain the form's properties for each item when using ng-repeat?
I try to put data from another array into one part in table.
My first json "names" :
[
{
"name": "AAAAAA",
"down": "False"
},
{
"name": "BBBBBB",
"down": "True"
},
{
"name": "CCCCC",
"down": "False"
}
]
Second json "data" :
[
{
"data": "35%"
}
]
Javascript:
var app = angular.module('app', []);
app.service('service', function($http, $q){
this.getNames = function () {
var names = $http.get('names.json', {cache: false});
var datas = $http.get('data.json', {cache: false});
return $q.all({datas,names});
};
});
app.controller('FirstCtrl', function($scope, service, $http) {
var promise = service.getNames();
promise.then(function (data) {
$scope.names = data.names.data;
$scope.datas = data.datas.data;
$scope.namesanddata = $scope.names.concat($scope.datas);
console.log($scope.namesplit);
console.log($scope.datas);
});
});
Table in HTML :
div ng-controller="FirstCtrl"
<table>
<tbody>
<tr ng-repeat="name in namesanddata">
<td>{{name.name}}</td>
<td ng-if="name.down=== 'False'">{{name.down}}</td>
<td ng-if="name.down !== 'False'">{{name.data}}</td>
</tr>
</tbody>
</table>
</div>
My problem - <td ng-if="name.down !== 'False'">{{name.data}}</td>dont show in table. in console.log it concat to array like 4 object and probably by this it don't show in table next to {{name.name}}, but i don't know how to show {{name.data}} from another json in table instead {{name.down}}.
Thanks for answers in advance.
To give you an output of:
AAAAAA False
BBBBBB 35%
CCCCC False
Remove this line from the Controller:
$scope.namesanddata = $scope.names.concat($scope.datas);
Then either:
Solution 1 - Inline in the view
Change your ng-repeat as follows:
<tr ng-repeat="name in names">
<td>{{ name.name }}</td>
<td>{{ (name.down === 'False') ? name.down : datas[0].data }}</td>
</tr>
OR
Solution 2 - Keep the view clean by using a Filter
app.filter('myFilter', function () {
return function(items, datas) {
var filtered = [];
angular.forEach(items, function (i) {
if (i.down !== "False")
i.down = datas[0].data;
filtered.push(i);
});
return filtered;
}
});
Change your ng-repeat as follows:
<tr ng-repeat="name in names | myFilter: datas">
<td>{{ name.name }}</td>
<td>{{ name.down }}</td>
</tr>
I am using ng-table for AngularJS and i am using single JSON file for the table data. i want to import multiple JSON files into a single table. I am able to import single JSON file using this code below.
Thank you in advance.
HTML
<table ng-table="tableParams" show-filter="true" class="table ng-table-responsive">
<tr ng-repeat="user in $data">
<td data-title="'ID'" sortable="'ID'">
{{user.ID}}
</td>
<td data-title="'Name'" sortable="'Name'" filter="{ 'name': 'Name' }">
{{user.Name}}
</td>
<td data-title="'Email'" sortable="'email'">
{{user.email}}
</td>
<td data-title="'Address'" sortable="'address'">
{{user.address}}
</td>
<td data-title="'PersonalEmail'" sortable="'personalemail'">
{{user.personalemail}}
</td>
<td data-title="'PhoneNo'" sortable="'phoneno'">
{{user.phoneno}}
</td>
<td data-title="'Status'" sortable="'status'">
{{user.status}}
</td>
</tr>
</table>
JavaScript
var app = angular.module('main', ['ngTable']).controller('DemoCtrl', function($scope, ngTableParams, NameService) {
var data = NameService.data;
$scope.tableParams = new ngTableParams(
{
page: 1, // show first page
count: 10, // count per page
sorting: {name:'asc'}
},
{
total: 0, // length of data
getData: function($defer, params) {
NameService.getData($defer,params,$scope.filter);
}
});
$scope.$watch("filter.$", function () {
$scope.tableParams.reload();
});
});
app.service("NameService", function($http, $filter){
function filterData(data, filter){
return $filter('filter')(data, filter)
}
function orderData(data, params){
return params.sorting() ? $filter('orderBy')(data, params.orderBy()) : filteredData;
}
function sliceData(data, params){
return data.slice((params.page() - 1) * params.count(), params.page() * params.count())
}
function transformData(data,filter,params){
return sliceData( orderData( filterData(data,filter), params ), params);
}
var service = {
cachedData:[],
getData:function($defer, params, filter){
if(service.cachedData.length>0){
console.log("using cached data")
var filteredData = filterData(service.cachedData,filter);
var transformedData = sliceData(orderData(filteredData,params),params);
params.total(filteredData.length)
$defer.resolve(transformedData);
}
else{
console.log("fetching data")
$http.get("json/1.json").success(function(resp)
{
angular.copy(resp,service.cachedData)
params.total(resp.length)
var filteredData = $filter('filter')(resp, filter);
var transformedData = transformData(resp,filter,params)
$defer.resolve(transformedData);
});
}
}
};
return service;
});
Retrieve all your JSON content with $q.all() and merge your arrays in the callback. Finally pass the resulting array to ng-table.
My select is populating with the contents of the model, but when I select an option, the model does not update.
I'm using ng-options, not ng-repeat and my ng-model is an object on the parent scope, not a primitive, so I think I've avoided the "child-scope" issues I've seen on similar posts. I've recreated the problem on jsfiddle:
http://jsfiddle.net/bobweil/wfdjrej5/
When the user clicks on a row in the table, a small form shows up below that row, permitting a new status value to be selected for that row for posting to the backend service.
Here's my javascript:
angular.module('myApp', [])
.controller('TaskCtrl', function HomeController($scope, $filter) {
$scope.statusMasters = [{
"Id": 1,
"DisplayOrder": 100,
"Text": "Review"
}, {
"Id": 2,
"DisplayOrder": 200,
"Text": "New"
}, {
"Id": 3,
"DisplayOrder": 300,
"Text": "Working"
}, {
"Id": 4,
"DisplayOrder": 400,
"Text": "Complete"
}]
$scope.tasks = [{
"taskId": 1000,
"Descr": "My first task",
"statusId": 1
}, {
"taskId": 2000,
"Descr": "My second task",
"statusId": 1
}, {
"taskId": 3000,
"Descr": "My third task",
"statusId": 1
}];
$scope.selectedTask = null;
$scope.newTaskStatus = {};
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
$scope.newTaskStatus = {};
$scope.newTaskStatus.taskId = thisTask.taskId;
$scope.newTaskStatus.statusId = thisTask.statusId;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
});
And here's my html:
<div ng-controller="TaskCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Task #</th>
<th>Description</th>
<th>StatusId</th>
<th>Status Text</th>
</tr>
</thead>
<tbody ng-repeat="item in tasks" ng-click="selectTask(item)" ng-switch on="isSelected(item)">
<tr>
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{newTaskStatus | json}}</pre>
</div>
<label>Select a new status for task {{newTaskStatus.taskId}}:</label>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="newTaskStatus.taskId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
</table>
Part of the issue is you are trying to set a click event on the tbody, you need to set the ng-click on the row (tr).
Secondly, unless it is needed for another reason, I wouldn't duplicate the values from the "selectedTask" into a "newTaskStatus" when you are planning on changing the status and sending back that value, it can all be done with one object on the scope.
Third, you could clean up your .js a little by changing the 'ng-switch on' to do the check if it is selected. It replaces an entire function with a comparison.
I would do something like this.
<tbody ng-repeat="item in tasks" ng-switch on="selectedTask.taskId == item.taskId">
<tr ng-click="selectTask(item)">
<td>{{item.taskId }}</td>
<td>{{item.Descr}}</td>
<td>{{item.statusId}}</td>
<td>{{statusMasters[item.statusId - 1].Text}}</td>
</tr>
<tr ng-switch-when="true">
<td colspan="10">
<div>Debug: contents of new task status object: <pre>{{selectedTask | json}}</pre>
</div>
<label>Select a new status for task {{selectedTask.taskId}}:</label>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'A')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
<select ng-model="selectedTask.statusId" ng-show="(opGroup == 'B')" class="form-control" ng-options="rec.Id as rec.Text for rec in statusMasters | orderBy : 'DisplayOrder'"></select>
</td>
</tr>
</tbody>
With the .js I would remove the unnecessary items:
$scope.selectedTask = null;
$scope.opGroup = "A";
$scope.selectTask = function (thisTask) {
$scope.selectedTask = thisTask;
};
$scope.isSelected = function (thisTask) {
if (thisTask.hasOwnProperty('taskId')) {
return $scope.selectedTask.taskId === thisTask.taskId;
} else return false;
};
I forked your jsfiddle here to demonstrate what I mean. Good Luck!