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.
I'm attempting to build an angular app that is driven by the CaaS/CMS known as Prismic.io.
Phase one of this project will be a straight forward content-silo (CMS-managed), and phase two will add on more complex web app components. Knowing this, I've decided that Angular would be my best bet, but I'm struggling to think of a good solution to have all of the content lazy-loaded from the Prismic API.
One solution I've decided to explore would be to have a standardized $scope variable, let's call it $scope.loaded. Each controller will do what it must to query my Prismic service for its respective content, and once it's completed, it would set $scope.loaded = true.
The part I'm stuck on with this approach is how exactly to display the page while all of these components are loading. The easiest way would be to include ng-if directives that reference this loaded value, but I feel like there'd be a massive flash of unstyled content. And yes I could use spinners, but the idea of having 90% of the page covered in spinners seems chintzy.
Then I got to wondering: what if I pull up a loading screen for the app until all controllers' $scope.loaded values are truthy? In that case, how would I know which controllers are currently active on the page and reference their respective scopes?
(If you have comments about why this approach is bad, I'd love to hear them as replies rather than answers. I imagine this could create too many http request, for example).
A couple of options here:
Have you looked at ngCloak to see if will help you here with the flicker problem? https://docs.angularjs.org/api/ng/directive/ngCloak.
If you're using jquery, you could have a global spinner that works on concurrent ajax requests http://api.jquery.com/category/ajax/global-ajax-event-handlers/.
Or have a look at something like this global angular spinner https://github.com/monterail/angular-global-spinner/tree/master/src.
If none of these work, you could always create an array on the root scope where each controller/directive registers itself and sets its loading flag. Then add a watch to that variable to see when all components in that array are finished loading.
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. :)
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.
When I start my application, my computer loads it 15 seconds. It loads a whole bunch of grids and things that I want to load dynamically.
What do I have to do if I want for those grids to be loaded "on demand"? Do I have to load controllers dynamically or just the grids? And how?
Thank you. :)
That depends on your application structure and the configuration of the stores. Following are some tweaks that you may apply:
remove autoLoad from the stores and keep in mind that you now need to care about loading if you don't apply paging or filtering. Why? Any store that you place in your controller store array will be instantiated as soon as the controller get instantiated, which is great but cause the load of the store if autoLoad is true. Based on the implementation the store will get loaded again for example a pagingToolbar will defiantly again fire a load so the first could be spared.
apply a sort of lazy controller loading. Meaning; only apply that controllers into the application controller array that you need right at the start. Load any other controller only when you need it by calling this.application.getController('ControllerName') within a controller or directly on the application controller. This will give you the conrtoller instance and init the controller (this is quite new, so I dunno since when this happens automatically. I check 4.1.3). Anyway, the lazy controller initialization will defer all Ext.Loader request for each of these controllers till the controller get initialized, so this will help you most I guess.
It looks to me as if those scripts are being loaded by the ExtJS loader itself, I'd imagine it's loading what it thinks it needs due to the structure of the page based on the settings provided.
Have a read over the loader docs to get a feel for what it's doing and why:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Loader