send functions to a directive in a directive - angularjs

In my AngularJS project, I can pass scope functions into a directive, if the directive is used by the top-level view. I can also successfully pass parameters to those functions, invoking with the ({obj: mapping}) syntax.
If a higher-level directive is used, and that higher-level directive needs to pass the function to the low-level (inner most) directive, I have limited success. I can pass scope functions if the scope functions do not need a parameter when invoked by the low-level directive. However I have not been able to make the low-level directive pass a parameter if the scope function takes a parameter.
I've tried many variations on the syntax for passing functions at various levels, using (), not using (), using a placeholder at various spots, using object mapping by the directive ({someData: 'arg'}).
The example below works, but how do I change it to pass an argument in the low-level directive?
Here is the plnkr demo.
JS code
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'placeholder';
$scope.setName = function(aName) {
aName = aName || 'it was undefined';
$scope.name = aName; //wish i could make this be 'low level'
};
});
app.directive('lowLevel', function() {
return {
// todo: pass arg, e.g. action('low level')
template: '<div><span ng-click="action(\'low level\')">click me once</span></div>',
scope: {
action: '&'
}
}
});
app.directive('highLevel', function(){
return {
template: '<div><low-level action="func({val: \'high level\'})"></low-level></div>',
scope: {
func: '&'
}
}
});
HTML usage
<!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.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<high-level func="setName(val)"></high-level>
</body>
</html>

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'placeholder';
$scope.setName = function(aName) {
aName = aName || 'it was undefined';
$scope.name = aName; //wish i could make this be 'low level'
};
});
app.directive('lowLevel', function() {
return {
// todo: pass arg, e.g. action('johnny')
restrict:'E',
template: '<div><span ng-click="action({val: \'low level\'})">click me once</span></div>',
scope: {
action: '&'
}
}
});
app.directive('highLevel', function(){
return {
restrict:'E',
template: '<div><low-level action="func({val: val})"></low-level></div>',
scope: {
func: '&'
}
}
});
/* Put your css in here */
span:hover {
background-color: red;
}
span {
border: 3px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="plunker">
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<high-level func="setName(val)"></high-level>
</body>
</html>
You can use this in your template to pass along the argument:
template: '<div><low-level action="func({val: val})"></low-level></div>',

Related

AngularJS Directive Template is not display

I start learning AngularJS today with directive ... I can't get directive work. Please help
<html ng-app="ngBasic">
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script>
var app = angular.module('ngBasic', []);
app.controller('ngBasicCtrl', function($scope) {
$scope.age = 20;
});
app.directive('myDirective', function($scope) {
return {
template: '<h1>{{age}}</h1>'
}
});
</script>
</head>
<body ng-controller="ngBasicCtrl">
<h1>{{age}}</h1>
<my-directive></my-directive>
<div my-directive></div>
<div class='my-directive'></div>
</body>
</html>
https://jsfiddle.net/bigzidane/t8Lv3mpg/2/
Currently, the page onlys show 20. Expecting tag but not displaying yet.
Use the scope option to create an isolated scope the template can refer to :
app.directive('myDirective', function() {
return {
scope: {
age: '='
},
template: '<h1>{{age}}</h1>'
}
});
Now you can pass the controller $scope's age to the directive scope, for example this way :
<my-directive age="age+1"></my-directive>
Will render out 21. Updated fiddle -> https://jsfiddle.net/t8Lv3mpg/3/
You have to remove the $scope from the function returning your directive as it does not take a $scope parameter.
app.directive('myDirective', function() {
return {
template: '<h1>{{age}}</h1>'
}
});

How to call a controller function, inside a directive link function using '&' with a parameter?

I'm trying to pass a parameter into a function, inside of a directive link function, similar to this question:
Angular: calling controller function inside a directive link function using &
However, while I've seen a few examples, like this one: Passing Parameters from a Directive to a function and I've seen a parameter value get set inside the link inside a directive. Yet, I've not seen any where you are passing a primitive, like a number in as a parameter to the directive which passes that into the controller function.
I've tried multiple things, but haven't figured out the syntax.
HTML CODE:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl">
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>
SCRIPT.JS
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function() {
$scope.count = 0;
console.log('In mainCtrl ctrlFn!');
$scope.count += count;
// console.log("count is: " + JSON.stringify($scope.count));
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '&',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({count: 10})'>Click Here</button></div>",
link: function(scope, element, attributes) {
var count = null;
scope.ctrlFn = scope.ctrlFn({count: count});
//scope.text = scope.fn({ count: 0 });
}
};
});
My plunker is here: http://plnkr.co/edit/6uDntNeqe0g343PmeCED?p=preview
Can a primitive get passed in as a parameter in this use case? If so, what am I missing here?
Aftermath:
In case someone is looking for this syntax: ctrlFn({count: 10}) in the angularjs docs, it's mentioned here under custom directives:
Often it's desirable to pass data from the isolate scope via an
expression to the parent scope, this can be done by passing a map of
local variable names and values into the expression wrapper function.
For example, the hideDialog function takes a message to display when
the dialog is hidden. This is specified in the directive by calling
close({message: 'closing for now'}). Then the local variable message
will be available within the on-close expression.
You've made two mistakes:
scope.ctrlFn = scope.ctrlFn({count: count}); -
this line overrides passed reference to a function and sets it to be value returned by this function (undefined in this case)
to pass count value to your direcive you should use = instead of &
Below is code of simplified example.
script.js:
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function(count) {
console.log(count)
console.log('In mainCtrl ctrlFn!', count);
$scope.count += count;
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '=',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({ count: 100 })'>Click Here</button></div>"
};
})
index.html:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl"> {{count}}
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>

$compile() does not fire in custom Angular directives

I have the following directive:
angular.module("example_module", [])
.directive("example_directive", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
and the following HTML code:
<!DOCTYPE html>
<html ng-app="example_module">
<head>
<meta charset="utf-8">
<title>Example title</title>
<script src="lib/angular/angular.min.js"></script>
<script src="js/example.js"></script>
</head>
<body>
<div example_directive></div>
</body>
</html>
I would expect the directive to compile to Hello world, but it compile to an empty string instead. Where is the error?
I could use template or a link function, but my goal here is to understand how the compile function works.
This has to do with how angular handles directive names. I've changed your example to match angular's naming conventions and put together a Plunk
angular.module("example_module", [])
.directive("exampleDirective", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
<body>
<div example-directive></div>
</body>

AngularJS : Adding attribute to directive element inside ng-repeat not working

I have a directive that works fine when used in isolation, but when it is used inside an ng-repeat it quits working. I know that ng-repeat creates it's own isolate scope, but that shouldn't matter since I'm not trying to do anything outside of the isolate scope of my directive. I've created a plunker to demonstrate the problem which you can see here: http://plnkr.co/edit/Y4ADmznnDCZvuxJrcZQ0?p=preview.
In the compile function of the directive I am adding an ng-click attribute to the element. Notice that the "This works" link (which is not in an ng-repeat) works fine but the other links (which are inside ng-repeat) do not work.
Here is the directive from the plunker:
var app = angular.module('plunker', []);
app.controller("AppCtrl", function($scope) {
$scope.users = [{
name: 'John',
id: 1
}, {
name: 'anonymous'
}];
});
app.directive("myDir", function($compile) {
return {
controller: 'directiveController',
compile: function(el) {
el.removeAttr('my-dir');
el.attr('ng-click', 'fxn()');
var fn = $compile(el);
return function(scope){
fn(scope);
};
}
};
})
.controller("directiveController", function($scope) {
$scope.fxn = function() {
alert('It works');
};
});
And here is 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="http://code.angularjs.org/1.2.12/angular.js" data-semver="1.2.12"></script>
<script src="app.js"></script>
</head>
<body>
<div>
<a my-dir>This works</a>
<a my-dir ng-repeat="id in [1,2]"><br>This does not work</a>
</div>
</div>
</body>
</html>
Try it with binding the event in the link function instead of adding another attribute that has to be compiled (DEMO):
app.directive("myDir", function($compile) {
return {
controller: 'directiveController',
compile: function(el) {
el.removeAttr('my-dir');
var fn = $compile(el);
return function(scope, el){
el.bind('click', scope.fxn);
fn(scope);
};
}
};
})

Watch not working when HTML loaded from Directive in AngularJS

I am loading a partial in Angular dependant on the route of the URL.
When I load the partial it loads, and responds to the Controllers functions. However I have a directive which has a watcher. This does not work when I use the
It works fine when I load the HTML inside the main page. I have a Plunker of this here
http://plnkr.co/edit/DK33pIrp0HyhUOjwm5X2?p=preview
Essentially clicking "hello" should change the $scope.origin and the watcher should then fire its event. It does not.
My HTML:
<!DOCTYPE html>
<html ng-app="App">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.1.2/coffee-script.min.js"></script>
<script src="app.js"></script>
<script src="directive.js"></script>
</head>
<body ng-controller="MapCtrl">
<ng-view></ng-view>
</body>
</html>
app.js
var app;
app = angular.module("App", []);
app.config(function($routeProvider) {
return $routeProvider.when("/", {
templateUrl: "home.html",
controller: MapCtrl
});
});
this.MapCtrl = function($scope) {
return $scope.clicked = function() {
console.log("clicked");
$scope.origin = Math.floor(Math.random() * 11);
return console.log($scope.origin);
};
};
directive.js
(function(angular) {
var app;
app = angular.module("App");
return app.directive("leaflet", function() {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<section id='map' class='map'></section>",
scope: {
origin: "=origin"
},
controller: function($scope, $attrs) {
return $scope.$watch("origin", (function(newValue, oldValue) {
return alert("its changed");
}), true);
}
};
});
})(angular);
home.html
<button ng-click="clicked()">hello</button>
how can I get this working?
edit: I have just made this pure JS and not coffeescript.
Thanks so all who helped me find the issue.
This can be done by setting the
<button ng-click="clicked()">hello</button>
to
<button ng-click="$parent.clicked()">hello</button>
This is because the ng-view will be a child. This simple fix is now working.

Resources