I'm trying to pass in the url for the template via a scope variable. The scope will not change so the template doesn't need to update based on it, but currently the scope variable is always undefined.
<div cell-item template="{{col.CellTemplate}}"></div>
Ideally the directive would be:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
templateUrl: template // or {{template}} - either way
};
}])
This doesn't work however. I've tried a lot of different permutations in accomplishing the same concept, and this seems the closest, however it still doesn't work.
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
link: function (scope, element, attrs) {
var templateUrl = $parse(attrs.template)(scope);
$http.get(templateUrl, { cache: $templateCache }).success(function (tplContent) {
element.replaceWith($compile(tplContent)(scope));
});
}
};
}])
I've also tried using ng-include, but that also doesn't evaluate scope variables before compiling. The CellTemplate value is coming from a database call so is completely unknown before evaluation. Any suggestions for getting this working would be greatly appreciated!
Edit:
I'm using angular 1.0.8 and am not able to upgrade to a newer version.
You are not far off at all.
You don't need to use an isolated scope for the directive. You can pass the templateUrl like this:
<div cell-item template="col.CellTemplate"></div>
Then add a watch to detect when the template value changes:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
restrict: 'A',
link: function(scope , element, attrs) {
scope.$watch(attrs.template, function (value) {
if (value) {
loadTemplate(value);
}
});
function loadTemplate(template) {
$http.get(template, { cache: $templateCache })
.success(function(templateContent) {
element.replaceWith($compile(templateContent)(scope));
});
}
}
}
}]);
Here is a working Plunker: http://plnkr.co/edit/n20Sxq?p=preview
If you don't want to deal with the linking logic yourself, or you want the isolate scope, I think this is simpler:
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
scope: {
template: '#template'
},
template: "<div ng-include='template'></div>"
};
}])
or:
template:"<ng-include src='template'></ng-include>"
It's an old post but I thought its useful if anyone lands in here for the answer.
You can try the templateUrl function as #caub mentioned in a comment. Same can also be used for components.
.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
templateUrl: function(element, attrs) {
return attrs.template || 'someDefaultFallback.html';
}
};
}]);
We don't need any of the injected dependencies here. Hope this helps someone.
Related
I have a isolated directive, my controller looks like:
app.controller('ZacksController', ['$scope', '$http', 'ngDialog', '$timeout', function($scope, $http, ngDialog, $timeout){
//some code here
}]);
The HTML in the file looks like:
<div class="income-older-block" ng-show="selectedAge!=1">
<income-form></income-form>
</div>
I have a directive in related HTML folder,
app.directive("incomeForm", ['$timeout', function ($timeout) {
function link($scope) {
var hello = function () {
alert("1");
}
$timeout(hello, 0);
}
return {
restrict: 'E',
templateUrl: "app/zacks/your-income/income-form/income-form.html",
link: link,
controller: function ($scope, $timeout) {
$scope.$watch("zacks.AgeRet.value",function(newValue,OldValue,scope){
if (newValue){
alert((newValue));
}
});
}
}
}]);
I want to alert after I load the directive in the page, the alert appears at initial page itself. May I know what to do?
The actual problem is I'm using a rz-slider and want to initialize it once the directive is loaded to DOM., as its not taking the values provided. Is there any other approach for this problem?
<rzslider rz-slider-model="zacks.AgeRet.value" rz-slider-floor="zacks.AgeRet.floor" rz-slider-ceil="zacks.AgeRet.ceil"></rzslider>
In case if the timeout works, I'm planning to initialize something like this:
$timeout(function () {
$scope.$broadcast('rzSliderForceRender');
});
UPDATE
Added a controller in the directive, so now I'm able to get the value when I move the slider, but still not able to initialize the value of the slider.
I kind of found 2 solutions for these kind of problems, but still not serving the purpose of what I really need with rz-slider.
Solution 1
HTML:
<div ng-controller="ZacksController">
<after-render after-render="rzSliderForceRender">element</after-render>
</div>
JS:
var app = angular.module('myApp',[]);
app.directive('afterRender', ['$timeout', function ($timeout) {
var def = {
restrict: 'E',
link: function (scope, element, attrs) {
$timeout(scope.$eval(attrs.afterRender), 0); //Calling a scoped method
}
};
return def;
}]);
app.controller('ZacksController', ['$rootScope', '$scope', '$http', ' $timeout', function($rootScope, $scope, $http, $timeout){
$scope.rzSliderForceRender = function()
{
alert('Fired!');
};
}]);
Solution 2
HTML:
<div class="income-older-block" ng-show="selectedAge!=1">
<income-form></income-form>
</div>
JS:
app.controller('ZacksapiController', ['$rootScope', '$scope', '$http', 'ngDialog', '$timeout', 'dataService', function($rootScope, $scope, $http, ngDialog, $timeout, dataService){
$scope.$watch('selectedAge', function(newValue, oldValue) {
if (newValue !== oldValue) {
$timeout(function() {
alert("reCalcViewDimensions");
$scope.$broadcast('rzSliderForceRender'); // This is not working, but alert works.
}, 0);
}
});
Update:
Triggered the window resize event inside the $timeout, but this would be a temporary hack. Would be great if someone help me out with the real approach to solve the problem.
$timeout(function() {
window.dispatchEvent(new Event('resize'));
$scope.$broadcast('rzSliderForceRender');
}, 0);
I want to be able to use the variable "videoUrlId" from the controller 'useSpreadsheetData' below in my directive 'meetings'. How can I do this? I have looked at require but could not get it to work.
Controller:
app.controller('useSpreadsheetData', ['$scope', '$sce', 'getSpreadsheetData',
function($scope, $sce, getSpreadsheetData){
for(var x in videos) {
if(videos[x].switchValue) {
var videoUrlId = videos[x].videoUrl;
$scope.videoUrlId = videoUrlId;
break;
}
}
};
Directive:
app.directive('meetings', [ 'getCalendar', '$timeout', '$window',
function(getCalendar, $timeout, $window){
return {
restrict: 'E',
templateUrl: 'scripts/directives/meetings.html',
controller: 'useSpreadsheetData',
link: function(scope){
//Use videoUrlId variable here
}
}
}]);
Since you mentioned you attempted to use require, I have to assume the meetings directive will be a child element somewhere within the useSpreadsheetData controller, however without seeing your HTML, we can't be sure.
As you're not utilizing an isolate scope, your directive will prototypically inherit from the parent controller above itself, in this case useSpreadsheetData. As a result, we can simply get videoUrlId by accessing it via an interpolated expression: {{videoUrlId}}. Note the template in the meetings directive. It'll also be available within link or controller via scope.videoUrlId and $scope.videoUrlId, respectively.
Plunker: http://plnkr.co/edit/MZIgXEiku4Z2PLzl3apz
HTML
<div ng-controller="useSpreadsheetData">
Controller: <code>videoUrlId = {{videoUrlId}}</code><br>
Directive: <meetings></meetings>
</div>
JavaScript
app.controller('useSpreadsheetData', function($scope) {
var videos = [service call];
for (var x in videos) {
if (videos[x].switchValue) {
var videoUrlId = videos[x].videoUrl;
$scope.videoUrlId = videoUrlId;
break;
}
}
});
app.directive('meetings', ['$timeout', '$window',
function($timeout, $window) {
return {
restrict: 'E',
template: '<code>videoUrlId = {{videoUrlId}}</code>'
}
}
]);
Output
Controller: videoUrlId = /1
Directive: videoUrlId = /1
Hi I am trying to learn AngularJS Directives and I came really close but would like to extend my learning by cleaning and de-coupling my directive code.
Directive:
app.directive('ngSparkline', function () {
var url = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=imperial&cnt=14&callback=JSON_CALLBACK&q=";
return {
restrict: 'A',
require: '^ngCity',
transclude: true,
scope: {
ngCity: '#'
},
templateUrl: 'app/partials/weatherTemplate.html',
controller: ['$scope', '$http', function($scope, $http) {
$scope.getTemp = function(city) {}
}],
link: function (scope, iElement, iAttrs) {
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);
}
});
}
};
});
As you can see I am not trying to write inline template rather use templateUrl. Now the problem is for the controller when I try using a .js controller instead of writing the controller code inline, I receive an error. How do I achieve this.
I tried:
I tried passing
controller: '#',
name: 'ctrl'
and I pass the 'ctrl' as:
<div ng-sparkline ng-city="San Francisco" ctrl="weatherController"></div>
it gives me controller not found. My project structure is something like below.
What am I doing wrong?
Is there a better/correct way of doing this?
Please suggest.
Note: I am learning this exercise from "http://www.ng-newsletter.com/posts/directives.html"
Why not just specify ng-controller on your element? If WeatherController is defined somewhere else then it doesn’t affect your directive definition, just leave the controller out of there.
<div ng-sparkline ng-city="San Francisco" ng-controller="WeatherController"></div>
Provided somewhere you do have the controller defined like
app.controller('WeatherController', ['$scope', '$http', function ($scope, $http) {
$scope.getTemp = // …
}]);
(BTW, if you noticed, the AngularJS convention is to name controllers in UpperCase fashion.)
I want to be able to specify the controller of my directive with an inline function, but I also want to use ng-strict-di. What syntax is required to do this?
(function(){
angular.module("myAngularModule")
.directive("myDirective", function(){
return {
restrict: 'E',
templateUrl: "templates/my-template.html",
link: function ($scope, element, attrs) {
// ...
},
// This causes an ng-strict-di exception because I'm using implicit annotation for the dependencies - what is the correct syntax?
controller: function($scope, myService) {
// ...
}
};
})
// This syntax is fine
.controller("myWorkingController",["$scope","myService", function($scope, myService){
// ...
}]);
});
Just because the controller is anonymous doesn't meant the syntax changes. Pass an array like you would any other controller assignment. Angular will understand.
controller: ["$scope","myService", function($scope, myService){
// ...
}]
Inject the service in to your directive as, its something like injecting into the controller,
.directive("myDirective", function(myService){
and remove it from the controller.
.directive("myDirective", function(myService){
return {
restrict: 'E',
templateUrl: "templates/my-template.html",
link: function ($scope, element, attrs) {
// ...
},
controller: function($scope) {
// ...
}
};
})
then myService can be access in the controller of the directive.
Are there cases in which link is called more than once for a controller?
angular.module('theApp').directive('theDirective', ['$http', '$interval', '$q', function($http, $interval, $q) {
/* ... */
return {
scope: {
onselect: "&onselect"
},
link: function (scope, element, attrs) {/*...*/},
controller: function ($scope) {/*...*/},
templateUrl: function(element, attr) {
return "/template.html";
},
};
}]);
So far have not noticed more calls to link than controller. But I am worried it could happen, do I need to add counter mesures in case link was called more than once for a controller?
Yes. Link can be called more than once if you are using ng-if or ng-switch.