AngularJS - Add directive from within controller - angularjs

I have defined the following directive:
app.directive('copyExpenditure', function(){
return {
restrict: 'A',
scope: true,
template: '<button ng-click="copyExpenditure()">click me</button>'
}
});
If I add this directive directly into the html it works as expected.
<div copy-expenditure></div>
Now I'm creating a grid from within a controller and want to dynamically add to each row the copyExpenditure directive. For this task I'm using Slickgrid and a custom Formatter.
var LinkFormatter = function (row, cell, value, columnDef, dataContext) {
var tag = '<div copy-expenditure></div>';
return tag;
}
But it seems as this custom formatter is not rendered as a directive, but only as HTMl. Any idea what to do? My goal is only to call a scope function using ng-click and a tag rendered by the LinkFormatter.

You should do:
$compile(angular.element('<div copy-expenditure></div>'))($scope);
and if you have some values passed into it from scope, you do this after compile:
$scope.$digest();

Related

Accessing ng-repeat scope on a custom directive

I'm having a go at a directive which will dynamically load a template based on a scope value passed into it.
I am using ng-repeat on my directive, and am using the iterated item as an attribute property:
<my-form-field ng-repeat="field in customFields" field="field">
In my directive I have the following code to set the template being used.
(function() {
'use strict';
angular
.module('app')
.directive('myFormField', myFormField);
function myFormField() {
var directive = {
restrict: 'E',
scope: {
field: '='
},
link: function(scope){
scope.getContentUrl = function() {
return 'app/modules/form_components/form_field/' + scope.field.type + '-template.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
};
return directive;
}
})();
Whilst the above works (which I found from other posts), I wonder if there is a better way.
For example I have seen examples of calling a function on the templateUrl config option instead, and then in that function access the scope attributes being passed in. When I tried this way, my field attribute was a literal 'field' string value (they are objects in my customFields array), so I think at that point the scope variables had not yet been evaluated.
With this current solution I am using, all of my templates get wrapped in an extra div since I am using ng-include, so I am just trying to make the rendered markup more succinct.
Any suggestions\advice is appreciated.
Thanks

Assign random ngModel to directive

I'm trying to assign a random string to ngModel. But I can't even seem to assign a regular string to it. In the code below, I'm trying to change the ngModel to "new", but in chrome, it's still showing me that the ngModel is "placeholder". What am I doing wrong?
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
template: '<div ng-model="placeholder"></div>',
controller: function ($scope, $element) {
$scope.placeholder = "new";
}
}});
The plain div tag was just an example. What I actually have is a content editable div that I've bound to a textarea with a contenteditable directive. I made a button to allow me to add as many of these directives as I want, but when I add this directive, I'd like a new ng-model for each one, because that's what I'm using to save the content of each content editable div to a file.
This is the full example in case it might help someone else. I put the addPage directive to a button, which, when clicked, appends a new content editable div (which I'm calling a page). There is one more directive that I didn't include (contenteditable) because I got it from the bottom of the docs over here
app.directive("addPage", function($compile){
return function(scope, element, attrs){
element.bind("click", function(){ angular.element(document.getElementById('container')).append($compile('<page></page>')(scope));
});
};
});
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
// template: '<div class="page" contenteditable strip-br="true" ng-model="chapter"></div>',
template: function (elem, attr) {
var test="chapter.two"; //in reality, I will generate a random string to keep these divs unique
return '<div class="page" contenteditable strip-br="true" ng-model=' + test +'></div>'
}
}});
In my controller, I have this object that is used to store all the ng-model content
$scope.chapter = {};
I think you are approaching this a bit backwards. Starting with the model, you'd need an array to keep the values from all of the inputs / contenteditables.
.controller("MainCtrl", function($scope){
$scope.data = [{v: ""}]; // first element
$scope.addContentEditable = function(){
$scope.data.push({v: ""});
};
})
Then, you can bind to each element of that array easily, and add elements at will:
<button ng-click="addContentEditable()">Add</button>
<div ng-repeat="item in data" ng-model="item.v" contenteditable></div>
I'm not sure exactly how the page directive needs to be used here, but one way or another, the approach is the same as above.
If you just want to pass a string in you could use attributes and template(fn)
HTML
<div page="new">
Directive
app.directive("page", function () {
return {
restrict: 'E',
replace: true,
template: function (elem, attr) {
return '<div ng-model="' + attr.page + '"></div>';
}
}
});

Angularjs - Pass argument to directive

Im wondering if there is a way to pass an argument to a directive?
What I want to do is append a directive from the controller like this:
$scope.title = "title";
$scope.title2 = "title2";
angular.element(document.getElementById('wrapper')).append('<directive_name></directive_name>');
Is it possible to pass an argument at the same time so the content of my directive template could be linked to one scope or another?
here is the directive:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
replace:true
};
})
What if I want to use the same directive but with $scope.title2?
You can pass arguments to your custom directive as you do with the builtin Angular-directives - by specifying an attribute on the directive-element:
angular.element(document.getElementById('wrapper'))
.append('<directive-name title="title2"></directive-name>');
What you need to do is define the scope (including the argument(s)/parameter(s)) in the factory function of your directive. In below example the directive takes a title-parameter. You can then use it, for example in the template, using the regular Angular-way: {{title}}
app.directive('directiveName', function(){
return {
restrict:'E',
scope: {
title: '#'
},
template:'<div class="title"><h2>{{title}}</h2></div>'
};
});
Depending on how/what you want to bind, you have different options:
= is two-way binding
# simply reads the value (one-way binding)
& is used to bind functions
In some cases you may want use an "external" name which differs from the "internal" name. With external I mean the attribute name on the directive-element and with internal I mean the name of the variable which is used within the directive's scope.
For example if we look at above directive, you might not want to specify another, additional attribute for the title, even though you internally want to work with a title-property. Instead you want to use your directive as follows:
<directive-name="title2"></directive-name>
This can be achieved by specifying a name behind the above mentioned option in the scope definition:
scope: {
title: '#directiveName'
}
Please also note following things:
The HTML5-specification says that custom attributes (this is basically what is all over the place in Angular applications) should be prefixed with data-. Angular supports this by stripping the data--prefix from any attributes. So in above example you could specify the attribute on the element (data-title="title2") and internally everything would be the same.
Attributes on elements are always in the form of <div data-my-attribute="..." /> while in code (e.g. properties on scope object) they are in the form of myAttribute. I lost lots of time before I realized this.
For another approach to exchanging/sharing data between different Angular components (controllers, directives), you might want to have a look at services or directive controllers.
You can find more information on the Angular homepage (directives)
Here is how I solved my problem:
Directive
app.directive("directive_name", function(){
return {
restrict: 'E',
transclude: true,
template: function(elem, attr){
return '<div><h2>{{'+attr.scope+'}}</h2></div>';
},
replace: true
};
})
Controller
$scope.building = function(data){
var chart = angular.element(document.createElement('directive_name'));
chart.attr('scope', data);
$compile(chart)($scope);
angular.element(document.getElementById('wrapper')).append(chart);
}
I now can use different scopes through the same directive and append them dynamically.
You can try like below:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
scope:{
accept:"="
},
replace:true
};
})
it sets up a two-way binding between the value of the 'accept' attribute and the parent scope.
And also you can set two way data binding with property: '='
For example, if you want both key and value bound to the local scope you would do:
scope:{
key:'=',
value:'='
},
For more info,
https://docs.angularjs.org/guide/directive
So, if you want to pass an argument from controller to directive, then refer this below fiddle
http://jsfiddle.net/jaimem/y85Ft/7/
Hope it helps..
Controller code
myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {
$scope.person = {
name:"sangeetha PH",
address:"first Block"
}
}]);
Directive Code
myApp.directive('searchResult',function(){
return{
restrict:'AECM',
templateUrl:'directives/search.html',
replace: true,
scope:{
personName:"#",
personAddress:"#"
}
}
});
USAGE
File :directives/search.html
content:
<h1>{{personName}} </h1>
<h2>{{personAddress}}</h2>
the File where we use directive
<search-result person-name="{{person.name}}" person-address="{{person.address}}"></search-result>
<button my-directive="push">Push to Go</button>
app.directive("myDirective", function() {
return {
restrict : "A",
link: function(scope, elm, attrs) {
elm.bind('click', function(event) {
alert("You pressed button: " + event.target.getAttribute('my-directive'));
});
}
};
});
here is what I did
I'm using directive as html attribute and I passed parameter as following in my HTML file. my-directive="push" And from the directive I retrieved it from the Mouse-click event object. event.target.getAttribute('my-directive').
Insert the var msg in the click event with scope.$apply to make the changes to the confirm, based on your controller changes to the variables shown in ng-confirm-click therein.
<button type="button" class="btn" ng-confirm-click="You are about to send {{quantity}} of {{thing}} selected? Confirm with OK" confirmed-click="youraction(id)" aria-describedby="passwordHelpBlock">Send</button>
app.directive('ngConfirmClick', [
function() {
return {
link: function(scope, element, attr) {
var clickAction = attr.confirmedClick;
element.on('click', function(event) {
var msg = attr.ngConfirmClick || "Are you sure? Click OK to confirm.";
if (window.confirm(msg)) {
scope.$apply(clickAction)
}
});
}
};
}
])

Passing controller scope from directive to partial

I am new to AngularJs. I have a route configured to a controller and a template. In the template I am calling a custom directive. The custom directive loads a partial file in which I need to fetch the scope which is set by the controller. How can I pass the scope from the directive to the partial so that the partial file can consume the scope data.
Kindly let me know how to get this implemented in AngularJs
Code snippet inside the link function of the directive:
myApp.directive('basicSummary', function() {
return {
restrict: 'E',
replace:'true',
templateUrl: "partials/basicSummary.html",
link: function(scope, elem, attrs) {
console.log(scope.testURL);
}
}
});
Output on the console is : undefined
Update: I found the root cause of why the variable was not getting initialized. The issue, is that the variable is being fetched by making an ajax call in the controller and by the time the ajax call is completed and the variable is put inside the scope inside the controller, the partial file is already loaded and hence I am getting the value of the variable inside the directive as undefined.
How can I make sure that only on success of the http call, I load the partial and the directive?
Adding the jsfiddle link: http://jsfiddle.net/prashdeep/VKkGz/
You could add a new variable to your scope in the definition of your directive to create a two-way binding, that you could safely watch for changes (for use in Javascript once the variable has been populated via ajax), and in your template use ng-show to show/hide based on whether or not the variable is defined. See this JSFiddle for an example of how that would work: http://jsfiddle.net/HB7LU/3588/
Default Template
<div ng-controller="MyCtrl">
<my-test my-test-url="myAjaxProperty"></my-test>
</div>
App Definition
var myApp = angular.module('myApp',[]);
myApp.directive('myTest', function(){
return {
restrict: 'E',
repalce: 'true',
template: '<div ng-show="myTestUrl">{{myTestUrl}}</div>',
scope: { myTestUrl: '=' },
link: function(scope, elem, attrs){
scope.$watch('myTestUrl', function(newVal, oldVal){
if(newVal){
console.log("Watched value is defined as: "+scope.myTestUrl);
}
})
}
}
});
function MyCtrl($scope, $timeout) {
$timeout(function(){
$scope.myAjaxProperty = "My Test Url";
console.log("Ajax returned");
}, 3000, true)
console.log("Default Controller Initialized");
}
as long as you don't isolate your scope with,
scope: {}
in your directive, your scope has access to its parent controller's scope directly.

Binding HTML to the scope in Angular UI popup

I'm using the Angular UI Bootstrap directive to show a popover which functions as a dropdown menu. If I specify a HTML template for the content (using the attribute popover-template) I can use clickable links which call a function on my directive to change the value. Now, however, I need to be able to specify options on the fly so I've tried creating the HTML list and passing it to the "popover" attribute in my directive's link function. This works, in that it displays the HTML in the popover correctly, however the links aren't clickable because they're within a ng-bind-html unsafe container. I've tried compiling the HTML string I'm passing to the "popover" attribute but it prints [object Object].
Here's my directive:
MyApp.directive('dropDown', ['$compile', function($compile){
return{
restrict:'EA',
replace:true,
transclude:true,
scope:{
value : '#',
options : '='
},
controller: function($scope, $element) {
$scope.doSelect = function(option, text){
alert(option);
}
},
template: '<div>'+
'<button class="btn btn-dropdown" data-html="true" popover-append-to-body="true" popover-placement="bottom" popover-trigger="click">'+
'{{value}}'+
'<span class="icon-triangle-down"></span></button>' +
'</div>',
link: function (scope, elem, attrs) {
scope.list = '<ul class="dropdown">';
for (opt in scope.options){
if(scope.options.hasOwnProperty(opt)){
scope.list+='<li><a ng-click="doSelect(\''+opt+'\', \''+scope.options[opt]+'\');">'+scope.options[opt]+'</a></li>';
}
}
scope.list += '</ul>';
var but = elem.find("button");
var template = $compile(scope.list)(scope);
//$(but).attr('popover', template); // prints [object Object] instead of compiled html
$(but).attr('popover', scope.list); // prints html not bound to scope and therefore not clickable
$compile(elem.contents())(scope);
}
}}]);
I've created a fiddle to illustrate the problem:
http://jsfiddle.net/CaroD/7B5qB/3/
So: is there any way to compile this HTML so it can interact with the scope, or am I taking a completely wrong approach here?
All suggestions most welcome,
Thanks!
to do not a big directive like that, have you try to use the html-unsafe attribute of the tooltip provider? it gives you the possiblity to put html text into popover and so you surely interact with it.

Resources