I've wrote a directive that should emulate the AngularJS-DataTable.
In this case I need to execute some function on the last <td> since they're buttons. I don't want to pass the functions to the directive to keep the directive as independet as possible.
So in this case, when I specify "renderable" on a data, and a "render" function, if it got a ng-click I need that function, defined in the controller, to be executed, but when i Click on the buttons, nothing happens.
This is the data I've in my Controller, with the function "print()" that I need to call from the directive
$scope.print = function(){
console.log("It worked!");
};
$scope.tableData = {
data: data.response,
columns: [{
title:"",
data: "priority",
renderable: true,
render: function(data){
return "<span class='btn btn-xs fa fa-fw fa-angle-down' ng-click='lowerPriority()'></span>";
}
},
{
title: "Nome Servizio",
data: "title"
},
{
title: "Descrizione",
data: "description",
renderable: true,
render: function(data, row){
var html = "<div ng-click='print()'>"+row.sum+"</div>";
return html;
}
},
],
}
In my page I'm calling
<smart-table data="tableData" ></smart-table>
And then in my directive template
<tr ng-repeat="row in data.data | filter: search.value" repeat-done>
<td ng-repeat="cell in data.columns">
<span ng-if="cell.renderable" ng-bind-html="trustHtml(cell.render(row[cell.data], row))"></span>
<span ng-if="!cell.renderable">{{row[cell.data]}}</span>
</td>
</tr>
Lastly, this is my directive
var smartTable = angular.module('smartTable', ['ngSanitize']);
smartTable.directive('smartTable',['$compile', '$sce', '$templateRequest', function($compile, $sce, $templateRequest) {
return {
restrict: 'AE',
replace: true,
templateUrl: '/public/components/directives/smartTable.tpl.html',
link: function(scope, elem, attrs, parentScope) {
scope.trustHtml = function(data){
var template = angular.element(data);
elem.append(template);
// $compile(angular.element(data))(scope);
return $sce.trustAsHtml(data);
};
$templateRequest('/public/components/directives/smartTable.tpl.html').then(function(html){
console.log(scope);
scope.$watch(attrs.data, function(elemasd) {
var template = angular.element(html);
elem.append(template);
elem.html(html);
scope.data = scope[attrs.data];
$compile(elem)(scope);
});
});
}
};
}]);
Ater your template slightly to use $last, if you're looking for the last td:
<td ng-if="$last" ng-click="vm.print()"></td>
Do something like this bind your function
smartTable.directive('smartTable',['$compile', '$sce', '$templateRequest', function($compile, $sce, $templateRequest) {
return {
restrict: 'AE',
replace: true,
scope: {data: '=',
print: '&'},
templateUrl: '/public/components/directives/smartTable.tpl.html',
link: function(scope, elem, attrs, parentScope) {
scope.trustHtml = function(data){
var template = angular.element(data);
elem.append(template);
// $compile(angular.element(data))(scope);
return $sce.trustAsHtml(data);
};
$templateRequest('/public/components/directives/smartTable.tpl.html').then(function(html){
console.log(scope);
scope.$watch(attrs.data, function(elemasd) {
var template = angular.element(html);
elem.append(template);
elem.html(html);
scope.data = scope[attrs.data];
$compile(elem)(scope);
});
});
}
};
}]);
HTML
<smart-table data="tableData" print="print"></smart-table>
Related
I have two different module in which I have two directives. I need to pass data between these two directive. I use require property. but I get some error
Error: [$compile:ctreq] Controller 'yearSort', required by directive 'budgetSort', can't be found!
My first directive is
angular.module('movieApp.yearsort.directives', []).directive('yearSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'YearsortController',
templateUrl : 'app/components/yearsort/yearsort.html',
};
}]);
In the YearsortController I have the code
angular.module('movieApp.yearsort.controller', []).controller('YearsortController', ['$scope','HomeFactory','$timeout','$state',function($scope,HomeFactory,$timeout,$state) {
this.sayHello = function() {
$scope.words = "my requier";
console.log( $scope.words);
};
}]);
In my second directive I have the code
angular.module('movieApp.budgetsort.directives', []).directive('budgetSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'BudgetsortController',
templateUrl : 'app/components/budgetSort/budgetSort.html',
require : "yearSort",
link : function(scope,element, attrs, demoCtrl){
demoCtrl.sayHello();
}
};
}]);
Why don't you try using a Service/Factory? It is a good option when you need to pass data through components or directives
I've made this plunkr to explain: http://plnkr.co/edit/V7BLbOrrtNhXl1QlUKxA?p=preview
HTML:
<body ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>
Javascript:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
The Demo
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>
I want to send that to my directive but I want that data to stay updated if the data in the controller changes.
// Controller
angular
.module('app')
.controller('IndexController', IndexController)
IndexController.$inject = [];
function IndexController() {
var vm = this;
vm.name = 'John';
newName = function() {
vm.name = 'Brian';
}
newName();
}
// Directive
angular
.module('app')
.directive('userName', userName);
userName.$inject = ['$document'];
function userName($document) {
var directive = {
restrict: 'EA',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.data);
}
}
return directive;
}
this is how I use the directive. the problem is that it always returns the first name and not the new name after the change in the controller.
<div ng-controller="indexController">
<user-name name="indexController.name">
</div>
thank you.
Try this, you just have to inject $scope into your Indexcontroller
angular
.module('app', [])
.controller('IndexController', function($scope) {
var vm = this;
vm.name = 'John';
vm.newName = function() {
vm.name = 'Brian';
console.log(vm.name);
}
//vm.newName();
})
.directive('userName', ['$document', function() {
var directive = {
restrict: 'E',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.name);
}
}
return directive;
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="IndexController as vm">
<user-name name="vm.name"></user-name>
<button ng-click="vm.newName()">Click</button>
</div>
Without using as in controller, you cannot use controller.prop inside the scope.
Inside the controlleryou need to call the method using its $scope or this.
Check the below code.
angular
.module('app', [])
.controller('IndexController', function($scope) {
$scope.name = 'John';
$scope.newName = function() {
$scope.name = 'Brian';
}
$scope.newName();
})
.directive('userName', ['$document', function() {
var directive = {
restrict: 'E',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.name);
}
}
return directive;
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="IndexController">
<user-name name="name"></user-name>
</div>
The value is never shown on the DOM and i'm just trying to see if this way works..
What i am trying to do is create a new scope value inside the directive and show it to the DOM
app.directive("rest", ["restFactory", function($rest){
return {
restrict: "A",
scope: {
uri: "#rest",
},
link: function(scope){
scope.$rest = [];
$rest.batch(scope.uri)
.then(function(data){
scope.$rest = data;
});
}
}
}]);
the data i am trying to show comes from a function that returns a promise, and with the promise comes the data i want to use in the DOM
the HTML is coded like this:
<div rest="accounts">
<div ng-repeat="data in $rest">
{{data.id}}
</div>
</div>
this is the first directive i ever did.
I made this plunker to explain why your directive doesn't work.
<div rest="accounts">
<!-- You can't access the directive variable $rest from here
The directives variables are only available on it's template. -->
<div ng-repeat="data in $rest">
{{data.id}}
</div>
</div>
This will work:
app.directive("restTwo", function() {
return {
restrict: "A",
scope: {},
// It will work. I put the template in here.
// You can only access the directive variables from it's template
template: '<div ng-repeat="data in $rest">{{data.id}}</div>',
link: function(scope, el, attr) {
console.log(attr.rest); // output = accounts
//data
scope.$rest = [{
'id': 1,
'name': 'John Doe'
}, {
'id': 2,
'name': 'Johana Doe'
}];
console.log(scope.$rest);
}
}
});
I suggest you to make a factory and make your api call's in it like this:
app.factory('myFactory', function($http) {
// GET example
this.get = function(string) {
return $http({
method: 'GET',
url: 'https://api.github.com/search/repositories?q=' + string
});
}
// Your request here
this.yourRequest = function(uri) {
// return $rest.batch(uri);
}
return this;
});
And in your controller:
app.controller('MainCtrl', function($scope, myFactory) {
$scope.myData = [];
myFactory.get('tetris').then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
$scope.myData = response.data.items;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
});
View:
<div ng-repeat="data in myData">
{{data.id}}
</div>
If you REALLY want to use a directive for this (I do not recommend):
Directive:
app.directive("restThree", function() {
return {
restrict: "A",
scope: {
'data': '='
},
link: function(scope, el, attr) {
//console.log(attr.rest); // output = accounts
//data
scope.$rest = [{
'id': 1,
'name': 'John Doe'
}, {
'id': 2,
'name': 'Johana Doe'
}];
scope.data = scope.$rest;
}
}
});
View:
<div rest-three="accounts" data="directiveData">
<div ng-repeat="data in directiveData">
{{data.id}}
</div>
</div>
I have a collection of buttons that sort a list of items when clicked:
<div ng-controller="MainCtrl">
<sort-buttons target="filters.sort">
<sort-button></sort-button>
<sort-button></sort-button>
<sort-button></sort-button>
</sort-buttons>
</div>
I want the parent directive to save the results of the buttons to the $scope.filters.sort property on the MainCtrl controller via the target attribute, but how can I actually save to where the target attribute points to?
Here's what I have:
app.directive('sortButtons', [function(){
return {
restrict: 'E',
controller: function($scope, $element, $attrs) {
// this.foo() should save to target
this.foo = function(){
console.log('click');
};
}
}
}]).directive('sortButton', ['Config', function(Config) {
var basePath = Config.get().paths.base;
return {
restrict: 'AE',
replace: true,
require: '^sortButtons',
scope: {
label: '#',
orderBy: '&'
},
templateUrl: basePath + 'js/fantasy/templates/sort-button.htm',
link: function(scope, elem, attrs, ctrl) {
elem.on('click', function(){
ctrl.foo();
});
}
}
}]);
Try $eval on attr.target like
var data = $scope.$eval($attrs.target)
Or if your data is dynamic you can $watch the attr
var data = [];
$scope.$watch($attrs.target, function(newValue, oldValue){
data = newValue;
})
Also correct your controller injection like below, else if you will get error if you minified your source code.
controller: ['$scope','$element','$attrs', function($scope, $element, $attrs) {
var data = $scope.$eval($attrs.target)
this.foo = function(){
console.log('click');
};
}]
What I went with was removing the target attribute altogether, and instead broadcasting on the $rootScope.
Directive:
this.foo = function(){
$rootScope.$broadcast('sortButtons', {
predicate: 'foo',
reverse: false
});
};
Controller:
$rootScope.$on('sortButtons', function(event, data){
$scope.filters.sort = data;
});
iI have this directive :
amDirective.directive('directiveList', function() {
return {
restrict: 'A',
scope: {
'event': '&onEvent'
},
transclude: true,
templateUrl: ''
};
});
and in my page html
<div data-dy-directive-list data-elements="elements" on-event="listItemClicked(item)"></div>
and in my directive- template html
<table class="table" data-ng-show="elements!=null && elements.length>0">
<tr data-ng-repeat="element in elements">
<td><span data-ng-click="event(element)">{{element.title}}</span></td>
</tr>
<tr></tr>
</table>
How can I pass in my directive the complex object "item"?
angular directives use '&' to bind to parent scope expressions.
It means that your directive would evaluate it when an event occurs inside the directive.
Example
app.directive('directiveList', function() {
return {
restrict: 'A',
scope: {
'event': '&onEvent'
},
link: function(scope){
var myItem = {}
scope.event = function(item) {
element.bind('click', function(){
scope.event({item: myItem})
})
}
};
});
If you want to raise an event from the parent scope and let your directive know you should use "="
Example
app.directive('directiveList', function() {
return {
restrict: 'A',
scope: {
'event': '=onEvent'
},
link: function(scope){
scope.event = function(item) {
// manipulate item
return "something";
}
}
};
});
From your directive (either controller or link function) you would call: scope.event({item: item}), passing a named map from argument names to values to provide to the callback.
Example:
amDirective.directive('directiveList', function() {
return {
restrict: 'A',
scope: {
'event': '&onEvent'
},
transclude: true,
templateUrl: '',
link: function(scope, element, attrs) {
element.bind('click', function() {
scope.event({ item: { hello: 'world', x: 3 } });
});
}
};
});
Usage:
<a directive-list on-event="myHandler(item)">Click Me<a>
Here's an example plnkr.
See: Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?