I'm having an issue with injecting/inheriting a scope from a controller to a directive. Importantly, the module is not separated into its own var.
angular.module('articles').controller('ArticlesController', ['$scope, ...
]).directive('comments', ['$scope' ... //does not work!
You don't inject the scope into a directive as a dependency. Should be like this:
.directive([function() {
return {
"link": function($scope, $element, $attrs) {
//code here
}
}
}]);
The best way is to think about directives as black boxes. You provide it with some data and it updates/displays it. You can read all required info about directives here and declare directive's inputs and outputs like this:
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
myParameter: '=' //this declares directive's interface (2 way binded)
},
link: function (scope, element) {
console.log(scope.myParameter);
}
};
});
and then you can use this directive as:
<my-directive my-parameter='variable_declared_in_controller'></my-directive>
I just bypassed $scope entirely with a couple of other scopes:
.directive('comments', ['$stateParams', '$location', 'Articles',
function ( $stateParams, $location, Articles) {
if ($stateParams.articleId != null){
var article = Articles.get({
articleId: $stateParams.articleId
});
var comments = {articleId: article.articleId, created: Date.now, comment:"Hello, World!", user: "admin" };
return{
restrict: "A",
scope: true,
template: "<div></div>"
};
}
}
]);
Related
Currently, I am facing one issue related to angularjs directive. I want to send outlet object from directive1 to directive2. Both directives having same controller scope. I tried with emitting event from directive1 to controller, broadcasting that event from controller to directive2 and listening to that event on directive2. but that is not working.
Directive1:
angular.module('moduleName')
.directive('directive1', function() {
return {
restrict: 'E',
templateUrl: 'directive1.html',
scope: false,
link: function(scope) {
scope.selectOutlet = function(outlet) {
scope.order.entityId = outlet.id;
scope.navigation.currentTab = 'right';
};
}
};
Here, in directive1, scope.selectOutlet() setting outletId to scope.order.entityId. I want to move/set that line to directive2 save function.
Directive2:
angular.module('moduleName')
.directive('directive2', function(config, $rootScope, $state) {
return {
restrict: 'E',
templateUrl: 'directive2.html',
scope: false,
link: function(scope) {
scope.save = function() {
// Save functionality
// scope.order.entityId = outlet.id; This is what i want to do
};
}
};
});
});
Any help.
you can use a factory or a service. Inject that factory into your directive. Now when you are trying set the data in function written into factory. `app.factory('shared',function(){
var obj ={};
obj.setData = function(){
// call this function from directive 1.
}
return obj;
})`
So if you include this factory into your directives you will get the data in 2 directives.
I will try to make some jsfiddle or plunker. If it is not clear.
Do the following in first directive
angular.module('moduleName')
.directive('directive1', function($rootScope) {
return {
restrict: 'E',
templateUrl: 'directive1.html',
scope: false,
link: function(scope) {
scope.selectOutlet = function(outlet) {
$rootScope.$broadcast('save:outlet',outlet);
//scope.order.entityId = outlet.id;
//scope.navigation.currentTab = 'right';
};
}
};
and in second
angular.module('moduleName')
.directive('directive2', function(config, $rootScope, $state) {
return {
restrict: 'E',
templateUrl: 'directive2.html',
scope: false,
link: function(scope) {
$rootScope.$on('save:outlet',function(event,data){
// do staff here
});
}
};
});
I need to watch a model from within a directive.
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: {
modelToWatch: '#'
},
link: function(scope, element, attrs) {
scope.$watch(scope.modelToWatch, function(val) {
// do something...
});
}
};
]})
.controller('MyController', ['$scope', function($scope) {
$scope.obj = {
foo: 'val'
};
}]);
<div ng-controller="MyController">
<div my-directive model-to-watch="obj.foo"></div>
</div>
The above works fine.
However, I encounter a problem when there is an intermediary scope between the actual owner of the model and the directive.
I used another controller to demonstrate the scenario below:
.controller('AnotherController', ['$scope', function($scope) {}])
<div ng-controller="MyController">
<div ng-controller="AnotherController">
<div my-directive model-to-watch="obj.foo"></div>
</div>
</div>
In the case for above, I could look up the $parent tree to find the scope which owns the property I want to watch using the code below:
...
link: function(scope, element, attrs) {
var contextScope = scope;
// find for the scope which owns the property that we want to watch
while (contextScope != null && contextScope.hasOwnProperty(attrs.modelToWatch)) {
contextScope = contextScope.$parent;
}
// use the scope found to watch the model
if (contextScope != null) {
contextScope.$watch(scope.modelToWatch, function(val) {
// do something...
});
}
}
Additional problem, however is if the modelToWatch is a complex expression (e.g: "tableParams.filter().shop_id" then the hasOwnProperty cannot be relied upon.
Is there an easy way to watch a model in the context of its owner scope? Or is it's possible to watch a model even from a prototypal child?
Or can I pass scope as a parameter, so at least I don't have to look for it...
restrict: 'A',
scope: {
modelToWatch: '#',
sourceScope: '=', // don't know how to do this..
}
Note: I need to use isolate scope
As suggested by #pixelbit, I tried using the $eval to find the correct scope
link: function(scope, element, attrs) {
var contextScope = scope;
// find for the scope which owns the property that we want to watch
while (contextScope != null && contextScope.$eval(attrs.modelToWatch) != undefined) {
contextScope = contextScope.$parent;
}
...
}
Works for most cases except when the modelToWatch expression actually evaluates to undefined.. There is an ambiguity whether the modelToWatch doesn't exist in the current scope (meaning it's not the owner) or the modelToWatch expression just happens to evaluate to undefined.
You can declare a controller directly inside your directive :
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: {
modelToWatch: '='
},
link: function(scope, element, attrs) {
scope.$watch(scope.modelToWatch, function(val) {
// do something...
});
},
controller: 'MyController'
};
]})
.controller('MyController', ['$scope', function($scope) {
$scope.obj = {
foo: 'val'
};
}]);
<div my-directive model-to-watch="obj.foo"></div>
That way, when you will call your directive, your controller will be instanciated first, then the link will be executed, sharing the same scope.
You can watch a function instead:
scope.$watch(function() {
return scope.modelToWatch;
}, function(val) {
// do something
});
There is no need for an isolated scope - you can inherit scope instead. Also to address complex expressions, you can use scope.$eval to evaluate the model and find the appropriate scope. Once you've evaluated the model, return it from a watched function:
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: false,
link: function(scope, element, attrs) {
scope.$watch(function() {
return scope.$eval(attrs.modelToWatch);
}, function(val) {
// do something...
});
}
};
]})
If you must to use an isolated scope, then watch a function and return the model:
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: {
modelToWatch: '='
},
link: function(scope, element, attrs) {
scope.$watch(function() {
return scope.modelToWatch;
}, function(val) {
// do something...
});
}
};
]})
Just wondering what's the best way to communication from controller to directive function, i have got an ng-click on one of the button, but the function sit in the directive, is there a way i can call the function within the controller (which sits in directive). i understand u can apply double binding with scope, is there any better way of doing so?
Cheers
app.controller('leadsListing', ['$scope', function($scope){
$scope.filterresultcount = 0;
$scope.records = [];
$scope.filtertotal = '';
$scope.$watch('filtertotal', function(){
$scope.filterresultcount = parseInt($scope.filtertotal / 20);
});
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.heyJoe(); // It's in diretive
};
}]);
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function($scope, iElm, iAttrs, controller) {
$scope.heyJoe()
}
}
}
I believe the best way to implement this kind of controller --> directive communication is to use $scope.$broadcast from the controller, and $scope.$on in the directive's controller/ linking function.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.$broadcast('joeCalled');
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.$on('joeCalled', function(){
// Do something...
});
});
};
}
Edit:
Created a working example of this technique:
http://jsfiddle.net/9p3eyy5h/2/
Calling a function directly in the directive from the controller could be done by placing an empty object on the controller scope, binding it to the directive's scope with '=', and attaching a function to it in the directive's linking function/ controller, which could later be called by the wrapping controller.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.directiveFuncs = {};
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.directiveFuncs.heyJoe();
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount',
// Binding to the controller's func obj
funcs: '='
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.funcs.heyJoe = function(){
// Do something...
}
});
};
}
HTML:
<div ng-controller="leadsListing">
<div recordfilter funcs="directiveFuncs"></div>
</div>
I would however advise to use my other approach, as it prevents direct dependency between the controller and the directive, and therefor, more robust, so it won't throw an error if the directive is missing or changes.
Working example:
http://jsfiddle.net/9pm3zg5s/1
Not sure if I am misunderstanding how directives are created here. Say for example I have a controller such as:
angular.module('myApp.controllers').controller('MyController', ['$scope', 'MyService', function($scope, MyService) {
$scope.restangularService = MyService;
}
I then have a directive such as:
angular.module('myApp.directives').directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
//access $scope.restangularService to run some queries
}
};
});
I then use my directive as such:
<div data-my-grid data-restangular-service='restangularService'></div>
I would expect that in my directive I could access $scope.restangularService and make calls however it's not being populated correctly. Am I doing this totally wrong? Any input? I have a feeling I need to be using the ngModel directive somehow.
The "&" prefix of an isolate scope value in a directive provides "one-way binding" which makes available a getter function in the directive's scope.
Any changes you make to the object will not make their way back up to the parent controller of the directive (it is "read-only"). So you can't access your 'restangularService' variable as you would in the controller's scope, without calling the getter function:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService()); // outputs service value
}]
};
})
Alternatively, you could use "=", which would allow you directly access the scope object you pass in:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '='
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService); //outputs service value
}]
};
})
Plunk demonstrating both types
Updating as there is some confusion as to what I am asking. I would like to use a directive to inject a variable into the controller used by that directive. I realize I can use the $scope for that, but I don't find that an intuitive solution.
Essentially I want my controller to have the proposal variable injected into it.
My intended usage:
<blah-directive proposal="proposal"></blah-directive>
The directive (so far):
app.directive('blahDirective', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, scope: {
proposal: '='
}
, templateUrl: 'blahTemp.html'
, controller: blahController
};
});
blahTemp.html
<form class="form-horizontal" role="form" name="myBidForm">
**{{ proposal }}**
</form>
this is displaying the value proposal variable in the $scope fine, but it is not what I want. Essentially I would like to define my controller like:
var blahController = function($scope, SomeOtherResource, proposal) {
}
If you want to inject locals into a controller use $controller.
Here is an example (plunker):
app.directive('blahDirective', function ($controller) {
return {
restrict: 'E',
scope: {
proposal : "="
},
transclude: true,
replace: true,
templateUrl: 'blahTemp.html',
link : function (scope, elm, attrs){
scope.proposal = {};
var locals = {
$scope: scope ,
proposal: scope.proposal
};
$controller('blahController', locals);
}
};
});