Transclusion (true) - Combining values - angularjs

Consider the following code (http://jsbin.com/IfejIWES/1/):
HTML:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<div ng-controller="MyCtrl">
<div my-directive>
<button>some button</button>
and a link
</div>
</div>
</body>
</html>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
//$scope.log=[];
}
myApp.directive('myDirective', function(){
return{
transclude: true,
template: '<div class="something" ng-transclude> This is my directive content</div>'
};
});
Using version 1.1.3 of AngularJS, the output combines the button and anchor from my-directive (in the HTML) with the template inner text 'This is my directive content'.
If I change the version to 1.2.1 the my-directive content replaces the template inner text.
Is there a way to have angular 1.2.1 (and later) do the older behavior?

No. This was a very intentional change. See this commit.

The link provided by Jeff Hubbard (thanks Jeff) started me in the right direction. From the comments of that post somebody (https://github.com/angular/angular.js/commit/eed299a31b5a6dd0363133c5f9271bf33d090c94#commitcomment-4685184) posted a work around: http://plnkr.co/edit/EjO8SpUT91PuXP0RMuJx?p=preview
Essentially, you can get the old behavior back by changing the JavaScript to use the transcludeFn function in a separate directive. See below my updated code:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
}
myApp.directive('myDirective', function(){
return{
transclude: true,
template: '<div class="something" ng-transclude-append> This is my directive content</div>'
};
});
myApp.directive('ngTranscludeAppend', function() {
return function(scope, element, attrs, ctrl, transcludeFn) {
if (!transcludeFn) return;
transcludeFn(function(clone) {
element.append(clone);
});
};
});
Link to my updated jsbin: http://jsbin.com/IfejIWES/3/
One last note, I tried embedding the transcludeFn in my link function directly like:
link: function(scope, element, attrs, ctrl, transcludeFn) {
transcludeFn(function(clone) {
element.append(clone);
});
}
But this had the effect of creating the button and anchor twice. Moving it out into its own directive solved it for me.

Related

Attach and handle ng-change from attribute directive

We have the "myCustomHandler" attribute directive:
<input type="text" my-custom-handler ng-model="myModel">
The simplified version of the directive looks like this:
.directive('myCustomHandler', function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs) {
scope.change = function(){
console.log('model changed');
}
}
}});
I need a way to handle the ng-change event in the directive (trigger scope.change() function).
The reason i specifically asked for ng-change is that my input is type=text and i need to handle each key. Also, when handling the change, i need the old and new values too(so i prefer avoiding jQuery approaches).
I've already considered using $watch for the model, but it won't do it because i need to handle the event only if the model is changed by the user.
Thanks!
I hope this can help you
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.model = { name: 'World' };
$scope.name = "Felipe";
$scope.$watch('name',function(o,n){
console.log(o)
})
});
app.directive('myDirective', function($compile) {
return {
restrict: 'E',
scope: {
myDirectiveVar: '=',
//bindAttr: '='
},
template: '<div class="some">' +
'<input ng-model="myDirectiveVar"></div>',
replace: true,
//require: 'ngModel',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
//var textField = $('input', elem).attr('ng-model', 'myDirectiveVar');
// $compile(textField)($scope.$parent);
}
};
});
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<my-directive my-directive-var="name"></my-directive>{{name}}
</body>
</html>
From jquery doc:
For select boxes, checkboxes, and radio buttons, the event is fired
immediately when the user makes a selection with the mouse, but for
the other element types the event is deferred until the element loses
focus.
THis means the onChange event will fire when you click out of the input.
function listener() {
return {
link: function(scope, elem, attrs) {
elem.bind("change", function() {
alert('change');
});
}
}
}
angular.module('myApp', []);
angular
.module('myApp')
.directive('listener', listener);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<input type="text" listener>
</div>

AngularJs dynamic Event handler without using directive

<body ng-controller="MyController">
<mydirective></mydirective>
</body>
Is there any way in AngularJS to attach dynamic event without using directive tag because it may need to add additional tag in html.
<!DOCTYPE html>
<html ng-app="appname">
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
</head>
<body ng-controller="MyController">
<mydirective></mydirective>
</body>
<script>
var app = angular.module('appname', []);
app.controller('MyController', function($scope){
});
app.directive('mydirective', function(){
return {
restrict: 'E',
link: function(scope, elem, attrs){
angular.element(document).bind('mousedown', function(){
console.log('clicked on document');
});
}
}
});
</script>
</html>
Sure you can. Take a look at the following JSFiddle.
However, always and always remember to unbind events as well, otherwise it might lead to memory leaks and other unwanted scenarios.
var buttonElement = angular.element('#myButton');
buttonElement.bind('click', function (e) {
alert('This is a code bound event');
});
Also, using directives such as 'ng-click', ng-mouseover' is always advised, since these are well-developed and well-tested by thousands and thousands of developers. It will help you to develop robust applications
JSFiddle: Bind events through code << Updated the fiddle based on OP's comments

AngularJS isolate scope with collection does not work

I want to use multiple instances of the same directive to render a collection. I am using an isolate scope to map an array to a name in the isolate scope. The link function in the isolate scope correctly sees the mapped array, however ng-repeat in it does not work.
<html ng-app="MyApp">
<head>
<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript">
var MyCtrl = function($scope) {
$scope.blah = [1,2,4,5];
$scope.bar = [14,52,64,25];
}
angular.module("MyApp", [])
.directive("sephBlah", function($parse) {
return {
scope: {
tags: "=sephBlah"
},
link: function(scope, elem, attrs) {
console.log(scope.tags[0]);
}
}
});
</script>
</head>
<body ng-controller="MyCtrl">
<div seph-blah="blah">
<p data-ng-repeat="t in tags">{{t}}</p><!-- why this renders nothing? -->
</div>
<div seph-blah="bar">
<p data-ng-repeat="t in tags">{{t}}</p><!-- why this renders nothing? -->
</div>
</body>
</html>
I am not sure why the ng-repeat renders nothing. The link function correctly sees the arrays.
You're not supposed to use directives like this. The sort of functionality you're trying to get here should be done with a controller.
When using directives, use the template or templateUrl attribute to provide content. So this will work:
.directive("sephBlah", function($parse) {
return {
scope: {
tags: "=sephBlah"
},
template: '<p data-ng-repeat="t in tags">{{t}}</p>',
link: function(scope, elem, attrs) {
console.log(scope.tags[0]);
}
}
});
And in the html just do:
<div ng-attr-seph-blah="blah"></div>

Required angular directive not found

I am trying to understand the execution of link function in angular directives. However, I am stuck in a very simple setup. Following is the html code:
<!doctype html>
<html ng-app="demoApp">
<body>
<div ng-controller="DemoController" class="container">
<directiveone directivetwo ></directiveone>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
"demoApp" is defined in app.js as follows:
angular.module('demoApp', [])
.controller("DemoController", function(){
})
.directive("directiveone", function(){
var linker = function(scope, element, attrs){
console.log("directiveone link called");
};
return {
link : linker
}
})
.directive("directivetwo", function(){
var linker = function(scope, element, attrs){
console.log("directivetwo link called");
};
return {
require: "directiveone",
link : linker
};
});
When I run this code, I get the error:
Controller 'directiveone', required by directive 'directivetwo', can't be found!
I am unable to figure out the error here. Documentation suggests it might be a typo, but it is not apparent to me.
Directives default to attribute-only. Your directiveone directive isn't being picked up - you need to specify restrict: 'E' in its definition, or apply it as an attribute (<div directiveone directivetwo>...)

angularjs directive to append something to dom html value

I want to add something to existing element's html value.
For example
<button type="button">Something</button>
I want this to be shown as
<button type="button">Something Else<button>
Code what i expect to be is something like below ,,,,
.directive('button', function (){
return {
restrict:'E',
scope: { text: 'NOT_SURE_WHAT_TO_PUT_TO_GET_DOM_HTML',
template: '{{text}} <span>Else</span>',
link: function (scope, element, attrs) {
}
};
});
Seems inside link function, element.text() gets
<span>Else</else>
So how can i get the original text value of a button and append new string to it?
------------- UPDATE --------------
All I wanted to know is how i can reuse existing text on the element in my directive.
Button is already a html element, so you can't make a directive out of it. I suggest something like my-button instead. Also, I changed restrict to 'A' as I am using the directive as an attribute. So something like this would work:
The html:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js" data-semver="1.2.17"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<button type="button" my-button xyz="'something else'">Something</button>
</body>
</html>
and the javascript:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('myButton', function (){
return {
restrict:'A',
scope: { xyz: "#"},
template: '{{xyz}}',
link: function (scope, element, attrs) {
}
};
});
OK, on the additional information you provided, I suppose you want to use transclude within the directive. The code would then look like this (working plunker demo):
<body ng-controller="MainCtrl">
<button type="button" add="Something Else">Something</button>
</body>
and the javascript:
app.directive('button', function (){
return {
restrict:'E',
scope: { add: "#"},
template: '{{add}} <div ng-transclude></div>',
transclude: true,
link: function (scope, element, attrs) {
}
};
});

Resources