Angular directive transclude <tr> breaks layout - angularjs

I'm trying to transclude a table row (a) in a ng-repeat loop so that another table row (b) can be shown/hidden when a user clicks (a). Both table rows are rendering, however, the <ng-transclude> element - needed to include (a) before (b), in the directive template, is breaking the layout. How can I use a directive to render two table rows without breaking the layout?
// main template
<tr id="a" campaign-item ng-if="vm.campaignData.length > 0" ng-repeat="campaign in vm.campaignData | orderBy:vm.sortBy:vm.sortReverse | filter:searchCampaigns track by $index" ng-click="vm.showCampaignPreview(campaign)">
<td>{{ campaign.name }}</td>
<td>{{ campaign.priority }}</td>
<td>{{ campaign.status }}</td>
<td>{{ campaign.creator }}</td>
<td>{{ campaign.approver }}</td>
<td>{{ campaign.release_date }}</td>
<td>{{ campaign.expiration_date }}</td>
<td><select ng-init="campaignOptions = vm.campaignOptions[0]" name="campaignOption" class="form-control" ng-model="campaignOptions" ng-options="campaign.name for campaign in vm.campaignOptions track by campaign.value"></select></td>
</tr>
// directive
(function(){
'use strict';
angular.module('vsmsCampaignModule')
.directive('campaignItem', campaign)
function campaign(){
var directive = {
link: link,
restrict: 'EA',
transclude: true,
templateUrl: 'app/vsms/admin/campaign/campaign.tpl.html'
};
return directive;
function link(scope, el, attr, ctrl, transclude) {
// do something with clone compiled and linked
// in child scope of directive's scope:
}
}
})();
// directive template
<ng-transclude></ng-transclude>
<tr id="b">
<td colspan="8">{{ campaign.description }}</td>
<td colspan="8">
<div class="row">
<div class="col-lg-1">
<ul>
<li ng-repeat="package in campaign.packages">
{{ package.name }}
</li>
</ul>
</div>
<div class="col-lg-1">{{ campaign.policy }}</div>
<div class="col-lg-1">{{ campaign.region }}</div>
<div class="col-lg-1">
<ul>
<li ng-repeat="vehicle in campaign.vehicles">
{{ vehicle.name }}
</li>
</ul>
</div>
</div>
</td>
</tr>
// update, I solved the issue by simply using ng-repeat-start/end
<tr ng-class-odd="'odd'" ng-class-even="'even'" ng-if="vm.campaignData.length > 0" ng-repeat-start="campaign in vm.campaignData | orderBy:vm.sortBy:vm.sortReverse | filter:searchCampaigns track by campaign.id" ng-click="vm.showCampaignPreview(campaign)">
<td>{{ campaign.name }}</td>
<td>{{ campaign.priority }}</td>
<td>{{ campaign.status }}</td>
<td>{{ campaign.creator }}</td>
<td>{{ campaign.approver }}</td>
<td>{{ campaign.release_date }}</td>
<td>{{ campaign.expiration_date }}</td>
<td>
<select ng-init="campaignOptions = vm.campaignOptions[0]" name="campaignOption" class="form-control" ng-model="campaignOptions" ng-options="campaign.name for campaign in vm.campaignOptions track by campaign.value">
</select>
</td>
</tr>
<tr class="campaign-preview" ng-class-odd="'odd'" ng-class-even="'even'" ng-repeat-end ng-show="vm.selectedCampaign == campaign.id">
<td colspan="8">{{ campaign.description }}</td>
</tr>

I fixed the issue by simply using ng-repeat-start/end (see update above) instead of a directive.

It will require you to rethink your directives a bit but you can use ng-repeat-start and ng-repeat-end to render several TRs per each ng-repeat iteration, like that:
<tr ng-repeat="vehicle in campaign.vehicles"
ng-click='vehicle.showDetails = !!!vehicle.showDetails'>
<td> {{ vehicle.name }} </td>
</tr>
<tr ng-repeat-end ng-show='vehicle.showDetails'>
<td> {{ vehicle.details }}
</td>
</tr>

Related

angularjs if else condition does not work

i am new in AngularJS and working on a small module in a project. I want to show two div based on condition. The condition is if {{ merchandiser.popCode }} means popcode value exist in database then show div1 else show div2. Here is my code snippet
<tr ng-repeat="merchandiser in data.merchandisers | filter: filter()">
<td title = "({{ merchandiser.id }})">{{ $index + 1 }}</td>
<td>{{ merchandiser.createdOnDate }}</td>
<!--<td>{{ merchandiser.createdOnTime }}</td>-->
<td>{{ merchandiser.pjpCode }} </td>
<td>{{ merchandiser.sectionCode}}</td>
<td>{{ merchandiser.popCode }}</td>
<td>{{ merchandiser.popName }}</td>
<td>{{ merchandiser.popStatus }} </td>
<td>{{ merchandiser.channel }} </td>
</table>
<div class="row">
<div id = "div1" class = "col-sm-4" >
<table class = "table">
<tr class="colour">
<th>POP Classification</th>
<td>{{ popData.popClassification }}</td>
</tr>
<tr>
<th class="colour">Owner's Name</th>
<td>{{ popData.ownerName }}</td>
</tr>
</div>
<div id = "div2" class = "col-sm-4" >
<table class = "table">
<tr>enter code here
<th class="colour">Owner's Name</th>
<td>{{ popData.ownerName }}</td>
</tr>
</div>
Try this :
<div id="div1" ng-if="merchandiser.popCode">
<!-- Condition true show Div1 block content -->
</div>
<div id="div2" ng-if="!merchandiser.popCode">
<!-- Condition false show Div2 block content -->
</div>
You can use ng-if for if(){..} else{..} logic in Angular template.
For your current situation,
<div id="div1" ng-if="merchandiser.popCode">
<!-- If block i.e div1 -->
</div>
<div id="div2" ng-if="!merchandiser.popCode">
<!-- Your Else Block i.e div2 -->
</div>

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.

AngularJS searching from navbar in view

I have a navbar on my page that has an input on it that I want to search a view, that should load in index.html after the user types in their search items. I can only figure out how to search with an input if it's in the same page that the table of ng-repeat items is in. Is there a way to search the table outside of the view? I've created a plnkr. It doesn't work. I'm not sure how to make it work. http://plnkr.co/edit/nqChzn5OATNMeSZL7ItJ?p=preview
Here is some of my code:
navbar
<input type="text" class="form-control" placeholder="Search" ng-model="vm.query">
Here is my table where the data displays.
<table ng-if="query" class="table table-hover table-responsive" >
<thead>
<tr>
<th>
({{filteredResults.length}}) Results Found
</th>
</tr>
<tr>
<td>Acc. ID</td>
<td>Acc. Name</td>
<td>Acc Address</td>
<td>City</td>
<td>Zip</td>
<td>Phone</td>
<td>Parent Name</td>
<td>Account Type</td>
<td>Account Status</td>
<td>Credit Term</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in vm.results | filter:query as filteredResults">
<td>{{ result.accountId }}</td>
<td>{{ result.accountName }}</td>
<td>{{ result.address }}</td>
<td>{{ result.city }}</td>
<td>{{ result.state }}</td>
<td>{{ reuslt.zip }}</td>
<td>{{ result.phone }}</td>
<td>{{ result.parentName }}</td>
<td>{{ result.accountType }}</td>
<td>{{ result.accountStatus }}</td>
<td>{{ result.accountStatus }}</td>
</tr>
</tbody>
</table>
Is it possible to do what I want to do?
I've looked at your plunker and I don't believe that your code hasn't initialized angular. I can't see an ng-app tag anywhere in your code.
That a side, you will be able to use any input to filter/search (via. various implementations) so long as the input and table are contained with your ng-app and controller parts of the DOM. Otherwise you won't be able to access it the controller.
Can I suggest that you distill your question down? give the smallest amount of code that demonstrates your problem.
here's a rough outline:
<body ng-app="app" ng-controller="cntrl">
<!-- nav bar -->
<div>
<input ng-model="filterVal"/>
</div>
<!-- table -->
<div>
<table>
<tr ng-repeat="r in data.rows | filter:filterVal">....</tr>
</table>
</div>
</body>
Here's a plunker where the default filter functionality is working: http://plnkr.co/edit/GDJs5xTCpqq48SDFTk67
If you need to have your nav bar outside of your app/controller or in an different controller (etc.) then there are solutions to this.
Assuming you are using ui-route, this is how to use a service to communicate between controllers:
functioning example:
http://plnkr.co/edit/fZZLUvlHO9zUFwQYd1an?p=preview
Eggheads video:
https://egghead.io/lessons/angularjs-sharing-data-between-controllers

AngularJs ngSwitch with ngRepeatStart / End

I'm tring to figure out how I could us the ngSwitch with ngRepeatStart and End on two table rows, where second row is only shown when you click on the top one.
Here's what I've got and want to make work (doesn't work):
<tr
data-ng-repeat-start="user in users | filter : { name: search, active: searchActive }"
data-ng-click="selectUser(user)"
data-ng-switch on="isSelected(user)">
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.active }}</td>
<td>{{ user.dob | date: 'EEEE, dd MMMM yyyy' }}</td>
</tr>
<tr data-ng-repeat-end data-ng-switch-when="true">
<td colspan="4">
{{ user.description }}
</td>
</tr>
and here's what works:
<tbody
data-ng-repeat="user in users | filter : { name: search, active: searchActive }"
data-ng-click="selectUser(user)"
data-ng-switch on="isSelected(user)">
<tr>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.active }}</td>
<td>{{ user.dob | date: 'EEEE, dd MMMM yyyy' }}</td>
</tr>
<tr data-ng-switch-when="true">
<td colspan="4">
{{ user.description }}
</td>
</tr>
</tbody>
I would like to avoid the wrapping tbody tag and instead use ngRepeatStart / End.
Is there a way of modifying my first example to make it work?
Don't use ng-switch, just use ng-show or ng-if. Here's a plunker
Edit
Okay, I guess if you really want you can use ng-switch, though I would suggest not doing it if all you're doing is checking true/false. Here's the updated plunker. The trick is that the switch needs to be on the second <tr>, and the ng-switch-when needed to be on the <td>.
Now if you need each switch to be a table row, then you're just going to have to use ng-if. The ng-switch attribute needs to be on the parent of all the ng-switch-when's. That's why the switch only works when you put it on <tbody>, or when you switch over <td>'s.

Table with details in AngularJS

I've got tabular data loaded in AngularJS app. I want to display them in a table. But I also want to have detail view for some rows. Something like:
<table>
<tr ng-repeat="datum in data">
<!-- if datum.showDetail => detail view -->
<td colspan="N">
<h1>{{ datum.field1 }} <small>{{ datum.field2 }}</small></h1>
...
<p>{{ datum.fieldN }}</p>
</td>
<!-- else => row view -->
<td>{{ datum.field1 }}</td>
<td>{{ datum.field2 }}</td>
...
<td>{{ datum.fieldN }}</td>
</tr>
</table>
What's the Angular way of doing this?
Why not just adding ng-show and ng-hide directives to the td's?
<table>
<tr ng-repeat="datum in data" >
<!-- if datum.showDetail => detail view -->
<td colspan="N" ng-show="datum.showDetail">
<h1>{{ datum.field1 }} <small>{{ datum.field2 }}</small></h1>
...
<p>{{ datum.fieldN }}</p>
</td>
<!-- else => row view -->
<td ng-hide="datum.showDetail">{{ datum.field1 }}</td>
<td ng-hide="datum.showDetail">{{ datum.field2 }}</td>
...
<td ng-hide="datum.showDetail">{{ datum.fieldN }}</td>
</tr>
</table>
Also note that, in case you're using the Angular UI extensions, the ui-if directive would probably be a better fit, since it would actually remove the unused td's from the table, instead of just hiding them, like ng-hide.

Resources