Dynamically add AngularJS Script - javascript

I've got a project that's has a very rapid development cycle which causes many changes in the main app.js file. This file has the configs as well as controllers for the AngularJS app that's being used as part of the project. This created Caching issues which I tried solving as follows:
<script type="text/javascript">
var s = document.createElement('script')
s.setAttribute('src', 'assets/js/app-v2.js?v='+(new Date().getMilliseconds()));
document.body.appendChild(s);
</script>
However, this gives me an error in angular saying:
Failed to instantiate module appName due to:
Error: [$injector:nomod] Module 'appName' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
My HTML is setup as
<html ng-app="appName">....</html>
So I tried setting the ng-app dynamically after loading the script but that doesn't work either. Gives me the same issue as earlier. Is it possible for me to add the appName dynamically during or after the load of the app-v2.js file?

I have found this answer from the stackoverflow while I was facing same issue.
bootstrap() will call the AngularJS compiler for you, just like ng-app.
// Make module Foo and store $controllerProvider in a global
var controllerProvider = null;
angular.module('Foo', [], function($controllerProvider) {
controllerProvider = $controllerProvider;
});
// Bootstrap Foo
angular.bootstrap($('body'), ['Foo']);
// .. time passes ..
// Load javascript file with Ctrl controller
angular.module('Foo').controller('Ctrl', function($scope, $rootScope) {
$scope.msg = "It works! rootScope is " + $rootScope.$id +
", should be " + $('body').scope().$id;
});
// Load html file with content that uses Ctrl controller
$('<div id="ctrl" ng-controller="Ctrl" ng-bind="msg">').appendTo('body');
// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
// so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
// Here I cannot get the controller function directly so I
// need to loop through the module's _invokeQueue to get it
var queue = angular.module(moduleName)._invokeQueue;
for(var i=0;i<queue.length;i++) {
var call = queue[i];
if(call[0] == "$controllerProvider" &&
call[1] == "register" &&
call[2][0] == controllerName) {
controllerProvider.register(controllerName, call[2][1]);
}
}
}
registerController("Foo", "Ctrl");
// compile the new element
$('body').injector().invoke(function($compile, $rootScope) {
$compile($('#ctrl'))($rootScope);
$rootScope.$apply();
});
Hope it will help you

Related

Odoo 11 Web JS Framework doesn't initialize my module and thus doesn't register my client action

I'm following the Building Interface Extensions guide with Odoo 11.
In the guide, it is stated that
In Odoo web, modules are declared as functions set on the global odoo
variable. The function's name must be the same as the addon (in this
case oepetstore) so the framework can find it, and automatically
initialize it.
But my module isn't being initialized and I get the following error:
Action error - Could not find client action 'petstore.homepage'.
I put some logging in the module and I found that the file is being fetched by the browser, as expected, but the initialization isn't happening.
This is my JS file:
odoo.oepetstore = function(instance, local) {
console.log('Started odoo.oepetstore'); ////////// [1] - This never runs
local.HomePage = instance.Widget.extend({
template: 'HomePageTemplate',
start: function() {
this.$el.append($('<div>').text('Hello dear Odoo user!'));
}
});
instance.web.client_actions.add('petstore.homepage', 'instance.oepetstore.HomePage');
}
console.log('Loaded petstore.js'); ////////// [2] - This always runs
With Odoo 9 (after renaming the file __manifest__.py to __openerp__.py and renaming the variable odoo to openerp), everything works as expected.
Why isn't it working with Odoo 11?
EDIT
Here's my working code after following Tchi-Odoo's answer:
odoo.define('PetStoreHomePage', function(require){
"use strict";
var core = require('web.core');
var Widget = require('web.Widget');
var HomePageWidget = Widget.extend({
template: 'HomePageTemplate',
start: function() {
this.$el.append($('<div>').text('Hello dear Odoo user!'));
}
});
core.action_registry.add('petstore.homepage', HomePageWidget);
});
in new version of odoo when you define a javascript module use this:
// key of your module so other require it.
odoo.define('your_module_name.name_to_discript_functionality', function(require) {
'use strict'
// user require to load module that your module depends on them
var web = require('web.code');
// if someone need your module function he will load it by it's key
// var YouModule = require('your_module_name.name_to_discript_functionality');
// if you define new class return them so other can use them
return {
NewClass : NewClass,
...
...
}
});

Share Service Between Two Different Modules and two different js files

I have checked many questions but no one clearly give examples which uses different js files.
I am working on angular js, and I'm stuck in the following issue.
Issue is, I want to Call functions which is in different js file and has different modules.
Now I Create "Service",which handles both modules and both functions,here is my Code,
First Module in first.js
var TaskAPP = angular.module('TaskApp', []);
TaskAPP.factory("TestService", function () {
var users = ["ad", "bf", "tt"];
return {
all: function () {
return users;
},
first: function () {
return users[0];
}
};
});
second module in second.js
var LabAPP = angular.module('LabApp', ['TaskApp', 'TestService']);
LabAPP.controller("LabController", function ($scope, TestService) {
$scope.aa = TestService.first();
});
but here on this page,error occur Error: $injector:modulerr
Module Error
Can any one help?
Your TestService is defined on TaskAPP, which is why it can't be used in LabApp. The solution is to create a module just for services, you want shared across modules.
For example, create a module just to hold shareable services:
angular.module('SharedServices', [])
.service('TestService', function(){
// code
}
Then inject SharedServices module into whichever module you want to access the service from:
angular.module('TaskAPP', ['SharedServices'])
Then access the service you want using SharedServices.TestService.fooMethod()
Your mistake is to use the service name TestService as a module dependency - this line:
var LabAPP = angular.module('LabApp', ['TaskApp', 'TestService']);
This fails, because there is no module called TestService, there is only a service inside the module TaskApp with that name. This should work:
var LabAPP = angular.module('LabApp', ['TaskApp']);
LabAPP.controller("LabController", function ($scope, TestService) {
$scope.aa = TestService.first();
});
Love from Switzerland :)

When I reference local angular lib I get error: Argument is not a function, got undefined and CDN works ok? [duplicate]

I am writing a sample application using angularjs. i got an error mentioned below on chrome browser.
Error is
Error: [ng:areq] http://errors.angularjs.org/1.3.0-beta.17/ng/areq?p0=ContactController&p1=not%20a%20function%2C%20got%20undefined
Which renders as
Argument 'ContactController' is not a function, got undefined
Code
<!DOCTYPE html>
<html ng-app>
<head>
<script src="../angular.min.js"></script>
<script type="text/javascript">
function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}
</script>
</head>
<body>
<h1> modules sample </h1>
<div ng-controller="ContactController">
Email:<input type="text" ng-model="newcontact">
<button ng-click="add()">Add</button>
<h2> Contacts </h2>
<ul>
<li ng-repeat="contact in contacts"> {{contact}} </li>
</ul>
</div>
</body>
</html>
With Angular 1.3+ you can no longer use global controller declaration on the global scope (Without explicit registration). You would need to register the controller using module.controller syntax.
Example:-
angular.module('app', [])
.controller('ContactController', ['$scope', function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}]);
or
function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}
ContactController.$inject = ['$scope'];
angular.module('app', []).controller('ContactController', ContactController);
It is a breaking change but it can be turned off to use globals by using allowGlobals.
Example:-
angular.module('app')
.config(['$controllerProvider', function($controllerProvider) {
$controllerProvider.allowGlobals();
}]);
Here is the comment from Angular source:-
check if a controller with given name is registered via $controllerProvider
check if evaluating the string on the current scope returns a constructor
if $controllerProvider#allowGlobals, check window[constructor] on the global window object (not recommended)
.....
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true) ||
(globals ? getter($window, constructor, true) : undefined);
Some additional checks:-
Do Make sure to put the appname in ng-app directive on your angular root element (eg:- html) as well. Example:- ng-app="myApp"
If everything is fine and you are still getting the issue do remember to make sure you have the right file included in the scripts.
You have not defined the same module twice in different places which results in any entities defined previously on the same module to be cleared out, Example angular.module('app',[]).controller(.. and again in another place angular.module('app',[]).service(.. (with both the scripts included of course) can cause the previously registered controller on the module app to be cleared out with the second recreation of module.
I got this problem because I had wrapped a controller-definition file in a closure:
(function() {
...stuff...
});
But I had forgotten to actually invoke that closure to execute that definition code and actually tell Javascript my controller existed. I.e., the above needs to be:
(function() {
...stuff...
})();
Note the () at the end.
I am a beginner with Angular and I did the basic mistake of not including the app name in the angular root element. So, changing the code from
<html data-ng-app>
to
<html data-ng-app="myApp">
worked for me. #PSL, has covered this already in his answer above.
I had this error because I didn't understand the difference between angular.module('myApp', []) and angular.module('myApp').
This creates the module 'myApp' and overwrites any existing module named 'myApp':
angular.module('myApp', [])
This retrieves an existing module 'myApp':
angular.module('myApp')
I had been overwriting my module in another file, using the first call above which created another module instead of retrieving as I expected.
More detail here: https://docs.angularjs.org/guide/module
I just migrate to angular 1.3.3 and I found that If I had multiple controllers in different files when app is override and I lost first declared containers.
I don't know if is a good practise, but maybe can be helpful for another one.
var app = app;
if(!app) {
app = angular.module('web', ['ui.bootstrap']);
}
app.controller('SearchCtrl', SearchCtrl);
I had this problem when I accidentally redeclared myApp:
var myApp = angular.module('myApp',[...]);
myApp.controller('Controller1', ...);
var myApp = angular.module('myApp',[...]);
myApp.controller('Controller2', ...);
After the redeclare, Controller1 stops working and raises the OP error.
Really great advise, except that the SAME error CAN occur simply by missing the critical script include on your root page
example:
page: index.html
np-app="saleApp"
Missing
<script src="./ordersController.js"></script>
When a Route is told what controller and view to serve up:
.when('/orders/:customerId', {
controller: 'OrdersController',
templateUrl: 'views/orders.html'
})
So essential the undefined controller issue CAN occur in this accidental mistake of not even referencing the controller!
This error might also occur when you have a large project with many modules.
Make sure that the app (module) used in you angular file is the same that you use in your template, in this example "thisApp".
app.js
angular
.module('thisApp', [])
.controller('ContactController', ['$scope', function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}]);
index.html
<html>
<body ng-app='thisApp' ng-controller='ContactController>
...
<script type="text/javascript" src="assets/js/angular.js"></script>
<script src="app.js"></script>
</body>
</html>
If all else fails and you are using Gulp or something similar...just rerun it!
I wasted 30mins quadruple checking everything when all it needed was a swift kick in the pants.
If you're using routes (high probability) and your config has a reference to a controller in a module that's not declared as dependency then initialisation might fail too.
E.g assuming you've configured ngRoute for your app, like
angular.module('yourModule',['ngRoute'])
.config(function($routeProvider, $httpProvider) { ... });
Be careful in the block that declares the routes,
.when('/resourcePath', {
templateUrl: 'resource.html',
controller: 'secondModuleController' //lives in secondModule
});
Declare secondModule as a dependency after 'ngRoute' should resolve the issue. I know I had this problem.
I was getting this error because I was using an older version of angular that wasn't compatible with my code.
These errors occurred, in my case, preceeded by syntax errors at list.find() fuction; 'find' method of a list not recognized by IE11, so has to replace by Filter method, which works for both IE11 and chrome.
refer https://github.com/flrs/visavail/issues/19
This error, in my case, preceded by syntax error at find method of a list in IE11. so replaced find method by filter method as suggested https://github.com/flrs/visavail/issues/19
then above controller not defined error resolved.
I got the same error while following an old tutorial with (not old enough) AngularJS 1.4.3. By far the simplest solution is to edit angular.js source from
function $ControllerProvider() {
var controllers = {},
globals = false;
to
function $ControllerProvider() {
var controllers = {},
globals = true;
and just follow the tutorial as-is, and the deprecated global functions just work as controllers.

Cant unit test application with manual bootstrap in AngularJS

my code needs to do manual bootstrap cause requires loading files before laoding application.
var bootstrapModule = angular.module('bootstrapModule', []);
// the bootstrapper service loads the config and bootstraps the specified app
bootstrapModule.factory('bootstrapper', function ($http, $log, $q, $timeout) {
return {
bootstrap: function (appName) {
var deferred = $q.defer();
$http.get('config/urls/development.json')
.success(function (data) {
.....
deferred.resolve();
})
.error(function (data) {
...
});
return deferred.promise;
}
};
});
// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');
// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {
bootstrapper.bootstrap('angular3App').then(function () {
// removing the container will destroy the bootstrap app
if (appContainer) appContainer.remove();
});
});
// make sure the DOM is fully loaded before bootstrapping
angular.element(document).ready(function () {
angular.bootstrap(appContainer, ['bootstrapModule']);
});
I want my unit tests to replicate this behaviour so it can have the info in the loaded file.
I am trying to bootstrap my app in beforeEach but it is not working.
beforeEach(function(done) {
var bootstrapModule = module('bootstrapModule')
// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');
// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {
bootstrapper.bootstrap('angular3App').then(function () {
// removing the container will destroy the bootstrap app
appContainer.remove();
});
});
// make sure the DOM is fully loaded before bootstrapping
angular.element(document).ready(function () {
angular.bootstrap(appContainer, ['bootstrapModule']);
});
});
It is not bootstrapping accordingly. The information in my scope that is dependent from load of the bootstrapped loaded file is not available.
Anyone has any ideas?
This is an old question, but I just dealt with this issue and it seems that at least one other person has recently had a similar issue. I was banging my head against the wall to get my unit tests to run with a setup my like #saulovenancio. The factory was a way I found to import the config.json file needed for the project and it worked - it just didn't play nice with our tests.
As far as I could tell, the tests were trying to run before the promise in the bootstrapMOodule.factory was resolved. Luckily, while loading the config JSON was a requirement - loading it via service was not a project requirement - so I scrapped the service that was fetching the JSON.
I pretty much followed the approach outlined here:
How to configure your AngularJS application using environment variables
In the index.html document I added a script tag for an env.js file before the file that loads my angular app.
<html>
...
<script src="js/jquery.js"></script>
<!-- Gets the config.json -->
<script src="env.js"></script>
<!-- The App -->
<script src="js/main.js"></script>
</html>
The env.js file contains the code that loads the config.json onto the window. Mine looks something like this:
(function (window) {
window.__env = window.__env || {};
$.get( 'config.json', function( data ){
window.__env = data;
})
.error( function handleErrorResponse( xhr, status, error ){
console.error( "An AJAX error occured: " + status + "\nError: " + error ) ;
});
}(this));
From here I just loaded the window.__env into my main.js file and used it for my constants.
var env = {};
if(window){
Object.assign(env, window.__env);
}
...
angular.module('app').constant('AppSettings', env);
Once I made those changes everything worked like a charm ... big thanks to Jurgen Van de Moere. Hopefully, this will be helpful to someone.

Why do I get the error: Unknown provider: personHubProvider <- personHub

I am trying to work things out with angularjs, dependency injections, factories and signalR hubs, but I get the error Unknown provider: personHubProvider <- personHub and I cannot figure out what is causing it.
My code is like this:
First I declare the module:
var ucp = angular.module('UCP', []);
which is used in the html tag by ng-app="UCP".
Then I do some configuration on the ucp module for the hub settings and creating a signalRHub factory to get the hubs without typing much of the same code:
ucp.config(function($routeProvider) {
//Declaring routes, removed this part of code cause I think it hasn't to do
//with the problem I got
$.support.cors = true;
$.connection.hub.url = config.signalR.connectionURL;
$.connection.hub.logging = config.signalR.logging;
$.connection.hub.start();
}).factory('signalRHub', [function() {
return {
person: $.connection.Persons.server
};
}]);
Then, I create another factory, that takes care of getting the data from the server:
ucp.factory('personHub', ['signalRHub', function(signalRHub) {
return {
get: function(onsuccess, onerror) {
signalRHub.person.get()
.done(function(persons) {
onsuccess(persons);
})
.fail(function(error) {
onerror(error);
});
}
}
}]);
and this factory I inject in my controller so I can execute the call to get the data from the server and put it in the scope which provides to show the data in the browser:
ucp.controller('personController', ['$scope', 'personHub', function($scope, personHub){
var self = this;
$scope.init = function() {
personHub.get(self.ongetsuccess, self.ongeterror);
}
self.ongetsuccess = function(persons) {
$scope.persons = persons;
};
self.ongeterror = function(error) {
};
}]);
When I open the webpage I get the error I mentioned before: Error: Unknown provider: personHubProvider <- personHub.
I think something goes wrong with creating the personHub factory service, which on his turn causes an Dependency Injection error for the controller. My question is, what is causing the error, am I doing something wrong with creating the personHub factory and if so, what am I doing wrong?
As I had stated in the comments I know this error occurs because the injected $provider isn't defined therefore it's reporting that there is nothing to provide said $provider. A $provider might be a factory, service, controller, or value (there's probably others I'm forgetting) and must be defined before it is referenced for an injection.
More on providers and injection here: https://github.com/angular/angular.js/wiki/Understanding-Dependency-Injection
The way I've been dealing with organizing code is by encapsulating most of the parts necessary for a given view into one js file. In that JS file I start off with a services section where I define a new module like:
angular.module("loginModule.services",[/*dependencies*/]).service("loginService", [/*dependencies*/function(){ return {get:function(){return "what up!"}};}]);
Then lower in the file I define my controllers like
angular.module("loginModule.controllers",[/*dependencies*/]).controller("LoginCtrl" ,["$scope", function($scope) { /* code here* /}]);
then at the bottom of the file
angular.module("loginModule", ["loginModule.services", "loginModule.controllers"]);
And finally in my mainApp.js (with the main ng-app module)
angular.module("mainApp", ["loginModule"]);
I've defined my script blocks in the head at the moment.
<script type="text/javascript" src="components/loginModule.js"></script>
<script type="text/javascript" src="mainApp.js"></script>
Also to note my loginModule actually depends on other services for which the javascript files come afterward. This doesn't seem to be an issue since the mainApp.js is being deferred until the end.
I just checked some of this in Chrome and it appears the JS files from my computer do load in order and as pairs (only two per domain at a time then when a response comes back the next request is fired). I also tried moving the script blocks from the head to the bottom of the HTML and I can't tell any difference (my laptop has an SSD and mostly local files so probably more appreciable in a production scenario).

Categories

Resources