Trying to use a key/value map to determine if an angular controlled element should be displayed :
http://jsfiddle.net/9fR23/181/
But I receive a exception :
angular.js:6173 TypeError: fnPtr is not a function
at Object.elementFns [as get] (angular.js:6802)
at Object.$get.Scope.$digest (angular.js:8563)
at Object.$get.Scope.$apply (angular.js:8771)
at angular.js:986
at Object.invoke (angular.js:2873)
Using scope in this way is not illegal ? How to use a key/value map to determine if element should be displayed ? This map will be updated at runtime, so to ensure this update is reflected on UI I need to include apply() method ?
fiddle code :
<div ng-app="myapp" ng-controller="FirstCtrl">
<table class="table table-striped">
<tr ng-repeat="person in people">
<td ng-show="errorMap('1')">{{ person.first + ' ' + person.last }}</td>
</tr>
</table>
</div>
var myapp = angular.module('myapp', []);
myapp.controller('FirstCtrl', function ($scope) {
var errorMap = new Object()
errorMap['1'] = 'true'
errorMap['2'] = 'false';
$scope.errorMap = errorMap
$scope.people = [
{ id: 1, first: 'John', last: 'Rambo' },
{ id: 2, first: 'Rocky', last: 'Balboa' },
{ id: 3, first: 'John', last: 'Kimble' },
{ id: 4, first: 'Ben', last: 'Richards' }
];
});
errorMap('1') is not a function, its an object. so you can do this:
ng-show="errorMap['1']"
You are trying to call a method on your scope that is called errorMap.
Use square brackets to access it.
<div ng-app="myapp" ng-controller="FirstCtrl">
<table class="table table-striped">
<tr ng-repeat="person in people">
<td ng-show="errorMap['1']">{{ person.first + ' ' + person.last }}</td>
</tr>
</table>
</div>
Related
I'm fairly new to Angular, and a simple binding doesn't work for me.
It just shows me {{ trip.name }} and {{trip.created}} just as they're written.
My controller:
(function () {
"use strict";
angular.module("app-trips")
.controller("tripsController", tripsController);
function tripsController() {
var vm = this;
vm.trips = [{
name: "US Trip",
created: new Date()
}, {
name: "World Trip",
created = new Date()
}];
A part of my class:
#section Scripts {
<script src="~/lib/angular/angular.min.js"></script>
<script src="~/js/app-trips.js"></script>
<script src="~/js/tripsController.js"></script>
}
<div class="row" ng-app="app-trips">
<div ng-controller="tripsController as vm" class="col-md-6 col-md-offset-3">
<table class="table table-responsive table-striped">
<tr ng-repeat="trip in vm.trips">
<td>{{ trip.name }}</td>
<td>{{ trip.created }}</td>
</tr>
</table>
</div>
the view of the controller:
(function () {
"use strict";
angular.module("app-trips", ['ngSanitize']);
})();
BTW - supposedly according to the course I'm following, I don't need ['ngSanitize'] yet, but without it it doesn't even show the DIVs.
EDIT: as #jellyraptor noticed , I had a typo with = instead of :
EDIT 2 :
It was the typo + the [ngSanitize] which I really didn't needed. I fixed the typo and passed an empty array and everything works. Thanks all
Either Angular isn't loading or it is encountering an error. Bring up the dev tools with F12 and see if there are errors in the console. Also in the console, you can type 'angular' and hit enter and if it reports that angular is undefined then angular is not loading properly.
Because you are using controllerAs syntax and here the variable is vm. so for access scope object use vm instead of tripsController
<tr ng-repeat="trip in vm.trips">
<td>{{ trip.name }}</td>
<td>{{ trip.created }}</td>
</tr>
(function() {
"use strict";
angular.module("app-trips", []);
})();
angular.module("app-trips")
.controller("tripsController", tripsController);
function tripsController() {
var vm = this;
vm.trips = [{
name: "US Trip",
created: new Date()
}, {
name: "World Trip",
created: new Date()
}];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script>
<div ng-app="app-trips" ng-controller="tripsController as vm" class="col-md-6 col-md-offset-3">
<table class="table table-responsive table-striped">
<tr ng-repeat="trip in vm.trips">
<td>{{ trip.name }}</td>
<td>{{ trip.created }}</td>
</tr>
</table>
</div>
You put an equals sign in your trips array's second object's 'created' attribute.
vm.trips = [{
name: "US Trip",
created: new Date()
}, {
name: "World Trip",
created = new Date()
}];
should be
vm.trips = [{
name: "US Trip",
created: new Date()
}, {
name: "World Trip",
created: new Date()
}];
I have a php api that returns a list of companies and a list of users,
Each user has a company_id assigned.
(Users : CustomerList) (Companies : CompanyList)
The companies have a company_id and company_name value.
I'm using resolve to get both these arrays.
When displaying the CustomerList, the company_id of the customer is displayed and everything is working fine.
But what i require is instead of the company_id being shown, I need the company_name being displayed in the CustomerList.
The company_id of the CustomerList is related to the id in the CompanyList.
Just that the company_name is contained in the companyList and not in the CustomerList. But the ID's are related and contained in the userList.
I need to get the company_name of the id that's in CustomerList and display it in the view.
resolve: { userList:function($http,$stateParams){
return $http.post('/api/get_users.php',{role:$stateParams.role},{headers: { 'Content-Type': 'application/x-www-form-urlencoded' }})
.then(function(response){
console.log(response);
return response.data.data;
})
},
companyList:function($http,$stateParams){
return $http.get('/api/get_companies.php')
.then(function(response){
console.log(response);
return response.data.data;
})
}
}
controller("CustomerList", function($scope,$location,$http,$stateParams,userList,companyList){
$scope.customers = userList;
$scope.companies = companyList;
})
VIEW
<table>
<thead>
<tr>
<th>Username</th>
<th>Company Name</th>
<th>Contact Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="customer in customers">
<td>{{ customer.username }}</td>
<td>{{ customer.company_id }}</td>
<td>{{ customer.contact_name }}</td>
</tr>
</tbody>
</table>
the customer.company_id is related to the ID in the companyList , i need to return the companyList.company_name instead of showing the company ID in the customers.
Thanks in advance for any help.
You need to join them.
So you need to create a new object with will contain the username, contact_name, company_name and other properties you need. You need to use the map() which will return an array of new objects. With username and contract_name it is easy, just assign the properties to the new created object. With company_name, we need to find the appropriete company and assign it's name to the newly created object.
Working Example with simple data, which joins two arrays based on the id.
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function($scope){
var userList = [
{username: 'A user', contact_name: 'A contract', company_id: 1},
{username: 'B user', contact_name: 'B contract', company_id: 2},
{username: 'C user', contact_name: 'C contract', company_id: 3},
];
var companyList = [
{id: 1, name: 'A Company'},
{id: 2, name: 'B Company'},
{id: 3, name: 'C Company'},
];
$scope.customers = userList.map(item => {
var foundCompany = companyList.find(company => company.id === item.company_id);
return {
username: item.username,
contact_name: item.contact_name,
company_name: foundCompany ? foundCompany.name : ''
};
});
$scope.companies = companyList;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app" ng-controller="ctrl">
<table>
<thead>
<tr>
<th>Username</th>
<th>Company Name</th>
<th>Contact Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="customer in customers">
<td>{{ customer.username }}</td>
<td>{{ customer.company_name }}</td>
<td>{{ customer.contact_name }}</td>
</tr>
</tbody>
</table>
</body>
You can structure your companies list in such a way that each object in list is key value pair with key being id of company and value being complete object of company details and then access it easily.
eg
$scope.companies = {
{
"companyId1" : {
"name" : "companyName",
"field2" : "vlue2"
}
},
{
"companyId2" : {
"name" : "companyName2",
"field2" : "vlue2"
}
}
}
and then in your html access it using
{{companies[customer.company_id].name}}
This will work for you
map your userList so that it will contain companyName
var mappedCustomerList= userList.map(function(user){
var user=userWithCompanyName;
userWithCompanyName['companyName']=
companyList[companyList.findIndex(function(company){return company['id']==user['company_id']})].companyName;
return userWithCompanyName;
});
$scope.customerList=mappedCustomerList;
so then from your view you can access companyName
<td>{{customer.companyName}}</td>
In following table there can be more than one list on particular date. So For list column there can be more than one list for given date.
I am able to insert more than one value in single cell but it shift the row in which I inserted more than one value, Please look at DEMO.
Example: Table
Date...............List
12/1/2016 .... python, angularjs
13/1/2016..... java, html
data:
$scope.todolists = [{
date: '12/1/2016',
list: {python, angularjs}
}, {
date: '13/1/2016',
list: {java, html}
}];
view:
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist">{{subject}}</td>
</tr>
</tbody>
I tried ng-repeat inside ng-repeat but it is not working. So my question is how to insert more than one value in single cell in table.
Your references are wrong.
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist.list">{{list}}</td>
</tr>
</tbody>
or, preferably,
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td>
<span ng-repeat="list in todolist.list">
{{list + ($last ? "" : ", ") }}
</span>
</td>
</tr>
</tbody>
Your JSON is invalid. If you want to store values without properties, you need to use Array but not Object. Also, if the values are string you need to wrap them with comma like: ['val1', 'val2', ...];
You can do ng-repeat inside ng-repeat but you need to iterate the right property todolist.list.
You can't use {{subject}} if you have not property subject in you model. So you need to use {{list}} when you do the ng-repeat like ng-repeat="list in todolist.list".
The full code:
angular.module('app', []).
controller('ctrl', function($scope) {
$scope.todolists = [{ date: '12/1/2016', list: ['python', 'angularjs'] }, { date: '13/1/2016', list: ['java', 'html'] }];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table>
<tr ng-repeat="todolist in todolists">
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist.list">{{list}}</td>
</tr>
</table>
</div>
Your datas are not properly declared. It should be:
$scope.todolists = [{
date: '12/1/2016',
list: [
'python',
'angularjs'
]
}, {
date: '13/1/2016',
list: [
'java',
'html'
]
}];
Note the '' around python, etc. and the list should be a list => [] and not {}.
A nice way to go is to write a custom filter:
angular.module("myApp", [])
.filter('nicelist', function() {
return function(input) {
if (input instanceof Array) {
return input.join(",");
}
return input;
}
});
Then, you can use :
<table>
<tr ng-repeat="todolist in todolists">
<td>{{todolist.date}}</td>
<td>{{todolist.list | nicelist}}</td>
</tr>
</table>
Here is a working fiddle.
I have following markup
<div ng-app>
<table ng-controller="test">
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="col in cols">
<input type="text" ng-model="row[col].val" />
</td>
</tr>
</tbody>
</table>
</div>
controller looks like this
function test($scope) {
$scope.cols = ["col1", "col2", "col3"];
$scope.rows = [{
col1: { val: "x"}
}, {
col2: { val: "y" }
}]
}
When i try to set column value that does not yet exist, i get
"Cannot set property 'val' of undefined".
Example is here http://jsfiddle.net/z9NkG/2/
In documentation is note:
ngModel will try to bind to the property given by evaluating the
expression on the current scope. If the property doesn't already exist
on this scope, it will be created implicitly and added to the scope.
But it fails when using indexer instead of property name.
Is there a way to define ngModel expression dynamically or am i misusing angular?
And what about:
<input type="text" ng-model="row[col].val" ng-init="row[col] = row[col] || {}" />
Fiddle: http://jsfiddle.net/z9NkG/8/
I'm not sure about your model, but I think you are using a 2 dimensional array. I'm oversimplifying, not knowing,
make cols collection a child or rowsL
function test($scope) {
$scope.colnames = ["col1", "col2", "col3"];
$scope.rows = [{
cols: { col1: "x"}, { col2: "y"}
}, {
cols: { col1: "z"}, { col2: "a"}
}]
}
And then use nested ng-repeats
<table>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="col in row.cols">
<input type="text" ng-model="col.val" />
</td>
</tr>
</tbody>
</table>
The main problem is that you're setting an undefined property of an undefined object as the input's model. Angular does indeed automatically create the corresponding variables for your model binding, but to do so, you would have to bind the input directly to the row[col] property instead of row[col].val :
<input type="text" ng-model="row[col]" />
Here's the live demo: http://jsfiddle.net/z9NkG/9/
try this
make changes in controller
add below code
$scope.cols.forEach(function(x, y) {
angular.forEach($scope.rows, function(i, j) {
if ((i[x])== null)
i[x] = {};
});
})
yo can refer fiddle http://jsfiddle.net/z9NkG/10/
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>