Angular - ng-click function on transcluded directive content is not triggered - angularjs

I have two directives, parent directive should simply wrap around its child. The transclusion is used for this purpose.
However, then any other directive, such as ng-click, bound to the child directive element as its attribute does not work (is it not compiled?).
Here is the JS:
(function(angular) {
'use strict';
angular.module('docsIsoFnBindExample', [])
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
$scope.name = 'Tobias';
$scope.message = '';
$scope.hideDialog = function(message) {
$scope.message = message;
$scope.dialogIsHidden = true;
$timeout(function() {
$scope.message = '';
$scope.dialogIsHidden = false;
}, 2000);
};
}]) //controller is not important now
.directive('myDialog', function() { //parent directive
return {
restrict: 'E',
transclude: true,
scope: {
'close': '&onClose'
},
template: '<div class="alert"><a href class="close" ng-click="close({message: \'closing for now\'})">×</a><div ng-transclude></div></div>'
};
})
.directive('daka', function() { //child directive
return {
restrict: 'E',
scope: {
'input': '#'
},
link: function(scope, element, attributes) {
scope.func= function() {
console.log("blablabla"); //no console output after click event
};
}
};
});
})(window.angular);
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-directive-transclusion-scope-production</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="docsIsoFnBindExample">
<div ng-controller="Controller">
{{message}}
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog(message)">
<daka ng-click="func()" input="11">BLABlABLA</daka>
</my-dialog>
</div>
</body>
</html>

It is trying to look ng-click in your parent directive.
So you can add click event for your child directive.
.directive('daka', function() { //child directive
return {
restrict: 'E',
scope: {
'input': '#'
},
link: function(scope, element, attributes) {
element.on('click', function() {
alert('outcome clicked: ');
});
}
}; });
working jsfiddle link -https://jsfiddle.net/p2vht8sb/

Related

best way in remove directive by unique id in angularjs

i have simple directive that create a text, after click to add buttom.
and after click to each directive remove and destroy directive properly.
but i need delete all directive after selected.
for example i clicked to add button for 5 step and result same bellow
Directive content
Directive content
Directive content
Directive content
Directive content
i need click to item 2 then remove and destroy scope of item 3,4,5
another question is , can i delete directive by spsephic id ?
<body ng-app="app">
<div ng-controller="MainController">
<button ng-click="Stage()">{{stage}}</button>
<div class="my-directive-placeholder"></div>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('MainController', function($scope, $compile, $element){
$scope.stage = 'Add';
var childScope;
$scope.Stage = function(){
childScope = $scope.$new();
var el = $compile( "<b my-directive></b>" )(childScope);
$('.my-directive-placeholder').append(el);
}
})
app.directive('myDirective', function($interval){
return {
template: 'Directive content<br>',
link: function(scope, element, attrs){
element.on('click', function () {
scope.$destroy();
element.remove();
});
scope.$on('$destroy', function(){
console.log('destroid');
});
}
}
});
</script>
https://jsfiddle.net/0vucwqrc/
Instead creating html on compile time, may be its better to have dedicated directive for this like, See if this fits your requirment
angular.module('MyApp', [])
angular.module('MyApp')
.controller("DirectivePageController",
function() {
var self = this;
self.fields = [{
Name: 'Directive1'
}];
self.newField = function() {
self.fields.push({
Name: ('Directive' + (self.fields.length + 1))
});
};
self.removeField = function(field) {
var index = self.fields.indexOf(field);
if (index >= 0) {
self.fields.splice(index, 1);
}
};
})
.controller("appDirectiveController", ['$scope', '$attrs',
function($scope, $attrs) {
var self = this;
var directiveScope = $scope.$parent;
self.options = directiveScope.$eval($attrs.model);
self.onOk = function() {
alert(JSON.stringify(self.options) + ' button clicked');
}
}
])
.directive('appDirective', function($compile) {
return {
transclude: true,
template: '<div ng-click="dirCtrl.onOk()" type="">{{type|uppercase}}</div>',
scope: {
index: '#',
type: '#'
},
restrict: 'E',
replace: true,
controller: 'appDirectiveController',
controllerAs: 'dirCtrl',
}
})
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<body>
<div ng-app="MyApp">
<div ng-controller="DirectivePageController as pageCtrl">
<span>Add Button</span>
<div ng-repeat="field in pageCtrl.fields track by $index">
<app-directive type="{{field.Name}}" model="field">
</app-directive>
Remove
</div>
</div>
</div>
</body>
</html>

Isoleted scope also for transclude?

I want to add my custom directive within my other directive.
Second directive should use scope from first directive.
Problem is that first directive has isolated scope and apperently it is also isolated for second directive and in my opinion it shouldn't because I'm using transclude.
Here is example. When I comment scope: {test:"#"} all wroks as it should.
How to fix it?
angular.module("myApp", [])
.controller("initCtrl", function ($scope) {
});
angular.module('myApp')
.directive('firstDirective', ['$timeout', function ($timeout) {
return {
restrict: 'E',
scope: {test: "#"}, //everything is OK when I comment that.
transclude: true,
template: '<div> First Directive {{myVar}} {{test}}<div ng-transclude></div></div>',
controller: "firstDirectiveCtrl",
link: function (scope, element, attributes) {
}
};
}])
.controller("firstDirectiveCtrl", ['$scope', '$timeout', function ($scope, $timeout) {
$scope.myVar = "Var from first directive";
$timeout(function () {
$scope.myVar = "Var from first directive has changed";
}, 1000);
}])
.directive('secondDirective', [function () {
return {
restrict: 'E',
scope: false,
require: "^firstDirective",
template: '<div> Second Directive {{myVar}}</div>',
link: function (scope, element, attributes) {
//scope.myVar = "Var from second directive";
}
};
}]);
<!DOCTYPE html>
<html data-ng-app="myApp">
<head lang="en">
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<title></title>
</head>
<body>
<div ng-controller="initCtrl" class="container">
<first-directive test="test">
<second-directive></second-directive>
{{myVar}}
<br><i>no var unless I comment scope: {test:"#"} i first directive.</i>
</first-directive>
</div>
</body>
</html>
You can access $parent scope ex. $parent.myVar.
Works only in angular 1.3
angular.module("myApp", [])
.controller("initCtrl", function ($scope) {
});
angular.module('myApp')
.directive('firstDirective', ['$timeout', function ($timeout) {
return {
restrict: 'E',
scope: {
test:"#",
//myVar:"="
},
transclude: true,
template: '<div> First Directive {{myVar}} {{test}}<div ng-transclude></div></div>',
controller: "firstDirectiveCtrl",
link: function (scope, element, attributes) {
}
};
}])
.controller("firstDirectiveCtrl", ['$scope', '$timeout', function ($scope, $timeout) {
$scope.myVar = "Var from first directive";
$timeout(function () {
$scope.myVar = "Var from first directive has changed";
}, 1000);
this.getMyVar = function () {
return $scope.myVar;
};
//console.log($scope.getMyVar());
}])
.directive('secondDirective', [function () {
return {
restrict: 'E',
scope: false,
require: "^firstDirective",
template: '<div> Second Directive {{$parent.myVar}}</div>',
link: function (scope, element, attributes, ctrl) {
//console.log(scope.$parent);
//console.log(ctrl);
// console.log(ctrl.myVar);
// console.log(ctrl.getMyVar());
//scope.myVar = "Var from second directive";
}
};
}]);
<!DOCTYPE html>
<html data-ng-app="myApp">
<head lang="en">
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<title></title>
</head>
<body>
<div ng-controller="initCtrl" class="container">
<first-directive test="test">
{{$parent.myVar}}
<second-directive></second-directive>
</first-directive>
</div>
</body>
</html>
It is because the first directive uses isolated scope that myVar is not visible inside of the directive's template. myVar is visible inside of the transcluded contents because it is linked to the transclusion scope, which is a child scope of your parent controller scope. The transclusion scope, and firstDirective's isolated scope are sister scopes - but separate from eachother.
Note: This is only true for 1.2+. In 1.3, it looks like things have changed, and transclusion scope is a child scope of the next scope higher up the chain.
To fix this, you just have to pass myVar to your isolated scope:
.directive('firstDirective', ['$timeout', function ($timeout) {
return {
restrict: 'E',
scope: {test: "#", myVar: "="}, //pass myVar into your isolated scope.
transclude: true,
template: '<div> First Directive {{myVar}} {{test}}<div ng-transclude></div></div>',
controller: "firstDirectiveCtrl",
link: function (scope, element, attributes) {
}
};
}])
HTML
<div ng-controller="initCtrl" class="container">
<first-directive test="test" my-var="myVar">
<second-directive></second-directive>
{{myVar}}
...
</first-directive>
</div>
angular.module("myApp", [])
.controller("initCtrl", function ($scope) {
});
angular.module('myApp')
.directive('firstDirective', ['$timeout', function ($timeout) {
return {
restrict: 'E',
scope: {test: "#", myVar:"="}, //everything is OK when I comment that.
transclude: true,
template: '<div> First Directive {{myVar}} {{test}}<div ng-transclude></div></div>',
controller: "firstDirectiveCtrl",
link: function (scope, element, attributes) {
}
};
}])
.controller("firstDirectiveCtrl", ['$scope', '$timeout', function ($scope, $timeout) {
$scope.myVar = "Var from first directive";
$timeout(function () {
$scope.myVar = "Var from first directive has changed";
}, 1000);
}])
.directive('secondDirective', [function () {
return {
restrict: 'E',
scope: false,
require: "^firstDirective",
template: '<div> Second Directive {{myVar}}</div>',
link: function (scope, element, attributes) {
//scope.myVar = "Var from second directive";
}
};
}]);
<!DOCTYPE html>
<html data-ng-app="myApp">
<head lang="en">
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<title></title>
</head>
<body>
<div ng-controller="initCtrl" class="container">
<first-directive test="test" my-var="myVar">
<second-directive></second-directive>
{{myVar}}
<br><i>no var unless I comment scope: {test:"#"} i first directive.</i>
</first-directive>
</div>
</body>
</html>

Basic issue with AngularJS directive not working

I have the following html:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<link rel="stylesheet" href="css/app.css"/>
</head>
<body>
<div ng-controller="myCtrl">
<cmp>
<enhanced-textarea ng-model="name"></enhanced-textarea>
<h3>{{name}}</h3>
<notice></notice>
</cmp>
</div>
<script src="lib/angular/angular.js"></script>
<script src="js/directives.js"></script>
<script src="js/controllers.js"></script>
<script src="js/app.js"></script>
</body>
</html>
I suspect there is an issue with my app.js:
'use strict';
angular.module('myApp', [
'myApp.directives',
'myApp.controllers'
]);
Here is controllers.js:
'use strict';
angular.module('myApp.controllers', [])
.controller('myCtrl', ['$scope', function($scope) {
$scope.name = 'test';
}]);
And finally directives.js:
'use strict';
angular.module('myApp.directives', [])
.directive('cmp', function () {
return {
restrict: 'E',
controller: 'cmpCtrl',
replace: true,
transclude: true,
scope: {
name: '='
},
template: '<div ng-transclude></div>'
};
})
.controller('cmpCtrl', ['$scope', '$element', '$attrs' , function ($scope, $element, $attrs) {
$scope.$parent.$watch('name', function (newVal) {
if (newVal) {
$scope.$parent.updatedSize = newVal.length;
console.log(newVal.length);
}
}, true);
}])
.directive('enhancedTextarea', function () {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<textarea ng-transclude></textarea>'
};
})
.directive('notice', function () {
return {
restrict: 'E',
require: '^cmp',
replace: true,
scope: {
updatedSize: '='
},
template: '<div>{{size}}</div>',
link: function ($scope, $element, $attrs, cmpCtrl) {
console.log(cmpCtrl);
$scope.$parent.$watch('updatedSize', function (newVal) {
if (newVal) {
$scope.size = newVal;
}
}, true);
}
};
});
My code is bloated I know, but I am in the process of pruning it down. Bear with me.
I don't understand why the size model attribute inside the notice element is not updated...
Full app is located on github here
The problem is scope inheritance, your enhancedTextarea directive' scope inherits the name property from your controller because it's undefined. But as soon as you change the textarea value, it's property is created and what you change after that changes this directive's scope property.
Take a look at this DEMO.
When you inspect the console without changing the textarea, you won't see the name property of the scope. When you type something, you see the property is created which will override the parent's scope name property.
When you change the code like this, it works:
<enhanced-textarea ng-model="name"></enhanced-textarea>
<cmp>
<h3>{{name}}</h3>
<notice></notice>
</cmp>
DEMO
In order to create loosely coupled code, I recommend you not to rely on $scope.$parent inside you directives. You should try directive bindings to bind with parent properties.

Passing arguments to AngularJS directive not working

I'm trying to code my first AngularJS directive. This directive should extend the DIV element to include a picture and a parameter (thumbnail). If thumbnail == true, the image should be resized. I managed to display the picture, yet it is not resized.
<!DOCTYPE html>
<html ng-app="ExampleDirective">
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script>
angular.module('ExampleDirective', [])
.controller('MyController', function($scope, $window) {
})
.directive('mypicture', function () {
return {
restrict: 'A',
template: '<img src="picture.gif" width="calc()" />',
scope: {
thumbnail: '=',
},
link: function (scope, elem, attrs) {
scope.calc = function() {
if (scope.thumbnail === 'true') {
return 50;
}
};
}
}
});
</script>
</head>
<body ng-controller="MyController">
<div mypicture thumbnail="true" ></div>
</body>
</html>
Any idea how to fix it ?
Thanks!
Try to use ng-style instead:
js
app.directive('mypicture', function () {
return {
restrict: 'A',
template: '<img src="http://pagead2.googlesyndication.com/simgad/8173680700251715003"
ng-style="calc()" />',
scope: {
thumbnail: '=',
},
link: function (scope, elem, attrs) {
scope.calc = function() {
if (scope.thumbnail == true) {
return {width: 150 + 'px'};
}
return {width: 100 + 'px'}
};
}
}
});
Demo Fiddle

Transcluded element not being deleted on removal of list item when using ng-repeat

I have a widget that I'm instantiating using ng-repeat. Initial creation works fine, but after that it stops updating. Here's an excerpt from index.html:
<div>
<x-node ng-repeat="node in nodes"></x-node>
</div>
partials/node.html:
<div>{{node.name}}</div>
And the directive:
angular.module('directive', []).directive('node', function() {
return {
restrict: 'E',
scope: true,
templateUrl: 'partials/node.html',
replace: true,
compile: function(tElement, tAttrs, transclude) {
return {
post: function(scope, iElement, iAttrs) {
scope.$on('$destroy', function(event) {
console.log('destroying');
});
}
};
}
};
});
If I modify the list of nodes in the console like this:
var e = angular.element($0);
var s = e.scope();
s.nodes.splice(1,1);
s.$apply()
... then the $destroy callback runs, but the rendered elements do not change. Is there something I'm missing from my directive?
Demo: Plunker
It seems this was indeed a bug, which is fixed in the 1.2 series of AngularJS. Here's an updated demo that uses 1.2.
index.html:
<!DOCTYPE html>
<html ng-app="my-app">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<link rel="stylesheet" href="style.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="AppController">
<div id="ct">
<x-node ng-repeat="node in nodes"></x-node>
</div>
<button id="test">Remove element [1]</button>
</body>
</html>
app.js:
var app = angular.module('my-app', [], function () {
})
app.controller('AppController', function ($scope) {
$scope.nodes = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}];
})
app.directive('node', function() {
return {
restrict: 'E',
scope: true,
templateUrl: 'node.html',
replace: true,
compile: function(tElement, tAttrs, transclude) {
return {
post: function(scope, iElement, iAttrs) {
scope.$on('$destroy', function(event) {
console.log('destroying');
});
}
};
}
};
});
$(function(){
$('#test').click(function(){
var el = $('#ct').children().first();
if(el.length){
var e = angular.element(el[0]);
var s = e.scope();
s.nodes.splice(1,1);
s.$apply()
}
})
});
node.html:
<div>{{node.name}}</div>

Resources