I have this directive, that i would like to make a component
angular.module('app')
.directive('year', function () {
var controller = ['$scope', function ($scope) {
$scope.setYear = function (val) {
$scope.selectedyear = val;
}
}];
return {
restrict: 'E',
controller: controller,
templateUrl: "views/year.html"
};
});
This is what I got so far:
angular.module('app')
.component('year', {
restrict: 'E',
controller: controller,
templateUrl: "views/year.html"
});
I am not sure how to move my var controller into .component
There are few things you should do convert your directive to component
There is no restrict property for component as it is restricted to elements only.
For controller you could just set as you did at directive declaration but outside of it.
Controllers for components use controllerAs syntax as default so get rid of $scope
So your component should look like this...
angular.module('app')
.component('year', {
controller: ComponentController,
templateUrl: "views/year.html"
});
function ComponentController(){
var $ctrl = this;
$ctrl.setYear = function (val) {
$ctrl.selectedyear = val;
}
}
Your component should look like this:
function YearController() {
var $ctrl = this;
$ctrl.setYear = function (val) {
$ctrl.selectedyear = val;
}
}
angular.module('app').component('year', {
templateUrl: 'views/year.html',
controller: YearController
});
For more details, please read the following Stack Overflow question for more deatils:
Angular 1.5 component vs. old directive - where is a link function?
and the official documentation:
https://docs.angularjs.org/guide/component
Also, please note that components are restricted to elements only by default.
Related
How to add a class from toggleClass directive to Header directive without using a jQuery selector? Not sure how I can do this in AngularJS, is directive to directive communication needed in this case?
<Header></Header>
<toggleClass></toggleClass>
in toggleClass directive I have:
module.exports = Directive;
function Directive(){
return {
restrict: 'E',
templateUrl: '/directive.html',
link : function(scope, element){
scope.expandToggle = function() {
// add class to Header directive
}
}
}
};
and its template:
<div ng-click="expandToggle()">
<span class="collapseText">Collapse</span>
<span class="expandText">Expand</span>
</div>
1'st way: use $emit
You can $emit event from toggleClass directive to parent controller, and change boolean value to control adding/removing your class.
ToggleClass directive:
module.exports = Directive;
function Directive(){
return {
restrict: 'E',
templateUrl: '/vic-compare-quotes-expand/templates/directive.html',
link : function(scope, element){
scope.expandToggle = function() {
scope.$emit('toggle');
}
}
}
};
Parent controller:
angular
.module('yourModule')
.controller('YourController', ['$scope', YourController]);
function YourController($scope) {
$scope.$on('toggle', function() {
$scope.apllyNeededClass = true;
})
}
Your HTML:
<Header ng-class="{'classToApply': apllyNeededClass}"></Header>
<toggleClass></toggleClass>
2'nd way: use service
You can use services for sharing data between controllers (directive and controller in your case).
ToggleClass directive:
module.exports = Directive;
function Directive(){
return {
restrict: 'E',
templateUrl: '/vic-compare-quotes-expand/templates/directive.html',
link : function(scope, element){
scope.expandToggle = function() {
yourService.apllyNeededClass = true;
// don't forget to inject "yourService" in this directive
}
}
}
};
Parent controller:
angular
.module('yourModule')
.controller('YourController', ['$scope', 'yourService', YourController]);
function YourController($scope, yourService) {
$scope.apllyNeededClass = yourService.apllyNeededClass;
// bind service variable to your $scope variable
}
Your HTML:
<Header ng-class="{'classToApply': apllyNeededClass}"></Header>
<toggleClass></toggleClass>
P.S.
I'm using ng-class on <Header> tag just for example and because I don't know the template of your Header directive.
I am trying to communicate between two directives. I tried the service way, it didn't work. Maybe I am doing something wrong.
<list-data testing="trial" template-url="grid.html" link-passed="data.json"></list-data>
My directive and service:
app.directive('listData', function($http, serVice){
return {
restrict: 'E',
scope: {
testing: '#',
linkPassed: '#'
},
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'some/path/default.html'
},
link: function($scope){
var url = 'js/' + $scope.linkPassed;
$http.get(url).then(success, error);
function success(data){
$scope.iconUrl = data.data;
}
function error(response){
console.log(response)
}
$scope.tryingToClick = function(icon){
serVice=icon.name;
console.log(serVice)
}
}
};
});
app.directive('render', function(serVice){
return {
restrict: 'E',
template: '{{rendering}}',
link: function($scope){
$scope.rendering = serVice.name;
console.log(serVice)
}
};
});
app.factory('serVice', function(){
return{items:{}};
})
grid.html is just a simple grid layout where I am trying to show the data in grid.
<div class="col-sm-6 grid" ng-repeat="icon in iconUrl">
<p ng-click="tryingToClick(icon)">{{icon.iconUrl}}</p>
</div>
I need to pass the data when I click the function tryingToClick and the icon passes to the render directive. I cannot use $rootscope here, nor make new controllers. I will be using the logic here in a pretty big enterprise app, just that I made a very simple version of it on my localhost, just to get the logic right.
Your service doesn't look quite right. I'd use
app.factory('serVice', function() {
var settings = {};
// optionally set any defaults here
//settings.name = 'me';
return settings;
});
and you're not setting the name property of the service here:
serVice=icon.name;
It should be
serVice.name = icon.name;
given that you're looking for the name property later: $scope.rendering = serVice.name;
What do you mean by not creating more controllers? Do you mean that you can't create more on the app or that you can't use controllers within the directives?
From what I understood of your question I threw this codepen together for experimentation http://codepen.io/ihinckle/pen/JWGpQj?editors=1010
<div ng-app="directiveShare">
<directive-a an-array="[1,2,3,4,5]"></directive-a>
<directive-b></directive-b>
</div>
angular.module('directiveShare', [])
.directive('directiveA', function(){
return {
restrict: 'E',
scope: {
anArray: '<'
},
controller: function($scope, aService){
$scope.clicked = aService.setIcon;
},
template: `
<ul>
<li ng-repeat="item in anArray" ng-click="clicked(item)">item</li>
</ul>`
}
})
.directive('directiveB', function(){
return {
controller: function($scope, aService){
$scope.displayIcon = aService.getIcon;
},
template: `
<h1>{{displayIcon()}}</h1>
`
}
})
.factory('aService', function(){
var srvc = {};
srvc.setIcon = function(x){
srvc.icon = x;
};
srvc.getIcon = function(){
if(srvc.icon){
return srvc.icon;
}else {
return '';
}
};
return srvc;
});
I used getters and setters in the service and controllers on the directives to expose the functions.
In my Angular template I use an attributive directive as follows:
HTML:
<div id="my-template-one" my-template-directive></div>
JS:
// ...
.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: myconfig.TEMPLATE_PATH + 'my-template-one.html',
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
For including another template, my-template-two.html, on another page, I would like to use the same directive. I do not want to duplicate the directive. How can I pass the template as an variable?
HTML on another page:
<div id="my-template-two" my-template-directive></div>
My goal is that somehow I can tell my directive to render my-template-two.html when this HTML is called.
The templateUrl property value may be a function which takes two arguments tElement and tAttrs and returns a string value:
app.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: function (tElem, tAttrs) {
var template = "my-template-one.html";
if (tAttrs.use) {
template = tAttrs.use;
};
return myconfig.TEMPLATE_PATH + template;
},
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
Usage
<div my-template-directive use="my-template-two.html"> </div>
For more information, see AngularJS Comprehensive Directive API -- template
I am working to add modals to my directives using ui-bootstrap and did so fine on the previous directive. I don't believe I am doing anything differently in this one but I get the ReferenceError: milestoneController is not defined when I run the edit() function from within the directive.
milestone.html (this is the template HTML for the directive below):
<div ng-controller = "milestoneController"></div>
milestone directive:
angular.module('ireg').directive('milestone', function (milestoneFactory,$modal) {
return {
restrict:'E',
scope: {
objectid:'#objectid'
},
templateUrl: '/ireg/components/milestone/milestone.html',
link: function ($scope, element, attrs) {
$scope.edit = function(data) {
milestoneController.editMilestoneDialog(data);
};
}
}
});
angular.module('ireg').controller('milestoneController', function ($scope, $modal){
$scope.editMilestonesDialog = function (objectid) {
//fun
}
});
EDIT: I allso felt I should mention that the milestone directive is repeated in a ng-repeat loop. Thanks!
ok you're going to want to use a transcluded scope in your directive to pass a controller function to the directive. Your directive now becomes:
angular.module('ireg').directive('milestone', function (milestoneFactory,$modal) {
return {
restrict:'E',
scope: {
objectid:'#objectid',
editMilestoneDialog:'&'
},
templateUrl: '/ireg/components/milestone/milestone.html',
link: function ($scope, element, attrs) {
$scope.edit = function(data) {
$scope.editMilestoneDialog(data);
};
}
}
and your markup becomes:
<milestone edit-milestone-dialog="editMilestoneDialog"></milestone>
Can anyone tell me how to include a controller from one directive in another angularJS directive.
for example I have the following code
var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: '/js/partials/home.html'
})
.when('/products', {
controller: 'ProductsController',
templateUrl: '/js/partials/products.html'
})
.when('/products/:productId', {
controller: 'ProductController',
templateUrl: '/js/partials/product.html'
});
}]);
app.directive('mainCtrl', function () {
return {
controller: function ($scope) {}
};
});
app.directive('addProduct', function () {
return {
restrict: 'C',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
//console.log(cartController);
}
};
});
By all account I should be able to access the controller in the addProduct directive but I am not. Is there a better way of doing this?
I got lucky and answered this in a comment to the question, but I'm posting a full answer for the sake of completeness and so we can mark this question as "Answered".
It depends on what you want to accomplish by sharing a controller; you can either share the same controller (though have different instances), or you can share the same controller instance.
Share a Controller
Two directives can use the same controller by passing the same method to two directives, like so:
app.controller( 'MyCtrl', function ( $scope ) {
// do stuff...
});
app.directive( 'directiveOne', function () {
return {
controller: 'MyCtrl'
};
});
app.directive( 'directiveTwo', function () {
return {
controller: 'MyCtrl'
};
});
Each directive will get its own instance of the controller, but this allows you to share the logic between as many components as you want.
Require a Controller
If you want to share the same instance of a controller, then you use require.
require ensures the presence of another directive and then includes its controller as a parameter to the link function. So if you have two directives on one element, your directive can require the presence of the other directive and gain access to its controller methods. A common use case for this is to require ngModel.
^require, with the addition of the caret, checks elements above directive in addition to the current element to try to find the other directive. This allows you to create complex components where "sub-components" can communicate with the parent component through its controller to great effect. Examples could include tabs, where each pane can communicate with the overall tabs to handle switching; an accordion set could ensure only one is open at a time; etc.
In either event, you have to use the two directives together for this to work. require is a way of communicating between components.
Check out the Guide page of directives for more info: http://docs.angularjs.org/guide/directive
There is a good stackoverflow answer here by Mark Rajcok:
AngularJS directive controllers requiring parent directive controllers?
with a link to this very clear jsFiddle: http://jsfiddle.net/mrajcok/StXFK/
<div ng-controller="MyCtrl">
<div screen>
<div component>
<div widget>
<button ng-click="widgetIt()">Woo Hoo</button>
</div>
</div>
</div>
</div>
JavaScript
var myApp = angular.module('myApp',[])
.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomethingScreeny = function() {
alert("screeny!");
}
}
}
})
.directive('component', function() {
return {
scope: true,
require: '^screen',
controller: function($scope) {
this.componentFunction = function() {
$scope.screenCtrl.doSomethingScreeny();
}
},
link: function(scope, element, attrs, screenCtrl) {
scope.screenCtrl = screenCtrl
}
}
})
.directive('widget', function() {
return {
scope: true,
require: "^component",
link: function(scope, element, attrs, componentCtrl) {
scope.widgetIt = function() {
componentCtrl.componentFunction();
};
}
}
})
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
}