Inside a larger legacy app I have a view that uses AngularJS. In this view, I use a directive to display a Bootstrap modal (its content is created on the backend, then appended to the DOM).
No AngularJS methods seem to be working inside the modal. I understand it's because the modal content is appended to the DOM after AngularJS has compiled.
I'm looking for a way to allow the use AngularJS braces and methods inside the modal template. I've tried looking into $compile, $apply, and $digest, but so far I haven't been able to get it to work.
Relevant part of the code:
myApp.directive('formModal', ['$http', function($http) {
return {
restrict: 'E',
scope: true,
replace: true,
template: '...',
link: function postLink(scope){
// ...
$http({url: url}).then(function (response) {
if (response.data.status) {
modal.show({
title: 'Modal title',
content: response.data.form_html,
onShow: function ($modal) {
// ...
}
});
};
})
};
}
};
}]);
And part of the template where the modal is opened:
<form-modal class="btn-success" id="btn-form">
</form-modal>
I'm new to AngularJS. Need some help with the directive I created.
This is My HTML:
<data-table template-url="dataTable.html" info="someData"></data-table>
I get "someData" from server in my controller - directive.js:
app.directive('dataTable', function() {
return{
restrict: 'E',
scope: {
data : '=info'
},
link: function($scope,elem,attrs){
///some code here.
},
templateUrl : function(elem, attrs) {
return attrs.templateUrl;
}
});
The issue is when I debug my code, it come to the directive by doesn't go inside. (I used javascript debug in chrome). Is there anything I'm missing. The restrict Tag is proper, name is correct what else is needed? I did look at the similar questions but couldn't find any solution. Here is a fiddle : Demo
You can't use directive names starting with data-* because its reserved by AngularJS ng core namespaces. Just use an other name to start with and you will be fine.
<my-data-table template-url="dataTable.html" info="someData"></my-data-table>
And your directive:
myApp.directive('myDataTable', function() {
return {
scope: {
data: '=info'
},
link: function($scope, elem, attrs) {
///some code here.
console.log(attrs.templateUrl);
},
templateUrl: function(elem, attrs) {
return attrs.templateUrl;
}
}
});
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
I'm looking for an answer to this question:
Multi State button like toggle in angularJS
...but using a directive. The main reason being that I'm trying to achieve isolate scope in order to create a reusable button. I have tried:
angular.module('myApp', [])
.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
myBtnTxt: ["AND", "OR", "NOT"],
template: '<button>{{ myBtnTxt[myBtnArr] }} </button>'
}
});
With something like this in the HTML:
<div button-toggle my-btn-arr=0></div>
But Angular doesn't seem to like any flavor of this, either showing the button but not the text or throwing the cryptic a.match is not a function error. Thoughts?
You need to modify your directive to include a link function. Then place myBtnTxt on scope in there. Like so:
app.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
template: '<button>{{myBtnTxt[myBtnArr]}}</button>',
link: function(scope){
scope.myBtnTxt = ["AND", "OR", "NOT"];
}
};
});
I am trying to use a Bootstrap Popover in an AngularJS ngRepeat directive. The problem is the title attribute isn't being interpolated. How can I resolve this issue?
View
<div ng-app="app" id="ng-app">
<ul ng-controller="Main">
<li ng-repeat="i in items" data-toggle="popover" title="{{ i.title }}" <!-- Title isn't interpolated so Popover doesn't render a title -->
data-content="Events: {{ day.events.length }}"
init-popover>{{ i.data }}</li>
</ul>
Code
var app = angular.module('app', []);
app.controller('Main', ['$scope', function($scope){
$scope.items = [
{ title: 'Title 1', data: 'lorem' },
{ title: 'Title 2', data: 'ipsum' },
];
}]);
app.directive('initPopover', function() {
return function(scope, element, attrs) {
if (scope.$last){
$('[data-toggle="popover"]').popover({container: 'ul'});
}
};
});
FIDDLE
You can make it work but it's pretty ugly, here is my solution
var app = angular.module('app', []);
app.controller('Main', ['$scope', function($scope){
$scope.items = [
{ title: 'Title 1', data: 'Test in ngRepeat' },
{ title: 'Title 2', data: 'Test in ngRepeat' },
];
}]);
app.directive('initPopover', function() {
return function(scope, element, attrs) {
if (scope.$last) {
attrs.$observe('title', function () {
$('[data-toggle="popover"]').popover({container: 'ul'});
});
}
};
});
You shouldn't use title="{{i.title}}", but ng-attr-title="{{i.title}}"...
I had the same problem. I'm using bootstrap v3.3.6 and Angular v1.5.0
Adding attribute data-original-title="{{someTitle}}" fixed the problem.
<button type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="{{someTitle}}" data-original-title="{{someTitle}}" data-content="Some Content">Click to toggle popover</button>
Also, as bootstrap requires, call .popover() in directive
module.directive('myDir', ['jQuery', function (jQuery) {
return {
link: function () {
jQuery('[data-toggle="popover"]').popover();
}
};
}]);
Add $timeout to directive will solve your problem.
$timeout will gets call when last element of ng-repeat rendered on UI & run $digest cycle.
Directive
app.directive('initPopover', function($timeout) {
return function(scope, element, attrs) {
if (scope.$last) {
$timeout(function() {
angular.element('[data-toggle="popover"]').popover({
container: 'ul'
});
});
}
};
});
Working Fiddle
Hope this could help you. Thanks.
Consider using ui-bootstrap for native Angular directives instead of Bootstrap's jQuery version. They're a lot easier to work with in an Angular project.
Edit: If you can't do that, here's a solution:
<li ng-repeat="i in items" data-toggle="popover" data-ng-attr-popover_title="{{ i.title }}"
data-content="Popover Content"
init-popover>{{ i.data }}</li>
With a small change to the directive:
app.directive('initPopover', function() {
return function(scope, element, attrs) {
if (scope.$last){
$('[data-toggle="popover"]').popover({ container: 'ul', title: attrs.popoverTitle });
}
};
ng-attr-popup_title will create an HTML attribute called popupTitle on the element with the Angular syntax inside {{}} parsed correctly. Then, in the directive, we can just examine that attribute to set the popover title.
Fiddle: here
Just to add to the knowledge base here. Here's the angular way, in case anyone needs. I based my solution on Mark Coleman's PopOver tutorial, and of course what has been contributed by Nate Barbettini.
My situation was I have a nested ng-repeat over items in items, so I need to have a title for my popup that is based on an array stored in a scope, while the content of the popup is from another array stored in the array of the scope.
I added ng-attr-title to my HTML element, like so:
<div pop-over items='week.items' ng-attr-title="{{'Week number:'+ week.week}}"> Total sales this week : {{week.sales}}</div>
Then, in my directive, I followed mostly what Mark Coleman did.
angular.module('vegadirectives', [])
.directive('popOver', function ($compile, $templateCache) {
var itemsTemplate = "<ul class='popper'><li ng-repeat='thing in items track by $index'>";
itemsTemplate+=" <i class='fa fa-square' ng-class=";
itemsTemplate+="{purple:thing.color==='purple','orange':thing.color==='orange','white':thing.color==='white','pink':thing.color==='pink','red':thing.color==='red','green':thing.color==='green','yellow':thing.color==='yellow'}";
itemsTemplate+="></i> {{thing.model}} - {{thing.qty}}</li></ul>";
var getTemplate = function () {
var template=itemsTemplate;
return template;
}
return {
restrict: "A",
transclude: true,
template: "<span ng-transclude></span>",
link: function (scope, element, attrs) {
var popOverContent;
if (scope.items) {
var html = getTemplate();
popOverContent = $compile(html)(scope);
var options = {
trigger:'click',
content: popOverContent,
placement: "right",
html: true,
title:scope.title
};
if (scope.$last) {
}
$(element).popover(options);
}
},
scope: {
items: '=',
title:'#'
}
};
});
Explanation:
To fill up my popover with a list of items, with some fancy color square to indicate different types of items, I created a chunk of HTML code. This chunk named itemsTemplate will be used by $compile to create the popover content with scope. I think the usage is $compile(htmlstring)(scope).
Next comes the standard stuff for options settings for the popover.
For the interpolation of scope with title, I specified the option title: with scope.title. Then, in the scope: option way down at the end, the two scope elements are specified with '=' and '#'.
So, the title:scope.title refers or binds to scope: {items:'=', title:'#' }, and the title:'#' is the attributes of the title in the HTML, which is ng-attrs-title={{week.week}}, where week is from the outer ng-repeat loop.
Hope it helps.
After finding this answer at https://stackoverflow.com/a/17864868/2379762, I tried data-original-title={{i.title}}, and it works.
http://jsfiddle.net/f1tjj8x5/
<li ng-repeat="i in items" data-toggle="popover" data-original-title="{{ i.title }}"
data-content="Popover Content"
init-popover>{{ i.data }}</li>