Angular-ui tooltip with HTML - 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>
Related
Dynamic ui-sref in directive template Angular Js
In my angular js Application, I have a directive inside a ng-repeat. Directive has a template in which the HTML part is loaded. Inside that template I am declaring a ui-sref Router dynamically setting states! This does not Work! I have tried a Fiddle Without using a Directive ui-sref works Fine. But I need it in Directive's template. MY HTML PART <div ng-repeat="sp in obj.PEs"> <div draw-pe proc="{{sp.peId}}"></div> </div> <div style="border:1px;" ui-view="properties"></div> My Script Part var myApp = angular.module('myApp', ["ui.router"]); myApp.controller("testCtrl", function($scope) { $scope.obj = { "PEs": { "1": { "peId": "1", "peName": "Exp1", "peDisplayName": "Exp", "peType": "Exp", "peCategory": "PE" }, "2": { "peId": "2", "peName": "RN1", "peDisplayName": "RNull", "peType": "RN", "peCategory": "PE" } } } }) myApp.config(function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/'); $stateProvider .state('Exp', { url: '/Exp/:peId', views: { "properties": { template: ".<h3>I am Exp ! </h3>", controller: function($scope, $stateParams) { var peId = $stateParams.peId; alert("Peid-> " + angular.toJson($scope.obj.PEs[peId])); } } } }) .state('RN', { url: '/RN/:peId', views: { "properties": { template: "<h3> I am RN ! </h3>", controller: function($scope, $stateParams) { var peId = $stateParams.peId; alert("Peid-> " + angular.toJson($scope.obj.PEs[peId])); } } } }) }); myApp.directive("drawPe", function() { return { restrict: "AE", template: '<div ui-sref="{{peObj.peType}}( { peId:peObj.peId } )"> <h5>{{peObj.peDisplayName}}</h5></div>', link: function($scope, element, attrs) { var procId = $scope.$eval(attrs.proc); alert(procId); // alert(angular.toJson(scope.obj.processElements[procId])) $scope.peObj = $scope.obj.PEs[procId]; } } }) See the Browser Console, on clicking the output Part! ERROR Error: Invalid state ref '( { peId:peObj.peId } )' What will be the best Practice to call dynamic state names inside directives Template? I Have read some previous asked question and answer, But I am not clear with the idea as I am new to Angular Js. Any Idea/help appreciated Thanks
You can use $compile service to compile your directive's template when your scope changes. Your directive could look like this: myApp.directive("drawPe", function($compile) { return { restrict: "AE", tranclude: true, scope: { peObj: '=' }, template: ' <h5>{{peObj.peDisplayName}}</h5>', link: function(scope, element, attrs) { console.log(scope.peObj); scope.$watch( function(scope) { // watch the 'compile' expression for changes //return scope.$eval(attrs.compile); }, function(value) { $compile(element.contents())(scope); } ); } } }); And your html: <div ng-repeat="sp in obj.PEs"> <div draw-pe pe-obj="sp" proc="{{sp.peId}}"></div> </div> <div style="border:1px;" ui-view="properties"></div> Working fiddle is here.
Angular js $parse does not work
I have written a directive by using typescript. here is my directive below. 'use strict'; module App.Directives { interface IPageModal extends ng.IDirective { } interface IPageModalScope extends ng.IScope { } class PageModal implements IPageModal { static directiveId: string = 'pageModal'; restrict: string = "A"; constructor(private $parse: ng.IParseService) { } link = (scope: IPageModalScope, element, attrs) => { element.click((event) => { event.preventDefault(); var options = { backdrop: 'static', keyboard: false }; event.openModal = function () { $('#' + attrs['targetModal']).modal(options); }; event.showModal = function () { $('#' + attrs['targetModal']).modal('show'); }; event.closeModal = function () { $('#' + attrs['targetModal']).modal('hide'); }; var fn = this.$parse(attrs['pageModal']); fn(scope, { $event: event }); }); } } //References angular app app.directive(PageModal.directiveId, ['$parse', $parse => new PageModal($parse)]); } Use in HTML <button class="btn blue-grey-900" target-modal="emplpyeeViewModal" page-modal="vm.addEmployee($event)"> <i class="icon-plus m-b-xs"></i> </button> Use in Controller addEmployee($event) { $event.openModal(); }; This line does not work. var fn = this.$parse(attrs['pageModal']); . I can not understand what is wrong. The error is this.$parse is undefined. and Service is called two times
It's quite trivial: your this is not your class'es scope because the function openmodal(event) { defines its own. Declare the function on a class level or use arrow function instead, e.g. link = (scope: IPageModalScope, element, attrs) => { element.click((event) => { event.preventDefault(); var options = { backdrop: 'static', keyboard: false }; event.openModal = function () { $('#' + attrs['targetModal']).modal(options); }; event.showModal = function () { $('#' + attrs['targetModal']).modal('show'); }; event.closeModal = function () { $('#' + attrs['targetModal']).modal('hide'); }; var fn = this.$parse(attrs['pageModal']);//does not work here fn(scope, { $event: event }); }); }
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
ion-slide-box issues with modal in Ionic App
I am using ion-slider to show images and after clicking slide image get show full Image in ion-slider to sliding full image. But I have problem when I back directly first full image to home screen then slider not working. I have used following views and controllers. Home Template View : <ion-view view-title="Home"> <ion-content> <div class="padding"> <ion-slide-box> <ion-slide ng-repeat="Image in gallery" ng-click="showImages($index)" repeat-done="repeatDone()"> <img data-ng-src="{{sdcardpath}}{{Image.Path}}" width="100%"/> <ion-slide> </ion-slide-box> </div> </ion-content> </ion-view> Controller : .controller('HomeController', function($scope,$cordovaFile,$ionicBackdrop, $ionicModal, $ionicSlideBoxDelegate, $ionicScrollDelegate, $ionicPopup, $timeout) { $scope.sdcardpath = cordova.file.externalRootDirectory + foldername; $scope.gallery = [ { Path: 'img1.png' }, { Path: 'img2.png' }, { Path: 'img3.png' }, ]; $scope.repeatDone = function() { $ionicSlideBoxDelegate.update(); }; /* * Show Full Screen Sliding Gallery Images */ $scope.showImages = function(index) { $scope.activeSlide = index; $scope.showModal('templates/image-popover.html'); }; $scope.showModal = function(templateUrl) { $ionicModal.fromTemplateUrl(templateUrl, { scope: $scope }).then(function(modal) { $scope.modal = modal; $scope.modal.show(); }); }; $scope.zoomMin = 1; $scope.updateSlideStatus = function(slide) { var zoomFactor = $ionicScrollDelegate.$getByHandle('scrollHandle' + slide).getScrollPosition().zoom; if (zoomFactor == $scope.zoomMin) { $timeout(function(){ $ionicSlideBoxDelegate.enableSlide(true); }); } else { $timeout(function(){ $ionicSlideBoxDelegate.enableSlide(false); }); } }; } Modal View : image-popover.html <div class="modal image-modal"> <ion-slide-box active-slide="activeSlide" show-pager="false"> <ion-slide ng-repeat="Images in gallery"> <ion-scroll direction="xy" locking="false" scrollbar-x="false" scrollbar-y="false" zooming="true" min-zoom="{{zoomMin}}" style="width: 100%; height: 100%" delegate-handle="scrollHandle{{$index}}" on-scroll="updateSlideStatus(activeSlide)" on-release="updateSlideStatus(activeSlide)"> <img data-ng-src="{{sdcardpath}}{{Images.Path}}" width="100%"/> </ion-scroll> </ion-slide> </ion-slide-box> </div> And Directive : repeatDone .directive('repeatDone', function () { return function (scope, element, attrs) { if (scope.$last) { // all are rendered scope.$eval(attrs.repeatDone); } } So please help to solve this issues.
I got solution by some changes in Controller. Controller .controller('HomeController', function($scope,$cordovaFile,$ionicBackdrop, $ionicModal, $ionicSlideBoxDelegate, $ionicScrollDelegate, $ionicPopup, $timeout) { $scope.sdcardpath = cordova.file.externalRootDirectory + foldername; $scope.gallery = [ { Path: 'img1.png' }, { Path: 'img2.png' }, { Path: 'img3.png' }, ]; $scope.repeatDone = function() { $ionicSlideBoxDelegate.update(); }; /* * Show Full Screen Sliding Gallery Images */ $scope.showImages = function(index) { $scope.activeSlide = index; $scope.showModal('templates/image-popover.html'); }; $scope.showModal = function(templateUrl) { $ionicModal.fromTemplateUrl(templateUrl, { scope: $scope }).then(function(modal) { $scope.modal = modal; $scope.modal.show(); }); }; //Cleanup the modal when we're done with it! $scope.$on('$destroy', function() { $scope.modal.remove(); $ionicSlideBoxDelegate.enableSlide(true); $ionicSlideBoxDelegate.update(); }); // Execute action on hide modal $scope.$on('modal.hidden', function() { // Execute action $ionicSlideBoxDelegate.enableSlide(true); $ionicSlideBoxDelegate.update(); }); // Execute action on remove modal $scope.$on('modal.removed', function() { // Execute action $ionicSlideBoxDelegate.enableSlide(true); $ionicSlideBoxDelegate.update(); }); $scope.zoomMin = 1; $scope.updateSlideStatus = function(slide) { var zoomFactor = $ionicScrollDelegate.$getByHandle('scrollHandle' + slide).getScrollPosition().zoom; if (zoomFactor == $scope.zoomMin) { $timeout(function(){ $ionicSlideBoxDelegate.enableSlide(true); }); } else { $timeout(function(){ $ionicSlideBoxDelegate.enableSlide(false); }); } }; }
How to eval an attribute containing {{}}
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); } } } })