Reputation: 635
I'm fairly new to angular JS and am finding it a steep learning curve, I get the feeling im really missing the point here but here goes:
I want to add a directive to my page from a controller. So I thought if I add the directive tag to the page, the directive and associated controller/template etc get added with it. After reading about the $compile method, I thought this would then be used to bind this directive to its newly created tag. This part is commented out below, but with or without this, I need the word login to appear and its controller to control it?
I can find lots of examples of similar around the web when the directive tag is on the page at load time, and can get those to work fine, so this is whats making think it is related to the $compile method - what am I missing?
HTML:
<div ng-app="application" ng-controller="myController"></div>
JS:
var myApp = angular.module('application', []);
myApp.controller('myController', ['$scope', function($scope) {
function showLoginDirective () {
$scope.login = angular.element(document.createElement('login'));
angular.element(document.body).append($scope.login);
};
showLoginDirective();
}
]);
angular.module('directives', [])
.directive('login', function($compile) {
return {
restrict: 'E',
controller: 'LoginController',
template: '<div>login</div>',
link: function(scope, element, attrs) {
//$compile(element.contents())(scope.$new);
console.log('should I not have a div containing login controlled by loginController at this point?');
}
};
});
the above code is also here: http://jsfiddle.net/d5n6L/7/
Upvotes: 8
Views: 18283
Reputation: 26982
I want to add a directive to my page from a controller.
You should be able to define a boolean variable on the $scope that determines whether or not to show the login.
$scope.loginShouldBeShowing = false;
$scope.showLogin = function() {
$scope.loginShouldBeShowing = true;
};
You can then use this in the template, with the ngIf directive, to only show the login template if this variable is set to true
<login ng-if="loginShouldBeShowing"></login>
You can see this in your modified JSFiddle at
I've also added a button so you can see in the console that the link
function only runs after after you press the button, and loginShouldBeShowing
gets set to true
I can find lots of examples of similar around the web when the directive tag is on the page at load time, and can get those to work fine, so this is whats making think it is related to the $compile method
From my understanding and previous use, it's perfectly usual to include directives in the template that are only used in certain situations, i.e. when certain $scope variables are set to certain values, using ngIf, or maybe ngSwitch or ngShow. I think things would soon get very messy if you tried to $compile every part of the template that may or may not be used at any one time. Although I'm only a relative beginner in AngularJS, so far I've only ever had to use $compile when evaluating attributes of custom directives, so
<login after-login="doThisFunction()"></login>
might then need to use $compile to call doThisFunction()
at an appropriate point.
As a small sidebar, the names of your variables are a bit telling that in the controller, you're concerned with what's happening in the template. It's more usual to keep some sort of model/business "state" in the controller, and then show the appropriate thing in the template. So you might have in the controller:
$scope.loginState = 'loggedOut';
And then in the template:
<login ng-if="loginState == 'loggedOut'"></login>
Edit: I also noticed that the login directive was in a different module to the rest of the app. I suspect this caused issues, so I modified that aspect in my JSFiddle so there was only the one module.
Edit: I think I am confused between $compile and $parse above, so I would check against the docs/other sources about my use of $compile.
Upvotes: 3
Reputation: 6896
You shouldn't really be dynamically adding elements to the page with Angular. Many people, myself included, who come from a jQuery background, assume that we can continue with this practice and just add things to the page as we need them.
However, with Angular, the logic should really all be visible in the markup. What does that mean? In your case, you should have the directive there no matter what, and then control its visibility with ng-show
or ng-hide
or ng-class
.
So, something like this would be best:
<login ng-show="showLogin"></login>
And then you can use your directive as you programmed.
Note that you can also define an inline controller (assign an array of dependencies and a function of those dependencies as the controller
property of your directive). That keeps all related code in the same place.
E.g.,
angular.module('directives', [])
.directive('login', function($compile) {
return {
restrict: 'E',
controller: ['$scope', function($scope) {
function showLoginDirective () {
$scope.showLogin = true;
};
showLoginDirective();
}
],
template: '<div>login</div>',
link: function(scope, element, attrs) {
//$compile(element.contents())(scope.$new);
console.log('should i not have a div containing login controlled by loginController at this point?');
}
};
});
Upvotes: 8
Reputation: 7646
Rather than compiling dynamically from within your controller I suggest you use ng-if to declaratively express which DOM elements should exist on the DOM.
HTML
<div ng-app="application" ng-controller="myController">
<div ng-if="showLogin" login></div>
</div>
JS
var myApp = angular.module('application', []);
myApp.controller('myController', ['$scope', function($scope) {
function showLoginDirective () {
$scope.showLogin = true;
};
showLoginDirective();
}
]);
angular.module('directives', [])
.directive('login', function($compile) {
return {
restrict: 'E',
controller: 'LoginController',
template: '<div>login</div>',
link: function(scope, element, attrs) {
//$compile(element.contents())(scope.$new);
console.log('should i not have a div containing login controlled by loginController at this point?');
}
};
});
Upvotes: 4