Weird thing going on with nested directives AngularJS - angularjs

I am having a problem with nested directives, in particular within the hide-element directive.
Firstly, there is an edit-mode directive that does not seem to work with ngAnimate. It would still hit hit addClass fade on button, but would not have ng-animate binded.
I tried moving the hide-element to a sibling element at divColumn and the edit-element directive works and does the animation on both buttons and divColumn.
It just doesn't work when they are nested. Why is that?
(I'm new to AngularJS, kindly enlighten me)
HTML
<!-- ngview has UIController everything here is nested within ngview -->
<h3>Data Page</h3>
<div class="divContainer" ng-mouseenter="fadeToggle()" ng-mouseleave="fadeToggle()">
<div style="overflow:hidden">
<div class="divHeader">Data Name</div>
<div class="divHeader">Parent Data</div>
<div class="divHeaderHidden" hide-element="isHidden">Edit</div>
</div>
<div ng-controller="myController">
<div ng-repeat="data in datas">
<div class="divRow" ng-mouseenter="fadeToggle();" ng-mouseleave="fadeToggle();">
<div class="divColumn">
{{data.name}}
</div>
<div class="divColumn">
{{data.parentData.name}}
</div>
<div class="divColumnHidden" hide-element="isHidden">
<button class="editColumnBtn" ng-click="editToggle()" edit-mode="isEdit">Edit</button>
<button class="removeColumnBtn" edit-mode="isEdit">Remove</button>
</div>
</div>
</div>
</div>
Angular
app.factory('Data', function($http) {
var dataService = {
getDepartment: function() {
var promise = $http.get('http://localhost/orgchart/app/json/data').then(function(response) {
return response.data;
});
return promise;
}
};
return dataService;
});
app.controller('UIController', ['$scope', function fadeController($scope) {
$scope.fadeToggle = function () {
this.isHidden = !this.isHidden;
};
}]);
app.controller('myController', ['$scope', 'Data', function myController($scope, Data) {
Data.getData().then(function(data) {
$scope.data = data;
});
$scope.editToggle = function() {
this.isEdit = !this.isEdit;
};
}]);
app.directive('editMode', ['$animate', function ($animate) {
return function(scope, element, attrs) {
scope.isEdit = false;
scope.$watch(attrs.editMode, function (newVal) {
if(newVal) {
$animate.addClass(element, 'fade');
}
else {
$animate.removeClass(element, 'fade');
}
});
};
}]);
app.directive('hideElement', ['$animate', function ($animate) {
return function(scope, element, attrs) {
scope.isHidden = true;
scope.$watch(attrs.hideElement, function (newVal) {
if(newVal) {
$animate.addClass(element, 'fade');
} else {
$animate.removeClass(element, 'fade');
}
});
};
}]);
app.animation(".fade", function() {
return {
addClass: function(element, className) {
TweenMax.to(element, 0.3, {opacity: 0});
},
removeClass: function(element, className) {
TweenMax.to(element, 0.3, {opacity: 1});
}
};
});

Related

Click only on child element

I made a directive that I insert it in the div parent to restrict some things (such restrictions that I do not put in the example), but I also want to listen when you click on the second element only in the second element, how can I listen when you click?
note: I just want to insert in div father the directive
angular
.module('MyApp', [])
.directive(
'clickMe',
function() {
return {
link: function(scope, element) {
element.on('click', function() {
console.log('click');
});
},
};
}
);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div click-me>
clickeame
<div id="one">
click 2
</div>
</div>
</div>
Change your directive as follows,
angular
.module('MyApp', [])
.directive(
'clickMe',
function() {
return {
link: function(scope, element) {
element.on('click', function(event) {
if (event.target.id == "one") {
console.log('click');
}
});
},
};
}
);
DEMO
angular
.module('DemoApp', [])
.directive(
'clickMe',
function() {
return {
link: function(scope, element) {
element.on('click', function(event) {
if (event.target.id == "one") {
console.log('click');
}
});
},
};
}
);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="DemoApp">
<div click-me>
clickeame
<div id="one">
click 2
</div>
</div>
</div>
This would be an alternative way to add the event listener to only the child
https://codepen.io/anon/pen/eGzBQm
angular
.module('MyApp', [])
.directive(
'clickMe',
function() {
return {
link: function(scope, element) {
element.find("div").on('click', function(event) {
console.log('click');
});
},
};
}
);

How to expose directive methods using a service

How to expose directive methods without using $broadcast or '=' between modules?
Using $broadcast (events) if there are multiple directives all will be notified. It cannot return value too.
Exposing directive's function by html attribute I think it is not that best that Angular has to offer.
Angular Bootstrap UI do it using services (I guess): It have a service named "$uibModal".
You can call a function "$uibModal.open()" of Modal Directive by injecting $uibModal service.
Is that the right way?
An example of a directive that registers its API with a service:
app.service("apiService", function() {
var apiHash = {};
this.addApi = function (name,api) {
apiHash[name] = api;
};
this.removeApi = function (name) {
delete apiHash[name];
};
this.getApi = function (name) {
return apiHash[name];
};
});
app.directive("myDirective", function (apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs)
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.removeApi(name);
});
}
});
Elsewhere in the app, the title of the directive can be set with:
apiService.getApi('myDirective').setTitle("New Title");
Notice that the directive registers the api with a name determined by the name attribute of the directive. To avoid memory leaks, it unregisters itself when the scope is destroyed.
Update
How could I use it from a controller?
app.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<body ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
The DEMO
angular.module('myApp', [])
.service("apiService", function() {
var apiHash = {};
this.addApi = function(name, api) {
apiHash[name] = api;
};
this.getApi = function(name) {
return apiHash[name];
};
})
.directive("myDirective", function(apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs) {
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.addApi(name, null);
});
}
})
.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
this service exposes function charCount();
in your directive you have to inject it like this
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
and, of course call it
$scope.strLen = myService.charCount( $scope.myTestString );
<html>
<style>
#out {
width:96%;
height:25%;
padding:10px;
border:3px dashed blue;
font-family: monospace;
font-size: 15px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var APP = angular.module('MYAPP', []);
APP.controller('main', ['$scope', '$element', '$compile', 'myService', function($scope, $element, $compile, myService) {
$scope.test = 'my Test Controller';
$scope.directiveTest = "directive test";
var testSvc = myService.charCount($scope.test);
$scope.showTestDir = true;
}])
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
.filter('toUpper', function() {
return function(input) {
return input.toUpperCase();
}
})
.filter('toLower', function() {
return function(input) {
return input.toLowerCase();
}
})
;
</script>
<body ng-app="MYAPP">
<div id="out" ng-controller="main">
{{test}} - not filtered
<br/>
{{test|toUpper}} - filtered toUpper
<br/>
{{test|toLower}} - filtered toLower
<br/>
<br/>
<div test-directive ng-if="showTestDir"></div>
</div>
</body>
</html>

How to bind multiple custom events in angularjs?

I need to bind custom events in angularjs(1.x) and I tried with the following code,
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link href="https://www.polymer-project.org/components/polymer/polymer.html" rel="import">
<link href="https://www.polymer-project.org/components/paper-button/paper-button.html" rel="import">
<div ng-app="demo-app">
<div ng-controller="DemoController">
<template bind-angular-scope is="auto-binding">
<paper-button raised on-tap="{{clickMe}}" on-mouseover="{{mouseOver}}">click me</paper-button>
</template>
<pre><code>{[{text}]}</code></pre>
</div>
</div>
Script
<script>
angular.module('demo-app', [])
.config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
})
.directive('bindAngularScope', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
for (k in scope) {
if (!element[0][k]) {
element[0][k] = scope[k];
}
}
}
}
})
.controller('DemoController', function ($scope) {
$scope.text = '';
$scope.clickMe = function () {
$scope.text += '\nyou clicked me!!';
$scope.$apply();
};
$scope.mouseOver = function () {
$scope.text += '\nyou hovered me!!';
$scope.$apply();
}
});
</script>
This is not working.Could you point out me the issue or Is there is any solution for binding custom events(multiple) ? Do we need to create a custom directive for each of them ?
Note:
The above code is referred from the following url,
How to bind custom events in AngularJS?
Thanks in advance!
angular.module('demo-app', [])
.config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
})
.directive('bindAngularScope', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
for (k in scope) {
if (!element[0][k]) {
element[0][k] = scope[k];
}
}
elem.bind('click', function() {
/* Place your click logic here * /
});
}
}
})

Make directive function accessible in parent scope without events

I have a directive with an isolated scope and want to call its function to update data from the parent controller without using events.
var myApp = angular.module('MyApp',[]);
myApp.directive('myDirective', function() {
return {
scope: {},
link: function(scope) {
scope.update = function() {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.updateDirective = function() {
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
You could apply this solution.
In this way you are passing a variable in two way binding:
my-directive="myFunction" in the html
and myFunction: '=myDirective' in the directive)
Then assign the function in the directive:
scope.myFunction = function () {
alert('Directive updated!');
}
In this way you can use a function defined in a directive.
var myApp = angular.module('MyApp', []);
myApp.directive('myDirective', function () {
return {
scope: {
myFunction: '=myDirective'
},
link: function (scope) {
scope.myFunction = function () {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.myFunction = {};
$scope.updateDirective = function () {
console.log( $scope.myFunction );
$scope.myFunction();
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button> <span my-directive="myFunction"></span>
</div>
You could tackle this issue by introducing a new directive that is required by your isolated directive. Conveniently, you can assign the controller to this new directive.
Once required you then 'register' your isolated directive to the 'parent' directive as the target for your function. In the code snippet below I only provided a way to add 1 directive, but you could easily extend this to be an array of child directives. A good of example of such a setup are tabs, where each tab is a child directive of a common tabs directive.
angular.module("MyApp", []);
angular.module('MyApp').directive("myParentDirective", function(){
return {
controller: function ($scope) {
var childUpdate;
this.registerChild = function(_childUpdate_){
childUpdate = _childUpdate_;
};
$scope.updateDirective = function() {
childUpdate();
};
}
};
});
angular.module('MyApp').directive('myDirective', function() {
return {
require: '^myParentDirective',
scope: {},
link: function(scope, element, attrs, myParentController) {
myParentController.registerChild(update);
function update() {
alert('Directive updated!');
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div my-parent-directive>
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
</div>

What is the correct AngularJS 1.2 way to implement this directive that triggers this animation?

I created a directive that tweens like so:
app.directive('fadeInDown', function ($animate) {
var directive = {
link: link,
restrict: 'A',
};
return directive;
function link(scope, element, attrs) {
scope.$watch(attrs.delay, function (newVal) {
TweenMax.set(element, { position: 'relative' });
TweenMax.from(element, attrs.speed, { opacity: 0, top: '-20px', delay: attrs.delay, onComplete: done });
function done() {
var test = "DONE";
}
});
};
});
And I call it from my view like this:
<div id="home-view" class="body-wrapper" data-ng-controller="home as vm">
<div class="container">
<div class="row">
<div class="col-md-12 col-sm-12" data-fade-in-down data-delay=".2" data-speed="1">
<div class="callout-box clearfix">
<div class="callout-content">
<h2 class="h1-section-title">BLAH BLAH BLAH.</h2>
</div>
</div>
</div>
</div>
</div>
The animation works perfectly, but I wanted to know how do I do this using $animate with angular 1.2.13???
I have tried the following:
app.directive('fadeInDown', function ($animate) {
var directive = {
link: link,
restrict: 'A'
};
return directive;
function link(scope, element, attrs) {
scope.$watch(attrs.hideMe, function () {
$animate.addClass(element, "fade-in");
});
}
});
But how do you pass the parameters back to the animation????
The class is loaded in another module like this:
(function () {
'use strict';
var app = angular.module('app');
app.animation('.fade-in', function () {
return {
addClass: function(element, done) {
TweenMax.set(element, { position: 'relative' });
TweenMax.from(element, attrs.speed, { opacity: 0, top: '-20px', delay: attrs.delay, onComplete: done });
}
};
});
})();
Can anyone explain how to get this right???
PLEASE LET ME KNOW IF MY QUESTION IS NOT CLEAR.
Thanks!!!

Resources