AngularJS directive with ngTransclude not showing {{bound}} content - angularjs

I'm attempting to create an Angular directive that creates a standardised structure of a table that I wish to use around my application.
I want to specify the structure of the tr when I declare the directive in HTML so that I can have different layouts depending on the data that is passed in. However, I can't seem to get the content of ng-transclude to actually render.
Plunker: Example
I'd like the following:
<custom-table table="data">
<td>
{{row.Username}}
</td>
<td>
{{row.FirstName}}
</td>
<td>
{{row.LastName}}
</td>
</custom-table>
to be injected into the within the template.
How do I get the {{row.Username}} etc tags to resolve within the ng-transclude in the angular directive?
Edit1: I think this is a similar question that I've just found, although most top voted answer seems to recommend avoid using table, tr, td etc within directives :\

This doesn't answer your question, but I think it is a more generic way of doing what you want.
First pass the list of columns you want to display to your directive:
<custom-table table="data" columns="columns">
</custom-table>
In your controller:
app.controller('MainCtrl', function($scope) {
$scope.data = [
{Username: "Test1", FirstName: "Bill", LastName: "Jones"},
{Username: "Test2", FirstName: "Sophie", LastName: "O'Grady"},
{Username: "Test3", FirstName: "Tim", LastName: "Cross"}
];
$scope.columns = ["Username", "FirstName", "LastName"]
});
In your directive:
app.directive('customTable', ['$compile', function($compile){
return {
restrict: 'E',
templateUrl: 'tableTemplate.html',
scope: {
table: '=',
columns: '='
}
};
}]);
And finally change your template to:
<div>
<table>
<thead>
<tr>
<th ng-repeat="col in columns">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in table">
<td ng-repeat="col in columns">
{{ row[col] }}
</td>
</tr>
</tbody>
</table>
</div>
And here's the updated plunker: http://plnkr.co/edit/dYwZWD2jB2GsmnvmuSbT

I found a work-around which solves the problem for me.
I've updated the plunker with a working example. I had to create a directive:
app.directive('myTransclude', function() {
return {
compile: function(tElement, tAttrs, transclude) {
return function(scope, iElement, iAttrs) {
transclude(scope.$new(), function(clone) {
iElement.append(clone);
});
};
}
};
});
I found the issue here within the comments.
I also had to update the directive so it uses a CSS/div based table rather than using an actual HTML table.

Related

ng-click in a ng-repeat within a directive

I have a directive with a template to show a table of persons (name, nationality, dates and a button Details). When the button Details is clicked and alert is displayed. The problem is that I don't know how to make the ng-click of the button to work in the ng-repeat inside the directive. They told me to use the following configuration for the directive, using the show function from the controller for the ng-click:
angular.module('app', []).directive('persons', function() {
return {
restrict: 'E',
scope: {
data: '=',
action: '&'
},
template: '...'
};
});
The controller:
angular.module('app', []).controller('labController', [function() {
var vm = this;
vm.persons = [{
name: 'Mark Twatin',
nationality: 'American',
dates: '1835-1910'
}, {
name: 'A. A. Milne',
nationality: 'English',
dates: '1882-1956'
}, {
name: 'Ernest Hemingway',
nationality: 'American',
dates: '1899-1961'
}, {
name: 'Charles Dickens',
nationality: 'English',
dates: '1812-1870'
}, {
name: 'Jane Austen',
nationality: 'English',
dates: '1775-1817'
}];
vm.show = show;
function show(person) {
alert('Show details for: ' + person.name);
}
}]);
So I added the following template:
<table class="table">
<thead>
<th>Name</th>
<th>Nationality</th>
<th>Dates</th>
<th></th>
</thead>
<tbody>
<tr ng-repeat="person in data">
<td>{{person.name}}</td>
<td>{{person.nationality}}</td>
<td>{{person.dates}}</td>
<td>
<input
type="button"
ng-click="action(person)"
value="Details"
class="btn btn-primary"
/>
</td>
</tr>
</tbody>
</table>
And the HTML:
<body ng-app="app">
<div class="container" ng-controller="labController as vm">
<h1>Directives</h1>
<persons data="vm.persons"></persons>
</div>
</body>
So, how can I make the button Details work?
I've created the following fiddle: http://jsfiddle.net/6xyztpxt/3/
You need to change your call inside of the directive for the ngClick.
In a directive, if you want to call a bound function with parameters, you need to pass it an object with to parameters.
Change the ng-click in the template to this: ng-click="action({person: person})"
That means, call the action bounded function with a parameter person that has the value of the current person in the ngRepeat.
Then you need to bind your controller show function to the directive like so:
<persons data="vm.persons" action="vm.show(person)"></persons>
That means, bind the vm.show function as the action to the directive, and when is called pass a parameter person to it.
I have updated your fiddle: http://jsfiddle.net/gmm8a06q/1/
I've made some changes to your fiddle. I've changed the directive scope for action to '=', so you can access directly to the controller method "show".
scope: {
data: '=',
action: '='
},
http://jsfiddle.net/6xyztpxt/6/

Angular directive doesn't work on table

I have the following table:
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<double-rows></double-rows>
<!--/directive template should be here-->
</table>
The directive is defined as:
angular.module('app', []);
angular.module('app').directive('doubleRows', function () {
return {
templateUrl: 'template.html',
restrict: 'E',
scope: {
row: '='
}
}
});
As simple as it looks, it doesn't render properly. e.g:
<double-rows class="ng-isolate-scope">
Cell1
Cell2
</double-rows>
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<!--/directive template should be here-->
</table>
You can see the full code in here: https://plnkr.co/edit/0Z65aK?p=preview
How to make it work?
This isn't exactly an angular problem but more to do with how the browser decides to interpret the HTML you wrote. Certain tags aren't allowed to be nested in other tags, this is particularly prevalent within lists (ul, or li) and tables (also applies to nested links and a few other things).
Instead of using restrict E if you use restrict A and use an element type that is allowed in the table then it works (I also used replace:true on your directive so it replaces the original element it's applied to instead of being a child of it)
https://plnkr.co/edit/fgRzZU?p=preview
angular.module('app').directive('doubleRows', function () {
return {
templateUrl: 'template.html',
restrict: 'A',
replace:true,
scope: {
row: '='
},
controller: function () {
console.log('hi');
}
}
});
The HTML
<table>
<thead>
<tr>
<th>Uno</th>
<th>Dos</th>
</tr>
</thead>
<!--directive template should be here-->
<tbody double-rows>
</tbody>
<!--/directive template should be here-->
</table>

How do i append my attribute to a element in my Angular directive

I have a table in a template I want to populate with different data. My approach is using directives in Angular. I managed to make a template out of my table but I have no idea how to apply the value for the ng-repeat attribute from my html.
Here's a part of my index.html
<div id='unannounced' kc-item-table>
</div>
And here's a part of my template
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='item in changableItemList'>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
</tr>
</tbody>
</table>
Heres my directive
app.directive('kcItemTable', function() {
return {
restrict: 'E',
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})
So in order to reuse the template I want to be able to change the
ng-repeat='item in itemList'
But I have no idea how to append it to right element.
Here is the simple explaination with your code./
Your html template -
<table class='table'>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='item in changableItemList'>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
</tr>
</tbody>
</table>
Directive-With an isolate Scope
app.directive('kcItemTable', function() {
return {
restrict: 'E',
scope :{
itemList :'='
},
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})
You can use directive with different list --
<div id='unannounced' kc-item-table item-list='ItemList1'>
</div>
<div id='unannounced' kc-item-table item-list='ItemList2'>
</div>
What you are trying to do is a very basic feature of AngularJS: data-binding to directives.
Check out the documentation about directives: https://docs.angularjs.org/guide/directive
Here is a very basic example forked from the above docs:
Main template:
<div my-customer name="naomi"></div>
<div my-customer name="boby"></div>
Directive:
.directive('myCustomer', function() {
return {
scope: {
name: "#"
},
template: 'Name: {{name}}'
};
});
http://plnkr.co/edit/r9tIzwxCFyEyAU3NX0G1?p=preview
To clarify, what you need in your case is a "scope" property on your directive. You will be able to pass the scope values through the DOM element attributes.
Thats easy just add this to your div where you add your attribute directive.
<div ng-controller="YourCustomController" id='unannounced' kc-item-table>
</div>
then in YourCustomController you would put a $scope property called.
$scope.changableItemList;
Or if you want multiple of these directives on the same page you can work with an isolated scope and do :
<div id='unannounced' kc-item-table customList='customList2'/>
<div id='unannounced' kc-item-table customList='customList1'/>
and in your directive do:
//you will need a controller above this which has $scope.customList declared
app.directive('kcItemTable', function() {
return {
restrict: 'E',
scope :{
customList :'=' //isolated scope with customList passed in
},
templateUrl: 'scripts/controllers/itemTableTemplate.html'
}
})

Table row as directive displays out of table context in angularjs

I have a table and each row is to displayed using a directive and ng-repeat.
Unfortunately, the rows are rendered out of the table context.
Please run the fiddle here: http://jsfiddle.net/nu1tu9qL/4/
The directive:
.directive('myrow', [function () {
return {
restrict: 'E',
replace: true,
scope: {
rowdata: "="
},
template: '<tr><td>{{rowdata.a}}</td><td>{{rowdata.b}}</td></tr>',
};
}]);
The HTML:
<table>
<thead>
<th>A</th>
<th>B</th>
</thead>
<tbody>
<myrow ng-repeat="d in data" rowdata="d"></row>
</tbody>
</table>
Without going deep into Angular's source code to diagnose this issue, I'd say that this is likely an artifact of the fact that you are using an unexpected element within <tbody>. <tbody> accepts only <tr> (for the purpose of this question).
And so, change your myrow directive to be an attribute, and make the template only add <td>s:
<tbody>
<tr myrow rowdata="d" ng-repeat="d in data"></tr>
</tbody>
and the directive:
.directive("myrow", function(){
return {
scope: {
rowdata: "=?"
},
template: "<td>{{rowdata.a}}</td><td>{{rowdata.b}}</td>"
};
});
(you could even use myrow as the scope, rather than rowdata)
EDIT:
For the sake of verification of the assumption above, I reproduced the behavior with the following:
<table>
<tr><th>Column 1</th></tr>
<tbody>
<myrow></myrow>
</tbody>
</table>
and using jQuery (something similar to what Angular would have done):
$(function(){
$("myrow").replaceWith("<tr><td>A</td></tr>");
});
and the result is that the inserted row appearing above <table>, as is the case in the original question. In fact, <myrow> is moved outside of table (in Chrome and IE - not sure if this is browser-specific behavior) even before the replaceWith happens.

limit the character to 10 for filter text-field - ngTable

I have created an application using angularjs and ngTable with filer feature, the application is working fine also the filtering, but the filtering text-field should accept only 10 characters according to my requirement
Can anyone please tell me how to limit the character to 10 for that text-field, my code is as given below:
JSFiddle
script
$scope.tableParams = new ngTableParams({
page: 0,
count: 0
}, {
total: 0,
counts:[],
getData: function ($defer, params) {
// use build-in angular filter
var orderedData = params.filter() ? $filter('filter')(data, params.filter()) : data;
$defer.resolve(orderedData);
}
});
html
<div ng-app="main" ng-controller="DemoCtrl">
<table ng-table="tableParams" show-filter="true" class="table">
<tr ng-repeat="user in $data">
<td data-title="'Name'" filter="{ 'name': 'text' }">{{user.name}}</td>
<td data-title="'Age'">{{user.age}}</td>
</tr>
</table>
</div>
You can't do that easily with the directives provided by ng-table.
In order to achieve that, you must create your own input filterer, apply it on the ng-repeat and use a simple directive which will limit the caracteres of the input field.
Here is a working example, hope it will help you.
fiddle
HTML:
<div ng-app="main" ng-controller="DemoCtrl">
<table ng-table="tableParams" class="table">
<!-- first row with custom input filterer using the charLimit directive -->
<tr>
<td data-title="'Name'"><input char-limit="10" ng-model="textFilter"/></td>
<td data-title="'Age'"></td>
</tr>
<!-- declare the filter on your ng-repeat directive -->
<tr ng-repeat="user in $data | filter: { 'name': textFilter }">
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</table>
</div>
DIRECTIVE:
directive('charLimit', function(){
return {
scope: {
limit: '#charLimit',
model: '=ngModel'
},
require: 'ngModel',
restrict: 'A',
link: function($scope, $node) {
var limit = $scope.limit ? parseInt($scope.limit, 10) : 0;
$scope.$watch('model', function(val) {
if(limit>0 && !isNaN(limit))
$scope.model = $scope.model.slice(0,limit);
});
}
};
})
EDIT
An html5 attribute called maxlength can do it for you so you can avoid the directive and simply add this attribute to your input field

Resources