I've created a directive that loads a template,
app.directive('youtubeTrailer', function() {
return {
restrict: 'E',
scope: {
show: '=info'
},
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
templateUrl: '../assets/angular-app/templates/_container-trailer.html',
};
});
This is the ng-click action,
%a{"ng-click" => "toggleModal()"}
Trailer {{$index+1}}
That calls this function,
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
};
And then the directive gets shown,
%youtube-trailer{:info => "modalShown", :show => "modalShown"}
This works fine, but my problem is that the template is shown in the inline code. I would like to retrieve it only when the toggleModal() function has been clicked.
angular.module('test', [])
.directive('customDirective', ['$http', '$templateCache', '$compile', function($http, $templateCache, $compile) {
function getTemplate() {
return '<div>Hello, {{ name }}!</div>';
};
return {
restrict: 'A',
link : function(scope, element, attrs, fn) {
// instead of that you need to load and cache real template via $http
var template = getTemplate();
scope.name = 'world';
element.replaceWith($compile(template)(scope));
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div custom-directive></div>
</div>
You just need to use Angular $http service and load your file manually inside this event handler.
After that you need to compile it and insert to your directive element:
element.replaceWith($compile(template)(scope));
You also can use { cache: $templateCache } setting with $http to cache loaded template when loaded once.
Example: (inside directive)
// you need to have injected following dependencies here:
// $http, $compile, $templateCache
link: function(scope, element, attrs, fn) {
//...
scope.onSomeClick = function() {
$http.get(templateUrl, { cache: $templateCache })
.then(function(template) {
element.replaceWith($compile(template)(scope));
});
};
//...
}
Related
I have a directive (DirectiveA), which makes an $http call and creates a new html code.
directiveA
(function(){
angular.module('app').directive('directiveA', directiveA);
})();
(function(){
angular.module('app').controller('DirectiveAController', DirectiveAController);
})();
function directiveA($timeout){
return {
restrict: 'E',
scope: {
url:'#'
},
template: '<div ng-if="template" ng-bind-html="template"></div>',
link: function ( scope, element, attrs ) {
scope.element = element;
},
controller: DirectiveAController
};
}
directiveA.$inject = ['$timeout']
function DirectiveAController($scope, $http, $sce){
$http.get(`${$scope.url}`).then(function(res){
if(res.success){
$scope.template = $sce.trustAsHtml(res.template);
}
});
}
DirectiveAController.$inject = ['$scope', '$http','$sce'];
this works fine.
On the new created element, i want to capture the click function using another directive.
Directive 2
(function(){
angular.module('mcq').directive('captureClick', captureClick);
})();
function captureClick($timeout, $compile){
return {
link: function ( scope, element, attrs ) {
console.log("i am called") // Working on page load but not on dynamic element
scope.element = element;
},
};
}
captureClick.$inject = ['$timeout', '$compile'];
response.template
<button capture-click></button>
Rendered a dummy element of response.template (as static content) and the directive works. How can i get it work on dynamically rendered element.
Using compile my directive is picked up.
(function(){
angular.module('app').directive('directiveA', directiveA);
})();
(function(){
angular.module('app').controller('DirectiveAController', DirectiveAController);
})();
function directiveA($timeout){
return {
restrict: 'E',
scope: {
url:'#'
},
template: '',
link: function ( scope, element, attrs ) {
scope.element = element;
},
controller: DirectiveAController
};
}
directiveA.$inject = ['$timeout']
function DirectiveAController($scope, $http, $sce, $compile){
$http.get(`${$scope.url}`).then(function(res){
if(res.success){
var com = $compile(res.template)($scope);
$scope.element.append(com[0].outerHTML);
}
});
}
DirectiveAController.$inject = ['$scope', '$http','$sce', '$compile'];
Note sure, this is the correct way and has any cons
In a directive i want to require a controller but i get the error that the controller can't be found. I am sure it is a small thing or maybe it is not possible the way i want to do it.
angular.module('myApp', []);
angular.module('myApp').controller('GreetingController', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
//some function here returning data
}]);
angular.module('myApp').directive('yoloswag', function() {
return {
require: ['^?ngModel', '^GreetingController'],
restrict: 'A',
scope: {
},
link: function(scope, element, attrs, controllers) {
var modelCtrl = controllers[0],
greetingsCtrl = controllers[1];
console.log(controllers)
}
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="GreetingController">
{{ greeting }}
<div yoloswag>test</div>
</div>
</div>
What am i doing wrong?
Thank you so much!
Your code does not having and dependency with your main module, that's why you are getting that error.
Your code should be the following
angular.module('myApp', [])
.controller('GreetingController', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
//some function here returning data
}])
.directive('yoloswag', function() {
return {
require: ['^?ngModel', '^GreetingController'],
restrict: 'A',
scope: {
},
link: function(scope, element, attrs, controllers) {
var modelCtrl = controllers[0],
greetingsCtrl = controllers[1];
console.log(controllers)
}
};
});
or set a variable for main module then you can add controller and directive with the main module like,
var MainModule = angular.module('myApp', []);
MainModule.controller('GreetingController', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
//some function here returning data
}]);
MainModule.directive('yoloswag', function() {
return {
require: ['^?ngModel', '^GreetingController'],
restrict: 'A',
scope: {
},
link: function(scope, element, attrs, controllers) {
var modelCtrl = controllers[0],
greetingsCtrl = controllers[1];
console.log(controllers)
}
};
});
You can use controller property of directive :
make sure don't update property in link function.
return {
restrict: 'A',
scope: {
},
controller : 'GreetingController',
link: function(scope, element, attrs) {
var modelCtrl = controllers[0],
greetingsCtrl = controllers[1];
console.log(controllers)
}
};
I wrote a plunker to see how to use bindToDirective to isolate scopes and using directive controller to call main controller function, but, I am doing something wrong. Could you suggest?
This is the plunker: http://plnkr.co/edit/UJLjTmIiHydHr8qRzAsX?p=preview
Code sample:
.controller('Ctrl', function() {
var self = this;
self.func = function() {
console.log('In the controller function');
};
})
.directive('myDirective', [ function() {
var self = {};
self.link = function (scope, elem, attrs, ctrl) {
elem.bind('click', function () {
ctrl.ctrlFunc();
});
elem.addClass('fa fa-file-excel-o fa-lg');
};
return {
restrict: 'E',
scope: {},
controller: function () {
},
controllerAs: 'DirCtrl',
bindToController: {
ctrlFunc: '&'
},
link: self.link
};
}])
html sample to associate main controller function to directive:
<div ng-controller="Ctrl">
<my-directive ctrlfunc="Ctrl.func()"></my-directive>
</div>
You have a number of issues:
You need a hyphen in your directive argument name and you should be passing the function reference, not calling the function directly (with params):
<my-directive ctrl-func="ctrl.func"></my-directive>
Second, you are using alias syntax in your controller (var self = this;), but not in your template. You need to update it to the following:
<div ng-controller="Ctrl as ctrl">
<my-directive ctrl-func="ctrl.func"></my-directive>
</div>
Finally, pass down the function reference with two-way binding instead of with & since that passes down values for implicit evaluation.
bindToController: {
ctrlFunc: '='
},
See working plunkr
I'm not sure you need bindToController...
This version calls your Ctrl's function: http://plnkr.co/edit/Rxu5ZmmUAU8p63hR2Qge?p=preview
JS
angular.module('plunker', [])
.controller('Ctrl', function($scope) {
$scope.func = function() {
console.log('In the controller function');
};
}) angular.module('plunker', [])
.controller('Ctrl', function($scope) {
$scope.func = function() {
console.log('In the controller function');
};
})
.directive('myDirective', [ function() {
return {
template: '<pre>[clickme]</pre>',
replace: true,
restrict: 'E',
scope: {
target: '&'
},
link: function (scope, elem, attrs) {
elem.bind('click', function () {
var fn = scope.target && scope.target(scope);
fn && fn();
});
elem.addClass('fa fa-file-excel-o fa-lg');
}
};
}])
HTML
<div ng-controller="Ctrl">
<my-directive target="func"></my-directive>
</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;
});
In documentation I can read next for the require option:
When a directive uses this option, $compile will throw an error
unless the specified controller is found. The ^ prefix means that this
directive searches for the controller on its parents (without the ^
prefix, the directive would look for the controller on just its own
element).
So I try to use it:
<div ng-sparkline></div>
app.directive('ngCity', function() {
return {
controller: function($scope) {}
}
});
app.directive('ngSparkline', function() {
return {
restrict: 'A',
require: '^ngCity',
scope: {},
template: '<div class="sparkline"><h4>Weather </h4></div>',
link: function(scope, iElement, iAttrs) {
// get weather details
}
}
});
But I have an error if my html have not ng-city attribute, so if I need controller of another directive - need to add exactly same attribute in html, but why (<div ng-sparkline ng-city="San Francisco"></div>)? And it looks on another directive's controller with this name (directive!!!) but not at controller with this name, is that true? Thanks. Just want to make it clear
With require you can get the controller of another (cooperating) directive. The controller in Angular is not semantically a function, but an object constructor, i.e. called essentially as var c = new Controller() (this is a simplification for the sake of clarity). Since the controller is an object, it can have properties and methods. By requiring the controller of another directive, you gain access to those properties/methods. Modifying your example to demonstrate:
app.directive('ngCity', function() {
return {
controller: function($scope) {
this.doSomething = function() {
...
};
}
}
});
app.directive('ngSparkline', function() {
return {
restrict: 'A',
require: '^ngCity',
scope: {},
template: '<div class="sparkline"><h4>Weather </h4></div>',
link: function(scope, iElement, iAttrs, ngCityController) {
// use the controller, e.g.
ngCityController.doSomething();
}
}
});
In your case, the city would be a property of the controller of the ngCity directive, exposed as a property. It will be read by the ngSparkline to know for which city the graph is about.
<b> added directives.js</b>
<code>
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngCity',
scope: {
ngCity: '#'
},
templateUrl: '/scripts/templates/tpl.html',
controller: ['$scope', '$http', function ($scope, $http) {
var url = "https://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=imperial&cnt=7&callback=JSON_CALLBACK&q=";
console.log(url + $scope.ngCity);
$scope.showTemp = function () {
$scope.getTemp($scope.ngCity);
};
$scope.getTemp = function (city) {
$http({
method: 'JSONP',
url: url + city
}).success(function(data) {
var weather = [];
angular.forEach(data.list, function(value){
weather.push(value);
});
$scope.weather = weather;
});
}
}],
link: function (scope, iElement, iAttrs, ctrl) {
scope.getTemp(iAttrs.ngCity);
scope.$watch('weather', function (newVal) {
if (newVal) {
var highs = [];
angular.forEach(scope.weather, function (value) {
highs.push(value.temp.max);
});
//chartGraph(iElement, highs, iAttrs);
}
});
}
}
}).directive('ngCity', function () {
return {
controller: function ($scope) {
//console.log("hello");
}
}
});
</code>
<b> and added tpl.htm</b>
<code>
<div class="sparkline">
<input type="text" data-ng-model="ngCity">
<button ng-click="showTemp()" class="btn1">Check {{ngCity}}</button>
<div style="color:#2743EF">{{weather}} ÂșC</div>
<div class="graph"></div>
</div>
</code>