Naming issue with AngularJS controllers - javascript

I just started playing around with AngularJS. I did however stumble upon a case that had me quite puzzled, when trying to use the angular bootstrap directives for creating tabbed panes. I followed this example, and the only thing I changed was renaming the TabsCtrl variable to TabsController, because I had been using *Controller convention in the rest of my application.
What I found really strange is that this renaming broke the entire thing! The TabsController function was never executed after the renaming. You can test it yourself on the plunker link.
I need to get this straight, as it seems scary that renaming an object like this would make it break without me knowing why. Is the *Controller variable name reserved somehow? How come I have been able to use the *Controller naming convention for the rest of my controllers without problems?
Edit: It seems that it is the exact word TabsController that is causing the issue. I tried naming it BajsController instead, and that worked..

Oh, I found it.
It seems that the bootstrap angular directives are already defining a controller called "TabsController", so that is where the conflict is.
I didn't find this conflict when I was looking for an existing object called "TabsController" in the debug console, but that is of course because it was not declared as a global variable inside the bootstrap directive.

Related

Difference between using Angular $scope dependency and no dependency

There is something that i don't understand with Angular.
I'm new to Angular, and i don't remember where but the tutorial that teached me used this syntax do apply properties to a controller's scope :
app.controller('someCtrl', function(){
this.variable = "hey!";
});
So, i was starting my web app, everything was fine, then i wanted to add some socket.io interactivity with my node.js server. So i was looking for some tutorials on how to make them work together i was confronted to this syntax :
app.controller('someCtrl', ['$scope', function($scope){
$scope.variable = "hey!";
}]);
I thought it was weird, so i looked into Angular's articles about dependency injection and scopes, i found that it was actually the way everyone is doing it... I also started to see that it allows you to interact with $rootScope and other stuff, which is i guess the way to make controllers interact with each other. But i still don't see the difference between the two. Is the first one one which is used to teach people the basics of angular in order to easily introduce them to the scope and dependency injection concepts ?
Thanks in advance for your answer.
Yes, that is confusing as hell and I wish the Angular guys would only use the second. The first one is easier to read and understand for any JS developer, however the docs used to state (AFAIK, that is gone from the docs now):
Previous versions of Angular (pre 1.0 RC) allowed you to use this interchangeably with the $scope method, but this is no longer the case. Inside of methods defined on the scope this and $scope are interchangeable (angular sets this to $scope), but not otherwise inside your controller constructor.
Since some versions, the second syntax (which injects $scope via dependency injection) is typically used, even if it is somewhat frightening to newbies, what with the array in the function variable list (which is needed for minification) and all.
The situation gets worse, though, because lately this makes a comeback in slightly different form, the "controller as" syntax:
HTML
<div ng-controller="MainController as main">
{{ main.someProp }}
</div>
Javascript
app.controller('MainController', function () {
var model = this;
model.someProp = 'Some value.'
});
So, most likely what you read is the "controller as" syntax that is currently pushing back more and more the second, dependency injection form. You can differentiate it from old examples if you look at the HTML to find XxxController as yyyy.
Typically, you don't work with this all the time in your controller's functions since the scoping of this in Javascript can easily introduce bugs. Therefore it is preferable to assign this to some local variable immediately and then work with that variable (model in my example) like you would with $scope.
TL;DR: work with the "controller as" syntax to be future-proof, but know until recently, the best practice was injecting $scope.
Basically, You should directly inject the $scope as a dependency to avoid minification problems.
When doing a minification, $scope will be translated into e and Angular will not know how to use e itself.
var app=angular.module("myApp",[]);app.controller("mainController",function(e){e.message="OH NO!"})
When directly injecting the dependency as you shown, $scope will not be renamed in any other way, so Angular will know how to handle it.
Take a look at this article on Scotch.io
I hope I've been helpful.

Angular controller sporadically not loading

I have a website set up with RequireJS and Angular, but at seemingly random moments, it will decide to either not load part of the Javascript or not databind part of the angular code.
The page in question has several different components on it with their own angular controllers. Everything loads fine about 90% of the time, but sometimes something will happen that prevents databinding (angular brackets visible, ng-hide not working,...). Additionally, the 'network' tab in Chrome devtools shows no files being loaded, though they are listed in 'sources'. I don't know if that is relevant somehow. I get no errors in the console at all.
Digging around in the JS console I've found that one of the broken controllers in question does exist ( = I get an object using angular.element(...).scope() ) but when I try to access one of its properties, they are either undefined OR in case of the init() function I use in all my controllers, it returns the function of the parent controller.
What could cause the controller to be loaded but its scope variables to be undefined at seemingly random times?
EDIT:
The only way I've found to sort of reproduce this issue without errors showing up in the console is to initialise the controller as an empty function. This produces similar scope behavior in the console, but it doesn't cause the angular curly braces to show up. I will accept any clue that leads me to the cause of the issue or a viable workaround.
It appears that RequireJS sometimes loaded everyting fast enough for the angular.bootstrap call to be executed before the DOM was completely loaded. This lead to angular processing what is already loaded and ignoring whatever came next. I therefore added a domReady requirement to the setup so I only bootstrap angular when I know the whole page will be there. Since it is hard to know for sure, I can only hope this was the cause and that we won't be seeing this issue again.
You might have to use AngularAMD. Works great for our angular website.

Is it possible to retrieve the controller of a directive compiled inside another directive?

I'm stuck on structuring components inside a large AngularJS application I've been maintaining and I'd really love some guidance since I'm at my wits end. Please bear with me!
Context:
I've got some directives that I'd like to have communicating with each other. As such, I thought it was appropriate to define a controller in each directive to expose an API for other directives to make use of. Now, I'm well aware of the require property in directives and how one can pull in the controllers of parent directives to use. Unfortunately, in my current circumstances, I have directives that don't necessary fit the use of requiring controllers.
Instead of using require, the code base I'm faced with has mostly chosen to add directives directly to the DOM and then to compile them afterwards. I suppose this was to allow for flexibility on customising how directives depend on each other.
I've included a snippet from the link function out of the demonstration Plunker further below that I created to help visualise the problem I'm facing. Note how directives are being attached to the DOM and then being compiled. I tried as best as I could to create a simplified version of the code I'm actually working on because I can't post it.
link: function(scope, elem) {
scope.data = '...';
var d2Elem = elem.find('li').eq(0);
d2Elem.attr('d2', '');
var input = angular.element('<input type="text" ng-model="data">');
elem.find('li').eq(-1).append(input);
$compile(d2Elem)(scope);
$compile(input)(scope);
// Able to get d1 directive controller
console.log(elem.controller('d1'));
// Not able to get compiled d2 directive controller
console.log(d2Elem.controller('d2'));
// Able to get compiled ng-model directive controller
console.log(input.controller('ngModel'));
}
Question:
Could somebody please explain why I'm seeing the behaviour I commented on in my Plunker? Why is it that when I compile a directive I've defined (i.e. d2), I cannot access it's corresponding controller even though it exists in the directive definition?
Coincidentally, I found that after compiling the built-in ng-model directive, I can in fact get its controller.
An extra point I'm pondering: Is the process I've described the least painful way to go about managing directives that communicate with each other? Noting that these directives don't necessary have strict parent-child relationships.
PLUNKER
Would very much appreciate some thoughts!
It is taking time until d2.html is being loaded asynchronously by Ajax, until it's loaded completely you cannot access it's controller, see attached screenshot, after ajax call it's able to access controller of d2.
I tried by replacing
console.log(d2Elem.controller('d2'))}
with
setTimeout(function(){console.log(d2Elem.controller('d2'))},1000);
And it worked for me, may be this will give you some hint or may be putting delay will resolve your issue, I know this is not good practice!!

Can't get Angularjs to update view when model updates

I am an Angular newbie and I'm stuck. I have some code on jsbin.com --->(http://jsbin.com/ratageza/7/edit?html,js,output)
It's not pretty but it's showing essentially what I am doing.
In my real code I am using $http.get calls a RESTful backend to load data from a database, and $http.post calls to add to the database. All works fine as far as the database fetches and updates. But after I create a new object using the "Create" form, if I click "List All" my tournaments object is not updated.
I'm confused about whether I would need $apply after the $http.post call? I've tried using it but am either using it wrong or that's not my problem here. I've looked all around stackoverflow and can't seem to find the answer to my problem.
If anybody can see what I'm doing wrong I would really appreciate the help.
Thanks!
Check out the slight changes I made here: http://jsbin.com/ratageza/8/edit?html,js,output
The change is how you define your controller usage. You use TournamentController in two different places which actually gives you two instances of that controller with two separate, isolated scopes. Your List All view was bound to one instance while the Create view was bound to a completely different instance.
Also, your question about $apply. As a general rule, you should very seldom have to use $apply and you should NEVER use it in a controller as it's not safe to use there. Directives are really the only safe place to use $apply and even there it should only be used if data is being modified outside the scope of angular. The order in which things are processed, you find that if you use $scope.$apply() in a controller, you will frequently get exceptions about already being in a digest cycle.
http://jsbin.com/ratageza/10/edit this works.
It's not a $apply or $diget issue. Because you put TournamentController into to part of views. One is create and the other one is to show. That makes a different copy of $scope.tournament;
So I add a div to wrapper the Form and the table. Put the controller to it. Remove the form and table controller.
Hope this can solve your problem. :)

Angular.js controllers

Note: Sorry for the length of the post, but the reason I decided not to break it down in separate questions was because I find these issues hard to address without a complex problem like this. I am dazzled, and a bit afraid, that I'm trying to force Angular to do stuff for me which is not the 'Angular way'. Any advice would be highly appreciated, and would probably get me on the right track with Angular.
My problem is as follows:
I have a dynamically generated form, controlled by myFormCtrl. I want to go extremely modular: I want to use it whenever and wherever. This means, that sometimes I'll need to put it somewhere as-is, sometimes I need to nest forms dynamically (like when I change a form value, and other sub-form appears), or control two separate forms as one in a view by a parent controller, with one 'Save' button for both. The myFormCtrl uses $scope.type_id and $scope.rowid to know, which record should it display from the database. The records are then Ajax-fetched by a service, and saved under the myFromCtrl's $scope.formItems. When saved, the form sends back data to the server (via service) with the type_id and scope credentials, so the restful api knows where to put the record.
In theory that would be really easy to do in Angular.js.
It definitely would be in every object-orientated language: The parent class could call a public method of getFormValues() of myFormCtrl. Now that can't be done in Angular: parent can't read children's scope.
For me, it seems, that this is not a simple 'how to communicate between controllers' issue. I know that the answer to that question is services, events, scope inheritance.
Also, a number of other problems seem to emerge from each solution I found sofar.
So I have a myFormCtrlBase class, which does basic stuff, and other more advanced classes extend this one. I also have a formControls.html, and a formLayout.html partial. The first contains an ng-switch, and gives the appropriate input element based on $scope.formItem.controltype, the second has the html layout of a common form, ng-including formControls.html at the right places. It utilizes ng-repeat="formItem in formItems", so that's where formControls.html's $scope.formItem comes from.
When I want the form to have a different layout for example, I create a customFormLayout.html partial ng-controlled by the myFormCtrl class.
First question: what if my form layout can't be put in an ng-repeat?
Like when form elements need to be placed scattered across the page, or form layout is not something which could be fit in an ng-repeat loop. my formControls.html still expects a $scope.formItem to work with. Easy OO solution: parent puts formItem in child's scope.
My solution: I created a <formItemScopeChanger formItemScope="formItems[1]"> directive which gets formItems[1] as an attribute, and turns it to $scope.formItem variable. This solutions feels messy: directives are not meant to be used like this. Doesn't seem very Angulary. Is this really the best solution?
Second question: Is ng-init really that evil?
Say, form is not put in the view by $routeProvider, but in a custom partial: rent-a-car.html. Here, I want to have a form where the user can select a car, and an other form, where I get his contacts. The two forms work with different $scope.type_id's, so there need to be two different forms:
<h1>Rent a car!</h1>
<div ng-controller="myFormCtrl" ng-init="type_id='rentOrder'">
<div ng-include="'formLayout.html'"></div>
</div>
<h2>Your contact information</h2>
<div ng-controller="myFormCtrl" ng-init="type_id='User';rowid='{{userData.rowid}}'">
<div ng-include="'formLayout.html'"></div>
</div>
Angular docs sais, that the only appropriate use of ng-init is when aliasing ng-repeat values. I don't see what the problem is with the example above - it is still the cleanest solution, isn't it?
I use the same technique with nested forms - I put a controller in with a template, initialized from the html by ng-init, and shown/hidden with an ng-if condition.
BTW, this is the only real initialization technique I found beside writing a new controllers (extending myFormCtrlBase). In an OO language, parent would write into the child's scope and then initialize it.
Perhaps my approach is influenced by my previously used languages and programming techniques, and is absolutely wrong.
Some would say, 'get init values from parent scopes!', but I can't seem to understand how that would be safe and efficient. I'd need to do $scope.type_id=($scope.type_id || $routeParams.type_id) with every scope property, which is first: really not nice to look at, second: is risky. Maybe this is a single form in a simple template, but somewhere in the scope hierarchy, there is a possibility, that it will find a completely different type_id. Perhaps it will be a completely different controller's type_id.
I don't see how using '.'-s in my scope variables would help. I has the same risk as I see it.
Third question: how to handle rentACar.html submission?
When hitting a Save button on my rentACar.html page, the rentACarCtrl (the controller in charge of the model of the view) should somehow retrieve the values of the two forms, and handle the validation and submission. I can't seem to understand how the common mantra 'controllers communicate through services' would be applicable here. A service for only to these two forms?
Am I on the right track? Every one of these solutions seem quirky. I feel lost :)
+ 1 question: Even after all this hassle, I can't seem to find a good reason why Angular wouldn't let parents call children's public stuff. Is there a good reason? Most of the above problems would have an easy answer in every true OO js framework.
You need to think about how you would test the logic of each of these components. Ask yourself how each of these 'features' work in isolation.
A few tips to help get you back on track:
Try and say away from a 'base' controller, I have hit many dead ends with scope inheritance, the logic gets muddled and hard to follow. Also this affects testing, because you find yourself having to stand up more objects than should be necessary for a test
Favor a singleton (angular service) for shared state over scope inheritance (a parent controller)
Create a directive and bind to the shared services state before using ng-include (prefer interacting with a service over scope inheritance)
Use an event pattern when another service or controller needs to now about events triggered from directives. A shared service (state) can listen for those events
What your asking is quite complex and I would like to help, Try to focus on one feature at a time and provide some code, I can show you how to use a shared service and the event pattern once you provide some examples
Also, taking a test first approach will often reveal the best 'Angular Way' of doing things.
Thanks to Mark-Sullivan, and a lot of work, trial-and-error attempts, the whole thing has boiled down to this. I'd like to get feedback from Mark, and other Angular gurus about this. What do you think?
You don't do class/prototypical inheritance in Angular.js. It is hard to test, and thats a big problem. For those, who are looking for 'inheritance' in Angular, I recommend this:
Your base class is the controller. The controller is an abstract model anyways, so it is perfect for that purpose. Use a $scope.init() function in your controller, but don't call it from there!
If you want to 'extend' your controller's functionality, use directives. In you directive link() function, call the controller's $scope.init(). (when compiling, angular runs controllers first, and directive link functions after). If scope had a $scope.name='base', in the directive link you will be able to redefine $scope.name=child, and after that, run $scope.init().
But wait! But this only allows a single-level inheritance. - Yes, thats true. But if you are looking for multilevel inheritance, you should use Services.
Multilevel inheritance is nothing else, but sharing the same code in a hierarchical class structure. For this purpose, use Services, and throw in these services with the dependency injector into your directives. Soo easy. This should be easy to accomplish, easy to understand, and tests run smooth.
Directives are very powerful tools, because you can dynamically combine partials with controllers.

Categories

Resources