updating angularjs libraries causes error in angular.bootstrap() - javascript

We have a legacy framework that we're looking to be able to do some major updates to soon. As part of this, we're working at bringing all our library files up to date. During this process, we upgraded angularjs from 1.0.7 to 1.3.13. Suddenly, we're getting a show-stopping error.
Error: [ng:areq] http://errors.angularjs.org/1.3.13/ng/areq?p0=MainCtrl&p1=not%20a%20function%2C%20got%20undefined
The question is: Why does this error happen when we upgrade to the newest library, and how am I supposed to do this now? I will add the most relevant lines of code below, and I can add more as requested, but it's kind of complex and I'm not sure how to break it down to something easy to stick into here.
The error occurs at angular.boostrap() The relevant code shows like so:
angular.module('app',['DataTools','ClientDataTools']);
angular.bootstrap(document, ['app']);
Main Ctrl is defined as such:
function MainCtrl($scope, $compile) {
The file structure is as such:
load.js
run.js
dataTools.js
load / controllers.js
load / config.js
load / ClientDataTools.js
load / libraries.html
The automatically generated html loads up along with the js code found in load.js. This first thing it does is load up libraries.html, which contains the imports for run.js, dataTools.js, controllers.js, and ClintDataTools.js. The code in load.js then calls a function defined in run.js. This function loads config.js and applies the settings there while going through and using jQuery to add angular tags to the form found in the automatically generated html. It then runs the angular.module() and angular.bootstrap() commands.
controllers.js holds the MainCtrl declaration. config.js is just a json string. dataTools.js and ClientDataTools.js holds the directives used by the angular - this error still happens even when removing the directive files, so I don't think they're part of it, but they are included here out of completeness of the issue..
And no, I can't just change the html in the form in the first place. I don't have access to it. It's automatically generated html to which we wish to add intelligent behavior like preventative data checking. It's an automatically generated form that we'd like to work a little more responsively. Please don't recommend just putting the angularjs markup into the html, and please don't ask why we can't touch the original html.

The way of declaring controller with
function MyCtrl() {}
has been deprecated - try declaring like
angular.module("app").controller("MyCtrl", function() {})

Related

Angular directive to load JS files for the other directives

I'm trying to minimize js/css/html footrpint for the user and to load only the files really needed. I've utilized RequireJS for that.
For my templates I'm trying to implement someting similar to
using section in C# or ///< reference path='...' > in TypeScript
But somehow depending on my template content it does or doesn't instantiate depends-on directive depending on template I have:
Works:
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
Doesn't work:
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
<other-directive></other-directive>
Doesn't work:
<div>
<depends-on path="..\..\test"></depends-on>
<login-form></login-form>
</div>
I'm obviously missing the way Angular parses and processes templates.
Is there a way to achieve what I'm trying to do?
OK, the problem was that it didn't wait until all directive template depends on are loaded. To ensure dependencies are loaded, dependent code should be in callback passed to require function.

How do I rename or selectively load angularJS?

We have a product that is a widget people load onto their site, which consists of a single JS file that also needs angular to run, so angular is bundled into the JS file.
However, if a site already is using and loading angular themselves, when they load our widget they get an error which kills everything with the following:
WARNING: Tried to load angular more than once
Which makes complete sense since angular was indeed loaded more than once.
What we'd like to do is either of the following:
In our script, rename / namespace angular so it does't conflict with
the host sites already loaded angular, or
Detect if angular is
already loaded, and if so don't load angular ourselves.
To show examples of our code would be difficult since it's spread over about 20 files etc, however it's based off the following angular seed project which uses requirejs to load everything, then we're compiling to a single file: https://github.com/tnajdek/angular-requirejs-seed
Would really appreciate any feedback / tips / solutions
NB This is not a duplicate of any "check if angular loaded correctly" type questions, angular is packaged inside our widget js, the issue comes when angular is also already loaded by the parent page. We need a way to rename angular inside our package.
I'd advise taking a look at this answer, it has to do with a chrome extension running in the same circumstance. The idea here is to separate your loading of angular from the website's, and it assumes that your widget will be loaded after the main content of the page has been loaded.
If you are loading in html content with an ng-app directive or ng-controller, wrap your html content in a container with ng-non-bindable as an attribute.
Angular looks immediately for an element with the ng-app attribute when you load in angular.js. If two ng-apps are present i.e., on your site, and the widget, it will cause errors. Defer the parsing with: window.name = "NG_DEFER_BOOTSTRAP!" + window.name; Then load in your script.
Once your script has loaded, set window.name to '' or whatever it was before.
Individually bootstrap (the term for angular finding an ng-app attribute) your html content using:
var appRoot = document.querySelector('#id');
angular.bootstrap(appRoot, ['angularModuleName']);
And that should do it... Caveat, I have no idea how this would work if your widget Angular is on a different version than the client website, and I've only made it work with extensions, which are a little bit different because they live in their own isolated 'worlds'.
That being said, I feel like this should get people going in the right direction when dealing with this problem.

AngularJS: How can I initialize controller embedded with ajax loaded template?

I am loading an angular 'template' using ajax. This template renders a bootstrap modal form when I call $compile ... this works, everything fine here. But what I need is support of embedding controllers within this lazy loaded 'template' (Preferably I want to handle this client side so on server side everything can just look normal).
The thing is when I use ng-controller inside this template and define a function controller inside a script tag it fails. It tells me it cant find the controller function. I understand why this is happening, the script has not yet been initialized. I am just looking for a solution. How can I make the embedded script tags initialize first? Should I extract them, inject them somewhere and then compile the remainder? Or is there a more elegant way?
Lets have look in AngularJS documentation and show the integration with the browser event loop.
So you can see that AngularJS have its own event loop, which consists of three phases: compile, digest and apply.
When you call compile it will only loads the html markup and insert it. You should call apply also.
With apply you will set the execution scope. This will register an watcher that listens to changes.

Umbraco 7 custom property editor using angular file upload

I am using angular file upload in a custom property editor for Umbraco 7.0.3. I am following this tutorial in order to create the editor.
I am quite new to angular, so my question maybe trivial for some of you: I cannot resolve dependency to file upload service in my controller soon enough. My HTML template looks like:
<div ng-controller="MyCustomController">
<input type="file" ng-file-select="onFileSelect($files)" multiple>
</div>
The controller function would need $upload service as an argument, however I inject that service at the beginning of the function:
angular.module("umbraco")
.controller("MyCustomController", function ($scope, assetService, $upload) {
assetsService
.load([
"/App_Plugins/MyCustomPlugin/angular-file-upload.min.js",
])
.then(function () {
alert('upload service loaded');
});
$scope.onFileSelect = function ($files) {
alert('file selected');
}
});
So, it is definitely not right this way. I was trying to load the upload service before the controller initialization, however it loads asynchronously, so the service cannot get defined before the controller anyway.
How can I declare my controller function only after the upload service is loaded, or how can I get a reference to the service other than an argument?
Thanks,
EDIT
The only solution I've found so far was to include the actual file upload codebase into the controller of the custom property editor. I used this paper as a starting point for my own codebase for its simplicity. However I am still looking for the 'proper' way of loading an external module in this case.
Include your code as a service instead.
The code angular.module line can take extra parameters if you see its documentation page. It is here that you should look to include any third party libs. For example, I use ngTable - an AngularJS wrapper around a jQuery sortable and filterable table plugin. To include its usage, I have to add it as a service to the current application's module like so:
var app = angular.module("umbraco", ['ngTable']);
All I then have to remember to do is to reference the scripts, and I can use the code as though I had included the body of the code itself. Therefore, I presume that you would reference your service JS files, and write your code to reference the service like this:
angular.module("umbraco", "MyCustomController");
I would however rename the code as services rather than controllers.
It's a steep learning curve for AngularJS, and even when you've used it a lot, there are a lot of common gotchas. I hope this explains it a bit more.

Remove helper HTML comments in Angular JS?

Is there a way to prevent Angular from creating "helper" HTML comments? For example,
<div ng-include="myTemplate"></div>
Will transform into something like
<!-- ngInclude: 'hurr-durr.html' -->
<div ng-include="myTemplate"></div>
How do I stop this? I've looked into the Angular source, and I've seen these "helpers" are generated by an unconditional document.createComment inside almost every directive, so I guess there's no way to stop them all at once by using a config setting on a provider or something.
But maybe there is some custom Angular build without "helpers"?
I suppose I could write some Yeoman/Grunt task to remove/comment the .createComment-s from Angular's source whenever I scaffold a new project. Or maybe you guys know of a fiddle that already does that? And also, this raises my last question:
Are those comments somehow crucial to the Angular's normal functioning? And if I remove them, will it cause some kind of instability in my app? Should a just rewrite the CSS and "deal with it"?
The comments are crucial to how Angular handles certain elements. Removing them is not currently an option. What issues are you having with it?
You are able to remove the contents of these angular comments, as well as some of the classes angular attaches to elements (e.g ng-scope) by adding this config to your angular module:
myApp.config(['$compileProvider', function ($compileProvider)
{
$compileProvider.debugInfoEnabled(false);
}]);
According to the angular.js docs, it is actually good to do this in production and should result in a performance boost.
From Angular Doc:
Disabling Debug Data By default AngularJS attaches
information about binding and scopes to DOM nodes, and adds CSS
classes to data-bound elements:
As a result of ngBind, ngBindHtml or {{...}} interpolations, binding
data and CSS class ng-binding are attached to the corresponding
element.
Where the compiler has created a new scope, the scope and either
ng-scope or ng-isolated-scope CSS class are attached to the
corresponding element. These scope references can then be accessed via
element.scope() and element.isolateScope().
Tools like Protractor and Batarang need this information to run, but
you can disable this in production for a significant performance boost
with:
myApp.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
If you wish to debug an application with this information then you
should open up a debug console in the browser then call this method
directly in this console:
angular.reloadWithDebugInfo();
The page should reload and the debug information should now be
available.
For more see the docs pages on $compileProvider and
angular.reloadWithDebugInfo.

Categories