I've populated a table with data from a JSON file, and then when the user clicks on an item in the table, a container and it's fields display below and it SHOULD be populated with that specific data, though it isn't.
The function select is invoked through list-patents.htm which contains the table with a list of patents. I have used the $rootScope object so the function is accessible from multiple controllers i.e. patentDetailsCtrl
Why isn't my table in patent-item.htm being populated with the ng-repeat directive?
var app = angular.module('myApp', ['ngRoute', 'angularMoment', 'ui.router', "chart.js"]);
$stateProvider
.state("patents.list.item", {
url: "/patent-item",
templateUrl: "templates/patents/list/patent-item.htm",
params: {
id: null,
appNo: null,
clientRef: null,
costToRenew: null,
renewalDueDate: null,
basketStatus: null,
costBandEnd: null,
nextStage: null
},
controller: "patentDetailsCtrl"
})
app.run(function($rootScope) {
$rootScope.select = function() {
return $rootScope.patentItem = item;
}
});
app.controller('patentDetailsCtrl', ['$scope', '$http', function($scope, $http) {
$scope.selectedPatent = $scope.patentItem;
console.log($scope.selectedPatent);
}]);
list-patents.htm
<tbody>
<tr ng-repeat="x in patents">
<td ng-click="select(x)"><a ui-sref="patents.list.item({id: x.id, appNo: x.applicationNumber, clientRef: x.clientRef, costToRenew: x.costToRenew, renewalDueDate: x.renewalDueDate, basketStatus: x.basketStatus, costBandEnd: x.costBandEnd, nextStage: x.nextStage})">{{x.applicationNumber}}</a></td>
<td ng-bind="x.clientRef"></td>
<td ng-bind="x.costToRenew">$</td>
<td ng-bind="x.renewalDueDate"></td>
<td><button type="button" class="btn btn-danger" ng-click="remove(x.id)">Remove</button></td>
</tr>
</tbody>
patent-item.htm
<table>
<tbody>
<thead>
<tr>
<td>applicationNumber</td>
<td>clientRef</td>
<td>costToRenew</td>
<td>renewalDueDate</td>
<td>basketStatus</td>
<td>costBandEnd</td>
<td>nextStage</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in selectedPatent">
<td>{{x.applicationNumber}}</td>
<td>{{x.clientRef}}</td>
<td>{{x.costToRenew}}</td>
<td>{{x.renewalDueDate}}</td>
<td>{{x.basketStatus}}</td>
<td>{{x.costBandEnd}}</td>
<td>{{x.nextStage}}</td>
</tr>
</tbody>
</table>
</tbody>
You need to correct below points:
your $rootScope.select() method doesn't accept data (select(x)) passed from your list-patents.htm, So put item as param.,
Like : $rootScope.select = function(item) { \\ code
Sine you do ng-repeat on $rootScope.patentItem, then I guess you need to make it array, and push the passed data to it. (No need of return statement as well.)
So run block should be like :
app.run(function($rootScope) {
rootScope.patentItem = [];
$rootScope.select = function(item) {
$rootScope.patentItem.push(item);
}
});
See this Example Fiddle
To me it seems like you're trying to access the patentItem selected in your select function. If you're passing it to the $rootScope this way, you will also need to access it this way.
So change the line as follows...
$scope.selectedPatent = $rootScope.patentItem
Related
I'm using angularfire for to save and display my user data. The form they fill out stacks or duplicates the data rather than updating. I only want one to display the most recent data, any ideas? I have it all contained in a panel and things like users name is listed 3 times.
app.controller('infoCtrl', ['$scope', '$location', '$firebaseArray', 'Auth',
function($scope, $location, $firebaseArray, Auth) {
this.userAuthData = Auth.$getAuth();
vm = this;
var ref = new Firebase('https://capstone-zhf.firebaseio.com/' + this.userAuthData.uid + '/surveyInfo');
ref.orderByChild('name').limitToLast(2).on("child_added", function(snapshot) {
});
vm.syncObject = $firebaseArray(ref);
var newInfo = {};
$scope.addInfo = function() {
vm.syncObject.$add({
q1: vm.newInfo.q1,
q2: vm.newInfo.q2,
q3: vm.newInfo.q3,
q4: vm.newInfo.q4,
q5: vm.newInfo.q5,
q6: vm.newInfo.q6,
q7: vm.newInfo.q7,
weight: vm.newInfo.weight,
height: vm.newInfo.height,
name: vm.newInfo.name
});
vm.newInfo = {};
};
Here is my HTML code
<table>
<tr ng-repeat="(id, info) in infoCtrl.syncObject">
<td class="panel-body">Goal: {{ info.q1 }}</td>
<td class="panel-body">{{ info.q2 }}</td>
<td class="panel-body">{{ info.q5 }}</td>
<td>
<button ng-click="editNew(info)">Edit</button>
</td>
<td>
<button ng-click="infoCtrl.syncObject.$remove(id)">x</button>
</td>
</tr>
</table>
I'm currently learning new MVC 6 and stucked completely with simple action - table data update on item selection change.The desired behaviour is to load questions that belong selected question block
I have angularJS factory:
(function () {
'use strict';
angular
.module('questionBlockApp')
.factory('questionBlockService', questionBlockService);
var questionBlockService = angular.module('questionBlockService', ['ngResource']);
questionBlockService.factory('Blocks', ['$resource',
function ($resource) {
return $resource('/api/blocks/', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}]);
questionBlockService.factory('Questions', ['$resource',
function ($resource) {
return $resource('/api/blocks/:blockId', {blockId : '#blockId'}, {
query: { method: 'GET', params: {}, isArray: true }
});
}]);
})();
Controller, which has refresh func (loadQuestions) built inside selection change function:
(function () {
'use strict';
angular
.module('questionBlockApp')
.controller('questionBlockController', questionBlockController);
//.controller('questionController', questionController);
questionBlockController.$inject = ['$scope', 'Blocks', 'Questions'];
//questionController.$inject = ['$scope', 'Questions'];
function questionBlockController($scope, Blocks, Questions) {
$scope.selectedBlock = 2;
if ($scope.Blocks == undefined | $scope.Blocks == null) {
$scope.Blocks = Blocks.query();
}
$scope.setSelected = function (blockId) {
$scope.selectedBlock = blockId;
$scope.loadQuestions();
}
$scope.loadQuestions = function () {
$scope.data = Questions.query({ blockId: $scope.selectedBlock });
$scope.data.$promise.then(function (data) {
$scope.Questions = data;
});
};
$scope.loadQuestions();
}
})();
And views:
View from which setSelected is called:
<table class="table table-striped table-condensed" ng-cloak ng-controller="questionBlockController">
<thead>
...
</thead>
<tbody>
<tr ng-repeat="block in Blocks" ng-click="setSelected(block.Id)" ng-class="{'selected': block.Id == selectedBlock}">
<td>{{block.Id}}</td>
<td>{{block.Name}}</td>
<td>{{block.Created}}</td>
</tr>
</tbody>
</table>
<table id="test" ng-controller="questionBlockController">
<thead>
<tr>
...
</tr>
</thead>
<tbody>
<tr ng-repeat="question in Questions">
<td>{{question.Id}}</td>
<td>{{question.Text}}</td>
<td>{{question.TimeLimit}}</td>
<td>{{question.Updated}}</td>
</tr>
</tbody>
</table>
When I click on different items in QuestionBlock table, $scope.Questions is updated properly, but the table does not reflect changes, as if no binding exists.
Okay, I am just a bit damaged.
There are two questionBlockController controllers defined, leading to intialization of different scopes => two $scope.Questions objects existence => refresh occured in Blocks scope, which was undesired behaviour.
I'm using BootGrid Data table and JSON file as my data source for the table.
Service
.service('datatableService', ['$resource', function($resource){
this.getDatatable = function(id, email, date) {
var datatableList = $resource("data/data-table.json");
return datatableList.get ({
id: id,
email: email,
date: date
})
}
}])
Controller
.controller('datatableCtrl', function($scope, datatableService){
//Get Data Table Data
$scope.id = datatableService.id;
$scope.email = datatableService.email;
$scope.date = datatableService.date;
$scope.dtResult = datatableService.getDatatable($scope.id, $scope.email, $scope.date);
})
Directive
.directive('bootgrid', ['$timeout', function($timeout){
return {
restrict: 'A',
link: function(scope, element, attr){
$('#data-table-basic').bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
}
}
}])
HTML
<div class="table-responsive" data-ng-controller="datatableCtrl">
<table id="data-table-basic" class="table table-striped" data-bootgrid>
<thead>
<tr>
<th data-column-id="id" data-type="numeric">ID</th>
<th data-column-id="sender">Sender</th>
<th data-column-id="received" data-order="desc">Received</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="w in dtResult.list">
<td>{{ w.id }}</td>
<td>{{ w.email }}</td>
<td>{{ w.date }}</td>
</tr>
</tbody>
</table>
</div>
When I run this, I'm getting no data inside <tbody> but when I remove the directive, I can see all the JSON data are rendered inside the table. I want both ng-repeat and and directive works together. I tried to set the priority in directive as,
...
return {
restrict: 'A',
priority: 1001,
...
But no luck. http://plnkr.co/edit/rWCVXTjxOGZ49CeyIn9d?p=preview
Please help me fix this. I would appropriate if you could fix the above pen.
Regards
Priority setting will not help here because it is used to regulate order of compilation of directives defined on the same element.
You can delay bootgrid directive initialization until very next digest cycle using $timeout service. You will also need to watch for data object changes since you are loading it with AJAX.
app.directive('bootgrid', function($timeout) {
return {
link: function(scope, element, attr) {
scope.$watch(attr.bootgrid + '.length', function(newVal, oldVal) {
if (newVal !== oldVal && newVal) {
$timeout(function() {
element.bootgrid({
css: {
icon: 'md icon',
iconColumns: 'md-view-module',
iconDown: 'md-expand-more',
iconRefresh: 'md-refresh',
iconUp: 'md-expand-less'
}
});
});
}
});
}
}
});
Demo: http://plnkr.co/edit/ehbzoFGOqhQedchKv0ls?p=preview
I got this directive:
.directive('studentTable', [function() {
return {
restrict: 'A',
replace: true,
scope: {
students: "=",
collapsedTableRows: "="
},
templateUrl: 'partials/studentTable.html',
link: function(scope, elem, attrs) {
...
}
}
}
Template:
<table class="table">
<thead>
<tr>
<th><b>Name</b></th>
<th><b>Surname</b></th>
<th><b>Group</b></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students track by $index">
<td>{{ student.name }}</td>
<td>{{ student.surname }}</td>
<td>{{ student.group }}</td>
</tr>
</tbody>
</table>
Use directive in my html like this:
<div student-table students="students"
collapsedTableRows="collapsedTableRows"></div>
And the parent controller:
.controller('SchoolController', ['$scope', 'User', function($scope, User){
$scope.students = [];
$scope.collapsedTableRows = [];
$scope.search = function(value) {
if(value) {
var orgId = $state.params.id;
var search = User.searchByOrg(orgId, value);
search.success(function (data) {
$scope.students = data;
$scope.collapsedTableRows = [];
_(data).forEach(function () {
$scope.collapsedTableRows.push(true);
});
});
}
}
}])
Now at the beginnig, the table is empty, because no users in students array. After I click search, and get list of students object, I put them to scope variable, but the directive does not update, neither it find change in model (scope.$watch('students',...). What am I missing?
P.S. If I simulate the data using $httpBackend, directive works as it should.
Please make sure that data object returning array of student because somtimes you have to use data.data that simple demo should helps you:
http://plnkr.co/edit/UMHfzD4oSCv27PnD6Y6v?p=preview
$http.get('studen.json').then(function(students) {
$scope.students = students.data; //<-students.data here
},
function(msg) {
console.log(msg)
})
You should try changing the controller this way
...
$scope.$apply(function() {
$scope.students = data;
})
...
This will start a digest loop, if it's not already in progress.
Another form that will do almost the same thing is this:
...
$scope.students = data;
$scope.$digest()
...
PS:
The first method is just a wrapper that execute a $rootScope.$digest() after evaluating the function, considering that a $digest evaluates the current scope and all it's children calling it on the $rootScope is pretty heavy.
So the second method should be preferred if it works.
I'm looking for a way to add rows to a table. My data structure looks like that:
rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
I want to create a table which looks like that:
table
row1
row1.1
row1.2
row2
Is that possible with angular js ng-repeat? If not, what would be a "angular" way of doing that?
Edit:
Flatten the array would be a bad solution because if i can iterate over the sub elements i could use different html tags inside the cells, other css classes, etc.
More than one year later but found a workaround, at least for two levels (fathers->sons).
Just repeat tbody's:
<table>
<tbody ng-repeat="row in rows">
<tr>
<th>{{row.name}}</th>
</tr>
<tr ng-repeat="sub in row.subrows">
<td>{{sub.name}}</td>
</tr>
</tbody>
</table>
As far as I know all browsers support multiple tbody elements inside a table.
More than 3 years later, I have been facing the same issue, and before writing down a directive I tried this out, and it worked well for me :
<table>
<tbody>
<tr ng-repeat-start="row in rows">
<td>
{{ row.name }}
</td>
</tr>
<tr ng-repeat-end ng-repeat="subrow in row.subrows">
<td>
{{ subrow.name }}
</td>
</tr>
</tbody>
</table>
You won't be able to do this with ng-repeat. You can do it with a directive, however.
<my-table rows='rows'></my-table>
Fiddle.
myApp.directive('myTable', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var html = '<table>';
angular.forEach(scope[attrs.rows], function (row, index) {
html += '<tr><td>' + row.name + '</td></tr>';
if ('subrows' in row) {
angular.forEach(row.subrows, function (subrow, index) {
html += '<tr><td>' + subrow.name + '</td></tr>';
});
}
});
html += '</table>';
element.replaceWith(html)
}
}
});
I'm a bit surprised that so many are advocating custom directives and creating proxy variables being updated by $watch.
Problems like this are the reason that AngularJS filters were made!
From the docs:
A filter formats the value of an expression for display to the user.
We aren't looking to manipulate the data, just format it for display in a different way. So let's make a filter that takes in our rows array, flattens it, and returns the flattened rows.
.filter('flattenRows', function(){
return function(rows) {
var flatten = [];
angular.forEach(rows, function(row){
subrows = row.subrows;
flatten.push(row);
if(subrows){
angular.forEach(subrows, function(subrow){
flatten.push( angular.extend(subrow, {subrow: true}) );
});
}
});
return flatten;
}
})
Now all we need is to add the filter to ngRepeat:
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Rows with filter</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows | flattenRows">
<td>{{row.name}}</td>
</tr>
</tbody>
</table>
You are now free to combine your table with other filters if desired, like a search.
While the multiple tbody approach is handy, and valid, it will mess up any css that relies on the order or index of child rows, such as a "striped" table and also makes the assumption that you haven't styled your tbody in a way that you don't want repeated.
Here's a plunk: http://embed.plnkr.co/otjeQv7z0rifPusneJ0F/preview
Edit:I added a subrow value and used it in the table to show which rows are subrows, as you indicated a concern for being able to do that.
Yes, it's possible:
Controller:
app.controller('AppController',
[
'$scope',
function($scope) {
$scope.rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
}
]
);
HTML:
<table>
<tr ng-repeat="row in rows">
<td>
{{row.name}}
<table ng-show="row.subrows">
<tr ng-repeat="subrow in row.subrows">
<td>{{subrow.name}}</td>
</tr>
</table>
</td>
</tr>
</table>
Plunker
In case you don't want sub-tables, flatten the rows (while annotating subrows, to be able to differentiate):
Controller:
function($scope) {
$scope.rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
$scope.flatten = [];
var subrows;
$scope.$watch('rows', function(rows){
var flatten = [];
angular.forEach(rows, function(row){
subrows = row.subrows;
delete row.subrows;
flatten.push(row);
if(subrows){
angular.forEach(subrows, function(subrow){
flatten.push( angular.extend(subrow, {subrow: true}) );
});
}
});
$scope.flatten = flatten;
});
}
HTML:
<table>
<tr ng-repeat="row in flatten">
<td>
{{row.name}}
</td>
</tr>
</table>
Plunker
Here is an example. This code prints all names of all the people within the peopeByCity array.
TS:
export class AppComponent {
peopleByCity = [
{
city: 'Miami',
people: [
{
name: 'John', age: 12
}, {
name: 'Angel', age: 22
}
]
}, {
city: 'Sao Paulo',
people: [
{
name: 'Anderson', age: 35
}, {
name: 'Felipe', age: 36
}
]
}
]
}
HTML:
<div *ngFor="let personsByCity of peopleByCity">
<div *ngFor="let person of personsByCity.people">
{{ person.name }}
</div>
</div>