Angular How Can I only trigger child element event - angularjs

<div ng-click="process1()" id="1">
<div ng-click="process2()" id="2">
</div>
</div>
When I click the div which id is two, it will both trigger process1 and process2,How can I just trigger process2?

You can use directive for this. It will be more generalise to stop event propagation.
HTML
<div ng-click="process1()" id="1">
<div ng-click="process2()" id="2" stop-event>
</div>
In controller:
$scope.process1 = function(){
alert(1);
}
$scope.process2 = function(){
alert(2);
}
and just use a directive
.directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.bind('click', function (e) {
e.stopPropagation();
});
}
}
jsFiddle: http://jsfiddle.net/6m290Lbt/3/
If you wanted, you could make this solution more generic like this answer to a different question: https://stackoverflow.com/a/14547223/347216

I think you should pass the $event from the html in the ng-click of id 2 element and then call the stopPropagation method of that event in the controller.
HTML -
<div ng-click="process1()" id="1">
<div ng-click="process2($event)" id="2">
</div>
</div>
and in the controller -:
app.controller('MainCtrl', function($scope) {
$scope.process1 = function(){
alert(1);
}
$scope.process2 = function(event){
event.stopPropagation();
alert(2);
}
});
JSFiddle - http://jsfiddle.net/SSHYK/131/
Thanks!

One solution is to use $event.stopPropagation. Also you should avoid to name elements id with numbers.
<div ng-click="process1();$event.stopPropagation();" id="div1">
event.stopPropagation:
Description: Prevents the event from bubbling up the DOM tree,
preventing any parent handlers from being notified of the event.
plunker
References
event.stopPropagation()

Related

How to pass the value to the local scope of the directive?

I'm learning directives, it's cool thing but sometimes a little complicated. Please can somebody explain this:
I have custom directive with template of little form and it own local scope, and want to change the list of items form the main controller.
Please see it:
By clicking on change button I open a custom directive with input form template
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} </div>
<button ng-click="edit()">Change</button>
<change ng-if='editable'></change>
</li>
</ul>
</body>
"Change" is the custom directive with the input form inside the other Html file
.directive('change', function(){
return {
restrict: "E",
replace: true,
scope: {
show: '='
},
templateUrl: "other.html"
}
})
Also there is another directive inside "change" directive. It's a button which I want to use inside "change" directive and inside my main controller. I can see my item list only from scope.$parent.item, but how to pass it in the function of my button directive?
How can I implement this?
.directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item)">SAVE</button>',
link: function(scope,element,attr){
scope.saving = function(item){
console.log(item);
console.log(scope.$parent.item)
}
}
}
})
Please see the example: Plnkr
P.S. Sorry for my explanation, I hope that everything is clear
Simply pass in the item to each of your directives that need access to it. For example:
<li ng-repeat="item in list">
//snip
<save item="item"></save>
//snip
</li>
And then define your directive to bind the attribute to the scope:
.directive('save', function(){
return {
//snip
scope: {
item: '=' //two-way binding to 'scope.item'
},
//snip
link: function(scope, element, attr){
scope.saving = function() {
console.log(scope.item);
}
};
});
In angularjs, you have the $emit event.
Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.
$rootScope.Scope
HTML
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} - <input type="text" ng-model="item">
<button ng-click="edit()">Change</button>
</div>
<div>
<change ng-if='editable'></change>
</div>
</li>
</ul>
</body>
Directive
directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item, $parent.$index)">SAVE</button>',
link: function(scope,element,attr, controller){
scope.saving = function(item, index){
//Build our object with the index of $scope.list which is updated & the item value
var obj = {
index: index,
item: item
};
//Emit a 'change' event, and we pass our object data
scope.$emit('change', obj)
}
}
}
})
In the "change" directive, we use $emit to pass event, and to notify our $rootScope.Scope.
In the "change" directive template, you can see that we pass the $parent.$index and not the $index, in order to get the current item of the list.
Controller
controller('testCtrl', function($scope){
$scope.list = [1,2,3,4,5,6,7,8,9];
//Listen for 'change' event
$scope.$on('change', function(event,value){
//Set to the list value.index our value.item
$scope.list[value.index] = value.item;
});
$scope.editable = false;
$scope.edit = function(){
$scope.editable = !$scope.editable;
}
})

angularjs directive calls image effect script

I have a directive to make news item to have an effect like usatoday.com when user hover on the news. I'm new to angularjs :D
Directive:
app.directive('hotEvent', ['$rootScope', function ($rootScope) {
return {
restrict: 'EA',
templateUrl: '/App/Main/views/home/events.html',
link: function (scope, iElement, attrs) {
//attrs references any attributes on the directive element in html
var dathumb = $(iElement).find('.da-thumbs > li a');
//$(function () {
dathumb.each(function () {
$(this).hoverdir();
});
//});
}
};
}]);
View: /App/Main/views/home/events.html
<ul class="row da-thumbs">
<li ng-repeat="news in featuredEvents">
<a href="/">
<img src="abc.jpg" /> >> no effect ???
<div>
<h4>aaa</h4>
<p>
bbb
</p>
</div>
</a>
</li>
<li>
<a href="/">
<img src="abc.jpg" /> >> show effect
<div>
<h4>bbb</h4>
<p>
ccc
</p>
</div>
</a>
</li>
</ul>
On Home.html: (which already binded with controller)
<div hot-event></div>
It works when i don't bind data from the controller <li ng-repeat="news in featuredEvents">, now the effect just doesn't show up. Console.log show 0 error.
UPDATED: i ended up using document ready
app.directive('hotEvent', function () {
return {
restrict: 'EA',
templateUrl: '/App/Main/views/home/events.html',
link: function ($scope, iElement, attrs) {
angular.element(document).ready(function () {
var dathumb = $(iElement).find('.da-thumbs > li a');
dathumb.each(function () {
$(this).hoverdir();
});
});
}
}
});
If you debug your code you'd see that your directive didn't find any elements.
It happens because when the template loads, the directive link function gets called, but the ng repeat didn't have time to populate it self (it starts off empty), there for it's no where to be found.
An easy workaround is to use the jquery find method in a setTimeout 0 function, or use $scope.evalAsync on the function that does the jquery find (it requires angular 1.3).
But the best solution would be to fix the code to actually not require jquery.
P.s. when you see your self using selectors in a directive you are usually doing things wrong in angular (note: usually), please refer to this "Thinking in AngularJS" if I have a jQuery background? awesome question and answer.
note that he actually says that when you first learn angular, don't use jquery at all :)

How to create an element on button click?

I want to create dynamically an element when I click on a button. Do I have to use ng-click or a directive for that?
Here is a JSFIDDLE of what I'm trying to achieve using jQuery :
HTML:
<button id="myButton">Click Me</button>
<div id="container"></div>
JS:
$("#myButton").on("click", function() {
$("#container").append('<div class="box"></div>');
});
Also, here is a base JSFIDDLE, of what I have so far, to work on if you want for an angularjs solution.
Warn:
Please avoid a solution with a controller using ng-repeat. The code above is a simplified example. The created elements won't be as a list, because I'll attach a drag directive to them.
Do I have to use ng-click or a directive for that?
To create new element I would use $compile. Any DOM manipulations I strongly recommend to do in directives only. You can trigger appending process through ng-click directive or to use bind like:
element.bind("click", function(e){
// do stuff here
});
Something like that:
demo.directive("boxCreator", function($compile){
return{
restrict: 'A',
link: function(scope , element){
element.bind("click", function(e){
var childNode = $compile('<button ng-click="doStuff()" >new button</button>')(scope)
element.parent().append(childNode);
});
scope.doStuff = function(){
// do stuff
}
}
}
});
Demo Fiddle
http://jsbin.com/cuciyu/2/edit
JS
var app = angular.module('app', []);
app.directive("addDiv", function($compile){
return{
restrict: 'AE',
link: function(scope , element,attr){
element.bind("click", function(e){
var container = angular.element(document.querySelector("#container"));
var childNode = $compile('<div class="box">BOX DIV</div>')(container);
container.append(childNode);
});
}
};
});
app.controller('firstCtrl', function($scope){
});
HTML:
<body ng-app="app">
<div ng-controller="firstCtrl">
<button add-div>Click Me</button>
<div id="container"></div>
</div>
</body>

Directive to directive scope

This is related to a question: Listen for broadcast in sub directive
I have two directives, one is a child, one is a parent. The issue is, I what the child to only catch an event of the parent directive. Here is what I need:
I have some check boxes and a select all button for a group of check boxes. When I click the "select all" button, I want it to select all the boxes. This part I have working. The catch is I have two instances of this on the page. Right now when I click "select all", all of the check boxes on the page are selected, not just the ones inside the directive instance. I'm sure this is a scope problem... but I'm not sure what. Here is my code:
HTML:
<div all-checkboxes-broadcast>
<div all-checkboxes-listener>
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a> <!-- this should ONLY check the boxes above, not the ones below. Currently clicking either select checks all the boxes on the page.-->
</div>
<div all-checkboxes-broadcast>
<div all-checkboxes-listener>
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a>
</div>
AngularJS:
app.directive('allCheckboxesBroadcast', [function () {
return {
restrict: 'A',
scope: true,
controller: ["$scope",function($scope) {
//select all checkboxes
$scope.checkAll = function () {
$scope.$broadcast('allCheckboxes',true);
};
}]
}
}]);
app.directive('allCheckboxesListener', [function () {
return {
restrict: 'A',
require: '^allCheckboxesBroadcast',
link: function(scope, element, attrs) {
scope.$on('allCheckboxes', function(event, shouldCheckAll) {
element.find('input').prop('checked',shouldCheckAll);
});
}
}
}]);
Edit: I found the answer myself. By adding "scope: true" to the parent directive, it creates a child scope that will prototypically inherit from its parent, which creates the functionality I was looking for. If anyone has a better way to do it, I'm all ears.
I found the answer myself. By adding "scope: true" to the parent directive (I changed my original question to include this edit), it creates a child scope that will prototypically inherit from its parent, which creates the functionality I was looking for. If anyone has a better way to do it, I'm all ears.
Perhaps you need to isolate the scope by adding a parameter in your return object in the allCheckboxesBroadcast directive, eg: scope: {}
Something you might wanna do also is label those directives:
<div all-checkboxes-broadcast="1">
<div all-checkboxes-listener="1">
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a> <!-- this should ONLY check the boxes above, not the ones below. Currently clicking either select checks all the boxes on the page.-->
</div>
<div all-checkboxes-broadcast="2">
<div all-checkboxes-listener="2">
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a>
</div>
Then:
app.directive('allCheckboxesBroadcast', [function () {
return {
restrict: 'A',
scope: { bcId: "&allCheckboxesBroadcast" },
controller: ["$scope",function($scope) {
//select all checkboxes
$scope.checkAll = function () {
$scope.$broadcast('allCheckboxes',true, $scope.bcId);
};
}]
}
}]);
app.directive('allCheckboxesListener', [function () {
return {
restrict: 'A',
scope: { bcId: "&allCheckboxesListener" },
require: '^allCheckboxesBroadcast',
link: function(scope, element, attrs) {
scope.$on('allCheckboxes', function(event, shouldCheckAll, id) {
if(id == scope.bcId){
element.find('input').prop('checked',shouldCheckAll);
}
});
}
}
}]);
This is a workaround I am using in one of my modules. If you find better options, let me know.
Update: I fixed your plnkr, took me longer than I thought, I am no angular expert and isolated scopes can mess with my mind still. The main issue was that you didn't use a template so your methods defined in your directives were not accessible in the html. I also replaced the & by = in the scope assignment. I thought & would work but I guess I should read the doc again. Anyway, there it is:
http://plnkr.co/edit/J2qBBj3R0rEkfxgPBE84?p=preview

Angularjs best practices for implementing multiple directives with common functionality

My page will have a growing list of directives that have common functionality. What would be the best way to implement that functionality keeping best practices and performance in mind.
For example:
Page will have 100 directives and each directive will have common events:
Show hidden layer on mouseover
Hide div > view and Show div > edit on click.
......
Template:
<div class="panel">
<div class="view">
<div class="edit-controls hidden">
Edit
</div>
<h3>{{......}}</h3>
<p>{{.....}}</p>
</div>
<div class="edit hidden">
<form>
........
</form>
</div>
</div>
Option 1. Directive:
appModule.directive('elemTest', [function() {
return {
restrict: 'E',
templateUrl: '.......',
replace: true,
controller: function($scope, $element) {
},
link: function(scope, element) {
element.on('mouseover', function() {
element.find(".edit-controls").show();
});
element.on('mouseout', function() {
element.find(".edit-controls").hide();
});
element.find(".edit").on('click', function(event){
event.preventDefault();
element.children(".view").hide();
element.children(".edit").show();
});
}
}
}]);
Option 2. Directive with no link functions but handle mouseover/out/click events with jQuery script snippet on the bottom of the page:
$(".panel").live('mouseover',function() {
.......
})
$(".panel").live('mouseout',function() {
.......
})
..........
Option 3. Directive with controller and ng-click instead of directives link function?
Option 4. ?
Using Angular 1.2.0
Option 4: Directives with support for ng-mouseover, ng-mouseout (mouseleave?) and an ng-click on the edit button.
In a nutshell, make your directive have a template which supports the functions:
In the template:
...
<div ng-mouseover="showEditControls = true" ng-mouseleave="showEditControls = false">
<div ng-show="showEditControls">
<button ng-click="edit()" />
</div>
</div>
...
In the directive:
...
controller: function($scope){
$scope.edit = function()
// do whatever the editor does
}
}

Resources