AngularJS Dynamic TemplateUrl Value - javascript

I'm trying to pass an already dynamic value to my directive to fetch a templateUrl for this value. Let me explain with some Sourecode:
<p ng:repeat="cell in field.Cells">
<cell-element handler="{{cell.handler}}"/>
<!-- cell.handler is e.g. "User" -->
</p>
myapp.directive('cellElement', function() {
return {
restrict: 'E',
templateUrl: function (tElement, tAttrs, $compile) {
return '/ajax/' + tAttrs.handler == undefined ? 'foo' : tAttrs.handler +'.html';
},
}
});
Unfortunately, the value of tAttrs.handler is always the literal expression "{{cell.handler}}" instead of the respective value. I tried so many different ways - any guess?
UPDATE:
myapp.directive('cellElement', function() {
return {
restrict: 'E',
scope: { handler: '=handler' },
template: '<ng-include src="\'/ajax/\' + handler"></ng-include>'
}
});
As workaround i used another approach that works. But i would prefer the initial way by using the templateUrl function as i e.g. want to check if "handler" is a valid value.

I think that this alternative can work:
myapp.directive('cellElement', function() {
return {
restrict: 'E',
template: '<div data-ng-include="templateUrl"></div>',
link: function ($scope, iElement, attr) {
$scope.templateUrl= $scope[attr.handler];
}
}
And you have to cosume without backets:
<p ng:repeat="cell in field.Cells">
<cell-element handler="cell.handler"/>
<!-- cell.handler is e.g. "User" -->
</p>

The thing is that probably the template is not compiled, so the value you want is not yet interpolated. If you tried to retrieve it in the link function, it would work.

Related

Using service inside directive?

I am learning how to create custom directives.
My service looks like that:
myApp.service('myService',function(){
this.myFunction=function(myParam){
// do something
}
});
Here is my directive:
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="myService.myFunction(param)">Do action</button>',
}
});
In HTML, when I use <my-directive my-param="something"></my-directive> it properly renders as a button. However when I click it, myService.myFunction, doesn't get executed.
I suppose I am doing something wrong. Can someone give me a direction?
I guess this has something to do with the directive's scope.
The service wont be available directly inside the template. You'll have to use a function attached to the directive's scope and call the service function from within this function.
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="callService(param)">Do action</button>',
link: function(scope, element, attrs) {
scope.callService = function() {
myService.myFunction();
}
}
}
});
It doesn't work because in your example a directive doesn't actually know what is myService. You have to explicitly inject it e.g.:
myApp.directive('myDirective', ['myService', function(myService){ ... }]);
See also this question or this question.
You should use a controller to do all DOM-modifications.
See this plunkr: https://plnkr.co/edit/HbfD1EzS0av5BG6NgtIv?p=preview
.directive('myFirstDirective', [function() {
return {
'restrict': 'E',
'controller': 'MyFirstController',
'controllerAs': 'myFirstCtrl',
'template': '<h1>First directive</h1><input type="text" ng-model="myFirstCtrl.value">'
};
}
You can inject the service in the controller and then call that function inside your template:
Inject myService into controller:
myApp.controller("ctrl", function($scope, myService) {
$scope.doService = function(myParam) {
return myService.myFunction(myParam);
};
});
Call doService method of the controller inside your template:
myApp.directive('myDirective',function(){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="doService(param)">Do action</button>',
}
});

Angular | Directive not passing correct value back

Hi i have created a directive and it does not pass back the correct message.
The directive is used to pass tool-tips back to the html page
this is what the html looks like
<info-text info-msg="Adding another applicant might help you to get approved."></info-text>
below is the directive
(function(){
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '&infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoText}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
}());
the message i get rendered on the page is as follows (it does send the glyphicon):
{{infoText}}
Any ideas,
thanks. Kieran.
You should not use & for this sort of binding, basically it is used for expression binding. I think one way binding (#) is efficient for what you are doing.
Also you should change directive template {{infoText}} to {{infoMessage}}
Markup
<info-text
info-msg="{{'Adding another applicant might help you to get approved.'}}"></info-text>
Directive
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '#infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoMessage}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
And making more cleaner and readable html you could place that string into some scope variable and pass that scope variable in info-msg attribute

AngularJs: Iterating over array inside directive in order to create nested set of directives

Inside angularJS directive I'm trying to iterate over array and based on values I would like to create nested list of directives.
Current version of directive
Directive type
.directive("type", function($compile, $log){
return{
restrict: "E",
replace: true,
transclude: true,
scope: {
type: '='
},
template: "<div></div>",
link: function(scope, element, attrs){
if (angular.isArray(scope.type)){
angular.forEach(scope.type, function(value, index){
$log.error(value);
element.append("<type type='scope.type['"+index+"]'></type>");
});
} else if (angular.isObject(scope.type)){
element.append("OBJECT")
} else {
element.append("<div>{{scope.type}}</div>")
}
$compile(element.contents())(scope)
}
};
})
I also tried to use above directive with next version of link function:
if (angular.isArray(scope.type)) {
element.append("<div ng-repeat='element in scope.type'><type type='element'></type></div>");
} else if (angular.isObject(scope.type)) {
element.append("OBJECT")
} else {
element.append("<div>{{scope.type}}</div>")
}
$compile(element.contents())(scope)
}
None of provided codes solve my issue.
Below you will find example explaining on specific case:
Let's say that I had next object in the scope.type = [null,"int"]. Now I would like to use <type type='type'><type> and as a result of first evaluation I want to have sth like:
<type type='type[0]'></type><type type='type[1]'></type>
Further evaluation of those values should lead to some simpler form but right now it is not important.
How I can achieve sth like this?
Edit
I tried even to exctract part of the code responsible for iteration to the seperate directive but it still does not work. Code:
Update link function in type directive:
link: function(scope, element, attrs) {
if (angular.isArray(scope.type)) {
element.append("<typeb type='scope.type'></typeb>")
} else if (angular.isObject(scope.type)) {
element.append("OBJECT")
} else {
element.append("<div>{{scope.type}}</div>")
}
$compile(element.contents())(scope)
}
New directive:
.directive("typeb", function($compile, $log){
return{
restrict: "E",
replace: true,
transclude: true,
scope: {
type: '='
},
template: "<div ng-repeat='t in type'>{{t}}</div>",
};
})
Problem still occurs but generated html contains only next pieces as a result of typeb directive:
<!-- ngRepeat: t in type -->
The problem you are getting is <!-- ngRepeat: t in type --> this is because your type didn't contains any value, when it is inside typeb directive
Your directive shouldn't be use scope.variable on view.
Scope variable will be directly accessible by their name like
{{type}} or <typeb type='type'></typeb>
Change your link code to below.
Directive link
link: function(scope, element, attrs) {
if (angular.isArray(scope.type)) {
element.append("<typeb type='type'></typeb>")
} else if (angular.isObject(scope.type)) {
element.append("OBJECT")
} else {
element.append("<div>{{type}}</div>")
}
$compile(element.contents())(scope)
}
Thanks.
Use an ng-repeat in the template
<your-directive attr="item.someattr" ng-repeat="item in items"></your-directive>
.directive("type", function($compile, $log){
return{
restrict: "E",
replace: true,
transclude: true,
scope: {
type: '='
},
template: "NG REPEAT HERE",
...
})

AngularJS directive to directive communication throwing an error about controller not found

I have 2 directives, one for searching and one for pagination. The pagination directive needs to access the search directive to find out what property we're currently searching by. When I load the page though, it throws an error saying Error: [$compile:ctreq] Controller 'search', required by directive 'pagination', can't be found!. However I have a controller setup in my search directive.
Here is my search directive:
angular.module('webappApp')
.directive('search', function ($route) {
return {
templateUrl: 'views/search.html',
restrict: 'E',
scope: {
searchOptions: '=',
action: '=',
currentProperty: '=',
currentValue: '='
},
controller: function($scope) {
$scope.searchBy = $scope.searchOptions[0].text;
$scope.searchByProperty = $scope.searchOptions[0].property;
$scope.setSearchBy = function(event, property, text) {
event.preventDefault();
$scope.searchBy = text;
$scope.searchByProperty = property;
};
$scope.search = function() {
$scope.searching = true;
$scope.currentProperty = $scope.searchByProperty;
$scope.currentValue = angular.element('#searchCriteria').val();
$scope.action($scope.searchByProperty, $scope.currentValue, function() {
$scope.searching = false;
});
};
$scope.reload = function() {
$route.reload();
};
}
};
});
Here is my pagination directive:
angular.module('webappApp')
.directive('pagination', function () {
return {
templateUrl: 'views/pagination.html',
restrict: 'E',
require: '^search',
scope: {
basePath: '#',
page: '=',
sort: '='
},
link: function(scope, element, attrs, searchCtrl) {
console.debug(searchCtrl);
scope.searchByProperty = searchCtrl.searchByProperty;
}
};
});
In order for one directive to use another's controller by use of require, it needs to either share the same element as the controller containing directive, or it has to be a child of it.
You can't use require in the way you have, where the elements are siblings.
Angular docs about directives, including require
If it doesn't make sense to rearrange the DOM in the way I've described, you should inject a service into both directives which contains the data/methods you wish to share between the two.
Note: you could also experiment with the $$nextSibling / $$prevSibling properties of the directives' scopes, but this would present only a very fragile solution
You cannot use require in directive like that, however , since the only thing you need to pass between directives is a string , just bind them to the same property in parent controller (it can be parent directive controller):
...
<div ng-app='app' ng-controller='MyCtrl as ctrl'>
<my-dir-one s1='ctrl.message'></my-dir-one>
<my-dir-two s2='ctrl.message'></my-dir-two>
and first directives:
app.directive('myDirOne', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s1: '=',
second directive
app.directive('myDirTwo', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s2: '=',

Angular JS - Is there a way to pass the directive's attribute to the template

on the Page
<rn-text-edit rn-scope="profile.first_name"></rn-text-edit>
on the js
app.directive("rnTextEdit", function () {
return {
restrict: 'E',
replace: true,
template:'<span>{{'+rn-scope+'}}</span>'
}
});
I know I can replace the DOM and access the attribute through link. I wonder if there is a way of passing the directive's attribute to a template.
If you are just displaying the value:
<rn-text-edit rn-scope="{{profile.first_name}}"></rn-text-edit>
-
app.directive("rnTextEdit", function () {
return {
restrict: 'E',
replace: true,
scope: {
rnScope: '#'
},
template: '<span>{{rnScope}}</span>'
}
});
If the directive needs to modify the value, you could use '=' and skip the double curlies.
fiddle
more info on scope and '#' in the Angular Directives page

Categories