ng-class is not applied on element with ng-repeat - angularjs

I have the following custom directive:
angular.module('Interfaces').directive('Interfaces', function() {
return {
restrict : 'A',
scope : {
minor : '#'
},
templateUrl : 'interfaces/interfaces.template.html',
controller : [ '$scope', 'InterfacesService', function InterfaceController($scope, InterfacesService) {
$scope.interfacesService = InterfacesService;
$scope.label = 'Interfaces';
$scope.optional = ($scope.minor == "true");
if ($scope.optional) {
$scope.label = '';
}
$scope.getInterfaces = function getInterfaces() {
return $scope.interfacesService.getInterfaces($scope.minor);
};
} ]
};
});
And the following template
<tr ng-class="{'optional': optional}"><td colspan="5">Just testing</td></tr>
<tr ng-class="{'optional': optional}" ng-repeat="interface in interfaces = (getInterfaces())" >
<td rowspan='{{interfaces.length}}' class='label-column' ng-if="$index === 0">{{label}}</td>
<td colspan='2' class='data'>{{interface.key}}</td>
<td colspan='2' class='data'>{{interface.value}}</td>
</tr>
I am using this directive as part of a table:
<table>
<tbody>
<!-- other table content -->
</tbody>
<tbody interfaces minor="true"></tbody>
<tbody interfaces minor="false"></tbody>
<tbody>
<!-- other table content -->
</tbody>
</table>
The first table row is just added for testing purposes. It correctly has the class "optional" according to the value of the variable 'optional'.
However, those table rows created by ng-repeat never have the "optional" class set, no mattter what the value of the 'optional' variable.
I have found the following article
Angular js Custom Directive on element with ng-repeat - Can't set CSS classes
which suggests using a priority of -1001 in my directive, but neither the 1001 of the original code in that post nor the -1001 suggested in the answer make a difference in my case.
Why is ng-class not applied on the element with the ng-repeat?

Ok, after talking to a friend it seems the problem was the following line in the template:
<tr ng-class="{'optional': optional}" ng-repeat="interface in interfaces = (getInterfaces())" >
Having put the getInterfaces() call in brackets seems to have lead to an endless iteration which stopped the ng-class from being applied. I had seen those error messages but did not make the connection between them and my issue.
I have solved the issue as follows:
Instead of retrieving the data from the InterfacesService, I am now passing them from the outer template as a parameter:
<tbody interfaces minor="true" interfaces="{{information.host.interfaces}}"></tbody>
<tbody interfaces minor="false" interfaces="{{information.host.interfaces}}"></tbody>
And in the directive I've added a new binding:
scope : {
minor : '#',
interfaces: '<'
},
And my interface template now directly references this new binding:
<tr ng-class="{'optional': optional}" ng-repeat="interface in interfaces" >
The reason for attempting to get this information from the InterfacesService instead of passing it into the directive was that I had tried that and failed, using the service had been a workaround.
I stumbled across the same problem again in a slightly different setting (see AngularJs: How to pass part of my data from one component to another) and the answer there (to use a '<' binding instead of '#' allowed me to get rid of the service and hence the
(getInterfaces())
part of my ng-repeat statement.

Related

Replace all ↵ for a new line in angular loop

I have a loop currently in my application located here:
<tr ng-repeat="audit in audctrl.audits | orderBy:'-created_at'">
<td>
{{audit.objects}}
</td>
</tr>
The object's code shows a lot of text, but with a lot of the ↵ symbol.
How would I go about making these replaced?
I have tried
{{audit.object.replace(/\u21B5/g,'<br/>')}}
What would be the best method to make this work?
I would suggest you to write an angular filter. For example:
myApp.filter('multiline', function () {
return function(text) {
return text.replace(/\n/g, '<br>');
}
});
and then call it with pipe:
<tr ng-repeat="audit in audctrl.audits | orderBy:'-created_at'">
<td>
{{audit.objects | multiline}}
</td>
</tr>
#Edit
I'm not sure about your new line character code but you can easy change it. Angular filters are good because they are reusable in other parts of application.
#Edit2
To display html tags in Angular binding u have to use $sce service and ng-bind-html="audit.objects | multiline" instead of {{audit.objects | multiline}}
http://jsfiddle.net/c1qwg776/
Write this in your controller at initialisation:
for(var i=0;i<audctrl.length;i++){
for(var j=0;j<audctrl[i].objects.length;j++){
if(audctrl[i].objects.charCodeAt(j)===10){
var temp=audctrl[i].objects.substring(0,j);
temp+="<br/>";
audctrl[i].objects= temp+audctrl[i].objects.substring(j+5,audctrl[i].objects.length-1);
}
}
}

AngularJS ngTable doesn't work

I already used ngTable with static data before, it worked well (listing, sorting and filtering). I'm using ngTable v 0.8.3
My Goal
This time I want to create a ngTable with data loaded from a Web Service.
I made a custom factory to be able to create my ngTable with future others entities.
Issue
It doesn't work and I have no JS console errors. When I debug, I can clearly see that my method query from my service Employee return me my complete and correct objet containing employees details. I enter in the getData() function, the ngTable is being created, but with empty data.
Relevant and lightweighten code
Here is my HTML markup :
<table ng-table="tableParams" class="table table-striped" show-filter="true">
<tbody>
<tr ng-repeat="employee in $data">
<td data-title="'employee.firstname' | translate" filter="{'firstname' : 'text'}" sortable="'firstname'"><a ui-sref="employee.detail({id:employee.id})">{{employee.firstname}}</a></td>
<td data-title="'employee.lastname' | translate" filter="{lastname : 'text'}" sortable="'lastname'">{{employee.lastname}}</td>
<td data-title="'employee.mail' | translate" filter="{mail : 'text'}" sortable="'mail'">{{employee.mail}}</td>
<td data-title="'employee.phone-number' | translate" filter="{phone_number : 'text'}" sortable="'phone_number'">{{employee.phone_number}}</td>
<td data-title="'employee.birthdate' | translate" filter="{birthdate : 'text'}" sortable="'birthdate'">{{employee.birthdate}}</td>
</tr>
</tbody></table>
my Angular controller :
myapp.controller('EmployeeController', function ($rootScope, $scope, Employee, ngTableParams, ngTableFactory) {
$scope.tableParams = ngTableFactory.create(20, {lastname: 'asc'}); // count, sorting
});
And my Angular factory :
myapp.factory('ngTableFactory', function(ngTableParams, Employee) {
return {
create: function(count, sorting) {
return new ngTableParams({
page: 1, // initial page
count: count, // count per page
sorting: sorting // initial sorting
}, {
total: 0,
getData: function($defer, params) {
Employee.query({page: params.page(), size: params.count()}, function(result) {
$defer.resolve(result);
});
}
});
}
}
});
Edit 1, progresses :
My items are being listed if I remove params from my query method, so it was a bad handling of my Employee service. But, the sort and filter does not work on my table by default. to make sorting works, I have to add this line on my ngTable factory, in the getData() function :
result = $filter('orderBy')(result, params.orderBy());
Normally, the sorting of basic elements works as I added the 'sortable' keyword in my HTML ngtable template columns, and as I'm fetching my data over $data item. Still investigating.
Thanks for your help mates !
As far as I can see, your code works.
Please see https://github.com/masa67/NgTable for a working example (based on Node). I did not change anything (except filled-in the missing parts, maybe the problem is there).
The table formatting is not correct, but at least the data is coming in.

angular filter name as variable

I'm designing universal table that reads data and columns from ajax.
In columns description is also filter name which angular should use for a specific column.
But in HTML templates I can't use variables for filter names:/
Is there a solution for that? Or should I code javascript loop with data source?
Here is code example:
<tr ng-repeat="item in data">
<td ng-repeat="col in cols">
{{item[col.source]}}
<span ng-if="col.ngFilter">
{{col.ngFilter}} // ex. "state" filter
{{item[col.source]|col.ngFilter}} //is not working - is looking for "col.ngFilter" not "state" filter.
</span>
</td>
</tr>
You cannot do it in your HTML. First, you need to apply the filter in your controller.
function MyCtrl($scope, $filter) {
$scope.applyFilter = function(model, filter) {
return $filter(filter)(model);
};
}
Then, in your HTML:
Instead of
{{item[col.source]|col.ngFilter}}
use
{{applyFilter(item[col.source], col.ngFilter)}}
For anyone looking to do something like
{{applyFliter(item[col.source], col.ngFilter)}}
where ngFilter might contains some colon separated parameters such as
currency:"USD$":0
I ended up writing this little helper
function applyFilter (model, filter){
if(filter){
var pieces = filter.split(':');
var filterName = pieces[0];
var params = [model];
if(pieces.length>1){
params = params.concat(pieces.slice(1));
}
return $filter(filterName).apply(this,params);
}else{
return model;
}
}

Angular.js - search filter for object with ng-repeat

I'm apologizing for messy description of my problem. I hope you understand it.
I have this HTML code:
<form>
<input ng-model="attr.query" type="text" placeholder="{{attr.attr_name}}" ng-repeat="attr in attrs">
</form>
<table>
<tr ng-repeat="element in elements">
<td ng-repeat="(key, value) in element">{{value}}</td>
</tr>
</table>
JS controller:
$scope.attrs = [{'descr':'descr1'},{'descr':'descr2'}];
$scope.elements = [{'property1" : 'value1', 'property2' : 'value2'},{'property1" : 'value3', 'property2' : 'value4'}];
I need to filter each by query from input. But i need to filter only with the same attr as in input field.
I have some troubles to apply filter to array of objects.
Thanks
If I understand you correctly (I don't have enough rep to ask in a comment, sorry), you want to filter the data on one or more of several attributes.
The simplest way to do this is probably by defining a custom filter function accessible to your scope. AngularJS's filter filter will happily accept that as an evaluator.
$scope.customFilter = function(item) {
var passed = true;
if(/* the item doesn't pass muster */) {
passed = false;
}
return passed;
}
If it helps, I put together a fiddle to demonstrate. (NB. The query fields are case-sensitive.)

How to ng-repeat into html table with multiple levels of json?

I have an object of social media stats. I'm trying to ng-repeat them into a table. Here's my plunker.
HTML:
<table>
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td>{{metricData}}</td>
</tr>
</table>
Controller object:
$scope.data = { buzz:0,
Delicious:121,
Facebook:
{
like_count: "6266",
share_count: "20746"
},
GooglePlusOne:429,
LinkedIn:820,
Twitter:4074
};
I run into a problem when I get to the Facebook results. Within the <td> that entire object gets displayed (as it should be with how I have my code setup). But what I'd rather have happen is to repeat through that object and display the key and value in the cell.
I tried doing something looking to see if metricData is an object and doing some sort of ng-repeat on that. But I wasn't having luck with that. Any idea on how I can display the inner object (keys & value) within the cells?
You can define a scope function returning the type of metricData :
$scope.typeOf = function(input) {
return typeof input;
}
And then you can display it according to its type :
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td ng-switch on="typeOf(metricData)">
<div ng-switch-when="object">
<div ng-repeat="(key, value) in metricData">
<span>{{key}}</span>
<span>{{value}}</span>
</div>
</div>
<span ng-switch-default>{{metricData}}</span>
</td>
</tr>
You can see it in this Plunker
Sounds like you'll need a specific directive that wires up children to be recursive, take a look at this example: Recursion in Angular directives
What you'd check on is if what you need to repeat is an object and not a value, then add the new element compile it, and start the process over again.
I'm assuming you want each of those values to have their own line but you don't explain exactly how you want it to work. I think the matter would best be handled by passing a clean version of what you want to the ng-repeat directive. I'm assuming you want two rows for facebook in your sample. You could create a filter to flatten the metrics so there are properties "Facebook_like_count" and "Facebook_share_count" (PLUNKER):
app.filter('flatten', function() {
function flattenTo(source, dest, predicate) {
predicate = predicate || '';
angular.forEach(source, function(value, key) {
if (typeof(value) == 'object') {
flattenTo(value, dest, predicate + key + '_');
} else {
dest[predicate + key] = value;
}
});
}
return function(input) {
var obj = {};
flattenTo(input, obj, '');
return obj;
}
});
Then your repeat can use the filter:
<tr ng-repeat="(metric, metricData) in data|flatten">

Resources