0

I'm building a third party widget that will be loaded onto multiple sites. I'm using angular for implementation, with a build script that wraps my angular so it doesn't interfere with any other angular that might be used on the page.

Everything works fine when I use my own custom directives, but when I tried to incorporate a loading indicator, using ng-show, it "sometimes" didn't work. I tracked the "sometimes" down to "on pages that already use angular".

It seems that the page's angular is still data binding my templates when they are inserted into the page. Is there any way I can make my dom a no-go zone for the page's angular?

ng-non-bindable seemed like it might work, but if I use that, I can't bootstrap underneath it.

Sean McMillan
  • 10,058
  • 6
  • 55
  • 65
  • I do not have hard evidence against it, but I would strongly advise against bundling Angular with your library. Angular likes to take control of the page, so if Angular is already loaded I would expect funny things occurring, e.g. two initializations (your Angular and the page's Angular). Additionally, Angular's size is not negligible; if you really must bundle it with your library, maybe try a custom build to leave out things you do not need. And maybe have 2 builds, one bundled with Angular for non-Angular users, one plain, as an Angular module, for users that already use Angular. – Nikos Paraskevopoulos Oct 14 '14 at 12:59
  • The problem is that if I piggyback on a page's angular, I don't know what version it is. I need my "widget" (it's really much bigger than a widget) to work even if they change angular versions, or if they're on angular 1.0 or something old. – Sean McMillan Oct 14 '14 at 13:29
  • Sure, you're right. But I still have a bad *feeling* about bundling Angular in a library. Just my 2 cents as they say... (Personally I would much rather *require* a minimum Angular version.) – Nikos Paraskevopoulos Oct 14 '14 at 13:40
  • if you could run it in iframe would remove collisions from the picture – charlietfl Oct 14 '14 at 14:23

2 Answers2

0

What you need to do is to bootstrap angular programatically instead of using a tag.

    <html>
    <body>
      // other parts of your site
      <div></div>



      // your angular stuff
      <div id='app' ng-controller="MyController">
        Hello {{greetMe}}!
      </div>
    </body>
    </html>



        // your controller
        angular.module('myApp', [])
          .controller('MyController', ['$scope', function ($scope) {
            $scope.greetMe = 'World';
          }]);

        // bootstrap angular programatically/manually
        angular.element('#id').ready(function() {
          angular.bootstrap(angular.element('#id'), ['myApp']);
        });

Without forgetting to use ng-non-bindable too on parts that u don't want to bind ofcoz

  • I am already bootstrapping manually. The problem is that the "outer" app is still binding to the "inner" app's directives. – Sean McMillan Oct 14 '14 at 13:27
0

So, here is what I have done, which is an abomination. I would much prefer a clean answer, but this protects my dom from the other angular instance.

First, I wrap my entire dom element with ng-non-bindable. This keeps the page-level angular from interfering with it.

Then, I add the following, adapted from AngularJS - how to override directive ngClick

var root = angular.module('rootModule', []);
root.config(function($provide){
    $provide.decorator('ngNonBindableDirective',
        ['$delegate', function($delegate){
            $delegate.shift();
            return $delegate;
        }]);
});

This removes the implementation of ng-non-bindable inside my angular instance. So the page angular will ignore the dom, but my angular won't ignore it. I just hope I don't have to implement ng-non-bindable-really later down the line.

Someone please save me from this horror by giving me a clean answer!

Community
  • 1
  • 1
Sean McMillan
  • 10,058
  • 6
  • 55
  • 65