Binding HTML to the scope in Angular UI popup - angularjs

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.

Related

angular to append a template on click of button

I am trying to append some html to the existing container through angular directives. here is the code what i have written for the same.
app.directive("addActivityRow", function($compile){
var template = "Some html template"
return{
restrict: 'A',
link: function(scope, element,attrs,controller){
element.on("click", function() {
console.log("clicked on add row");
element.parent().append(template);
});
}
}
});
here is the html markup
Can any one please help me in correcting the error what i have done ? When i click on anchor tag, nothing happens, nothing logs on console.
I am new to Angular please help ..
Replace this:
href="javascript:void(0);"
with this:
href=""
And use add-activity-row instead of addActivityRow as #PSL mentioned that directive names are used as dash-limited attributes in the view.

Is there an alternative to using a directive to create template code?

I had HTML that was duplicated on many screens so I created a directive like this:
app.directive('adminRetrieveButton', ['stateService', function (stateService) {
return {
restrict: 'AE',
template: "<button id='retrieveButton'\
ng-disabled='!home.forms.grid.$pristine'\
ng-click='ctrl.retrieve()' >Retrieve\
<span class='fa fa-fw mlr75'\
ng-class='{\"fa-spin fa-spinner\": stateService.action[Network.Retrieve], \"fa-download\": !stateService.action[Network.Retrieve] }' >\
</span>\
</button>",
link: function (scope, element, attrs) {
scope.stateService = stateService;
}
};
}]);
The directive and other similar ones has no functionality other than to create some template type of code. Now rather than having to code in 8 lines of HTML in each page I just have one line:
<admin-retrieve-button></admin-retrieve-button>
Is there another alternative to this that would make it even simpler without my needing to create a directive?
yes you dont need to create directive only for template you can simply use ng-include
Directives are a powerful tool for working with and modifying the DOM, If you dont manipulating with DOM you dont need directive
There are alternatives, but your method is the best. The way you have it is the correct Angular Way; if you have a standard control, make it a directive.
If you like, you can use the templateUrl option to include the HTML in a separate file instead of inline in the directive configuration:
app.directive('adminRetrieveButton', ['stateService', function (stateService) {
return {
restrict: 'AE',
templateUrl: "adminButton.html",
link: function (scope, element, attrs) {
scope.stateService = stateService;
}
};
}]);
...and in adminButton.html:
<button id="retrieveButton"
ng-disabled="!home.forms.grid.$pristine"
ng-click="ctrl.retrieve()" >Retrieve
<span class="fa fa-fw mlr75"
ng-class="{'fa-spin fa-spinner': stateService.action[Network.Retrieve],
'fa-download': !stateService.action[Network.Retrieve]}">
</span>
</button>
This directive can access its parent scope because it has not been set to isolate scope; if you included a "scope" option on the directive call you'd need to pass in the home and ctrl variables.
You can use ng-include to do this, but your view code will look less semantic and injecting stateService will be a bit more complicated.

Dynamically add angular attributes to an element from a directive

I'm trying to build a directive that change loading status on buttons for slow ajax calls. Basically the idea is to set an attribute "ng-loading" to a button element, and let the directive add the rest of stuff.
This is the html code:
<button class="btn btn-primary" name="commit" type="submit" ng-loading="signupLoading">
Submit
</button>
And this is the directive code:
.directive('ngLoading', ['$compile', function($compile) {
return {
restrict: 'A',
replace: false,
terminal: true,
link: function(scope, element, attrs) {
element.attr('ng-class', '{loading:' + attrs['ngLoading'] +'}');
element.attr('ng-disabled', attrs['ngLoading']);
element.append(angular.element('<img class="loading-icon" src="/assets/images/loading-icon.gif"/>'));
$compile(element.contents())(scope);
}
};
}]);
It all looks correct in the rendered HTML, but the attributes added from the directive is not funcioning at all. I can move those attributes to the HTML code and everything works great, but that's quite some redundant code in many places.
I referenced the post Angular directive to dynamically set attribute(s) on existing DOM elements but it does not solve my problem.
Any comment/suggestion are welcome. Thanks in advance.
You don't need to recompile that directive if all you want is some DOM manipulation, you can add and remove class in regards to the changes of a scope property. You can use $watch instead.
JAVASCRIPT
.directive('ngLoading', function() {
return function(scope, element, attrs) {
var img = angular.element('<img class="loading-icon" src="/assets/images/loading-icon.gif"/>');
element.append(img);
scope.$watch(attrs.ngLoading, function(isLoading) {
if(isLoading) {
img.removeClass('ng-hide');
element.addClass('loading');
element.attr('disabled', '');
} else {
img.addClass('ng-hide');
element.removeClass('loading');
element.removeAttr('disabled');
}
});
};
});
Note: Your code doesn't work because it compiles the contents of the elements, not the element itself, where you attach the attributes you have implemented.
try $compile(elem)(scope); and it should work properly, but I don't recommend it because each element with such directive will have to re-compile again.
UPDATE:
before using $compile remove the attribute 'ngLoading' to the element to prevent infinite compilation.
elem.removeAttr('ng-loading');
$compile(elem)(scope);

AngularJS - Add directive from within controller

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();

Creating layout using Angular directives

Folks.. i am trying to layout a bunch of columns in tablular format using Angular directive but am lost after having written some code.
What's the issue?
I need to display a bunch of columns as shown in the plnkr (Display option 2). However I want to achieve this using directives.
Here is the plnkr:
http://plnkr.co/edit/yTDxfvkCJHwrEDZGQJeX
Any help will be appreciated
regards,
If you add a template to the directive, you can output the same markup as your first example:
demo.directive("customTable",function(){
return ({
restrict : "A",
template: '<span ng-repeat="element in header" ng-style="{width: element.width}" class="box">{{element.column}}</span>'
});
function link($scope, element, attributes){
console.log($scope.header);
console.log("now what");
}
});
This one uses the parent scope so you don't need the header attribute:
<h1>Display option 2: Use Directive </h1>
<div custom-table>
</div>
Here is an updated plunker.
You could also use Isolated Scope and two-way bind the header. This allows the header object to be bound between the parent scope and the directive scope:
demo.directive("customTable",function(){
return ({
restrict : "A",
scope: {
header = '='
},
template: '<span ng-repeat="element in header" ng-style="{width: element.width}" class="box">{{element.column}}</span>'
});
function link($scope, element, attributes){
console.log($scope.header);
console.log("now what");
}
});
And then you can declare header as you did originally:
<h1>Display option 2: Use Directive </h1>
<div custom-table header="header">
</div>

Resources