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

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.

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.

Is it possible to influence a single Angular app from multiple JS scripts?

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.

Accessing Angular app's props and controller functions from external non-Angular plugins

I have an Angular SPA but in it I'm using some non-angular library (Medium Editor [ME from now on] to be exact). I created a directive for ME so if I add a contenteditable element on my views, ME get's instantiated and works properly. So that's not a problem.
The problem is that I also created a special ME extension that requires to make web requests to my server in order to insert correct markup into ME's editable element. But to make these requests it requires some view-model's data and also communicate it back:
it needs to read and set (when undefined) my view model ID
it needs to constantly manipulate some other view model value in order for my SPA to know that it's still processing so other processes get postponed
I thought I'd simply include input type="hidden" ng-model="..." on my page and change its value and trigger input event so Angular would update its model. Hidden input of course doesn't work. So I changed it to input type="text" class="hidden" and keep the functionality.
This does work, but it doesn't seem to be the proper way of doing things as it's hackish. And this mainly works for model value exchange (get/set). How about if I would have to call some controller function? Is that even remotely possible?
I don't want to make ME's extension to rely in any way on Angular library as it has to be purely ME extension and should be reused in non-Angular SPAs (maybe some other lib SPA or just pure simple DHTML web app). But I would like to make it usable in SPAs as well as ME can easily be used to manipulate some element's markup that can be set to view model through a directive.
What it the proper way of communicating with Angular app from external libs that aren't native to Angular?
Additional info
Basically I want to have 4 functions in my custom extension that should somehow access (and manipulate) my Angular view model:
getReferenceId() and setReferenceId(id)
incrementPending() and decrementPending()
Pending counter could be exposed publicly and accessed by my Angular SPA, so it wouldn't process data while extension is still doing its own stuff. But SPA doesn't have any authority over when to set reference ID so it would correctly be read by the extension...
This is usually solved by wrapping external plugins (or their parts) in ng services and directives. You don't need the input tag, just put the data on scope and after changing from non-ng code call $scope.$digest. If you need to watch for data change to trigger something in external library, you can use either ng-change or $scope.$watch.

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.

How can i force angular to re-run its bindings, manually?

I am trying to integrate angular into an older existing web-app. I have a few places where large portions of the page are re-rendered from rails partials via ajax requests.
Once a portion of the page is re-rendered in this way, any existing angular bindings (e.g.: ng-click) are lost and buttons no longer work. Is there any way to manually tell angular to re-check for binding attributes?
If you are loading some HTML and need angular to process it you'll need to run $compile on the HTML code for angular to parse for directives and you'll need to attach a scope to the returned link function. Show some code. If you are simply getting new data you can use $http which will automatically call $digest for you or else you can call $digest from some other callback to notify angular it needs to update the display.

Categories