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

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);
};
}
};
})

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 pass value as attribute in angularJs custom directive

I am trying to pass some value as attribute in custom directive and access inside template, but value is coming as blank.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
// $scope.temp = "temp123";
});
app.directive("dirName" , function(){
return {
template: "<div> temp = {{temp}} </div>",
temp:'#',
};
});
<!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.4.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js" data-semver="1.4.6"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<dir-name temp="my value"></dir-name>
</body>
</html>
Here is the plunker link :
http://plnkr.co/edit/r95YGxS19cMvWMWZMPcN
Please help.
you need to define the scope of the directive then, temp will be a variable within the directive scope.
return {
template: "<div> temp = {{temp}} </div>",
scope: {
temp:'#'
}
};
updated Plunker
if you need to bind the MainCtrl value to directive then use = instead of # as in this Plunker
and there is another one (&) to point to a function within MainCtrl. check this DOC

Watch a custom directives inner html in angular

I have a global search variable that is used by the whole app
newspaper.controller("MainController", function($scope) {
$scope.search = {query:''};
});
Then I have a contenteditable div that I want to bind to $scope.search
app.directive('search', function() {
return {
restrict: 'AE',
replace: true,
template: '<div ng-model="query" id="search"></div>',
scope: {
query: '='
},
controller: function($scope) {
// I want to watch the value of the element
$scope.$watch('query', function(newValue, oldValue){
console.log(newValue);
},true);
},
link: function(scope, element, attrs) {
// Medium JS content editable framework
new Medium({
element: element[0],
mode: Medium.inlineMode
});
}
}
});
The watch is not firing when I type new values into the div, I guess Im still confused on how to link a directive with a model. Here's the HTML
<nav ng-controller="MainControllerr">
<search context="search.query"></np-search>
</nav>
Don't think you need the watch. It's bad practise to use watch functions in your controllers anyway as it makes them really hard to test.
Here's a simplified version of what (I think) your trying to do.
DEMO
index.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.4.x" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
<script src="app.js"></script>
</head>
<body>
<nav ng-controller="MainController">
<pre>{{query}}</pre>
<search query="query"></search>
</nav>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.controller("MainController", function($scope) {
$scope.query = {value:''};
});
app.directive('search', function() {
return {
restrict: 'AE',
template: '<input ng-model="query.value" id="search"/>',
scope: {
query: '='
}
}
});
EDIT
If you really want to use a content editable div you'd have to try something like this:
Also, see this SO question from which I've taken and adapted code to create the demo below. Hope it helps.
DEMO2
index.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.4.x" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
<script src="app.js"></script>
</head>
<body>
<nav ng-controller="MainController">
<pre>{{query}}</pre>
<div search contenteditable="true" ng-model="query.value">{{ query.value }}</div>
</nav>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.controller("MainController", function($scope) {
$scope.query = {value:'rrr'};
});
app.directive('search', function() {
return {
restrict: 'AE',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.bind('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(element.html());
});
});
element.html(ctrl.$viewValue);
}
}
});

send functions to a directive in a directive

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>',

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