javascript – 在已经使用角度的页面上使用角度扩展

我正在为my library编写一个使用角度为its UI的Chrome扩展(here).它在不使用角度的页面上运行良好,但会导致有角度的页面出现问题.例如,在Angular文档页面上:

Uncaught Error: [$injector:modulerr] Failed to instantiate module docsApp due to:
  Error: [$injector:nomod] Module 'docsApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
  http://errors.angularjs.org/1.2.7/$injector/nomod?p0=docsApp
at chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:78:14
at chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:1528:19
at ensure (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:1453:40)
at module (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:1526:16)
at chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:3616:24
at Array.forEach (native)
at forEach (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:302:13)
at loadModules (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:3610:7)
at createInjector (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:3550:13)
at doBootstrap (chrome-extension://cfgbockhpgdlmcdlcbfmflckllmdiljo/angular.js:1298:22)
http://errors.angularjs.org/1.2.7/$injector/modulerr?p0=docsApp&p1=Error%3A…xtension%3A%2F%2Fcfgbockhpgdlmcdlcbfmflckllmdiljo%2Fangular.js%3A1298%3A22) angular.js:78

奇怪的是,我的扩展实际上是否使用角色似乎是发生的.所有我需要重现的问题是将我的manifest.json中的角度包含为content_script,并抛出此错误.任何关于如何使这项工作没有弄乱角度的网站的想法将不胜感激.

就像我说的那样,我是否真的使用角度并不重要,但这正是我正在使用的:

makeWishForAnchors(); // This just loads the global genie object. I don't believe it's related.

var lamp = '<div class="genie-extension"><div ux-lamp lamp-visible="genieVisible" rub-class="visible" local-storage="true"></div></div>';
$('body').append(lamp);

angular.module('genie-extension', ['uxGenie']);
angular.bootstrap($('.genie-extension')[0], ['genie-extension']);

谢谢!

问题

注入Angular后,它将使用ng-app指令解析DOM寻找元素.如果发现Angular会自动引导.当页面使用Angular本身时,这是一个问题,因为(虽然它们有单独的JS执行上下文)页面和内容脚本共享相同的DOM.

解决方案

您需要防止您的角度实例(由您的“我的意思是将您的扩展名注入为内容脚本”)自动引导.通常,您只需省略ng-app指令,您将很乐意,但由于您无法控制原始的DOM(也不想破坏页面的功能),这不是一个选择.

您可以使用manual bootstrapping与您的Angular应用程序结合deferred bootstrapping(以防止您的Angular尝试自动引导页面的Angular应用程序).

同时,您需要从页面的Angular实例中“保护”(即隐藏)应用程序的根元素.为了实现这一点,您可以使用ngNonBindable指令将根元素包含在父元素中,因此页面的Angular实例将一直保留.

总结上述文档中的步骤,您需要执行以下操作:

>使用NG_DEFER_BOOTSTRAP预置window.name!在注射你的角度之前.
例如.注入一个只包含一行的小脚本(angluar.js之前):

window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;

>将应用程序的根元素包含在父元素中,其属性为ng-non-bindable:

var wrapper = ...    // <div ng-non-bindable></div>
wrapper.appendChild(lamp);   // or whatever your root element is
document.body.appendChild(wrapper);

>在应用程序的主脚本中,手动引导Angular应用程序:

var appRoot = document.querySelector('#<yourRootElemID');
angular.bootstrap(appRoot, ['genie-extension']);

精美打印:我没有自己测试,但我保证尽快做到这一点!

UPDATE

以下代码旨在作为上述方法的概念证明.基本上,这是一个演示扩展,只要点击浏览器操作按钮,就会将Angular-powered内容脚本加载到任何http:/ https:页面中.

扩展需要所有必要的预防措施,以免干扰(或破坏)页面自己的角度实例(如果有的话).

最后,我不得不添加第三个要求(请参阅上面的更新的解决方案描述)来保护/隐藏内容脚本的Angular应用程序从页面的Angular实例.
(即,我使用ngNonBindable指令将根元素包装在父元素中.)

manifest.json的:

{
  "manifest_version": 2,
  "name": "Test Extension",
  "version": "0.0",

  "background": {
    "persistent": false,
    "scripts": ["background.js"]
  },

  "browser_action": {
    "default_title": "Test Extension"
//    "default_icon": {
//      "19": "img/icon19.png",
//      "38": "img/icon38.png"
//    },
  },

  "permissions": ["activeTab"]
}

background.js:

// Inject our Angular app, taking care
// not to interfere with page's Angular (if any)
function injectAngular(tabId) {
  // Prevent immediate automatic bootstrapping
  chrome.tabs.executeScript(tabId, {
    code: 'window.name = "NG_DEFER_BOOTSTRAP!" + window.name;'
  }, function () {
    // Inject AngularJS
    chrome.tabs.executeScript(tabId, {
      file: 'angular-1.2.7.min.js'
    }, function () {
      // Inject our app's script
      chrome.tabs.executeScript(tabId, {file: 'content.js'});
    });
  });
}

// Call `injectAngular()` when the user clicks the browser-action button
chrome.browserAction.onClicked.addListener(function (tab) {
  injectAngular(tab.id);
});

content.js:

// Create a non-bindable wrapper for the root element
// to keep the page's Angular instance away
var div = document.createElement('div');
div.dataset.ngNonBindable = '';
div.style.cssText = [
  'background:  rgb(250, 150, 50);',
  'bottom:      0px;',
  'font-weight: bold;',
  'position:    fixed;',
  'text-align:  center;',
  'width:       100%;',
  ''].join('\n');

// Create the app's root element (everything else should go in here)
var appRoot = document.createElement('div');
appRoot.dataset.ngController = 'MyCtrl as ctrl';
appRoot.innerHTML = 'Angular says: {{ctrl.message}}';

// Insert elements into the DOM
document.body.appendChild(div);
div.appendChild(appRoot);

// Angular-specific code goes here (i.e. defining and configuring
// modules, directives, services, filters, etc.)
angular.
  module('myApp', []).
  controller('MyCtrl', function MyCtrl() {
    this.message = 'Hello, isolated world !';
  });

/* Manually bootstrap the Angular app */
window.name = '';   // To allow `bootstrap()` to continue normally
angular.bootstrap(appRoot, ['myApp']);
console.log('Boot and loaded !');

印刷精美:
我已经进行了一些初步测试(包括角度和非角度网页),一切似乎都按预期工作.但是,我绝对不会彻底地测试这个方法!

任何人都有兴趣,this is what it took到“Angularize”Genie的灯.

翻译自:https://stackoverflow.com/questions/21048891/using-angular-in-extension-on-a-page-that-already-uses-angular

转载注明原文:javascript – 在已经使用角度的页面上使用角度扩展