AngularJs DOM manipulation in directive - angularjs

I have a directive which shows a list of Users with their names as links.
Inside the the template of this directive I have following loop:
<ng-repeat="user in myctrl.users />
<a href="" >{{user.name}}</a>
Now I want to add an attribute directive to all the anchor tags with
with name edit-confirm-popup as shown below.
<ng-repeat="user in myctrl.users />
<a href="" edit-confirm-popup>{{user.name}}</a>
What I want to do is whenever user click on link of user's name I want to show a popup with popup's html inserted as sibling of the anchor element. I don't want to repeat the popup html when directive is compiled rather I want to insert it dynamically when user clicks on the link.
I am able to achieve following things :
1) attaching click event listener on directive element that is anchor element in directive's link function.
Don't know
i) how I should insert the template as I want to?
ii) I want the current user to be available in event listener so that I can check some conditions before I show the popup.
Here is my directive code :
function editConfirmPopup() {
function linkFunction(scope, iElement, iAttrs){
console.log(iElement);
iElement.on('click',function onEditUser(e){
console.log(e);
console.log('in event handler');
});
}
var directiveDefinitionObject = {
restrict: 'A',
scope: {
user: '=',
populateUser : '&'
},
controller: 'UserEditConfirmController',
controllerAs: 'userEdit',
link : linkFunction,
bindToController: true
};
return directiveDefinitionObject;
}
angular
.module('mymodule')
.directive('editConfirmPopup', editConfirmPopup);

ng-if directive conditionally appends (or removes) - not just shows/hides - the elements on which it operates.
Using the fact that ng-repeat creates a child scope for each iteration, you can then just use a simple toggle variable to decide whether to show/hide the additional HTML (that you call "popup"):
<div ng-repeat="user in myctrl.users">
<a ng-click="showPopup = !showPopup">{{user.name}}</a>
<div ng-if="showPopup">
The "popup" HTML
</div>
</div>

Related

AngularJS parent div directive wait for children div stacked directives to complete

I have a portion of html that is like this
<div my-custom-security-directive>
<button ng-if={{some constraints}} ng-click={{logic}}>cancel</button>
<button ng-disabled={{some logic}} ng-click={{some logic}}>submit<button>
</div>
My custom security directive does dom manipulation to the buttons to show/hide them via css. The issue I am having is with timing and perhaps directive precedence? When all the code is finished executing I only see the submit button and not the cancel button. I believe this is because of the ng-if and I attempted to set the priority of my custom directive to a negative number to run last but I think that is only for stacked directives on the same element? I only have a link function defined in my directive which my understanding is that is the same as a post link function and should run once the children complete? Ultimately, my goal is to run my directive 'last' so that I can show both buttons if all the logic in the directive passes.
The shell of my directive :
angular.module('myModule')
.directive("myCustomSecurityDirective", function(a,b) {
//
return {
restrict: "A",
priority: -1,
link: function(scope, element, attrs, ctrl) {
//custom security role/perm logic using injected services a&b etc
if (userHasPermission ) {
element.find('input, textarea, button').addClass('my-show-css');
}
}
};
});
I did recently /today put that priority on the directive but I don't think it does anything in this particular scenario.
Even if my-custom-security-directive is able to attach a CSS class to the button and hide or show it, the button has its own ng-if condition. This means it's possible that the button could be destroyed and recreated later, and it wouldn't have the CSS class anymore. If the button uses ng-show instead of ng-if you may have more control, since the button would become hidden but remain in the DOM.
But I think my-custom-security-directive might want to have more control. You can use transclusion so that my-custom-security-directive acts as a container for each set of elements which should be destroyed or created based on userHasPermission.
Directive:
.directive('myCustomSecurityDirective', function () {
return {
transclude: true,
link: function (scope) {
scope.userHasPermission = true;
},
template: '<div ng-if="userHasPermission" ng-transclude></div>'
}
});
HTML:
<div my-custom-security-directive>
<button ng-if="...">cancel</button>
<button ng-disabled="...">submit</button>
</div>

how to make custom directive in angular?

I am trying to make custom directive in angular .I try to add input field in my view when I click on button .In other words I am trying to make one custom directive in which when user press the button it add one input field in the browser .I think it is too easy if I am not use custom directive Mean If I use only controller then I take one array and push item in array when user click on button and button click is present on controller.
But when need to make custom directive where I will write my button click event in controller or directive
here is my code
http://play.ionic.io/app/23ec466dac1d
angular.module('app', ['ionic']).controller('appcontrl',function($scope){
$scope.data=[]
}).directive('inputbutton',function(){
return {
restrict :'E',
scope:{
data:'='
},
template:'<button>Add input</button> <div ng-repeat="d in data"><input type="text"></div>',
link:function(s,e,a){
e.bind('click',function(){
s.data.push({})
})
}
}
})
I just need to add input field when user click on button using custom directive ..could you please tell me where i am doing wrong ?
can we make button template and click event inside the directive
The reason it doesn't work is because your registering your click handler with jQuery. So when the click handler fires it is out of the scope of angular so angular does not know it needs to update its bindings.
So you have two options, the first is to tell angular in the click handler, 'yo buddy, update your bindings'. this is done using $scope.$apply
$apply docs: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply
e.bind('click',function(){
s.$apply(function() {
s.data.push({});
});
});
However angular already has built in directive for handling things like mouse clicks you can just use that and let angular do the work for you. This would be the better option.
so first in your view register a click handler on your button
<button ng-click="add()">Add input</button> <div ng-repeat="d in data"><input type="text"></div>
Then in your link simply add the add() method of your scope
s.add = function () {
s.data.push({});
}
Heres a working fiddle showing both examples. http://jsfiddle.net/3dgdrvkq/
EDIT: Also noticed a slight bug in your initial click handler. You registering a click but not specifying the button to apply it to. So if you clicked anywhere in the directive, not just the button, the handler would fire. You should be more specific when registering events manually, using ids, class names attributes etc.
The e or element property of the link function is a jqlite or full jQuery object of the entire directive. If you have jQuery included before angular it will be a full jQuery object. If not it will a jqlite object. A thinned out version of jQuery.
Here is a basic example for your logic .
var TestApp = angular.module('App', []);
// controller
TestApp.controller('mainCtrl', function mainCtrl($scope) {
$scope.data = [];
$scope.addDataItem = function () {
$scope.data.push({
someFilield: 'some value'
});
console.log('pushing value ... ');
}
});
// view
<div ng-app="App" class="container" ng-controller="mainCtrl">
<button type="button" ng-click="addDataItem()">Add an input</button>
<div ng-repeat="d in data track by $index">
<custom-directive model="d"></custom-directive>
</div>
</div>
// directive
TestApp.directive('customDirective', function customDirective() {
return {
restrict: 'E',
scope: {
model: '='
},
template: 'item -> <input type = "text" />',
link: function (scope, elem, attrs) {
console.log('scope.model', scope.model);
},
controller: function ($scope) {
// do staff here
}
}
});

angularjs: click on a tag to invoke a function and pass the current tag to it

I need to handle a click on a tag that enables the opening of a popover.
I try to figure out the best way to do this with angularjs and naturally used hg-click.
<div ng-repeat="photo in stage.photos"
ng-click="openPopoverImageViewer($(this))"
>
$scope.openPopoverImageViewer = function (source) {
alert("openPopoverImageViewer "+source);
}
The issue is that I cannot manage to pass the $(this) to it.
Q1) How to pass the jQuery element?
Q2) In addition, ng-click sounds
to require the function being part of the controller: is it possible
to invoke a function in the partial instead?
You need to stop "thinking in jQuery" :)
Like #oori says, you can pass in photo.
Or better yet, create a custom directive. Directives is the way to go when you need new functionality in your dom, like an element that you can click to open an overlay. For example:
app.directive('popOver', function() {
return {
restrict: 'AE',
transclude: true,
templateUrl: 'popOverTemplate.html',
link: function (scope) {
scope.openOverlay = function () {
alert("Open overlay image!");
}
}
};
});
You can then use this as a custom elemen <pop-over> or as an attribute on regular HTML elements. Here is a plunker to demonstrate:
http://plnkr.co/edit/P1evI7xSMGb1f7aunh3G?p=preview
Update: Just to explain transclusion: When you say that the directive should allow transclusion (transclude:true), you say that the contents of the tag should be sent on to the directive.
So, say you write <pop-over><span>This will be passed on</span></pop-over>, then the span with "This will be passed on" is sent to the directive, and inserted wherever you put your ng-transclude element in your template. So if your pop-over template looks something like this:
<div>
<ng-transclude/>
</div>
Then your resulting DOM after the template has compiled will look like this:
<div>
<span>This will be passed on</span>
</div>
Pass it "photo"
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer(photo)">
or the current $index
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer($index)">

Replace the html of an element with the content of an external template in a directive?

I'm trying to create a directive which is a sidebar in my shell page that will change accordingly whenever a new route is hit, and will populate itself with the sub menu items relevant to that parent route. I have 4 different menus which are external templates and i want the contents of those html files to replace the menu, the link function of my directive looks like this so far:
link: function(scope, element, attrs, ngModel) {
scope.$on("$routeChangeSuccess", function (event, current, previous) {
element.html('<div ng-include=\'enterprisesMenu.html\'></div>');
});
};
But the element is not updating, however when i use inline templates the elements updates according, but because each template is complex i prefer not to have that html inside my directive, I've also tried element.html('<div ng-include src=\'enterprisesMenu.html\'></div>');
Any ideas?
Try $compile:
element.html($compile('<div ng-include=\'enterprisesMenu.html\'></div>')
(scope));
You could achieve this result by dynamically ng-including the desired template. For instance:
HTML:
<div class="your-sidebar" ng-controller="SidebarCtrl">
<div ng-include="sidebar.url" ></div>
</div>
Controller:
app.controller("SidebarCtrl", function($scope) {
$scope.sidebar = {
url: "initial-url"
};
$scope.$on("$routeChangeSuccess", function(event, current, previous) {
// decide potentially new value for $scope.sidebar.url
$scope.sidebar.url = newValueCalculatedAbove;
});
});
This solution does not require a directive, only an extra controller. It can be done with directive too, the HTML above is the template of the directive and the JS code the controller (no link function required).

append template partial on click event in directive

I would like to be able to append a template from a partial on the click event of a button multiple times.
I've managed to add a div in the link function below, but I'm stuck. How do i replace it with a template?
Fiddle: http://jsfiddle.net/LPUZG/1/
app.directive('clicker', function($compile) {
'use strict';
return {
restrict: "A",
replace:false,
link: function(scope, element, attributes) {
element.bind("click", function(e){
element.parent().append('<div>hi</div>');
});
}
}
});
<div clicker>Click me</div>
EDIT: This partial contains some form input fields that will be filled out by the user and saved later on.
Thank you
Specifying template or templateUrl in a directive makes the directive replace the element with the content of the template. So your clicker div wont be in existence anymore.
Instead you can use ng-include:
<div ng-include="'partials/template.html'" ng-repeat="item in items"></div>
<!-- on click the add method adds to the items -->
<div ng-click="add()">Click Me To Add</div>

Resources