I are trying to bind a $scope.model.value to a popover.
With all the articles I have something working by storing a copy in the $scope then using a watch to update it if it changes who ever that is not a viable solution as I want to take the popover and use it multiple times with different arrays.
my html on the the main body is:
<button href="#" colorpopover
type="button"
class="btn btn-success btn-rainbow"
data-toggle="popover"
data-trigger="click"
title="Button Color"
id="static-color-popover-{{$id}}"
ng-model="model.value.buttonStatic.buttonColor">
Button Color
</button>
my controller has this code
app.directive('colorpopover', function ($compile, $templateCache, $q, $http) {
var getTemplate = function () {
var def = $q.defer();
var template = '';
template = $templateCache.get('/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html');
if (typeof template === "undefined") {
$http.get("/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html").then(function (data) {
$templateCache.put("templateId.html", data);
def.resolve(data);
});
} else {
def.resolve(template);
}
return def.promise;
}
return {
restrict: "A",
link: function (scope, element, attrs, model) {
getTemplate().then(function (popOverContent) {
// Make sure to remove any popover before hand (please confirm the method)
var compileContent = $compile(popOverContent.data)(scope);
var options = {
bindToController: true,
content: compileContent,
placement: "left",
html: true,
date: scope.date, };
$(element).popover(options);
});
}
};
});app.directive('colorpopover', function ($compile, $templateCache, $q, $http) {
var getTemplate = function () {
var def = $q.defer();
var template = '';
template = $templateCache.get('/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html');
if (typeof template === "undefined") {
$http.get("/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html").then(function (data) {
$templateCache.put("templateId.html", data);
def.resolve(data);
});
} else {
def.resolve(template);
}
return def.promise;
}
return {
restrict: "A",
link: function (scope, element, attrs, model) {
getTemplate().then(function (popOverContent) {
// Make sure to remove any popover before hand (please confirm the method)
var compileContent = $compile(popOverContent.data)(scope);
var options = {
bindToController: true,
content: compileContent,
placement: "left",
html: true,
date: scope.date, };
$(element).popover(options);
});
}
};
});
the template in basic terms
is
<div class="row">
<div ng-repeat="colorGroup in model.value" ng-class="!$last ? '' : 'last'">
<p>{{colorGroup.val1}}</p>
<p>{{colorGroup.val2}}</p>
<p>{{colorGroup.val3}}</p>
the model structure is
$scope.model.value.buttonStatic.buttonColor[val1,val2,val3]
$scope.model.value.buttonStatic.buttonHover[val1,val2,val3]
$scope.model.value.buttonStatic.buttonFocus[val1,val2,val3]
so eventually I want three buttons as a above with each of the tree values passed.
so at present the ng-repeat is repeating on the model.value of the child scope which is a direct copy of the parent.
the value in the template is going to change on the popover so it needs to go back to the parent.
With much trial and error I have solved the problem.
in the directive I needed to tie add these 2 line before the link.
require: 'ngModel', // ensures the model is passed in
scope: { model: '=ngModel' }, //ties the ng-model to the scope of the popover
then all I needed to do was set the ng-model in my button to model.value......
and in the popover template i just use model
Related
Using the angular directive Max created on this post for easily importing SVGs, I've imported a handful of SVGs on my page. I now want to add a click event to an SVG, except the directive doesn't transfer the click method to the imported SVG. If I inspect the SVG in my browser I see that it is indeed missing the ng-click.
HTML
<svg-image class="svg foo" src="img/foo.svg" ng-click="bar()"></svg-image>
JS
$scope.bar = function() {
console.log("click");
};
If I move ng-click="bar()" to another element on my page it works just fine. I've also tried moving ng-click="bar()" to the svg file itself which didn't work, and I've tried doing what was suggested in this post which didn't work either.
plunker as requested: https://plnkr.co/edit/eqOZJO5Ar8oOmXCjg3Vs
One of possible solutions is to compile your new element and call resulting template function, passing in scope:
.directive('svgImage', ['$http', '$compile', function($http, $compile) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
var imgURL = element.attr('src');
// if you want to use ng-include, then
// instead of the above line write the bellow:
// var imgURL = element.attr('ng-include');
var request = $http.get(
imgURL,
{'Content-Type': 'application/xml'}
);
scope.manipulateImgNode = function(data, elem){
var $svg = angular.element(data)[4];
var imgClass = elem.attr('class');
if(typeof(imgClass) !== 'undefined') {
var classes = imgClass.split(' ');
for(var i = 0; i < classes.length; ++i){
$svg.classList.add(classes[i]);
}
}
$svg.removeAttribute('xmlns:a');
angular.element($svg).attr("ng-click", attrs.ngClick);
return $compile($svg)(scope);
};
request.success(function(data){
element.replaceWith(scope.manipulateImgNode(data, element));
});
}
};
}]);
Plunker
Try this
var jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.bar = function() {
console.log("click");
};
});
jimApp.directive('svgImage', function() {
return {
restrict: 'E',
replace: true,
scope: {
onClick: '&'
},
template: '<div ng-click="bar();">Hai</div>',
link: function(scope, element, attrs, fn) {
scope.bar = function(){
scope.onClick()();
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
asas
<svg-image on-click="bar"></svg-image>
</div>
i'm trying to create a layer on top of the md-chips called chip-filter
Which has some extra functionality build in;e.g.
a navigation chip (when removed, redirects the user back to a specific url)
add from any place n the app a chip
...
So I got the basics working, hooking in the componentsRegistry, and being able to call it from any place.
But now I'm trying to get the chips from the chipFilterController into the <md-chips>
html:
<chip-filter md-component-id="testId">
<md-chips ng-model="chips">
<md-chip-template>
<strong>{{$chip}}</strong>
<em>(type)</em>
</md-chip-template>
</md-chips>
</chip-filter>
and directive:
function chipFilterDirective($log) {
function postLink(scope, element, attr, sidenavCtrl) {
element.on("$destroy", function() {
sidenavCtrl.destroy();
});
}
return {
restrict: "E",
scope: {},
controller: "chipFilterController",
compile: function(element) {
return postLink;
}
};
}
and controller:
function chipFilterController($scope, $element, $attrs, $mdComponentRegistry, $q, $log) {
var self = this;
$scope.chips = [];
function addChip(input, type) {
var def = $q.defer();
$scope.chips.push({
name: input,
type: type
});
def.resolve();
return def.promise;
}
self.addNavigationChip = function() {
return addChip("stuff");
};
self.addChip = function() {
return addChip("stuff");
};
self.destroy = $mdComponentRegistry.register(self, $attrs.mdComponentId);
}
full codepen:
http://codepen.io/cskiwi/pen/eJryqK?editors=1010
setting the scope to false (or removing it from the object) made it work
I am trying to develop a FaceBook like notification (like when friend requests are received there is an icon that glows with number of notifications on the top right corner).
For this i wrote a popover directive.
app.directive('popOver', function ($compile) {
var itemsTemplate = "<div ng-repeat='item in items'>{{item}} <br/><hr/></div> ";
var getTemplate = function (contentType) {
var template = '';
switch (contentType) {
case 'items':
template = itemsTemplate;
break;
}
return template;
}
return {
restrict: "A",
transclude: true,
template: "<span ng-transclude></span>",
link: function (scope, element, attrs) {
var popOverContent = "<div></div>";
if (scope.items) {
var html = getTemplate("items");
popOverContent = $compile(html)(scope);
}
var options = {
content: popOverContent,
placement: "bottom",
html: true,
title: scope.title
};
$(element).popover(options);
},
scope: {
items: '=',
title: '#'
}
};
});
The items are populated in the Controller, and there i am using $timeout to fetch new data from database and fill scope.Items
In the UI i have a button which shows number of new notifications and on click of it i want to show a popover with items. The problem is when is click the button i the popover is not loading the new items.
<button pop-over items="items" class="navbar-btn btn-info round-button" title="Notifications:" > {{newAlertCount}} </button>
Directives have their own scope, so I'm supposing that when you change $scope.items in your controller you're talking about a different scope; I think that what you want is to look directly at the original $scope.items object, so I would add this:
scope : {
items : '=items'
},
to your directive.
I have a directive that is almost an exact copy of the old uiIf directive from the angular-ui project.
What is happening is that when I add my "restrict" directive, the button is successfully added/removed based upon what the user is authorized to do.
The problem is that the ng-click action no longer works. It doesn't call in to the controller's scope and trigger the function to be called. Does anyone see what might be causing my issue?
See: http://plnkr.co/edit/38UeVCCYkdzxBkxOLe5g?p=preview
<button restrict="'canPerformAction'" ng-click="action()">Action</button>
'use strict';
angular.module('directives.restrict', [])
.directive('restrict', function(_){
return{
transclude: 'element',
prioriry: 1000,
terminal: true,
restrict: 'A',
compile: function(element, attr, transclude) {
var user = { caps: [ 'canPerformAction', 'canDance', 'canWrite' ] };
return function(scope, element, attr) {
var childElement;
var childScope;
scope.$watch(attr.restrict, function(attributes) {
if (childElement) {
childElement.remove();
childElement = undefined;
}
if (childScope) {
childScope.$destroy();
childScope = undefined;
}
if(_.intersection(user.caps, attributes.split(' ')).length > 0) {
childScope = scope.$new();
transclude(childScope, function(clone) {
childElement = clone;
element.after(clone);
});
}
});
};
}
};
});
I am using bs-popover to display my contents on click(as a menu) in angularjs. But I need to hide this popover-menu when I click somewhere in the browser window. I want it to be dismissed on that type of event. How can I do that?
You need to write directive for this.
yourApp.directive('bndocumentclick',
function($document,$rootScope,$timeout) {
return {
restrict: 'EA',
link : function(scope, element, attrs) {
$document.on("click", function(ev) {
// Do stuff here to remove your popover.
}
}
}
});
HTML
<body bndocumentclick>
And
<div bs-popover ng-click="$event.stopPropagation()">
You need to use because you would not like to close your popover whenever user clicks inside popover.
The solution provied by #Jay Shukla doesn't work.
The "$event.stopPropagation()" on the element that triggers the popover doesn't stops it from closing when you make a click inside the popover.. if you have some interaction inside your popover this will be a problem.
This works:
angular.module('yourApp')
.directive('closePopovers', function ($document, $rootScope, $timeout) {
return {
restrict: 'EA',
link: function (scope, element, attrs) {
$document.on('click', function (ev) {
var targetElem = angular.element(ev.target);
if (targetElem.data('toggle') !== 'popover'
&& targetElem.parents('[data-toggle="popover"]').length === 0
&& targetElem.parents('.popover').length === 0) {
$('.popover').each(function () {
var $this = $(this);
var scope = $this.scope();
scope.$apply(function () {
scope.$hide();
});
}
);
}
});
}
};
});
On your body:
On your element that triggers the popover:
<button data-toggle="popover" [other data elements here] bs-popover>Toggle popover</button>