I'm probably missing something here on the way $observe works inside directive. I have to child directive that needs to communicate thru their parent directive:
<parent>
<button>Change text</button>
<child text="child element"></child>
</parent>
app.directive('button', function() {
return {
require: '^parent',
scope: true,
restrict: 'E',
link: function(scope, element, attrs, parentCtrl) {
element.bind('click', function() {
parentCtrl.changeText();
});
},
};
});
app.directive('parent', function() {
return {
restrict: 'E',
scope: true,
controller: function($scope, $element, $attrs) {
var child = $element.find('child');
this.changeText = function() {
child.attr('text', 'new text');
};
}
};
});
app.directive('child', function() {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
attrs.$observe('text', function(text) {
element.html(text);
});
}
};
});
That code is only to illustrate an problem I have in the app i'm developing. All directive needs to have a isolated scope, so I cannot communicate with it. I made a plunker. Feel free to let me know if there's a better way to communicate to the child directive from the parent one.
Thank's a lot, as always!
I didnt understand why the parent and child has to have isolated scope but if you just want to communicate between parent and child you can use $emit and $on.
app.directive('parent', function($rootScope) {
return {
restrict: 'E',
scope: true,
controller: function($scope, $element, $attrs) {
var child = $element.find('child');
this.changeText = function() {
child.attr('text', 'new text');
};
$rootScope.$emit('FromParent', somedata);
}
};
});
app.directive('child', function($rootScope) {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
attrs.$observe('text', function(text) {
element.html(text);
});
$rootScope.$on('FromParent', function(event, somedata){
//Do something with the data
});
}
};
});
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);
});
}
}
});
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 have a scenario where i need to apply different directives (attribute) to a DIV inside a Angular bootstrap Modal at runtime (button click).
I would know the name of the directive to apply. But i am not able to figure out how to change the template at runtime to add necessary directive name as an attribute to the DIV.
consider this plunker
Basically i want the div to have child directive as an attribute using synstax like this
<div {{child}}></div>
So when it works, it should generate <div child-directive></div>
How can this be done? is this even possible? What is the best way to change the template before opening the Modal so that it wires up correctly when loaded.
// Code goes here
var app = angular.module('main-module', ['ui.bootstrap']);
app.directive('parentDirective', function($uibModal, $compile) {
return {
restrict: 'E',
template: "<h2>I am Parent</h2><button ng-click='click()'>Click Me</button>",
scope: {
child:'#'
},
link: function($scope, elem, attrs) {
console.log('?',$scope.child, $scope);
var template = "<div><h3>This is modal</h3>"
+ "Ideally you should see the child directive below"
+ "<hr />"
+ "<div "+ $scope.child + "></div></div>";
$scope.click = function() {
$uibModal.open({
template: template,
scope: $scope,
size: 'lg',
});
}
}
};
})
.directive('childDirective', function() {
return {
restrict: 'A',
template: "<div><h4>I am Child</h4><a ng-click='click()'>Click Me!!</a></div>",
replace: true,
scope: {},
link: function($scope, elem, attrs) {
$scope.click = function() {
alert("I am in child scope");
}
}
};
}).directive('anotherChildDirective', function() {
return {
restrict: 'A',
template: "<div><h4>I am another Child</h4><a ng-click='click()'>Click Me!!</a></div>",
replace: true,
scope: {},
link: function($scope, elem, attrs) {
$scope.click = function() {
alert("I am in child scope");
}
}
};
});;
I have a directive that wraps another one like this :
<div direction from="origin" to="destination">
<div direction-map line-color="#e84c3d"></div>
</div>
the direction-map directive is transcluded, see my code (Fiddle available here) :
var directionController = function() {
//do stuffs
};
var directionMapController = function() {
//do other stuffs
};
var Direction = angular.module("direction", [])
.controller("directionController", directionController)
.controller("directionMapController", directionMapController)
.directive("direction", function() {
var directive = {
restrict: "AEC",
controller: "directionController",
scope: {},
transclude: true,
link: {
pre: function($scope, $element, attrs, controller, transclude) {
console.log("direction's controller is directionController : ");
console.log(controller.constructor === directionController);//true, that's ok
transclude($scope, function(clone) {
$element.append(clone);
});
}
}
};
return directive;
})
.directive("directionMap", function() {
var directive = {
require: "^direction",
controller: "directionMapController",
restrict: "AEC",
scope: true,
link: {
pre: function($scope, $element, $attrs, controller) {
console.log("directionMap's controller is directionMapController :");
console.log(controller.constructor===directionMapController);//false that's not OK!!!!
}
}
};
return directive;
});
So my question is:
Why my child directive direction-map gets as parameter the controller of its parent (I think it's because it is transcluded), is it possible to avoid this or should I just re-think my code ?
It's happening beacause you are using require: "^direction" if you remove this line the directive will get the controller of itself rather than the parent one.
Hope it help :)
Updated Fiddle