我正在为我的库编写一个Chrome扩展程序(此处),它的UI使用angular .它在不使用角度的页面上运行良好,但它会导致具有角度的页面出现问题.例如,在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中包含angular作为a content_script
并抛出此错误.任何关于我如何能够在不弄乱角度站点的情况下完成这项工作的想法将不胜感激.
就像我说的那样,无论我是否实际使用角度都没关系,但这就是我正在使用它的全部内容:
makeWishForAnchors(); // This just loads the global genie object. I don't believe it's related. var lamp = ''; $('body').append(lamp); angular.module('genie-extension', ['uxGenie']); angular.bootstrap($('.genie-extension')[0], ['genie-extension']);
谢谢!
注入Angular后,它会解析DOM以查找带有该ng-app
指令的元素.如果找到一个Angular将自动引导.当页面使用Angular本身时,这会成为一个问题,因为(尽管它们具有单独的JS执行上下文),页面和内容脚本共享相同的DOM.
您需要通过自动引导来阻止您的 Angular实例(通过"您的"我的意思是您的扩展注入的内容脚本).通常你会省略该ng-app
指令,你会很高兴,但由于你无法控制原始的DOM(你也不想打破页面的功能),这不是一个选择.
你可以做什么使用手动引导你的Angular应用程序与延迟引导(以防止你的Angular尝试自动引导页面的Angular应用程序).
同时,您需要从页面的Angular实例中"保护"(即隐藏)应用程序的根元素.要实现这一点,您可以使用ngNonBindable指令将根元素包装在父元素中,因此页面的Angular实例将使其保持不变.
总结上述文档中的步骤,您需要执行以下操作:
在注入Angular之前预先window.name
添加NG_DEFER_BOOTSTRAP!
.
例如,注入一个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']);
精细打印:我自己没有测试过,但我保证很快就会这样做!
以下代码旨在作为上述方法的概念证明.基本上,它是一个演示扩展,只要单击浏览器操作按钮,就会将Angular驱动的内容脚本加载到任何http:
/ https:
页面中.
扩展程序采取所有必要的预防措施,以免干扰(或破坏)页面自己的Angular实例(如果有的话).
最后,我必须添加第三个要求(请参阅上面更新的解决方案描述),以保护/隐藏内容脚本的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 !');
精细打印:
我已经进行了一些初步测试(包括Angular和非Angular网页),一切似乎都按预期工作.然而,我绝不会彻底测试这种方法!
如果有人有兴趣,这就是 "Angularize"Genie的灯所花的.