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.
Related
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
I made a directive that display a list with an 'add' button. That table is represented twice in the same page but with different forms and table-data:
<list ng-model="list1" form-name="form1"></list>
<list ng-model="list2" form-name="form2"></list>
In the link function, I define an 'add' function that will use the form and the model specified in the directive declaration:
scope: {
list: '=ngModel',
formName: '#',
},
link: function postLink(scope, element, attrs) {
scope.add = function() {
FormService.show(formName).then(function(item) {
list.push(item);
});
}
var addButton = angular.element('<a ng-click="add()">ADD ITEM</a>');
var container = angular.element('<div></div>');
container.append($compile(addButton)(scope));
element.replaceWith(container);
The problem is that the add function never get called by the 'ADD ITEM' link...
** I cannot use the template configuration object because I need the template to by dynamic
You can dynamically build your template by providing a function that returns a string to the template property of your directive:
scope: {
list: '=ngModel',
formName: '#',
},
template: function(element, attrs) {
var addButton = angular.element('<a ng-click="add()">ADD ITEM</a>');
var container = angular.element('<div></div>');
container.append(addButton);
var str = container.prop('outerHTML');
return str;
},
link: function postLink(scope, element, attrs) {
scope.add = function() {
FormService.show(formName).then(function(item) {
list.push(item);
});
}
try this:
element.append('<a ng-click="add()">ADD ITEM</a>');
$compile(element.contents())(scope)
DEMO
Imagine I have some markup, e.g.:
<my-input model="data.firstName"></my-input>
Now, I would like to create a my-markup directive that will add a button to show/hide its markup.
So, this:
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
should result in this:
and when the button is clicked, the markup should appear:
The my-markup directive should not break any data bindings of its children.
Here is my attempt to implement this.
The markup appears, but the button doesn't work. Any ideas how to fix this?
PLAYGROUND HERE
Here is my approach. Couple of things:-
1) Instead of isolated scope on myMarkup, create a child scope, ultimately the actual directive myInput will be isolated. This would be required if you do need to support multiple myMarkup directive under the same scope.
2) You need a click event on the button, i wouldn't do logic on the markup instead abstract out to a method on the scope.
3) You would just need one button, do not need 2 buttons. Just change the text of the button.
.directive('myMarkup', function($compile) {
return {
restrict: 'A',
scope: true, //Create a child scope
compile: function(element) {
//Just need one button
var showButton = '<button ng-click="toggleMarkup()">{{model.showMarkup ? "Hide": "Show"}} Markup</button>';
var markup = '<pre ng-show="model.showMarkup">' + escapeHtml(element.html()) + '</pre>';
//append the markups
element.append(showButton).append(markup);
return linker;
}
};
function linker(scope, element) {
scope.model = {
showMarkup: false
};
//Click event handler on the button to toggle markup
scope.toggleMarkup = function(){
scope.model.showMarkup = !scope.model.showMarkup;
}
};
});
Demo
Please see below
function escapeHtml(html) {
return html.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
angular.module('App', []).controller('AppCtrl', function($scope) {
$scope.data = {
firstName: 'David'
};
}).directive('myInput', function() {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<input class="my-input" type="text" ng-model="model">'
};
}).directive('myMarkup', function() {
return {
restrict: 'A',
scope: {},
link: function(scope, elem, attr) {
},
compile: function(element) {
var showButton = '<button ng-if="data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Hide Markup</button>';
var hideButton = '<button ng-if="!data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Show Markup</button>';
var markup = '<pre ng-if="data.showMarkup">' + escapeHtml(element.html()) + '</pre>';
element.append(showButton);
element.append(hideButton);
element.append(markup);
return function(scope, element) {
scope.data = {
showMarkup: true
};
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="AppCtrl">
<pre>data = {{ data | json }}</pre>
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
</body>
in my last design, i create my own template without bootstrap.
and i inject my code with angularjs.
So here's my case:
i want to show my data form in editing template and i need modal to show
there's a way to build my own modal directive?
i have my code like this:
angular.directive('myModal', function() {
var template = '<div class="modal" ng-show="viewModal">modal here!<a ng-click="close()">Close</a></div><a ng-show="showModal" ng-hide="viewModal" ng-click="view()">Click me</a>';
return {
scope: {
title: '=',
template: '='
},
link: function(scope, element, attrs) {
scope.view = function() {
scope.viewModal = true;
}
scope.close = function() {
scope.viewModal = false;
}
}
}
});
thanks for the answer
I'm trying to write a directive that has a template. The template is rendering some DOM elements I want to retrieve. However, when I try to retrieve my DOM elements in the linking function, the DOM elements are not found. If I add a window.setTimeout method before selecting the elements they are found. How can I wait for a template to finish rendering before trying to manipulate the DOM in the linking function?
Here is the directive code for what I'm trying to do:
module.directive('testLocationPicker', function() {
var linkFn = function(scope, element, attrs) {
console.log('in linking function');
window.setTimeout(function() {
var positions = $('.position');
console.log('number positions found: ' + positions.length);
positions.click(function(e) {
console.log('position clicked');
scope.$apply(function() {
scope.selectedPosition = $(e.currentTarget).html();
});
});
}, 500);
};
return {
link: linkFn,
restrict: 'E',
template: 'Choose a position: <div class="position" ng-repeat="position in positions">{{position}}</div>',
}
});
I have a JS Fiddle of what I'm trying to do: http://jsfiddle.net/bdicasa/XSFpu/42/
I would recommend doing something like this instead:
var module = angular.module('test', []);
module.controller('TestController', function($scope) {
$scope.positions = [
'Test Position 1',
'Test Position 2',
'Test Position 3'
];
$scope.selectedPosition = '';
$scope.handleClick = function (index) {
$scope.selectedPosition = $scope.positions[index];
}
});
module.directive('testLocationPicker', function() {
return {
restrict: 'E',
template: 'Choose a position: <div class="position" ng-repeat="position in positions" ng-click="handleClick($index)">{{position}}</div>',
}
});
Instead of trying to search through the dom and add a click event, just modify your template like this:
template: 'Choose a position: <div class="position" ng-repeat="position in positions" data-ng-click="positionClick($index)">{{position}}</div>',
And then create a positionClick function in the linking function:
var linkFn = function(scope, element, attrs) {
scope.positionClick = function(index){
scope.selectedPosition = index;
}
};
Working jsFiddle: http://jsfiddle.net/XSFpu/77/
The reason your method is not working is because the ng-repeat hasn't fired after the template has loaded. So it's loaded the directive in, and the link function has been hit, but the ng-repeat actually hasn't started repeating yet. This is why I'm suggesting moving some of your code around to accomdate that.