How to eval an attribute containing {{}} - angularjs-directive

In the following directive I'd like to eval {{selectedForApproval.length}}. This worked when it wasn't a directive, but once I put that into a directive, I'm unsure how to process the binding.
HTML: <button-large color="green" ng-click="completeWork()" label="Approve Selected ({{selectedForApproval.length}})"></button-large>
Directive:
directive('buttonLarge', function () {
return {
scope: false,
replace: true,
restrict: 'E',
template: '<button type="checkbox" class="buttonL"/>',
link: function (scope, element, attrs) {
var config = {
label: "Submit",
color: "Default"
};
angular.extend(config, attrs);
element.addClass("b"+capitalize(config.color));
element.html(scope.$parent.$eval(config.label));
//capitalize first letter of string
function capitalize(s) {
return s[0].toUpperCase() + s.slice(1);
}
}
}
})

The solution was to use transclusion...
<button-large color="green" ng-click="completeWork()">Approve Selected ({{selectedForApproval.length}})</button-large>
directive('buttonLarge', function () {
return {
scope: false,
replace: true,
restrict: 'E',
transclude: true,
template: '<button type="checkbox" class="buttonL" ng-transclude/>',
link: function (scope, element, attrs) {
var config = {
color: "Default"
};
angular.extend(config, attrs);
element.addClass("b"+capitalize(config.color));
//capitalize first letter of string
function capitalize(s) {
return s[0].toUpperCase() + s.slice(1);
}
}
}
})

Related

AngularJS: Setting ng-readonly variable to true inside directive does not work

I have created a custom directive for inputs.
Inside the HTML it is used like this:
<my-input class="input-text"
type="number"
ng-model="modelVariable"
ng-readonly="false" />
Now inside the directive myInput.js I have something like this:
var myInputDirective = (function() {
directives.directive('myInput', [function() {
return {
restrict: 'E',
transclude: 'element',
replace: true,
require: 'ngModel',
templateUrl: 'app/templates/common/myInput.tpl.html',
scope: {
ngModel: '=',
ngReadonly: '='
},
link: function($scope, $element, $attr, ngModelController) {
$scope.$watch('ngModel', function(newValue, oldValue) {
if (newValue != oldValue && newValue !== undefined && newValue !== null) {
$scope.ngReadonly = isReadOnly($attr.ngModel);
}
});
However, although this sets $scope.ngReadonly to true - the field is still not readonly - I am very confused as to why not.
You have to add in the view of your directive's tempalte set ng-readonly equals to the parent scope as one of the attributes of your input element.
Just like this:
return {
//..the other properties
scope: {
ngModel: '=',
ngReadonly: '=readOnly'
}
Then in the myInput.tpl.html find your input element and add this:
<input ng-readonly="readOnly">

how to pass argument from directive to directive

I have two directive and I want to pass argument from one directive to another directive.
Sample:
Directive1:-
app.directive('myDirective1', [function () {
return {
restrict : 'E',
templateUrl : 'templates/myDirective1.html',
link : function (scope, elem, attr) {
scope.items = 'myPara';
}
}]);
Directive2:-
app.directive('myDirective2', [function () {
return {
restrict : 'E',
templateUrl : 'templates/myDirective2.html',
scope : {
items : '='
}
link : function (scope, elem, attr) {
//here i want 'myPara'
}
}]);
Html:-
<my-directive1 items="items">
<my-directive2></my-directive2>
</my-directive1>
In the above example, when i change the value of scope.items in Directive1, it should be reflect on directive2 isolated scope(items). Now i can't get the value in Directive2. Can any one help me. Thanks.
Have a service that you inject in both directives..
app.service('myService',function(){ return {myPara : undefined}})
Now add this service to your both directives and use the myPara like myService.myPara = bla
because the service is a singleton you will have the same instance in both your directives.
directive1:
app.directive('myDirective1', ['myService',function (myService) {
return {
restrict : 'E',
templateUrl : 'templates/myDirective1.html',
link : function (scope, elem, attr) {
scope.items = myService.myPara;
}
}]);
directive2
app.directive('myDirective2', ['myService',function (myService) {
return {
restrict : 'E',
templateUrl : 'templates/myDirective2.html',
scope : {
items : '='
}
link : function (scope, elem, attr) {
//here i want 'myPara'
myService.myPara // Here is your myPara
}
}]);

How to display a modelpopup window using angularJS?

I need to show a model popup window in a button click.can any one suggest the best method to achieve this in angularjs without BootstrpJS?
I tried the below and is not working. :(
html
<div>
<button ng-click='toggleModal()'>Add Dataset</button>
<modal-dialog info='modalShown' show='modalShown' width='400px' height='60%'>
<p>Modal Content Goes here</p>
</modal-dialog>
</div>
controller
app.controller('DataController', function ($scope,$http) {
$scope.showModal = false;
$scope.toggleModal = function () {
$scope.showModal = !$scope.showModal;
};
$http.get("/api/product").then(function (responses) {
$scope.ProductData = responses.data;
});
.......
........
});
app.directive('modalDialog', function () {
return {
restrict: 'E',
scope: {
show: '=info'
},
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
link: function (scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
scope.hideModal = function () {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
};
});
It looks like you're messing with your scope a little too much. If you check out http://codepen.io/dboots/pen/vLeXPj, I used the same $scope.showModal variable and the same the same $scope.toggleModal function to show/hide.
angular.module('testApp', [])
.controller('DataController', function($scope) {
$scope.showModal = false;
$scope.toggleModal = function() {
$scope.showModal = !$scope.showModal;
};
})
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
},
template: "<div class='ng-modal' ng-show='showModal'><div class='ng-modal-overlay' ng-click='toggleModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='toggleModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
};
});
Make a directive. Then include it in your controller.
See: https://docs.angularjs.org/guide/directive

getting a attribute value in a directive in angular js

I have a directive
configModule.directive('iMemoryDiv',function () {
return {
restrict: 'E',
scope: {},
replace: true,
templateUrl: '/memoryDiv.html',
link: function(scope, el, attrs) {
scope.iObj = scope.$parent[attrs.bIObject];
if(angular.isDefined(scope.iObj)){
getSummary(scope.iObj);
}
function getSummary(iObj){
}
}
};
}
);
and I am calling it from html by passing an object attribute
<i-memory-div b-i-object="app"></i-memory-div> //here app is declared in a respective controller
but, here directive is getting loaded before the controller where value for 'app' is getting assigned.So scope.iObj = scope.$parent[attrs.bIObject] is always giving me undefined.
How can make directive load after the app value is getting assigned ??
Isn't it easier doing it this way:
configModule.directive('iMemoryDiv',function () {
return {
restrict: 'E',
scope: {
'bIObject': '=',
'getParentObject': '&'
},
replace: true,
templateUrl: '/memoryDiv.html',
link: function(scope, el, attrs) {
scope.$watch('bIObject', function(newValue) {
if(angular.isDefined(newValue)) {
getSummary(scope.getParentObject({name: newValue}));
}
});
function getSummary(iObj){
}
}
};
}
Or do you need more from the parent scope??
You could give your directive an isolated scope and add bIObject as a scope property:
scope: {
bIObject: '=',
},
you could then put your code in the controller: rather than link:
Use $observe service in link function,like this
if(attrs. bIObject) {
attrs.$observe('bIObject', function(value) {
console.log(value);
})
}

Angular-ui tooltip with HTML

I am currently adding some bootstrap tooltips in my application.
All of the "normal" tooltips are ok, but when I want to use tooltip-html-unsafe, all I got is an empty tooltip.
My tooltip:
<a><span tooltip-placement="right" tooltip-html-safe="{{myHTMLText}}"> Help </span></a>
In the DOM, I have:
<div class="tooltip-inner" ng-bind-html-unsafe="content"></div>
The div's content seems empty, so there is nothing to show in the tooltip. I tried to put directly in the DOM some HTML text like:
<div class="tooltip-inner" ng-bind-html-unsafe="content"><b>test</b></div> and it works.
Do you have an idea?
The html-unsafe directive is designed to point at it's content. What about this:
<div data-ng-controller="SomeCtrl">
<span data-tooltip-html-unsafe="{{yourContent}}" data-tooltip-placement="right">
Help
</span>
</div>
Then, in SomeCtrl, make a variable to hold the html:
$scope.yourContent = "<b>my html, yay</b>
IF you would like to modify bootstrap to take the content from a element, it can be done like this. First, you must change the tooltip template so that it calls a function to get the html:
angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
"<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
" <div class=\"tooltip-arrow\"></div>\n" +
" <div class=\"tooltip-inner\" ng-bind-html-unsafe=\"getToolTipHtml()\"></div>\n" +
"</div>\n" +
"");
}]);
Then, make a link function for the tooltipHtmlUnsafePopup:
.directive( 'tooltipHtmlUnsafePopup', function () {
return {
restrict: 'E',
replace: true,
scope: { content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html',
link: function(scope, element, attrs) {
scope.getTooltipHtml = function() {
var elemId = '#' + scope.content;
var htmlContent = $rootElement.find(elemId).html();
return htmlContent;
};
}
};
})
EDIT: Later I extracted the customized code from ui-bootstrap, which is good since you do not have to modify ui-bootstrap to use it now. Here is the extracted code, in a module called "bootstrapx". This is just for popovers (as I was not really using tooltips) but I feel like this should be easily adaptable for tooltips too.
angular.module("bootstrapx", ["bootstrapx.tpls","bootstrapx.popover","bootstrapx.popover.dismisser"]);
angular.module("bootstrapx.tpls", ["template/popover/popover-html.html","template/popover/popover-html-unsafe.html","template/popover/popover-template.html"]);
angular.module( 'bootstrapx.popover', [ 'ui.bootstrap.tooltip' ] )
.directive('popover', [ function() {
return {
restrict: 'EA',
priority: -1000,
link: function(scope, element) {
element.addClass('popover-link');
}
};
}])
.directive('popoverHtml', [ function() {
return {
restrict: 'EA',
priority: -1000,
link: function(scope, element) {
element.addClass('popover-link');
}
};
}])
.directive('popoverHtmlUnsafe', [ function() {
return {
restrict: 'EA',
priority: -1000,
link: function(scope, element) {
element.addClass('popover-link');
}
};
}])
.directive('popoverTemplate', [ function() {
return {
restrict: 'EA',
priority: -1000,
link: function(scope, element) {
element.addClass('popover-link');
}
};
}])
.directive( 'popoverHtmlPopup', [ function() {
return {
restrict: 'EA',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover-html.html'
};
}])
.directive( 'popoverHtml', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
return $tooltip( 'popoverHtml', 'popover', 'click' );
}])
.directive( 'popoverHtmlUnsafePopup', [ '$rootElement', function ( $rootElement ) {
return {
restrict: 'EA',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover-html-unsafe.html',
link: function(scope, element) {
var htmlContent = '';
scope.$watch('content', function(value) {
if (!value) {
return;
}
var elemId = '#' + value;
htmlContent = $rootElement.find(elemId).html();
});
scope.getPopoverHtml = function() {
return htmlContent;
};
}
};
}])
.directive( 'popoverHtmlUnsafe', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
return $tooltip( 'popoverHtmlUnsafe', 'popover', 'click' );
}])
.directive( 'popoverTemplatePopup', [ '$http', '$templateCache', '$compile', '$parse', function ( $http, $templateCache, $compile, $parse) {
return {
restrict: 'EA',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover-template.html',
link: function(scope, element, attrs) {
scope.getPopoverTemplate = function() {
var templateName = scope.content + '.html';
return templateName;
};
}
};
}])
.directive( 'popoverTemplate', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
return $tooltip( 'popoverTemplate', 'popover', 'click' );
}]);
angular.module("template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/popover/popover-html.html",
"<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
" <div class=\"popover-content\" ng-bind-html=\"content\"></div>\n" +
" </div>\n" +
"</div>\n" +
"");
}]);
angular.module("template/popover/popover-html-unsafe.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/popover/popover-html-unsafe.html",
"<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
" <div class=\"popover-content\" ng-bind-html-unsafe=\"{{getPopoverHtml()}}\"></div>\n" +
" </div>\n" +
"</div>\n" +
"");
}]);
angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/popover/popover-template.html",
"<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
" <div class=\"popover-content\" ng-include=\"getPopoverTemplate()\"></div>\n" +
" </div>\n" +
"</div>\n" +
"");
}]);
angular.module('bootstrapx.popover.dismisser', [])
.directive( 'dismissPopovers', [ '$http', '$templateCache', '$compile', '$parse', function ( $http, $templateCache, $compile, $parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('mouseup', function(e) {
var clickedOutside = true;
$('.popover-link').each(function() {
if ($(this).is(e.target) || $(this).has(e.target).length) {
clickedOutside = false;
return false;
}
});
if ($('.popover').has(e.target).length) {
clickedOutside = false;
}
if (clickedOutside) {
$('.popover').prev().click();
}
});
}
};
}]);
I have the dismissPopovers directive on the body tag (this is likely applicable for tooltips too, you will just have to modify it to suit your needs):
<body data-ng-controller="AppController" data-dismiss-popovers>
I have created custom directive which allows html tooltips for bootsrap in very simple way. No need to override any templates:
angular.module('vermouthApp.htmlTooltip', [
])
.directive('vaTooltip', ['$http', '$templateCache', '$compile', '$parse', '$timeout', function ($http, $templateCache, $compile, $parse, $timeout)
{
//va-tooltip = path to template or pure tooltip string
//tooltip-updater = scope item to watch for changes when template has to be reloaded [optional (only if template is dynamic)]
//All other attributes can be added for standart boostrap tooltip behavior (ex. tooltip-placement)
return {
restrict: 'A',
scope: true,
compile: function (tElem, tAttrs)
{
//Add bootstrap directive
if (!tElem.attr('tooltip-html-unsafe'))
{
tElem.attr('tooltip-html-unsafe', '{{tooltip}}');
}
return function (scope, element, attrs)
{
scope.tooltip = attrs.vaTooltip;
var tplUrl = $parse(scope.tooltip)(scope);
function loadTemplate()
{
$http.get(tplUrl, { cache: $templateCache }).success(function (tplContent)
{
var container = $('<div/>');
container.html($compile(tplContent.trim())(scope));
$timeout(function ()
{
scope.tooltip = container.html();
});
});
}
//remove our direcive to avoid infinite loop
element.removeAttr('va-tooltip');
//compile element to attach tooltip binding
$compile(element)(scope);
if (angular.isDefined(attrs.tooltipUpdater))
{
scope.$watch(attrs.tooltipUpdater, function ()
{
loadTemplate();
});
} else
{
loadTemplate();
}
};
}
};
}]);
Thats how you call it
<a va-tooltip="'tooltipContent.html'" tooltip-updater="item" tooltip-placement="bottom">
<b><i>{{item.properties.length - propertyShowLimit + ' properties more...'}}</i></b>
</a>
And template can be like this:
<script id="tooltipContent.html" type="text/ng-template">
<span ng-repeat="prop in item.properties>
<b>{{prop.name}}</b>:
<span ng-repeat="val in prop.values">{{val.value}} </span>
<br />
</span>
</script>
There is now buit-in template functionality: https://angular-ui.github.io/bootstrap/#tooltip
Custom template
<script type="text/ng-template" id="myTooltipTemplate.html">
<span>Special Tooltip with <strong>markup</strong> and {{ dynamicTooltipText }}</span>
</script>