Lets say I have an app, which I don't know it's reference name or where it's defined.
Let's say I have another module which I've created, (nav module) I want to inject this nav module inside the existing angular app so that it can function as normal.
<html ng-app="appA">
<body>
<!-- navController exists inside another app -->
<nav id="testElement" controller="navController"></nav>
</body>
</html>
Example:
$(document).ready(function() {
var nav = document.getElementById('testElement');
if (!angular.element(nav).length) return;
var appElement = angular.element('[ng-app]');
console.log('appname', appElement.attr('ng-app'));
var appToInject;
if (!appElement.length) {
// Manually run the new app from within js without a ng-app attrib
appToInject = angular.module('CustomModuleForNavigation', []);
angular.bootstrap(nav, ['CustomModuleForNavigation']);
} else {
appToInject = angular.module(appElement.attr('ng-app'), []);
}
if (angular.isUndefined(appToInject)) return;
console.log('winning?', appToInject)
appToInject.controller('navController', function($scope) {
console.log('extending app in new ctrl!', $scope);
});
});
When there is an existing app module definition, but you want to add additional dependencies to the app module it can be done this way.
var existingModule = angular.module('appModuleName');
existingModule.requires.push('additionaldependency');
The appModuleName can be found by ng-app attribute of the index.
Make sure this script runs right after the existing module definition script.
Also, scripts required for 'additionaldependency' to be loaded before this script is loaded.
I have figured this out by injecting the dependency into an existing app, or manually running my own up with angular bootstrap.
var appElement = $('[ng-app]').first();
function angularJsModuleMerger(elementToBindScope, dependencies) {
var scopeElement = document.getElementById(elementToBindScope);
// Dependencies should contain our mobile scopeElement app only, the mobile app should contain it's dependencies.
if (!angular.element(scopeElement).length) return;
// If There is an existing angular app, inject our app into it, else, create a new app with our dependencies.
var hasAngularApp = appElement.length;
var appToInject;
if (!hasAngularApp) {
appToInject = angular.module('appForModularInjection', dependencies);
// Manually run this app, so that we're not interrupting the current app.
} else {
// There is an existing app, so let's get it by it's name
appToInject = angular.module(appElement.attr('ng-app'));
// Inject our dependencies to the existing app, IF they don't alreay have these dependencies.
// Dependencies must also be loaded previously.
var currentDependencies = appToInject.requires;
var newDependencies = currentDependencies.concat(dependencies);
appToInject.requires = newDependencies;
appToInject.run(function($rootScope, $compile) {
$compile(angular.element(scopeElement).contents())($rootScope);
});
}
// If we still don't have an app, well, voodoo.
if (angular.isUndefined(appToInject)) return;
// console.log('ok');
// We must run the app after all the declarations above for both existing and non existing apps.
if (!hasAngularApp) angular.bootstrap(scopeElement, ['appForModularInjection']);
}
This will inject it self to an existing app, manually define an app and bind it all correctly to the element in question.
Scope/hierarchy is irrelevant.
Related
I'm trying to include typical javascript library(JSZip) in to my angularjs application.
At first i have added included JSZip library in to my application and then added script reference to my index page.
Next i have created a simple object of JSZip in one of my module and trying to create zip. but all of sudden, i am getting compilation error in typescript while building my application in VS2015(visual studio), saying that "Cannot find name JSZip".
How to load non angular dependency in angular application. i have spent a complete day. i didn't find any clue.
i have tried multiple ways to get the dependency dynamically and also tried oclazyload to load JSZip dependency ..but not helps.
var zip = new JSZip(); <=== this is where the problem is..
zip.file("File1", atob(response.Data));
zip.file("File2", atob(response.Data));
zip.generateAsync({ type: "blob" })
.then(function (content) {
// saveAs is from FileSaver.js
saveAs(content, "example.zip");
});
Inject non-angular JS libraries
Include the JS file into your HTML file
<script src="http://stuk.github.io/jszip/dist/jszip.js"></script>
In your module angular add constant 'jszip' for exemple
var app = angular.module('myApp', [])
.constant('jszip', window.JSZip)
.run(function($rootScope) {
$rootScope.JSZip = window.JSZip;
})
Inject 'jszip' in your controller, and you will solve your problem
app.controller('myCtrl', function($scope, 'jszip') {
var zip = new jszip();
...
});
For the downloading part (saveAs function), include FileSaver.js into your HTML file
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.js"></script>
In your controller for exemple
$scope.exportZip = function(){
var zip = new JSZip();
zip.file("Hello.txt", "Hello World\n");
zip.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "example.zip");
});
};
I'm developing a angular app, and this app has about a 10 configurable properties (depending on the environment and client).
I had those properties in json config files, but this is really troublesome: there must be specific builds per env/company. So I would like to retrieve those properties once from the backend on app load.
So in order to do this I created a Provider
var app = angular.module('myApp', [...]);
app.provider('environment', function() {
var self = this;
self.environment;
self.loadEnvironment = function (configuration, $http, $q) {
var def = $q.defer();
$http(...)
.success(function (data) {
self.environment = Environment.build(...);
def.resolve(self.environment);
}).error(function (err) {
...
});
return def.promise;
};
self.$get = function(configuration, $http, $q) {
if (!_.isUndefined(self.environment)) {
return $q.resolve(self.environment);
}
return self.loadEnvironment(configuration, $http, $q);
};
}
app.config(... 'environmentProvider', function(... environment) {
...
//The problem here is that I can't do environment.then(...) or something similar...
//Environment does exists though, with the available functions...
}
How to properly work with this Provider that executes a rest call to populate his environment variable?
Thanks in advance!
This is an excelent scenario to explore angularjs features.
Assuming that you really need the environment data loaded before the app loads, you can use angular tools to load the environment and then declare a value or a constant to store your environment configs before the app bootstraps.
So, instead of using ng-app to start your app, you must use angular.bootstrap to bootstrap it manually.
Observations: You mustn't use ng-app once you are bootstrapping the app manually, otherwise your app will load with the angular default system without respecting your environment loading. Also, make sure to bootstrap your application after declare all module components; i.e. declare all controllers, servieces, directives, etc. so then, you call angular.bootstrap
The below code implements the solution described previously:
(function() {
var App = angular.module("myApp", []);
// it will return a promisse of the $http request
function loadEnvironment () {
// loading angular injector to retrieve the $http service
var ngInjector = angular.injector(["ng"]);
var $http = ngInjector.get("$http");
return $http.get("/environment-x.json").then(function(response) {
// it could be a value as well
App.constant("environment ", response.data);
}, function(err) {
console.error("Error loading the application environment.", err)
});
}
// manually bootstrap the angularjs app in the root document
function bootstrapApplication() {
angular.element(document).ready(function() {
angular.bootstrap(document, ["myApp"]);
});
}
// load the environment and declare it to the app
// so then bootstraps the app starting the application
loadEnvironment().then(bootstrapApplication);
}());
I've just installed the following package with bower:
https://github.com/urish/angular-spinner
The package is added successfully. I've also added:
<script src="bower_components/spin.js/spin.js"></script>
<script src="bower_components/angular-spinner/angular-spinner.js"></script>
When I try to inject it like this:
(function()
{
angular.module('employeeApp',['angularSpinner']).controller('schoolController', schoolController);
It crashes and I receive the error:
Argument 'indexController' is not a function, got undefined
When I remove ['angularSpinner'] everything works again.
What should I do?
--EDIT--
indexController
angular.module('employeeApp').controller('indexController', indexController);
function indexController($location, authenticationFactory,constants)
{
var vm = this;
vm.setName = function()
{
return constants.firstname;
}
}
in angular you create module for your app and there you specify the dependencies. and once you create controller or service you get the module by name and create controller\ service in that module.
//create module for app
angular.module('employeeApp', [ /*add your dependencies here*/ ]);
//create controller\ service
angular.module('employeeApp').controller(function(){
//controller implementation
});
what might happen is you may re initialize your app by mistake.
for simplification you could store your angular module in a variable as follows:
var app = angular.module('employeeApp', ['angularSpinner']);
and define a controller like this:
app.controller('indexController',function(angularSpinner){
//controller code here
});
I am developing a mobile application using Cordova and AngularJS. How do I restrict bootstrapping of AngluarJS before Cordova device ready. Basically I don't want to use any of AngularJS controllers before device ready.
Manually bootstrap your Angular app:
Remove your ng-app attribute from your HTML code, so Angular doesn't start itself.
Add something like this to you JavaScript code:
document.addEventListener("deviceready", function() {
// retrieve the DOM element that had the ng-app attribute
var domElement = document.getElementById(...) / document.querySelector(...);
angular.bootstrap(domElement, ["angularAppName"]);
}, false);
Angular documentation for bootstrapping apps.
I'm using the following solution, which allows AngularJS to be bootstrapped when running with Cordova as well as when running directly in a browser, which is where much of my development takes place. You have to remove the ng-app directive from your main index.html page since that's what the manual bootstrapping is replacing.
UPDATE: I've since switched to the following method, which I think is cleaner. It works for Ionic as well as vanilla Cordova/PhoneGap. It should be the last bit of JavaScript to run - perhaps inside a script tag before the /body tag.
angular.element(document).ready(function () {
if (window.cordova) {
console.log("Running in Cordova, will bootstrap AngularJS once 'deviceready' event fires.");
document.addEventListener('deviceready', function () {
console.log("Deviceready event has fired, bootstrapping AngularJS.");
angular.bootstrap(document.body, ['app']);
}, false);
} else {
console.log("Running in browser, bootstrapping AngularJS now.");
angular.bootstrap(document.body, ['app']);
}
});
Here's the older solution I used:
// This is a function that bootstraps AngularJS, which is called from later code
function bootstrapAngular() {
console.log("Bootstrapping AngularJS");
// This assumes your app is named "app" and is on the body tag: <body ng-app="app">
// Change the selector from "body" to whatever you need
var domElement = document.querySelector('body');
// Change the application name from "app" if needed
angular.bootstrap(domElement, ['app']);
}
// This is my preferred Cordova detection method, as it doesn't require updating.
if (document.URL.indexOf( 'http://' ) === -1
&& document.URL.indexOf( 'https://' ) === -1) {
console.log("URL: Running in Cordova/PhoneGap");
document.addEventListener("deviceready", bootstrapAngular, false);
} else {
console.log("URL: Running in browser");
bootstrapAngular();
}
If you run into problems with the http/https detection method, due to, perhaps, loading a Cordova app into the phone from the web, you could use the following method instead:
function bootstrapAngular() {
console.log("Bootstrapping AngularJS");
// This assumes your app is named "app" and is on the body tag: <body ng-app="app">
// Change the selector from "body" to whatever you need
var domElement = document.querySelector('body');
// Change the application name from "app" if needed
angular.bootstrap(domElement, ['app']);
}
// This method of user agent detection also works, though it means you might have to maintain this UA list
if (navigator.userAgent.match(/(iOS|iPhone|iPod|iPad|Android|BlackBerry)/)) {
console.log("UA: Running in Cordova/PhoneGap");
document.addEventListener("deviceready", bootstrapAngular, false);
} else {
console.log("UA: Running in browser");
bootstrapAngular();
}
Note that you still need the same bootstrapAngular function from the first example.
Why manually bootstrap AngularJS with Cordova/PhoneGap/Ionic?
Some people getting here might not know why you would want to do this in the first place. The issue is that you could have AngularJS code that relies on Cordova/PhoneGap/Ionic plugins, and those plugins won't be ready until after AngularJS has started because Cordova takes longer to get up and running on a device than the plain old Javascript code for AngularJS does.
So in those cases we have to wait until Cordova/PhoneGap/Ionic is ready before starting up (bootstrapping) AngularJS so that Angular will have everything it needs to run.
For example, say you are using the NG-Persist Angular module, which makes use of local storage for saving data on a browser, iOS Keychain plugin when running on iOS, and the cordova-plugin-file when running on Android. If your Angular app tries to load/save something right off the bat, NG-Persist's check on window.device.platform (from the device plugin) will fail because the mobile code hasn't completed startup yet, and you'll get nothing but a white page instead of your pretty app.
If you are using Ionic, this solution works for browsers and devices. Credit to romgar on this thread.
window.ionic.Platform.ready(function() {
angular.bootstrap(document, ['<your_main_app']);
});
Still need to remove ng-app from your DOM element.
This solution became more robust when I used:
angular.element(document).ready(function () {
var domElement = document.getElementById('appElement');
angular.bootstrap(domElement, ["angularAppName"]);
});
UPDATE
My suggestion was to put the above within the appropriate deviceready function, e.g.:
document.addEventListener("deviceready", function() {
angular.element(document).ready(function () {
var domElement = document.getElementById('appElement');
angular.bootstrap(domElement, ["angularAppName"]);
});
}, false);
On using the solution from TheHippo:
document.addEventListener("deviceready", function() {
// retrieve the DOM element that had the ng-app attribute
var domElement = document.getElementById(...) / document.querySelector(...);
angular.bootstrap(domElement, ["angularAppName"]);
}, false);
It doesn't work in the browser because "cordova.js" gets resolved by the Cordova or Phonegap building process and is not available in your localhost or emulated testing environment.
Thus the "deviceready" event is never fired. You can simply fire it manually in your browsers console.
var customDeviceReadyEvent = new Event('deviceready');
document.dispatchEvent(customDeviceReadyEvent);
Also make sure, that the bootstrap of angular gets triggered after setting all of you angular modules/controllers/factories/directives etc.
In most cases you probably don't need to block loading your angular app until after deviceready (mind that it can take several seconds for deviceready to fire if you have a lot of plugins).
Instead you can use something like this lib (https://github.com/arnesson/angular-cordova) which solves the deviceready issues for you by automatically buffering calls and then execute them after deviceready has been fired.
I've just started learning Angular and following the tutorial here - http://docs.angularjs.org/tutorial/step_00
I'm downloaded the seed example from GitHub and it works great. I have a question though - if a partial view requires an external js file to be referenced, does it need to be added to the index.html file at the beginning? I want the app to be as lean as possible and only want to include the js references that are required for the present view. Is it possible to load the js files dynamically based on a view?
This just worked for me. Figured I would post it for anybody else seeking the lightest-weight solution.
I have a top-level controller on the page's html tag, and a secondary controller for each partial view.
In the top-level controller I defined the following function…
$scope.loadScript = function(url, type, charset) {
if (type===undefined) type = 'text/javascript';
if (url) {
var script = document.querySelector("script[src*='"+url+"']");
if (!script) {
var heads = document.getElementsByTagName("head");
if (heads && heads.length) {
var head = heads[0];
if (head) {
script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', type);
if (charset) script.setAttribute('charset', charset);
head.appendChild(script);
}
}
}
return script;
}
};
So in the secondary controllers I can load the needed scripts with a call like the following…
$scope.$parent.loadScript('lib/ace/ace.js', 'text/javascript', 'utf-8');
There's a slight delay before the objects contained in the external script are available, so you'll need to verify their existence before attempting to use them.
Hope that saves somebody some time.
I just tried the https://oclazyload.readme.io/. It works out of the box.
bower install oclazyload --save
Load it in your module, and inject the required module in controller:
var myModule = angular.module('myModule', ['oc.lazyLoad'])
.controller('myController', ['$scope', '$ocLazyLoad', '$injector',
function($scope, $ocLazyLoad, $injector) {
$ocLazyLoad.load(
['myExtraModule.js',
'orAnyOtherBowerLibraryCopiedToPublicFolder.js'
])
.then(function() {
// Inject the loaded module
var myExraModule = $injector.get('myExtraModule');
});
}
]);