3

I'm building a widget that depends on Angular along with a widget builder tool. The builder used Angular with ngApp attached to the html tag of the document.

When I load up the widget within the widget builder, I get the following error:

Error: [ng:btstrpd] App Already Bootstrapped with this Element '<div class="company-widget" id="widget-app" data-company="demoCorp">'

Here is the bootstrap function:

angular.bootstrap('#widget-app', ["myWidget"]);

For all intents and purposes, the rest of the myWidget app is a pretty standard mix of controllers and services.

I was following along with this blog post on how to allow multiple ngApp directives in a single page, however I didn't realize until after I set this thing up that it says right at the end of the blog post that you can't nest apps, which is what's happening here, and what may occur on a small number of sites that use this widget.

I can't redesign the widget-builder and I can reasonably assume that for any Angular sites where the widget is embedded, the host site will attach ngApp to the html tag.

My question is, is there a way to get around this limitation, even if it's a hacky solution? Is it possible to check if the page already has an app and inject the myWidget app into the host app as a dependency?

Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88
  • The fact that Angular is reporting that the element already bootstrapped means that there must be some way to tell - though that code might be internal to AngularJS. To find out you could reference the unminified version of AngularJS and breakpoint on that error to see the if condition. From what I can make out of what you are describing, it sounds like you want to dynamically "inject" a module at runtime. If so you might want to take a look at this article: http://weblogs.asp.net/dwahlin/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs – Luis Perez Jan 11 '16 at 21:43
  • No simple way to do this. Can you post the boostrapping code for the widget builder? Do you have access to the same `script` block that the boostrapping happens in? You said you can't redesign the widget builder, but are there any modules you can piggy back on? – matthewpavkov Jan 11 '16 at 22:02

1 Answers1

0

You could try something like this:

https://jsfiddle.net/pavy/vnnuxgwo/

// Parent/host application
var myApp = angular.module('myApp', []);

myApp.controller('AppCtrl',
    function($scope) {
        $scope.name = 'YourName';
    }
);

// Third party module
// This needs to happen after myApp is defined
var myWidget = angular.module('myApp');
myWidget.controller('MyWidgetCtrl',
    function($scope) {
        $scope.widgetName = 'This is my widget.';
    }
);

The third party module code (your widget) needs to come after the host app (in your case, this would be the Widget Builder) code.

You're also dependent on the module name of the host app, so if it changes, your code will break. There are likely ways to programmatically get the loaded Angular app name, which would help in this regard.

matthewpavkov
  • 2,918
  • 4
  • 21
  • 37
  • I'm not sure I understand how the widget injects itself into the host page's app as a dependency from your example, which I think is the only way to solve this. Anyway, I've put a lot more thought into it and I think an iframe may be the only route her after all, unfortunately. – Daniel Bonnell Jan 11 '16 at 22:48
  • Without seeing more code I can't say for sure. But, if you can get the host app via `angular.module('myApp')` during the bootstrapping, then you can tie your "widget" into the host app. I updated the fiddle, perhaps it'll be more clear now. As long as the host app is not using `angular.bootstrap` which it shouldn't be, since you said it's using the `ng-app` directive, then you should be able to tie in with the host app as a normal module, and not need to do any bootstrapping on your end. – matthewpavkov Jan 12 '16 at 16:33