Why is my ng-click not working when I appended it with a button? The same ng-click is working when I initially loaded the button.
app.controller('demoCtrl', function() {
this.clk = '<button ng-click="dctrl.click()">Button</button>';
this.click = function() {
alert('clicked');
}
})
app.directive('btnClick', function() {
return {
restrict: 'A',
scope: {
actionBtn: '='
},
link: function(scope, element, attrs) {
element.append(scope.actionBtn);
}
}
})
HTML
<body ng-controller="demoCtrl as dctrl">
<div btn-click action-btn="dctrl.clk"></div>
</body>
http://plnkr.co/edit/QPKXfGd9s7HzLvEfKvbG?p=preview
Update
I've also tried this way but no luck
element.append($compile(scope.actionBtn)(scope));
You need to compile an new dom element created manually for angularjs to work on it so
app.directive('btnClick', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.append($compile(scope.$eval(attrs.actionBtn))(scope));
}
}
}])
var app = angular.module('my-app', [], function() {})
app.controller('demoCtrl', function() {
this.clk = '<button ng-click="dctrl.click()">Button</button>';
this.click = function() {
alert('clicked');
}
})
app.directive('btnClick', ['$compile',
function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.append($compile(scope.$eval(attrs.actionBtn))(scope));
}
}
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app" ng-controller="demoCtrl as dctrl">
<div action-btn="dctrl.clk" btn-click></div>
</div>
Related
I have a directive Foo in Directive Bar i am trying to call a function in Foo
but it is not working.
http://jsfiddle.net/4d9Lfo95/3/
example fiddle is created.
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'E',
template: '<p>Foo</p>',
link: function($scope, element, attrs) {
$scope.message = function() {
alert(1);
};
},
controller: function($scope) {
this.message = function() {
alert("Foo Function!");
}
}
};
}
).directive('uiBar',
function() {
return {
restrict: 'E',
template: '<button ng-click="callFunction()">Bar</button> <ui-foo></ui-foo>',
require: 'uiFoo',
scope: true,
link: function($scope, element, attrs, uiFooController) {
$scope.callFunction = function() {
alert('Bar Function');
uiFooController.message();
}
}
};
}
);angular.module('myApp', ['ui']);
where as the UI looks like this
<div ng-app="myApp"> <ui-bar> </ui-bar></div>
You left out this error message:
Controller 'uiFoo', required by directive 'uiBar', can't be found!
The problem is that the require hierarchy searches up the tree, not down it. So, ui-bar is trying to find a uiFoo directive controller either on itself or (with the ^ symbol) in one of it's ancestors, not one of it's children.
If you want to call a method from the child directive, just use the scope: http://jsfiddle.net/4d9Lfo95/5/
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'E',
template: '<p>Foo</p>',
controller: function($scope) {
$scope.message = function() {
alert("Foo Function!");
}
}
};
}
).directive('uiBar',
function() {
return {
restrict: 'E',
template: '<button ng-click="callFunction()">Bar</button> <ui-foo></ui-foo>',
scope: true,
controller: function($scope) {
$scope.callFunction = function() {
alert('Bar Function');
$scope.message();
}
}
};
}
);
How can I pass a child attribute directive's scope or attr value to a parent directive?
Given widget directive, with in-viewport attribute directive, I want to update the attribute inView each time the document is scrolled, and pass the updated value to the parent directive widget:
<widget in-viewport></widget>
In Viewport directive: passed in as an attribute of parent directive "widget"
angular.module('app').directive('inViewport', function() {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(document).on('scroll', function() {
// I've tried binding it to attr and parent scope of "widget" directive
attr.inView = isElementInViewport(element);
scope.inView = isElementInViewport(element);
});
}
};
});
Widget Directive:
angular.module('app').directive('widget', function() {
return {
restrict: 'AE',
scope: {
inView: '='
},
transclude: false,
templateUrl: 'directives/widgets/widgets.tpl.html',
link: function(scope) {
console.log('In Viewport: ', scope.inView); // Null
Here is the way you can access parent directive variables,
angular.module('myApp', []).directive('widget', function() {
return {
restrict: 'E',
template: '<viewport in-view="variable"></viewport> <h1>{{variable}}</h1>',
link: function(scope, iAttrs) {
scope.variable = 10;
}
}
}).directive('viewport', function() {
return {
restrict: 'E',
scope: {
inView: "=",
},
template: '<button ng-click="click()">Directive</button>',
link: function(scope, iElement, iAttrs) {
scope.click = function() {
scope.inView++;
}
}
}
});
HTML
<div ng-app="myApp" ng-controller="Ctrl1">
<widget></widget>
</div>
Here is the working jsfiddle
http://jsfiddle.net/p75DS/784/
If you have any question, ask in the comment box
Here is a working fiddle using your directive structure:
http://jsfiddle.net/ADukg/9591/
Markup is like this:
<div ng-controller="MyCtrl" style="height: 1200px;">
{{name}}
<hr>
<widget in-viewport></widget>
</div>
Just scroll the window to trigger the event. Note that the parent directive has a watch just to prove that the var gets updated...
var myApp = angular.module('myApp',[]);
myApp.directive('inViewport', function($timeout) {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(window).bind('scroll', function() {
console.log('Called');
$timeout(function() {
scope.inView++;
}, 0);
});
}
};
});
myApp.directive('widget', function() {
return {
restrict: 'AE',
transclude: false,
template: '<p>This is a widget</p>',
link: function(scope) {
scope.inView = 0;
console.log('In Viewport: ', scope.inView); // Null
scope.$watch('inView', function(newVal, oldVal) {
console.log('Updated by the child directive: ', scope.inView);
});
}
}
});
function MyCtrl($scope) {
$scope.name = 'Angular Directive Stuff';
}
You can expose an API on your parent directive and use isolateScope() to access it.
Here's a working fiddle.
var app = angular.module("app",[]);
app.directive("widget", function($rootScope){
return {
template: "<div>Scroll this page and widget will update. Scroll Y: {{scrollPosY}}</div>",
scope: {}, // <-- Creating isolate scope on <widget>. This is REQUIRED.
controller: ['$scope', function DirContainerController($scope) {
$scope.scrollPosY = 0;
// Creating an update function.
$scope.update = function(position) {
$scope.scrollPosY = position;
$scope.$digest();
};
}],
}
});
app.directive("inViewport", function($window, $timeout, $rootScope){
return {
restrict: 'A',
link:function(scope, element, attrs, parentCtrl){
// Get the scope. This can be any directive.
var parentScope = element.isolateScope();
angular.element(document).on('scroll', function() {
// As long as the parent directive implements an 'update()' function this will work.
parentScope.update($window.scrollY);
console.log('parentScope: ', parentScope);
});
}
}
});
Where do I have to define my function button() in the directive, such that pressing the button will trigger the function?
I don't want to use the outer scope of the app, I just want to use the local scope.
var app = angular.module("myApp", []);
app.directive('aaaa', function() {
return {
restrict: 'E',
scope: {
data: '=',
//this is not working: button: function(){console.log('hello from button');}
},
link: function(scope, element) {
element.append('hello');
element.append(' <button type="button" ng-click="button()">Click Me!</button> ')
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="myApp">
<aaaa></aaaa>
</div>
As #tymeJV said , you need to compile the html first and then call button() in directive controller
var app = angular.module("myApp", []);
app.directive('aaaa', function($compile) {
return {
restrict: 'E',
scope: {
data: '=',
//this is not working: button: function(){console.log('hello from button');}
},
link: function(scope, element) {
element.append('hello');
var htmlText = ' <button type="button" ng-click="button()">Click Me!</button> ';
var template = angular.element($compile(htmlText)(scope));
element.append(template);
},
controller: function($scope, $element){
$scope.button = function(){
console.log('button clicked');
}
}
}
});
I would advise to use angular templates instead of element.append, see example code below. Then you don't need all the compiler code 'n stuff.
You could also replace "template: 'hello
var app = angular.module("myApp", []);
app.directive('aaaa', function() {
return {
restrict: 'E',
scope: {
data: '='
},
link: function(scope, element) {
scope.button = function(){
console.log('hello from button');
};
},
template: 'hello <button type="button" ng-click="button()">Click Me!</button>'
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="myApp">
<aaaa></aaaa>
</div>
you can out under link
link: function(scope, element) {
element.append('hello');
element.append(' <button type="button" ng-click="button()">Click Me!</button> ');
scope.button=function(){
//content goes here
}
}
So, this is my problem. I have two directives (say parent directive and child directive) and i am calling child directive from parent directive like this :
angular.module('components', [])
.directive('helloWorld', function() {
return {
restrict: 'E',
compile: function(element, attrs) {
var x = '<directive2></directive2>';
element.append(x);
}
}
})
.directive("directive2", function($compile, $parse) {
return {
restrict: 'E',
compile: function(iElement, iAttrs, transclude) {
iElement.append('<p>directive2</p>');
}
}
});
angular.module('HelloApp', ['components'])
This works fine. But now i am writing a condition in post function of compile and when that condition satisfy, the child directive should append.
I just added the append function inside the post function, but its not working.
angular.module('components', [])
.directive('helloWorld', function() {
return {
restrict: 'E',
compile: function(element, attrs) {
return {
post: function(scope, element, attrs) {
var x = '<directive2></directive2>';
element.append(x);
}
}
}
}
})
.directive("directive2", function($compile, $parse) {
return {
restrict: 'E',
compile: function(iElement, iAttrs, transclude) {
iElement.append('<p>directive2</p>');
}
}
});
angular.module('HelloApp', ['components'])
I dont know what went wrong. Guide me friends
jsFiddle
You need to use the $compile service before appending as below:
angular.module('components', [])
.directive('helloWorld', function($compile){
return {
restrict: 'E',
link: function(scope, element, attrs) {
var x = angular.element('<directive2></directive2>');
element.append($compile(x)(scope));
}
}
})
.directive("directive2", function() {
return {
restrict: 'E',
compile: function(element, attrs, transclude) {
element.append('<p>directive2</p>');
}
}
});
angular.module('HelloApp', ['components']);
http://jsfiddle.net/2zbabkjb/2/
Try to define template to your first directive :
angular.module('components', [])
.directive('helloWorld', function() {
return {
restrict: 'E',
template: '<directive2></directive2>'
}
}
})
I want to define a directive which will show the first child which contains a button at first. If we click on the button, the second child will be replace the first one.
HTML:
<div show-more>
<div>short <button ng-click="showMore()">click-me to show more</button></div>
<div>full</div>
</div>
Angular:
angular.module("app", [])
.directive("showMore", function() {
return {
restrict: 'A',
scope: {},
link: function(scope, element, attrs) {
var children = element.children();
var short = children[0];
var full = children[1];
element.empty().append(short);
scope.showMore = function() {
element.empty().append(full);
};
}
};
});
The problem is that when I click the button, there is nothing happen. I tried a lot but still not work.
You can see a live demo here: http://jsbin.com/rugov/2/edit
How to fix it?
Since you are changing the DOM of the element of your directive, you will need to re-$compile the element, like this:
.directive("showMore", function($compile) {
return {
restrict: 'A',
scope: {},
link: function(scope, element, attrs) {
var children = element.children();
var short = children[0];
var full = children[1];
element.empty().append(short);
$compile(element.contents())(scope);
scope.showMore = function() {
element.empty().append(full);
};
}
};
});
Working Example
The problem is that your directive creates new isolated scope, but ng-click directive is placed on element, that is in another scope.
I would implement your requirements via 2 directive, one depends from another.
angular.module("app", [])
.directive("showMoreWrapper", function() {
return {
restrict: 'A',
scope: {},
controller: function($element) {
this.showMore = function() {
var children = $element.children();
children.eq(0).hide();
children.eq(1).show();
};
},
link: function(scope, element, attrs) {
var children = element.children();
children.eq(1).hide();
}
};
})
.directive("showMore", function() {
return {
restrict: 'A',
require: '^showMoreWrapper',
link: function(scope, element, attrs, showMoreWrapper) {
element.on('click', showMoreWrapper.showMore);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div show-more-wrapper>
<div>short
<button show-more>click-me to show more</button>
</div>
<div>full</div>
</div>
</div>
I would take a custom component approach
angular.module("app", [])
.directive("showMore", function() {
return {
restrict: 'E',
scope: {},
transclude: true,
template: '<button ng-click="toggle()">{{title}}</button><div ng-transclude></div>',
controller: function($scope) {
var self = this;
$scope.title = "Show More";
$scope.toggle = function() {
self.short.show = !self.short.show;
self.full.show = !self.full.show;
$scope.title = self.short.show ? "Show More" : "Show Less";
};
this.addShort = function(short) {
self.short = short;
};
this.addFull = function(full) {
self.full = full;
};
}
};
})
.directive("short", function() {
return {
restrict: 'E',
scope: {},
require: '^showMore',
transclude: true,
template: '<div ng-if="show" ng-transclude></div>',
link: function(scope, element, attrs, showMoreCtrl) {
scope.show = true;
showMoreCtrl.addShort(scope);
}
};
})
.directive("full", function() {
return {
restrict: 'E',
scope: {},
require: '^showMore',
transclude: true,
template: '<div ng-if="show" ng-transclude></div>',
link: function(scope, element, attrs, showMoreCtrl) {
scope.show = false;
showMoreCtrl.addFull(scope);
}
};
});
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<div ng-app="app">
<show-more>
<short>short</short>
<full>full</full>
</show-more>
</div>