I want to configure my directive module through my app level controllers. Plunker
index.html
<div ng-controller="App">
<foodz index="index"></foodz>
</div>
app.js
angular.module('app', ['foodz']).
controller('App', ['$scope',function($scope){
$scope.index = 1;
}]);
foodz.js
angular.module('foodz', []).
controller('foodzController', ['$scope',function($scope){
//Data is coming in through external API
$scope.$on('foodzFetched', function(e,d) {
$scope.foodz = d;
});
//Lets say data to looks like:
//[{"name":"banana"},{"name":"smoothy"}]
}]).
directive('foodz', function() {
return {
restrict: 'E',
scope:{
index: '#'
},
replace: true,
controller: 'foodzController',
templateUrl: 'foodzTemplate.html',
link: function(scope, controller) {}
};
});
foodzTemplate.html
<div ng-controller="foodzController">
<span>
{{foodz[index].name}}
</span>
</div>
So in this example, I am trying to pass the index through my app level controller into an attribute of my directive element which also has its own controller.
What am I doing wrong here?
Try to make scope:{index:'='}
Look at Directive Definition Object
I think you are receiving index='index' (as string) in your controller. Using = it will get the value from parent controller.
Related
I have two different custom directive and wanted to pass data from one directive to another. The data is coming from server call. Problem is http being a asyn call doesn't return data upfront and controller of another widget doesnt receive it and it renders it's html without having it. here is the complete code (I have removed some code that might not make sense in the problem)-
The service which hits server is -
angular.module('myModule')
.service('MyService', [
'$http',
function($http) {
this.getData = (someId) => {
var url = someUrl + '/' + someId;
return $http.get(url);
};
}
]);
and the first directive that calls service and set "anotherData" in the scope to be transferred to another directive is -
angular.module('myModule')
.directive('myDirective', ['MyService',
function(MyService) {
return {
restrict: 'E',
scope: {
data: '='
},
templateUrl: 'my-template.html',
controller: ['$scope', function($scope) {
MyService.getData ($scope.data.id).then((response) => {
$scope.anotherData = response.data;
});
}]
}
}]);
and my-template.html from which i am calling another directive is (notice anotherData is passed here -
<other-directive mode="display" data="data" anotherData="anotherData" ></other-directive>
The other directive that should receive "anotherData" but giving me no result is -
angular.module('otherModule')
.directive('otherDirective', [function() {
return {
restrict: 'E',
scope: {
id: '#',
data: '=',
mode: '#',
anotherData: '#'
},
templateUrl: 'other-template.html',
controller: ['$scope', '$element', function ($scope, $element) {
console.log("other data in widget after server call:");
///THIS IS UNDEFINED.
console.log($scope.anotherData);
}],
link: function ($scope, $element) {
}
}
}]);
and other-template.html has iframe to display youtube widget -
<iframe width="{{anotherData.videoWidth}}"
height="{{anotherData.videoHeight}}"
src="{{anotherData.videoURL}}" frameborder="0" allowfullscreen></iframe>
You should use dashes instead of camel case, like this:
<other-directive mode="display" data="data" another-data="anotherData" ></other-directive>
Also, you are binding for text instead of two-way binding an object, so change the definition in your directive to this:
anotherData: '='
This should do the trick
<other-directive another-data=anotherData></other-directive>
You need to be caarefull with the camel case in html!
I have a directive i'm using to do the same search filtering across multiple pages. So the directive will be using a service and get pretty hefty with code. Because of that I want to link to a controller instead of have the controller inside the directive like this:
.directive('searchDirective', function($rootScope) {
return {
restrict: 'E',
templateUrl:'searchtemplate.html',
controller: 'searchCtrl',
controllerAs: 'search'
};
});
I also want access to parent scope data inside the template, so I don't want to use a isolated scope.
Anyway here's what i'm not sure how to do. My directive looks like this:
<search-directive filter="foo"/>
How do I pass in the value in the filter attribute so that I can access it in my controller using $scope.filter or this.filter?
If I were using an isolated scope it'd be simple. If i had the controller in the same page I could use $attrs. But since i'm using a controller from another spot and don't want an isolated scope i'm not sure how to get the attrs values into the controller.
Any suggestions?
What about using the link function and passing the value to the scope?
return {
restrict: 'E',
templateUrl:'searchtemplate.html',
controller: 'searchCtrl',
controllerAs: 'search',
link: function (scope, element, attr) {
scope.filter = attr.filter;
}
};
searchDirective.js
angular
.module('searchDirective', []).controller('SearchCtrl', SearchCtrl)
.directive('SearchDirective', directive);
function directive () {
var directive = {
templateUrl:'searchtemplate.html',
restrict: "E",
replace: true,
bindToController: true,
controller: 'searchCtrl as search',
link: link,
scope: { filter:'=' } // <-- like so here
};
return directive;
function link(scope, element, attrs) {}
}
SearchCtrl.$inject = [
'$scope',
'$filter'];
function SearchCtrl(
$scope,
$filter) {
/** Init SearchCtrl scope */
/** ----------------------------------------------------------------- */
var vs = $scope;
// ....
Also I highly recommend checking out this AngularJS style guide, how you are writing your directive above is how I use to do it too. John Papa shows some way better ways: https://github.com/johnpapa/angular-styleguide
Directives:
https://github.com/johnpapa/angular-styleguide#directives
Controllers:
https://github.com/johnpapa/angular-styleguide#controllers
Flip the values of bindToController and scope around.
{
....
scope: true,
bindToController: { filter:'=' }
...
}
I have just hit the same issue over the weekend, and made a simple complete example here: bindToController Not Working? Here’s the right way to use it! (Angular 1.4+)
I have a question, code like this:
HTML:
<div class="overflow-hidden ag-center" world-data info="target"></div>
js:
.directive('worldData', ['$interval', function($interval) {
return {
scope: {
chart: '=info'
},
template: '<div>{{chart.aaa}}</div>',
link: function($scope, element, attrs) {
$scope.target = {'aaa': 'aaa'};
aaa = $scope.chart;
}
}
}])
The chart value is undefined, and template no value, but when I declare $scope.target within controller, the code works, why?
This should be generally the pattern:
.controller('myController', function($scope){
$scope.target = {'aaa': 'aaa'}; //In reality, you'd normally load this up via some other method, like $http.
})
.directive('worldData', [function() {
return {
scope: {
chart: '=info'
},
template: '<div>{{chart.aaa}}</div>'
}
}])
--
<div ng-controller="myController">
<div class="overflow-hidden ag-center" world-data info="target"></div>
</div>
Alternatively, the directive could be responsible for going and fetching the data, and not pass in anything to it. You'd only want to consider that if you don't need the data in multiple places.
I have directive that dynamically sets the header bar content for a given application state.
I would like to be able to access the functions and variables in the Controller of the current view, but I am only ever able to access my RootCtrl.
The directive looks like this.
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs){
console.log(scope.test);
console.log(scope.test2);
}
}
And the controllers.
.controller('RootCtrl', function($scope, $state, $location, $rootScope) {
$scope.test = 'hello';
//...
})
.controller('ContactsCtrl', function($scope, $state, CustomerService) {
console.log('Contacts init');
$scope.test2 = 'hello 2';
//...
})
And when I navigate to the contacts state, the output looks like this.
hello
undefined
Contacts init
What should I do if I want to be able to access the test2 variable?
You will need to use the require property inside your directive.
This will make the scope of the defined controllers available inside the link function as 4th argument. You can access the scopes as an array inside the link function then.
Your code may look like:
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
require:['^RootCtrl', '^ContactsCtrl'],
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs, requiredControllers){
console.log(requiredControllers[0].test);
console.log(requiredControllers[1].test2);
}
}
See the Angular documentation for Directives for some more examples (under the title Creating Directives that Communicate) and the explanation of the ^controller syntax.
I have a problem with my directive and controller. The variable item in scope is undefined in directive, even though I passed it in html. This is my code:
app.js:
var app = angular.module("app", ["ngRoute"]);
app.config(["$routeProvider", function($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "views/main.html",
controller: "MainCtrl"
});
}]);
controller.js:
app.controller("MainCtrl", function($scope) {
$scope.item = "x";
});
directive.js:
app.directive("exampleDirective", function() {
return {
restrict: "A",
scope: {
item: "="
},
templateUrl: "views/item.html"
};
});
index.html:
<div ng-view></div>
main.html:
<div example-directive item="item"></div>
item.html:
<div>{{ item }}</div>
UPDATE
I changed my code to:
app.directive("exampleDirective", function() {
return {
restrict: "A",
scope: true,
templateUrl: "views/item.html"
};
});
and now there is "x" in scope.$parent.item. But why it isn't present inside directive?
Although it seems to work just fine with latest Angular stable http://plnkr.co/edit/6oXDIF6P04FXZB335voR?p=preview maybe you are trying to use templateUrl thats pointing to somewhere it doesn't exist.
Another thing, use primitives only when strictly needed. In case you ever need to modify value of item, you won't be able to do so since you are using a primitive. Plus, if you need "more info" to go inside your isolated scopes, and to avoid attribute soup (attr-this="that", attr-that="boop", my-otherstuff="anotheritem.member", etc) you can pass the two-way bind of an object that handle more data.
Or if you need to share state through multiple controllers, directives, etc, use a service instead and use dependency injection, and there's no need to pass in objects/primitives to your isolated scope, and you can assure state, and that's best practice "the Angular way".
This fiddle works: http://jsfiddle.net/HB7LU/2844/ which is essentially the same thing just without the route information.
var myApp = angular.module('myApp',[]);
myApp.directive("exampleDirective", function() {
return {
restrict: "A",
scope: {
item: "="
},
template: "<div>{{ item }}</div>"
};
});
function MyCtrl($scope) {
$scope.item = 'Superhero';
}
With the view:
<div ng-controller="MyCtrl">
<div example-directive item="item"></div>
</div>
This leads me to believe it could be a scope issue. So try encapsulating the controller scope variable in a container:
$scope.cont = { item: 'x' };
And in the view
<div example-directive item="cont.item"></div>