I have a fairly straightforward AngularJS question to which I cannot seem to find an answer:
How would I go about using $scope.$watch() in a directive controller while also using the controllerAs and bindToController options?
Please let me know if you need any clarification on what I mean exactly.
Well, $scope.$watch watches for expressions so assuming you're binding your controller to the name vm (e.g. controllerAs: 'vm') you should use
$scope.$watch('vm.somethingToWatch', function(newval, oldval) {...})
You will need to still inject the $scope though, since $watch is not available on controller instances by themselves.
Related
I've came across with problem exposing api of directives in order to its interaction with controller.
There is a simplified jsfiddle to describe my structure.
The problem is that directives has templateUrl property (I replaced it with template in fiddle above), it leads to loading templates async (correct behavior according to this question).
As a result directive's controller is called after main controller therefore directive's api function doWork is undefined (even if you wrap it call with something like $timeout):
.controller('MainCtrl', function($scope, $timeout) {
$scope.api = {};
$scope.init = function() {
$timeout(function() {
$scope.api.doWork();
})
}
$scope.init()
})
There is an approach that comes to my mind - use event in link function of directive and subscribe $scope.api.doWork to that event. But I'm not happy about using events. Also it's not clear how to handle such case if there are some directives nested to each other with similar way of exposing api.
On the other hand it's possible to replace templateUrl with template - but it's also quite bad decision in case of complex layout in template.
Hence, I stuck a bit with the way to resolve such kind of problem. What's the best way to fix it out? Probably there is another technique to expose directive's api (I was inspired by Andrej Kaurin answer in that thread)?
If you are using Angular 1.5+ and are using components you could try $postLink or $onInit function link. Otherwise you just create onDoWork directive scope property and then apply some function from main controller that will be fired when doWork will actually happen(since I think that directive should control when to doWork, if not then maybe you should just create service ?)
I'm implementing an AngularJS directive on Angular 1.4.12 using the controllerAs and bindToController "pattern" in order to have a clean controller which doesn't depend on $scope.
However I still find hard to get rid of $scope on these lines:
$scope.$on( '$destroy', function() {...} );
$scope.$on( '$stateChangeSuccess', function() {} );
Any idea how to handle this case?
Thanks
The idea behind not using $scope is to:
not pollute the HTML with variables that have no context and potentially have conflicting variable names in the same scope.
ng-model="name" VS ng-model="userController.user.name"
not pollute you javascript code by preceding every variable and
function with $scope
If you need to broadcast events or watch changes, it's perfectly fine to use $scope (especially if you have no alternative like .components in angular 1.5)
See $scope as a service provided to you by Angular just like $window or $state.
If you need it, you can use it. (but don't go and put code in there, even if you can)
I'm wondering where and if there's a documentation on what can be injected into a function with dependency injection.
I understand that all registered services/factories in angularjs can be injected but when stumbling upon a solution to a problem I had, I read about the following:
function AppController($scope, $element, $compile) {
// ...
}
So from the tutorials I know $scope (but I did not find anything about $scope or scope in http://docs.angularjs.org/api/ - all I found was $rootScope).
Same with the $element. But $compile is a service just like $http - I understand where they come from.
Obviously I'm missing a very basic point about dependency injection and would be happy if someone could explain it to me.
$rootScope is an instance of $scope, and $scope docs are found here: http://docs.angularjs.org/api/ng.$rootScope.Scope
$element is implicitly injected into compile, controller and linking directive functions and is explained here: http://docs.angularjs.org/api/ng.$compile
While $rootScope can be explicitly injected anywhere you want, $element is available only inside the directives.
I am looking at some samples of how controllers work in angular and I see two ways of declaring them, one with just controller name and one with "as somename". Examples that use ng-controller = "myController" take a $scope as dependency when defining controller.
Then model is then set on the $scope, something like this
$scope.mymodel = somevalue;
Example that uses "as" syntax such as ng-controller = "MyControler as vm" never uses $scope when setting up the model but ratther assigns it to "this" and binds using {{vm.something}}.
in controller:
var vm =this;
vm.something = somevalue;
How is that working in second example? Is that new way in latest version?
Using the "as" syntax exposes your entire controller to your view. In my opinion, that is a bad practice. Although I'm not sure which one is better performance wise, but using 'this' in javascript already has plenty of issues of its own, and I don't recommend adding another meaning to 'this'. So I would stick to $scope (since that is what they're using in the docs as well).
See this topic if you want to know more context about how the 'as' syntax work: 'this' vs $scope in AngularJS controllers
I'm writing an element-level directive that has a number of attributes on it. These attributes are then put into an isolate scope using the '#' modifier.
The directive is used on a page that populates the attributes with expressions i.e
<my-directive attr1="{{foo.bar}}"></my-directive>
I'm finding that when the directive controller executes, the $scope hasn't resolved the expressions yet. Is there a way to force the scope to resolve before entering the controller?
No, you can't force the scope to be resolved before the controller runs. Use $observe in the controller to asynchronously get the value (and to be notified whenever the value changes -- just like $watch):
controller: function($scope, $attrs) {
$attrs.$observe('attr1', function(newValue) {
....
});
}