Using page's angular JS in chrome extension

2019-01-09 16:30发布

问题:

I've a HTML page which has some DOM configured with Angular. Now I'm building a chrome extension to modifying value in a text box. element.value= newValue will not work as the text box is designed with Angular. After reading some resources, I came to know that I need to do the changes to the scope of the element. I tried doing this in the extension.

var eleScope = window.angular.element(document.querySelector('.textbox')).scope();
eleScope.$apply(function() {
  eleScope.item.request.box = newValue;
});

This doesn't seem to work as intended. When I dug into the problem more deeply, I came to know that the angular in the scope of window was not getting the right scope.

I also tried to inject angular.js from my extension and using angular.element directly which also doesn't seem to solve the problem.

Am I missing something. Appreciate your replies.

回答1:

Hmm.. Actually you should be just able to change the value using vanilla Javascript or JQuery and should not need to update the scope as it is two-way binding ie. Updating the view updates the model and vice-versa.

The only reason why the model does not update is because the watchers to update the model trigger only on particular events like "click" or "input".

So the solution here would be to manually trigger these events. This can be do by:

// the selector used is based on your example.
var elementTOUpdate = $('.textbox');
input.val('new-value');
input.trigger('input');

The last statement will trigger all the handlers on the element for the "input" event ( which will fire the angular watchers ) and update the scope. More info on the trigger function: http://api.jquery.com/trigger/



回答2:

Although your web page and your Chrome extension's content script share the same DOM, they have separate JS execution contexts. In other words, despite grabbing the element via angular.element().scope() in your Chrome extension, you're still not accessing the correct scope object. (See the Chrome dev docs for more on this.)

In order to get the modified data from your Chrome extension on to the page, you can either manipulate the DOM via your content script, or take some extra steps to get the modified data into the web page's execution context (and subsequently, into your controller's scope).

For the latter, the solution is to use the Chrome's Message Passing API; specifically, the API designed to communicate between the web page and the Chrome extension (see here).

Here's an example from the documentation:

First, enable the API for the appropriate website(s) in your manifest.json file:

"externally_connectable": {
  "matches": ["http://*.foo.com/*"]
}

From the web page, make a request to connect to the Chrome extension. In your callback, you'd update your model:

// Chrome extension's ID
var extensionId = "abcdefghijklmnoabcdefhijklmnoabc";

chrome.runtime.sendMessage(extensionId, {foo: 'bar'},
  function(response) {
    if (response.success) {
      $scope.myData.foo = response.data;
      $scope.$apply();
    }
  });

In your Chrome extension's background.js file, add a listener for the request:

chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (request.foo) {
      sendResponse({ data: 'modified data goes here' });
    }
  });

This process will allow your Chrome extension to send data into the correct execution environment, allowing your AngularJS app to work with it.



回答3:

The main problem is well explained in Jonathan Guerrera's answer: you can't just load Angular in your extension and expect it to work because it's a different instance of Angular; the real one is in another JS context.


Another possible (though hacky) solution is to inject code into the page's context. That way, you're actually manipulating the page's own Angular.

It can be combined with other methods of communicating between the page-level script and the content script.