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.
Related
Quick Summary:
I need to allow two script files to handle different operations for the same angular app. One needs to initialize the app, the other needs to assign a templateCache object to a piece of JSON in localStorage.
Context:
I have several python files which compile/generate html and I have constructed an angular app with this emitted html for my site (which uses CGIs).
The basic construct of the site comes pieces of HTML, which fit together like so:
|------------Header---------------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|------------Footer---------------|
My Header creates the <head> tag, instantiates my ng-app and uses $templateCache to set up a template that I call from my Navigation code. I had to go with templateCache instead of ngView and ngRoute due to some limitations with how the CGIs emit the html, and the order in which this happens.
My "Navigation" python/html sets up my app with JS like so:
<script>
var responsiveCatalog = angular.module('responsiveCatalog', ['ngStorage']);
....controllers...
....config, etc....
</script>
This Navigation also includes my default templateCache object:
<div ng-include=" 'responsiveItems.html' "></div>
This is all working to show my first templateCache object in the Content section. However, I need to grab many pieces of information from the python generator for the "Content" section (a totally separate file from the "Navigation"), store this data as JSON in localstorage (hence the inclusion of the ngStorage module), and call that as my second templateCache option.
I am not actually sure that I can use two separate instances of Javascript to reference and influence the same Angular app. I know this seems like bad practice, but I am trying to prevent the need to tear down a huge piece of legacy architecture to influence the angular app from two Javascript files in harmony.
Thanks in advance.
You can do
angular.module('myAppName').controllers.... in different files, just make sure the myAppName the same. Bug I don't feel like it work with config, no time to test. If you need any communication between them, check $emit and $broadcast.
I'm trying to follow this tutorial here: http://www.mattburkedev.com/multiple-angular-versions-on-the-same-page/
It works until I inject my widget into a page that already has includes AngularJS and is using ng-app somewhere in the page. Both my version, and the existing version see the 'ng-app' and try to bootstrap it. The result is an error like:
Uncaught Error: [ng:btstrpd] App Already Bootstrapped with this Element '<div ng-app="myApp" class="ng-scope" ng-controller="IndexController">'
So my question is, is it possible to load in my version of Angular I need for my widget, and then tell it to not try and bootstrap anything automatically. I am handling it via:
angular.bootstrap(angular.element(appDiv), ['myWidget']);
In documentation is written:
only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead.
So, it is possible, but you didn't provide any code.
Here is the example of two applications on one page.
I've searched and searched for a good answer to this and - I promise - and I know there a lot of answers out there that are similar.
I've read this: In AngularJS, any inline javascript code that included in HTML templates doesn't work
I've tried creating a directive that listens for the div to update and $compiles it again - something like this: http://www.bennadel.com/blog/2745-creating-custom-script-tag-directives-in-angularjs.htm
However, nothing I've found answers exactly what I need. Basically, I created a news site at work and the story pages are populated by producers via a Drupal install, that populates a Redis DB which powers our site via a REST API.
Then content of these pages is populated like so:
<div class="topExtraHtml" ng-bind-html="extras.html.top"></div>
From time to time the producers like to add embeds in stories that require external js, so the extras.html.top could potentially be something to the effect of:
<script src="http://outsidesource.com/embedcode.js"></script>
<div id="the-js-will-populate-me"></div>
Is it even possible to get this to work? I'm totally stumped.
If you are trying to embed javascript in real time, currently Angular 1.0 doesn't handle it. Angular 2.0 would be able to handle injection of scripts etc. One way to get around this is either use RequireJS or Browerserify. Would it be possible for the producers to put an entry into a JS file, (separate to the HTML)?
I have a multiple page website using RequireJS, which loads a boot strap file (boot.js), which then requires app.js.
app.js handles all the logic, and all other module initialization happens through app.initModule() (which is just a require() call wrapper)
I also have a app.loadPageJS() to load page specific JS files (based on window.location.pathname, for example, www.domain.com/path/to/file.html would auto-load /_assets/js/pages/path/to/file.js)
This feature can be turned on/off, and overridden by adding a class of "no-auto-load" or "auto-load" to the body, respectively.
Now, my approach isn't robust enough. For one, url rewriting would break the mechanism, and for two, if loadPageJS is turned off, unless I have access to the body tag, I can't include a page specific JS file (in the case of sites using templating systems, adding a class to the body tag isn't always an option).
What are other ways to include page specific code? I'd rather avoid the following:
adding page specific code to a global.js file and doing if checks and only running certain code sets
using a pageName variable (which would essentially be similar to the above)
Thanks in advance.
If you have different modules on the page sectioned by unique ID's (a newsletter module wrapped within a div with an ID of 'newsletter', etc), you could test for existence of the module element in the DOM and conditionally load in the JS file necessary to run that module. So rather than being page-specific, it is module specific.
I am Building a learning application where there are a bunch of different page types that a learner will go through and do activities. It will be a SCORM compliant learning object.
This is the structure I have so far...
application/
models/
scorm.js
sequence.js
session.js
pagetypes/
multichoice.js
truefalse.js
basic.js
utilities/
jquery.js
api.js
My pagetypes do the viewing and the controlling, should I seperate these out? The reason I have combined them is so when I build a new page type, I can just drop it into that folder and it will get recognised straight away by the code.
What do you guys think? amidoinrite?
I'm guessing you're separating out methods based on type of page interactions.
I don't see any reason not to do it your way. So long as everything the sco needs is in the manifest you can subdivide your scripts however you want. It might save just a bit of load time to separate out separate page types... But only if you are only loading what you need into the HTML page, & you are actually navigating pages within a sco session. If you're loading all script into a single HTML page, & then dynamically changing the content of page divs, then your scripts are all loaded 1 time & you may as well have 1 minified file for all page type scripts.
I would probably go with the latter, & tie interactions to classes or ids in the markup. 1 file, less work to minify, & I can use in other packages without having to make sure that I have every page type I need...
With JavaScript it can be tricky to separate it out since it lives so closely to the view. As long as the data is separated from the actual view (which it looks like it is in your example) it will be a good design. I would argue that the pagetypes are more controllers and the HTML is the view. The most important part is to keep the model separated from the view. Unless you're trying to build reusable JavaScript/HTML components it's ok for pagetypes to blur the role of controller and view.