Use directive in ng-repeat of a table - angularjs

I want to create a customized row with two rows. One visible and another one collapsed.
This is my table:
<table class="table table-bordered panel panel-default centered">
<thead class="panel-heading">
<tr>
<td>..</td>
<td>..</td>
<td>.. GENERALE</td>
<td>..</td>
</tr>
</thead>
<tbody>
<table-row ng-repeat="(sottotarget,pubblicazione) in gestTrt.treat.pubblicazioni" sottotarget="sottotarget"></table-row>
</tbody>
</table>
My directive is:
<tr>
<td>
<div class="collapse navbar-collapse">
<a ng-click="tableRow.rowVar = !tableRow.rowVar" role="button"><span class="glyphicon glyphicon-plus-sign"></span></a>
</div>
</td>
<td>{{tableRow.sottotarget}}</td>
<td></td>
<td></td>
</tr>
<tr class="collapsed" ng-class="{'collapse' : !tableRow.rowVar}"><td><div>Should be collapsed</div></td></tr>
With the js code:
export function tableRow(): angular.IDirective {
return {
restrict: 'E',
scope: {
sottotarget: '=sottotarget'
},
templateUrl: '...',
controller: TableRowController,
controllerAs: 'tableRow',
bindToController: true
};
}
export class TableRowController {
public rowVar = false;
}
But in my page I visualize the two directives and then the empty table.

<div ng-app="" ng-controller="customersCtrl">
<table>
<tr ng-repeat="x in names">
<td>{{ x.Name }}</td>
<td>{{ x.Country }}</td>
</tr>
</table>
</div>

I resolved using
<tbody ng-repeat="(sottotarget,pubblicazione) in gestTrt.treat.pubblicazioni">
<tr>
<td>
<div class="collapse navbar-collapse" ng-init="collapsed = true">
<a ng-click="collapsed = !collapsed" role="button"><span class="glyphicon glyphicon-plus-sign"></span></a>
</div>
</td>
<td>{{tableRow.sottotarget}}</td>
<td></td>
<td></td>
</tr>
<tr class="collapsed" ng-class="{'collapse' : collapsed}">
<td colspan="4"><div>Should be collapsed</div></td>
</tr>
</tbody>

Related

Assign value after ng-if

I'm trying to do a table with ng-repeat. This is my code
<table class="tab2" >
<thead>
<tr>
<th>Currency: USD '000s</th>
<th ng-repeat="s in periodosCF">{{s.periodo | date:"yyyy"}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="n in nombreFilasCF" class="{{n.fondo}}">
<td style="width: 32%;"><div class="bordeTab">
<div class="{{n.color}}First" >
{{n.nombre}}
</div> <br>
</div>
</td>
<td ng-repeat="dato in datosCF" ng-if="n.nombre == dato.nombre">
<div class="{{n.color}}" >
{{dato.valor}}
</div><br>
</td>
</tr>
</tbody>
</table>
Once it enter to this ng-repeat="dato in datosCF", I do and if to print a value, but if the if returns me false I need to print this "-".
It sounds like you want to include all rows, but the content of the row is different based on some condition. If that's the case, you can try moving the conditional logic into the body of your <td> element, and having two divs with contradictory ng-if expressions:
<table class="tab2" >
<thead>
<tr>
<th>Currency: USD '000s</th>
<th ng-repeat="s in periodosCF">{{s.periodo | date:"yyyy"}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="n in nombreFilasCF" ng-class="n.fondo">
<td style="width: 32%;"><div class="bordeTab">
<div ng-class="n.color + 'First'">
{{n.nombre}}
</div> <br>
</div>
</td>
<td ng-repeat="dato in datosCF">
<div ng-if="n.nombre === dato.nombre" ng-class="n.color">
{{dato.valor}}
</div>
<div ng-if="n.nombre !== dato.nombre">
-
</div><br>
</td>
</tr>
</tbody>
</table>
I also changed your interpolated class attributes to ng-class.

AngularJS merge directives

I hate it when I repeat code. So I want to know if there is a better way of doing this.
I have 2 directives, they are very simple and very similar.
The 2 directives look like this:
.directive('pkFilterProducts', function () {
return {
restrict: 'A',
templateUrl: 'assets/templates/directives/pkFilterProducts.html',
scope: {
products: '=pkFilterProducts',
filters: '='
}
}
})
.directive('pkStateProducts', function () {
return {
restrict: 'A',
templateUrl: 'assets/templates/directives/pkStateProducts.html',
scope: {
products: '=pkStateProducts',
states: '='
}
}
})
The only difference is one accepts filters and the other accepts states.
The templates are very similar too:
<div class="col-md-6">
<h3>Excluded ({{ excluded.length }})</h3>
<div class="table-responsive" ng-show="excluded.length">
<table class="table table-hover">
<thead>
<tr>
<th class="image-column"></th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | exclude: filters as excluded">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h3>Included ({{ included.length }})</h3>
<div class="table-responsive" ng-show="included.length">
<table class="table table-hover">
<thead>
<tr>
<th class="image-column"></th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | include: filters as included">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
</tbody>
</table>
</div>
</div>
and
<div class="col-md-6">
<h3>Excluded ({{ excluded.length }})</h3>
<div class="table-responsive" ng-show="excluded.length">
<table class="table table-hover">
<thead>
<tr>
<th class="image-column"></th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | statesExclude: states as excluded">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h3>Included ({{ included.length }})</h3>
<div class="table-responsive" ng-show="included.length">
<table class="table table-hover">
<thead>
<tr>
<th class="image-column"></th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | statesInclude: states as included">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
</tbody>
</table>
</div>
</div>
again, the only different is the ng-repeat filter. Instead of using include or exclude it uses statesInclude and statesExclude.
Is there a way I can combine these too? Maybe use a variable for the filter?
It might sound a bit too hacky but I'd join the two ng-repeats on a single template and use ng-ifs to make them check for which is valid/should run. Example:
<tr ng-if="states"
ng-repeat="product in products | statesInclude: states as included">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
<tr ng-if="filters"
ng-repeat="product in products | include: filters as included">
<td><img class="img-responsive" ng-src="{{ product.image }}" /></td>
<td>{{ product.shortTitle }}</td>
</tr>
You could then perhaps use a single directive declaration:
.directive('pkProducts', function () {
return {
restrict: 'A',
templateUrl: 'assets/templates/directives/pkProducts.html',
scope: {
products: '=pkFilterProducts',
filters: '=',
states: '='
}
}
})
I agree with wdanda.
Another solution would be to pass an boolean to your filter. Depending on the boolean you need to make it behave different.
See this for reference.

Bootstrap dropdown using accordion is not working

I am implementing bootstrap dropdown in my Angular application using accordion. When I click on accordion toggle, it is not sliding down and up.
This is my code:
<div class="container-fluid mt100 mr5 ml5">
<div class="row">
<table class='table s12'>
<tr style="background: #e4e9eb;">
<td>Name</td>
<td>Address</td>
<td>City</td>
<td>Edit</td>
<td></td>
</tr>
<tr ng-repeat="listItem in Items">
<td>
{{listItem.name}}
</td>
<td>
{{listItem.address}}
</td>
<td>
{{listItem.city}}
</td>
<td>
<a class="dropdown-toggle" ng-click="isCollapsed = !isCollapsed" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
</a>
</td>
</tr>
<tr>
<td colspan="3" class="hiddenRow">
<div class="collapse" collapse="isCollapsed">
<table class="table display" style="border-collapse:collapse;">
<thead>
<tr class='pix2' style="background: #e4e9eb;">
<th>First</th>
<th>Second</th>
<th>Third</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='item in listItem.subItems'>
<td>{{item.first}}</td>
<td>{{item.second}}</td>
<td>{{item.third}}</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
</div>
</div>
What is causing the issue?

Indexing ng-repeat to only edit one

I am trying to index my ng-repeat so that I can edit a row by making the one with text disappear and the one with inputs appear.
<div ng-controller="BusinessGoalsCtrl">
<table class="table table-hover">
<tr>
<th>Type:</th>
<th>Timeframe:</th>
<th>Goal:</th>
<th>Edit:</th>
</tr>
<tr ng-class="editMode ? 'ng-hide' : ''" ng-repeat="details in businessGoalDetails track by $index">
<td style="width:30%;">{{details.display_name}} {{$index}}</td>
<td style="width:30%;">{{details.timeframe}}</td>
<td style="width:30%;">{{details.threshold}}</td>
<td style="width:10%;"><i ng-click="toggleEdit(true, $index)" class="fa fa-pencil-square-o"></i></td>
</tr>
<tr ng-class="editMode ? '' : 'ng-hide'" ng-repeat="inputs in businessGoalDetails track by $index">
<td style="width:30%;"><input ng-model="inputs.display_name"/></td>
<td style="width:30%;"><input ng-model="inputs.timeframe"/></td>
<td style="width:30%;"><input ng-model="inputs.threshold"/></td>
<td style="width:10%;"><i ng-click="toggleEdit(false, $index)" class="fa fa-check"></i></td>
</tr>
</table>
</div>
Angular Code
$scope.editMode = false;
$scope.details = 0;
$scope.toggleEdit = function (showEdit, $index) {
console.log("editing view");
$scope.editMode = showEdit;
}
You could probably just use ng-if/ng-show instead of ng-class and use ng-repeat-start/end instead of creating 2 different ng-repeats. Also you could just make a single row editable by setting property on its child scope or on the object under iteration. In your code you are setting the property editMode at the parent scope level and not only that it is not specific to each row.
<tr ng-show="!details.editMode" ng-repeat-start="details in businessGoalDetails track by $index">
<!-- ... -->
<td style="width:10%;"><i ng-click="details.editMode=true" class="fa fa-pencil-square-o"></i></td>
</tr>
<tr ng-show="details.editMode" ng-repeat-end>
<td style="width:30%;"><input ng-model="details.display_name"/></td>
<!-- ... -->
<td style="width:10%;"><i ng-click="details.editMode=false" class="fa fa-check"></i></td>
</tr>
Or
<tr ng-show="!editMode" ng-repeat-start="details in businessGoalDetails track by $index">
<!-- ... -->
<td style="width:10%;"><i ng-click="toggleEdit(true)" class="fa fa-pencil-square-o"></i></td>
</tr>
<tr ng-show="editMode" ng-repeat-end>
<td style="width:30%;"><input ng-model="details.display_name"/></td>
<!-- ... -->
<td style="width:10%;"><i ng-click="toggleEdit(false)" class="fa fa-check"></i></td>
</tr>
and
$scope.toggleEdit = function (showEdit) {
this.editMode = showEdit; //'this' here is the child scope
}

AngularJS ng-include finished

I'm loading a large amount of data using ng-include.
I'd like to know if there is a way to know when a template is "rendered", because sometimes it seems like it's been "frozen", because I want to do a "loading screen" or something.
Thx.
Here's the code
controller code:
$scope.layout = {
current: 'single-table.html'
};
$scope.layout.get = function() {
return $scope.layout.current;
};
templates:
main.html
<div class="btn-group">
<button ng-click="layout.current = 'single-table.html'">Single</button>
<button ng-click="layout.current = 'multiple-table.html'">Multiple</button>
</div>
<div ng-include="layout.get()"></div>
single-table.html
<table class="table table-bordered">
<thead>
<th ng-repeat="column in columns">{{ column.title }}</th>
</thead>
<tbody>
<tr ng-repeat="record in records">
<td ng-repeat="field in record.fields"
ng-controller="FieldController"
ng-class="getClass()">
{{ field.display }}
</td>
</tr>
</tbody>
</table>
multiple-table.html
<table class="table table-bordered" ng-repeat="record in records">
<tbody>
<tr ng-repeat="field in record.fields">
<th>{{ columns[$index].title }}</th>
<td ng-controller="FieldController" ng-class="getClass()">
{{ field.display }}
</td>
</tr>
</tbody>
</table>
EDIT
I'm using version 1.2.0
One solution (probably not the best) is doing this (no ng-include)
<table class="table table-bordered" ng-show="layout.current == 'single-table.html'">
<thead>
<th ng-repeat="column in columns">{{ column.title }}</th>
</thead>
<tbody>
<tr ng-repeat="record in records">
<td ng-repeat="field in record.fields"
ng-controller="FieldController"
ng-class="getClass()">
<span lud-run="{{ field.ng_directive }}"></span>
</td>
</tr>
</tbody>
</table>
<table class="table table-bordered" ng-repeat="record in records" ng-show="layout.current == 'multiple-table.html'">
<tbody>
<tr ng-repeat="field in record.fields">
<th>{{ columns[$index].title }}</th>
<td ng-controller="FieldController" ng-class="getClass()">
<span lud-run="{{ field.ng_directive }}"></span>
</td>
</tr>
</tbody>
</table>
I have only 20 records, but (is not in the code), each cells loads a custom directive depends on the data type (string, date, datetime, image...). This "solution" I think runs the same data twice (ng-show, not ng-if), but when a switch the layout mode there is no "lag".
You can use the onload hook on ng-include to update a variable when the include has finished rendering. If you set it to true on clicking your button, then revert it back to false in onload, you should be able to leverage it to temporarily display a loading screen.

Resources