On my website, I want to standardize the way links to other persons look like, so I made a directive for it:
<my-person id="1"></my-person>
and
app.directive('myPerson', function() {
return {
restrict: 'E',
replace: 'true',
template: '{{person.name}}',
scope: {
person_id : '=id',
},
controller: function($scope, Repository) {
Repository.getPerson($scope.person_id).then(function(p) {
$scope.person = p;
});
},
};
});
This works well.
But in the next step, I want users to write news and in those news they want to refer to other persons. So basically, I want to display a text
'I met <my-person id="42"></my-person> yesterday.'
But when angularjs displays this context of my news entry, then the html tags are escaped of course and it is not compiled either.
Is there an easy way to achieve this? Here is a jsfiddle that shows my problem: jsfiddle link
You will need to compile your html inside the ng-repeat block.
For that make a directive like below
app.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
now inside your ng-repeat add your directive to the span like this:
<span class="row" bind-html-compile="newsEntry.text"></span>
working code here
Reference here
Related
Im having a hard time accessing the attributes passed in to my directive from the template of that directive. I want to be able to access 'companyId' from album.tmpl.html but no matter what i try i can't get it. The strangest part is i can see it has made its way in to the controller, but somehow it's not getting from the controller to the template. I know the template is correctly calling the controller as it can succesfully print out the value of 'testVar' which is initialised inside the controller. Any advice would be appreciated.
directive + directive controller
(function () {
'use strict';
angular.module('erCommon')
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective($log) {
function albumLink(scope, element, attrs, AlbumController) {
//watch vars in here
}
return {
restrict: 'E',
scope: {
companyId: '=companyId'
},
bindToController: true,
templateUrl: 'components/temp/album.tmpl.html',
controller: 'AlbumController',
controllerAs: 'albumCtrl',
link: albumLink
};
}
})();
template ( album.tmpl.html
<div ng-controller="AlbumController as albumCtrl">
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
usage
<er-album company-id="2"></er-album>
output
test var: test var initiated
companyId:
You need to remove ng-controller from your template:
<div>
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
To achieve the result you wanted i had to modify the structure of your code slightly. Hope this helps you to understand the issue. Look for materials about isolated scopes which Angular uses with directives.
HTML:
<div ng-app="erCommon" ng-controller="AlbumController as albumCtrl">
<er-album company-id="2" test = "albumCtrl.testVar"></er-album>
</div>
Controller:
angular.module('erCommon', [])
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective() {
return {
restrict: 'E',
scope: {
test: '=test',
companyId: '#companyId'
},
template: '<div> testVar: {{test}}<BR> companyId:{{companyId}}<BR> </div>', // it will work fine with templateUrl as well, just didn't want to cr8 another file...
link: function(scope, element, attrs){
//do whatever else you might need;
}
};
}
I try to loop a function through a nested directive. From the console.info in myCtrl I would expect the string "this should be logged".
angular.module('myApp')
.controller('myCtrl', function ($scope) {
$scope.aFunction = function(input) {
console.info(input.message);
}
})
.directive('levelOneDirective', function () {
return {
templateUrl: '<level-two-directive aFunction="aFunction(object)"></level-two-directive>',
restrict: 'EA',
scope: {
aFunction:"&"
},
link: function (scope, element, attrs) {
}
};
})
.directive('levelTwoDirective', function () {
return {
templateUrl: '<div ng-click="aFunction({message: 'this should be logged'})"></div>',
restrict: 'EA',
scope: {
aFunction:"&"
},
link: function (scope, element, attrs) {
}
};
});
And in my index.html I have something like:
<div ng-controller="myCtrl">
<level-one-directive aFunction="aFunction(object)"></level-one-directive>
</div>
But the console says undefined.
How to connect a function through nested directives?
You have several mistakes in your code but I assume it's because you try to adjust it to the question (such as aFunction as attribute instead of a-function and templateUrl instead of template).
You can have a 2-way binding (=) in your directives (both of them):
scope: {
aFunction:"="
},
And pass the function reference without the object:
<level-one-directive a-function="aFunction"></level-one-directive>
In the second directive HTML have:
<div ng-click="invokeFunction()"></div>
And then in the link function of your 2nd directive you can do:
scope.invokeFunction = function () {
scope.aFunction({message: 'this should be logged'});
}
The above works and I find it more convenient than & binding, which as you can see, is not quite easy to work with, and frankly I haven't messed around enough with it to figure out how (and if possible) to pass arguments through it.
I've seen this question, but it's binding straight on the link function, and you want it with an ng-click so it might not work for you. But perhaps you'll find your solution there.
I've used a directive to utilize jqueryUI dialogs.
app.directive('popUp', function() {
return {
restrict: 'E',
scope: {
myId: '#',
onCancel: '&'
},
template:
'<div id="{{myId}}">
<button ng-click="onCancel()">...</button>
...
</div>'
link: function(scope, element, attrs) {
scope.closeDialog = function() {
$("#" + id).dialog('close');
}
// question 1: how to reference id of this directive (self)?
// question 2: should it be here, in compile, or in directive controller?
// question 3: 'ng-click=closeDialog()' missing when popup element inspected in firebug/dev tool
// question 4: is there a way to avoid jquery like selector $("#" + id) to reference this element?
}
};
});
And this is the html:
<pop-up my-id="success" on-cancel="closeDialog()"> ... </pop-up>
If I declare an external controller and closeDialog function attached to its $scope, this works fine, like this:
app.controller('DialogCtrl', function($scope) {
$scope.closeDialog = function(id) {
$("#" + id).dialog('close');
};
});
html
<div ng-controller="DialogCtrl">
<pop-up my-id="success" on-cancel="closeDialog('success')"> ... </pop-up>
</div>
But what I want to avoid is redundancy of the id. So I want the directive to have its own close function. If you also have answers on the other questions above, it is very much appreciated. Thanks.
This is essentially what you want. There's no need to use $ selectors and ids to find your dialog since element in the link function will give you a reference to the element that the directive is applied to.
Define the closeDialog function on the directive's scope and you can reference it from the directive's template. Each instance of the directive will have its own close function.
app.directive('popUp', function() {
return {
restrict: 'E',
scope: {
myId: '#'
},
template:
'<div id="{{myId}}">
<button ng-click="closeDialog()">...</button>
...
</div>'
link: function(scope, element, attrs) {
// initialise dialog
element.dialog();
// the template's ng-click will call this
scope.closeDialog = function() {
element.dialog('close');
};
}
};
});
No need for an on-cancel attribute now.
<pop-up my-id="success"></pop-up>
I am trying to write a custom directive that will simply display a list of states. However I would like to build a directive which is as general as possible for lists that has objects that contain item_name and item_value. So for instance, i can use the same directive to populate cities, zipcodes, etc.
Here is what i have so far:
My template looks like this (not sure i need to iterate inside it)
{{defaultname}}
My directive looks like this:
app.directive('locselect', function () {
function link(scope, element, attrs) {
};
var select = {
replace:true,
link: link,
restrict: 'E',
templateUrl: 'app/search-filters/prp-select.html',
scope: {
defaultname: "defaulValue",
items:"="
}
}
return select;
});
And my implementation of the directive in the html looks like this:
<locselect items="states" default-value="State"></locselect>
Assume a scope of a controller that populates states or any other list inside the scope.
Here is a generic directive which prints a list of values:
app.directive('list', function () {
return {
scope : {
listItems : "="
},
template: '<ul><li ng-repeat="item in listItems">{{item.name}}, {{item.value}}</li></ul>',
link: function (scope, element, attrs) {
}
};
});
http://plnkr.co/edit/HHzDoYrqln0pawuL7MuU?p=preview
Edit
exmaple based on your plnkr:
http://plnkr.co/edit/SnpO2admMHmJV25lyi4p?p=preview
You may need to add ng-model also to your directive to bind the selected value to controller scope.
This might be what your are looking at:
app.directive('locselect', function () {
function link(scope, element, attrs) {
scope.ngModel = scope.defaultItem;
console.log(scope.items);
}
var select = {
restrict: 'E',
templateUrl: 'prp-select.html',
replace: true,
scope: {
items: "="
}
}
return select;
});
and your directive's template (app/search-filters/prp-select.html) should be
<select ng-options="item as item.full_name for item in items">
<option value="">Select State...</option>
</select>
and this is how you would want to use -
<locselect ng-model="modSelectedState" items="states"></locselect>
Check the plunkr here
I have a directive defined as
Application.Directives.directive('listview', function() {
return {
restrict: 'EAC',
templateUrl: 'directives/listview/view.html'
};
});
And then want to include it from the main view like this
<div class="{{directiveName}}">
</div>
where directiveName equals "listview". However, it does not work. It generates the below code, but the listview directive does not get loaded
<div class="listview">
</div>
Yet, when I type the above generated code directly into the main template, it does load the directive. How come? How can I make it work?
So I found a way. What you'd want is something like this
<div {{directiveNameInScope}}></div>
But again, that doesn't work. So I created a directive to do it for you. It works like
<div loaddirective="directiveNameInScope"></div>
where the loaddirective directive looks like
Application.Directives.directive('loaddirective', function($compile) {
return {
restrict: 'A',
scope: { loaddirective : "=loaddirective" },
link: function($scope, $element, $attr) {
var value = $scope.loaddirective;
if (value) {
// Load the directive and make it reactive
$element.html("<div "+value+"></div>");
$compile($element.contents())($scope);
}
},
replace: true
};
});
I put it up on github here: https://github.com/willemmulder/loaddirective