I am following this best practice style guide for angular development and I can't seem to get this one part working cleanly.
Best Practice Style Guide:
https://github.com/johnpapa/angular-styleguide
It recommends to declare controllers by the following:
angular
.module('app')
.controller('feedController', FeedController);
function feedController(){
var vm = this; //My $scope variable
}
The problem is I am trying to use $on to add an event listener but it gives me an undefined error for vm.$on. A temporary solution I found was to inject $scope into the controller by the following:
FeedController.$inject = ['apiservice', '$scope'];
and call $scope.$on which works but I feel its inconsistent now. Is there a way to still use vm in a clean way.
You can see the full controller here https://github.com/bliitzkrieg/TrailerFeed/blob/master/app/components/feed/FeedController.js
this/vm refers to the instance of the controller, not the scope that is associated with that controller.
Events only exist on scopes, so to use the event system it is correct to inject $scope to get a reference to the controller's scope where $on is available.
angular
.module('app')
.controller('feedController', FeedController);
function feedController($scope){
var vm = this; // the instance of the controller
$scope.on(...) // the controller's scope
}
Related
What is the disadvantage of using the $scope as variable in AngularJS, to share $scope of a controller inside the app.run()?
Actually I am doing that to make code generic to be called from all the controllers with same function in app.run().
The function I am using is with the
$rootScope.getUserInfo = function($scope){
$scope.userinfo = '---------';
}
where $scope is the variable that I am passing from every controller like that
$rootScope.getUserInfo($scope);
I don't think there's inherently something wrong with passing around a scope. People do this a lot in AngularJS services and it's internally done a lot, too: your created controller is passed a scope to work with.
However, I would say it's not necessary in your example to have getUserInfo to depend on a scope being passed. Why not return the user information and have the caller put it on the scope? That way, you can use it in parts of your app that don't have a scope.
Instead of using $scope, you can use var vm = this; and define everything in that controller with vm.variablename_or_funcname instead of $scope.variablename_or_funcname
And you can attach that controller in html like ng-controller = "mycontroller as vm"
More info:
https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
Problem Related codepen
I declared $rootScope.myproperty in main controller and it can be accessed in other controllers.
But in one of the controller I have to inject $rootScope as I used $watch. Then when I check back $rootScope.myproperty it became undefined. Strange why would this happens?
It was happening because you were using $rootScope as a variable and assigning an object to it
$rootScope = { myproperty: 'root abc' };
This is wrong. Instead you should add attributes to it as below and it will work:
$rootScope.myproperty = 'root abc';
I have updated the codepen and its working : http://codepen.io/anon/pen/RaeyJm
Working Demo
The problem is you have injected $rootScope into AttendeesCtrl controller. You don't need to inject $rootScope into a controller. When you inject $scope, you automatically get access to anything defined in $rootScope due to the prototypal inheritance of JavaScript. So AttendeesCtrl controller should look like this:
.controller('AttendeesCtrl', function($scope) {
//I used $watch and $rootScope somehere so I have to inject $rootScope in this controller
$scope.myproperty = $rootScope.myproperty;
});
For detail, you can have a look at the AngularJS Scope Hierarchies.
Hope that solve your problem.
In AngularJS, a Controller itself is an object defined by a Javascript constructor function and this constructor function is a callback to .controller method.
var myapp=angular.module('scopeExample', []);
myapp.controller('MyController',function MyController($scope){
$scope.name="john";
this.num=700;
});
In the above example, MyController is the constructor function which creates the Controller object with one property (num). I have three queries upon that:
Actually, what is the use of the Controller object in that case?
Does it have some more properties not visible and is it accessible from outside Angular?
How it is interconnected to its scope which in turn is another object?
I came upon the following queries because of the controller as syntax which creates a controller object that is a property of controller's scope and therefore easily accessible, e.g.
<div ng-app="scopeExample" ng-controller="MyController as ctrl">
<input id="box" ng-model="ctrl.num"> equals {{ ctrl.num }}
</div>
<script>
angular.module('scopeExample', [])
.controller('MyController', [function () {
this.num=12;
}]);
</script>
var x=angular.element('#box').scope().ctrl; //x is the controller object itself
1.a. What is the use of the Controller object in that case?
There is nothing special about this example, angular is an MVC framework(or any other buzz word you wish to use that describes almost the same thing), the controller's responsibility is to response to view events, update the model accordingly and execute business logic tasks (you can choose where to actually implement the logic, wheres in the controller itself or use services).
Of course that in this example the controller is pretty useless, because you have no logic, and only 2 proprieties.
1.b. Specking of ctrl-as syntax, in your example you injected 'scope' into the controller and added property ($scope.name), when you're using controller as it is recommended for you to avoid using scope unless you are obligated to do so. (e.g. $watch, parent...)
2.a. Does it have some more properties not visible?
No it doesn't have any invisible properties, you can check it easily by your self with the following code:
.controller('MyController', function () {
window.DoesThisControllerHaveInvisibleProps = this;
});
2.b. is it accessible from outside Angular?
I'm not sure that I fully understood what you've meant with "outside Angular", if so here is an example that the controller obj can be accessible from "outside":
class MyController {
static foo() {
console.log('hello!');
}
}
myapp.controller('MyController', MyController);
// maybe somewhere else in that module
MyController.foo();
3.How it is interconnected to its scope which in turn is another object?
As you said, when using controller as syntax angular is initializing the controller and put it on the $scope so it will be accessible in the template.
$scope is just an unnecessary glow and you should avoid using it. my way of seeing it is like it was angular implantation details, when migrating to ng-2 you will see that there is no more scope.
If you're interested in more detailed info about how exactly $scope and controllers in angular are working I suggest you have a look at
Can we use only this in controllers, without $scope?
In some cases of course yes, i know... But...
If we need $emit, $broadcast and some other angular features, which we can find only in scope? Can we get it in this may be, or some other ways to get it?
As far as I'm aware there are some special cases like $emit etc which you have to use $scope and can't use this.
When you create a controller in this format :
angular.module('myModule').controller('myController',['$scope',function($scope){
//controller code here
}]);
the function that you are passing is basically a constructor for the controller object
so when you write a code like this :
angular.module('myModule').controller('myController',['$scope',function($scope){
this.myObject = { key : value};
}]);
you are basically making myObject a property of the myController object
Controllers are basically used in angular.js to fascillitate communication between your view( which are handled by directives ) and services( which take care of the buisness logic )
To enable that you can use the dependency injection angular provides and insert the $scope object into your controller as shown above
when you instantiate a controller using ng-controller="myController" a new scope object is created to be used with your controller object. the $scope object that you have passed as an argument to the constructor function of the controller has the newly created scope.
Here is an excerpt from angular documentation :
The properties contain the view model (the model that will be presented by the view). All the $scope properties will be available to the template at the point in the DOM where the Controller is registered.
so basically whatever properties you attach to the $scope object only will be avalaible to be manipulated in DOM
any property you attach to this wont be available in the DOM. for example :
this is you angular configuration :
angular.module('myModule').controller('myController',['$scope',function($scope){
$scope.name = 'kiran'
this.message = 'hello world';
}]);
This is your html :
<p>{{name}}</p>
Output will be : kiran.
if you had tried : <p>{{message}}</p> instead it will throw an error.
only the scope object contains properties like $$watchers which is a list of all the variables that are watched on the current scope, $watch API,$on API for event handling and so on.
so you cannot use this to leverage those properties.Hope this clears stuff up.
I'm a newbie to Angular and I'm struggling to understand how views are updated with scope changes. I am trying to update a header in my app using Angular JS based on whether a user is logged in. This information is returned by a Login service.
I have distilled my problem into two plunkers, one working and one not.
In order to get it to work I have to assign my LoginService to a variable on the scope of the HeaderCtrl.
angular.module('app', [])
.controller('HeaderCtrl', function ($scope, LoginService) {
$scope.loginService = LoginService;
$scope.$watch('loginService.isLoggedIn()', function(newVal) {
$scope.isLoggedIn = newVal;
});
Here is the working version
http://plnkr.co/edit/KBzE9N?p=preview
Now if I remove the reference to the LoginService in the scope of the HeaderCtrl and just use the injected service in the watch directly, the view stops updating. That is demonstrated here
http://plnkr.co/edit/IjFS2w?p=preview
Can anyone explain to me why the second case doesn't work? I've also read that it is a bad idea to have watches inside a controller so I'm open to better solutions.
Because the $watch watches scope variables in the scope you are calling it from. In your second example, $watch is looking for a LoginService variable in your scope, which of course does not exists.