I'm wondering what's the way to do work this snippet:
//html
<div ng-app="app">
<div ng-controller="AppCtrl">
<a my-dir ng-repeat="user in users">{{user.name}}</a>
</div>
</div>
//js
var app = angular.module('app', []);
app.controller("AppCtrl", function ($scope) {
$scope.users = [{name:'John',id:1},{name:'anonymous'}];
$scope.fxn = function() {
alert('It works');
};
})
app.directive("myDir", function ($compile) {
return {
link:function(scope,el){
el.attr('ng-click','fxn()');
//$compile(el)(scope); with this the script go mad
}
};
});
I know it's about the compile phase
but I don't get the point so a short explanation would be
very appreciate.
A directive which adds another directive to the same element:
Similar answers:
How to get ng-class with $dirty working in a directive?
creating a new directive with angularjs
Here is a plunker: http://plnkr.co/edit/ziU8d826WF6SwQllHHQq?p=preview
app.directive("myDir", function($compile) {
return {
priority:1001, // compiles first
terminal:true, // prevent lower priority directives to compile after it
compile: function(el) {
el.removeAttr('my-dir'); // necessary to avoid infinite compile loop
el.attr('ng-click', 'fxn()');
var fn = $compile(el);
return function(scope){
fn(scope);
};
}
};
});
Much cleaner solution - not to use ngClick at all:
A plunker: http://plnkr.co/edit/jY10enUVm31BwvLkDIAO?p=preview
app.directive("myDir", function($parse) {
return {
compile: function(tElm,tAttrs){
var exp = $parse('fxn()');
return function (scope,elm){
elm.bind('click',function(){
exp(scope);
});
};
}
};
});
You can try this:
<div ng-app="app">
<div ng-controller="AppCtrl">
<a my-dir ng-repeat="user in users" ng-click="fxn()">{{user.name}}</a>
</div>
</div>
<script>
var app = angular.module('app', []);
function AppCtrl($scope) {
$scope.users = [{ name: 'John', id: 1 }, { name: 'anonymous' }];
$scope.fxn = function () {
alert('It works');
};
}
app.directive("myDir", function ($compile) {
return {
scope: {ngClick: '='}
};
});
</script>
Related
I have a directive with an isolated scope and want to call its function to update data from the parent controller without using events.
var myApp = angular.module('MyApp',[]);
myApp.directive('myDirective', function() {
return {
scope: {},
link: function(scope) {
scope.update = function() {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.updateDirective = function() {
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
You could apply this solution.
In this way you are passing a variable in two way binding:
my-directive="myFunction" in the html
and myFunction: '=myDirective' in the directive)
Then assign the function in the directive:
scope.myFunction = function () {
alert('Directive updated!');
}
In this way you can use a function defined in a directive.
var myApp = angular.module('MyApp', []);
myApp.directive('myDirective', function () {
return {
scope: {
myFunction: '=myDirective'
},
link: function (scope) {
scope.myFunction = function () {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.myFunction = {};
$scope.updateDirective = function () {
console.log( $scope.myFunction );
$scope.myFunction();
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button> <span my-directive="myFunction"></span>
</div>
You could tackle this issue by introducing a new directive that is required by your isolated directive. Conveniently, you can assign the controller to this new directive.
Once required you then 'register' your isolated directive to the 'parent' directive as the target for your function. In the code snippet below I only provided a way to add 1 directive, but you could easily extend this to be an array of child directives. A good of example of such a setup are tabs, where each tab is a child directive of a common tabs directive.
angular.module("MyApp", []);
angular.module('MyApp').directive("myParentDirective", function(){
return {
controller: function ($scope) {
var childUpdate;
this.registerChild = function(_childUpdate_){
childUpdate = _childUpdate_;
};
$scope.updateDirective = function() {
childUpdate();
};
}
};
});
angular.module('MyApp').directive('myDirective', function() {
return {
require: '^myParentDirective',
scope: {},
link: function(scope, element, attrs, myParentController) {
myParentController.registerChild(update);
function update() {
alert('Directive updated!');
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div my-parent-directive>
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
</div>
If I use something like this in a directives link function:
var vdo = element.find('video')[0];
vdo.on('loadstart', function () {
console.log('onloadstart');
});
The code isn't executed. Instead I have to use:
vdo.onloadstart = function() {
console.log('onloadstart');
};
or
vdo.addEventListener('loadstart', function () {
console.log('onloadstart');
});
Can someone explain me why? And is it a problem to use addEventListener at all?
The angular way would be a custom directive:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.src = 'w3schools.com/html/mov_bbb.mp4';
$scope.myFunc = function() {
alert('loadstart!');
};
}
myApp.directive('onLoad', function() {
return {
restrict: 'A',
scope: {
func: '&onLoad'
},
link: function(scope, element) {
element.on('loadstart', function() {
scope.func();
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<video controls on-load="myFunc()">
<source ng-src="{{src}}">
</video>
</div>
I have a custom filter set up that triggers when the user clicks on a checkbox. I use a directive to render the DOM elements, and attach a listener on the checkbox which when clicked, triggers the filter function that's exposed on the $scope.
The $scope.model which is used in the view should get overwritten by the result of the filter function, and the return object looks ok (e.g. the console.log()) but the view doesn't update. What am I doing wrong?
http://jsfiddle.net/r3pXc/1/
The view:
<body ng-app="app">
<div ng-controller="mainCtrl">
<div list-directive />
</div>
The template:
<script type="text/ng-template" id="list.html">
<input type="checkbox" class="control">
<div ng-repeat="player in model">
Name: {{player.firstName}}, Price: {{player.price}}
</div>
</script>
The module and controller:
var app = angular.module('app', []);
app.controller('mainCtrl', ['$scope', '$filter', function($scope, $filter){
$scope.model = [{ firstName: 'foo', price: 100 }, { firstName: 'bar', price: 50 }, { firstName: 'foobar', price: 0}];
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
console.log($scope.model);
}
}]);
The directive:
app.directive('listDirective', function(){
return {
restrict: 'AE',
templateUrl: 'list.html',
link: function($scope, iElm, iAttrs, controller) {
iElm.bind('click', function(e){
var el = angular.element(e.target);
if (el.hasClass('control')) {
$scope.filter();
};
});
}
};
});
And the filter:
app.filter('listFilter', function(){
return function(input) {
var results = [];
angular.forEach(input, function(val, key){
if (val.price != 0) {
results.push(val);
}
});
return results;
}
});
Need to manually call the digest cycle with $apply(), because I don't listen on the elements with ng- event handlers:
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
$scope.$apply();
}
I am having a problem with nested directives, in particular within the hide-element directive.
Firstly, there is an edit-mode directive that does not seem to work with ngAnimate. It would still hit hit addClass fade on button, but would not have ng-animate binded.
I tried moving the hide-element to a sibling element at divColumn and the edit-element directive works and does the animation on both buttons and divColumn.
It just doesn't work when they are nested. Why is that?
(I'm new to AngularJS, kindly enlighten me)
HTML
<!-- ngview has UIController everything here is nested within ngview -->
<h3>Data Page</h3>
<div class="divContainer" ng-mouseenter="fadeToggle()" ng-mouseleave="fadeToggle()">
<div style="overflow:hidden">
<div class="divHeader">Data Name</div>
<div class="divHeader">Parent Data</div>
<div class="divHeaderHidden" hide-element="isHidden">Edit</div>
</div>
<div ng-controller="myController">
<div ng-repeat="data in datas">
<div class="divRow" ng-mouseenter="fadeToggle();" ng-mouseleave="fadeToggle();">
<div class="divColumn">
{{data.name}}
</div>
<div class="divColumn">
{{data.parentData.name}}
</div>
<div class="divColumnHidden" hide-element="isHidden">
<button class="editColumnBtn" ng-click="editToggle()" edit-mode="isEdit">Edit</button>
<button class="removeColumnBtn" edit-mode="isEdit">Remove</button>
</div>
</div>
</div>
</div>
Angular
app.factory('Data', function($http) {
var dataService = {
getDepartment: function() {
var promise = $http.get('http://localhost/orgchart/app/json/data').then(function(response) {
return response.data;
});
return promise;
}
};
return dataService;
});
app.controller('UIController', ['$scope', function fadeController($scope) {
$scope.fadeToggle = function () {
this.isHidden = !this.isHidden;
};
}]);
app.controller('myController', ['$scope', 'Data', function myController($scope, Data) {
Data.getData().then(function(data) {
$scope.data = data;
});
$scope.editToggle = function() {
this.isEdit = !this.isEdit;
};
}]);
app.directive('editMode', ['$animate', function ($animate) {
return function(scope, element, attrs) {
scope.isEdit = false;
scope.$watch(attrs.editMode, function (newVal) {
if(newVal) {
$animate.addClass(element, 'fade');
}
else {
$animate.removeClass(element, 'fade');
}
});
};
}]);
app.directive('hideElement', ['$animate', function ($animate) {
return function(scope, element, attrs) {
scope.isHidden = true;
scope.$watch(attrs.hideElement, function (newVal) {
if(newVal) {
$animate.addClass(element, 'fade');
} else {
$animate.removeClass(element, 'fade');
}
});
};
}]);
app.animation(".fade", function() {
return {
addClass: function(element, className) {
TweenMax.to(element, 0.3, {opacity: 0});
},
removeClass: function(element, className) {
TweenMax.to(element, 0.3, {opacity: 1});
}
};
});
I would like the ng-click to change the value of the controller scope variable 'controllerLabel'. What's the best way of achieving this without using a controller scope function?
HTML:
<div ng-app="app">
<div ng-controller="Ctrl">
<p>{{controllerLabel}}</p>
<my-template></my-template>
</div>
<!-- my-template.html -->
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="controllerLabel = {{clickLabel.text}}">{{clickLabel.text}}</label>
</div>
</script>
</div>
JavaScript:
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
JSFiddle
You can add link to directive and write like:
.directive('myTemplate', function() {
return {
restrict: 'E',
link: function (scope) {
scope.onClick = function (clickLabel) {
scope.controllerLabel = clickLabel.text;
}
},
templateUrl: 'my-template.html'
};
});
HTML
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="onClick(clickLabel)">{{clickLabel.text}}</label>
</div>
</script>
Actually you can write like #Alborz posted but I think to add method into link and call from HTML will be clearer and easy to debug.
Demo Fiddle
I updated your fiddle;
Updated fiddle
You need to use controllerLabel as an object property to have a shared object with controller.
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.label = {};
$scope.label.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
Template:
Note to label.controllerLabel = clickLabel.text
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="label.controllerLabel = clickLabel.text">{{clickLabel.text}}</label>
</div>