How can I use the value of an attribute in a directive? My element looks like this:
<div class="tooltip-icon"
data-my-tooltip="click"
data-tooltip-title="foo"
data-tooltip-content="test content"></div>
I would like to use that in the template of my directive, which looks like this:
mainApp.directive('myTooltip',
function() {
// allowed event listeners
var allowedListeners = ["click"];
return {
restrict: 'A',
template: '<div class="tooltip-title">...</div>' +
'<div class="tooltip-content">' +
'...</div>',
link: function(scope, elm, attrs) {
if(allowedListeners.indexOf(attrs.myTooltip) != -1){
elm.bind(attrs.myTooltip, function(){
...
});
}
}
};
}
);
Where the triple dots are there should be code, but I cannot figure out how to get the contents of the attrs object (attrs.tooltipTitle
, etc) into that template.
Components are directives with templates. The only difference between Components and the other two types of directives is the Template. Attribute and Structural Directives don't have Templates. So, we can say that the Component is a cleaner version of the Directive with a template, which is easier to use.
The attribute directive changes the appearance or behavior of a DOM element. These directives look like regular HTML attributes in templates. The ngModel directive which is used for two-way is an example of an attribute directive.
Structural directives are directives which change the DOM layout by adding and removing DOM elements. Angular provides a set of built-in structural directives (such as NgIf , NgForOf , NgSwitch and others) which are commonly used in all Angular projects.
You can pull the attributes out and place them into the scope of the directive like this:
angular.module('myApp', []).
directive('myTooltip', function ($log) {
// allowed event listeners
var allowedListeners = ["click"];
return {
restrict: 'A',
template: '<div class="tooltip-title">{{tooltipTitle}}</div>' +
'<div class="tooltip-content">' +
'{{tooltipContent}}</div>',
scope: {
tooltipTitle: '@tooltipTitle',
tooltipContent: '@tooltipContent'
},
link: function (scope, elm, attrs) {
if (allowedListeners.indexOf(attrs.myTooltip) != -1) {
elm.bind(attrs.myTooltip, function () {
$log.info('clicked');
});
}
}
};
});
Here is fiddle: http://jsfiddle.net/moderndegree/f3JL3/
This question has already been answered, but I'm going to share my Angular code aswell, as this is one area where it's often useful to see a few working examples.
I have a few webpages, each with their own Angular controller, and I wanted a way to have one "Please Wait" popup on each page, which would appear when any of the pages called an HTTP GET or POST web service.
To do this, each of my webpages contains this line:
<please-wait message="{{LoadingMessage}}" ></please-wait>
...which is bound to a $scope
in that page's controller...
$scope.LoadingMessage = "Loading the surveys...";
Here's the code for my <please-wait>
directive:
myApp.directive('pleaseWait',
function ($parse) {
return {
restrict: 'E',
replace: true,
scope: {
message: '@message'
},
link: function (scope, element, attrs) {
scope.$on('app-start-loading', function () {
element.fadeIn();
});
scope.$on('app-finish-loading', function(){
element.animate({
top: "+=15px",
opacity: "0"
}, 500);
});
},
template: '<div class="cssPleaseWait"><span>{{ message }}</span></div>'
}
});
Notice how it picks up the message
attribute ({{LoadingMessage}}
in this example) and can display its value in the directive's template.
(That's actually the only part of my answer which directly answers this question, but read on, for a few more tips'n'tricks...)
Now, the cool part is that each of my controllers calls an Angular data service whenever it wants to load or save any data from/to a web service.
$scope.LoadAllSurveys = function () {
DataService.dsLoadAllSurveys($scope).then(function (response) {
// Success
$scope.listOfSurveys = response.GetAllSurveysResult;
});
}
The dsLoadAllSurveys
function looks like this...
myApp.webServicesURL = "http://localhost:15021/Service1.svc";
myApp.factory('DataService', ['$http', 'httpPostFactory', 'httpGetFactory',
function ($http, httpPostFactory, httpGetFactory) {
var dsLoadAllSurveys = function (scope)
{
// Load all survey records, from our web server
var URL = myApp.webServicesURL + "/getAllSurveys";
return httpGetFactory(scope, URL);
}
return {
dsLoadAllSurveys: dsLoadAllSurveys
}
}]);
And, crucially, all "GET" web service calls go via the following function, which displays the Please Wait control for us... then makes it go away when the service has completed.
myApp.factory('httpGetFactory', function ($http, $q) {
return function (scope, URL) {
// This Factory method calls a GET web service, and displays a modal error message if something goes wrong.
scope.$broadcast('app-start-loading'); // Show the "Please wait" popup
return $http({
url: URL,
method: "GET",
headers: { 'Content-Type': undefined }
}).then(function (response) {
scope.$broadcast('app-finish-loading'); // Hide the "Please wait" popup
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function (errorResponse) {
scope.$broadcast('app-finish-loading'); // Hide the "Please wait" popup
// The WCF Web Service returned an error.
// Let's display the HTTP Status Code, and any statusText which it returned.
var HTTPErrorNumber = (errorResponse.status == 500) ? "" : "HTTP status code: " + errorResponse.status + "\r\n";
var HTTPErrorStatusText = errorResponse.statusText;
var message = HTTPErrorNumber + HTTPErrorStatusText;
BootstrapDialog.show({
title: 'Error',
message: message,
buttons: [{
label: 'OK',
action: function (dialog) {
dialog.close();
},
draggable: true
}]
});
return $q.reject(errorResponse.data);
});
};
});
What I love about this code is that this one function looks after displaying/hiding the "Please wait" popup, and if an error occurs, it also looks after displaying the error message (using the excellent BootstrapDialog library), before returning the error result back to the caller.
Without this factory function, each time one of my Angular controllers would call a web service, it would need to show, then hide, the "Please wait" control, and check for errors.
Now, I can just call my web service, and leave it to inform the user if something goes wrong, otherwise I can assume it's all worked, and process the results.
This allows me to have much simpler code. Remember how I called that web service:
DataService.dsLoadAllSurveys($scope).then(function (response) {
// Success
$scope.listOfSurveys = response.GetAllSurveysResult;
});
That code looks as though it's not doing any error-handling, whereas actually, it's all looked after behind the scenes in one place.
I'm still getting the hang of factories and data services with Angular, but I think this is a stonking example of how they can help.
Hope this made sense, and helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With