Chrome扩展程序:从内容脚本

时间:2017-03-23 11:14:36

标签: javascript angularjs google-chrome-extension

我正在使用AngularJS开发Chrome扩展程序。

我使用以下内容脚本代码将控制器附加到网页所需的DOM元素

setController() {
    if(this.setContollerCondition) {
        this.controllerElement.attr('ng-controller', 'Controller as cntrl');
    }
}

这种方法并不总是有效,因为控制器构造函数没有被调用,而且' ng-scope'不适用于该元素。

我想问一下,动态连接控制器的正确方法是什么。

如果这种方法是正确的,我可能会出错?

以下是页面加载时Chrome扩展程序中发生的活动的流程。

  1. 如果未初始化ng-app,则将其初始化。
  2. 初始化充当事件侦听器的函数
  3. 检查主机url,具体取决于url运行相应的初始化函数
    • 如果添加了特定的dom元素,则添加扩展的自定义DOM元素,然后使用setController()函数将Controller添加到元素中。
  4. 几乎所有函数都是异步调用。

    目前发生的行为如下

    1. 控制器设置正确,可以与特定主机web.whatsapp.com一起使用
      • 此处所有来自扩展程序的按钮都放置在适当的位置并且工作效果惊人。
    2. 使用其他主机时,有时会设置控制器,有时不会设置。主持人是www.linkedin.com
      • 此处有时会放置按钮。未放置时, ng-scope 不会应用于DOM元素,因为控制器构造函数未初始化。
    3. 使用第三台主机,永远不会设置控制器。主持人是mail.google.com
      • 此处按钮始终放置,但范围未应用于所需的DOM元素,并且Controller的构造函数未被调用。
    4. 该项目使用Webpack和ES6。

      以下文件与实际代码类似,可帮助您更好地理解问题。

      的manifest.json

      {
      "manifest_version": 2,
      
      "name": "MyExtension",
      "short_name": "MyExtension",
      "omnibox": {
          "keyword": "MyExtension"
      },
      "description": "Description",
      "version": "2.0.7",
      "version_name": "2.0.7",
      "browser_action": {
          "default_icon": {
              "128": "assets/images/icon-v2-128px.png",
              "16": "assets/images/icon-v2-16px.png",
              "48": "assets/images/icon-v2-48px.png"
          },
          "default_title": "MyExtension",
          "default_popup": "index.html"
      },
      "content_scripts": [ {
          "js": [ "assets/lib/angular.min.js", "assets/lib/jquery-2.2.3.min.js", "assets/lib/bootstrap.min.js", "dist/contentScript.bundle.js" ],
          "css": ["assets/css/font-awesome.min.css", "assets/css/bootstrapInject.css", "assets/css/injectingStyle.css"],
          "matches": ["*://*.linkedin.com/*", "*://web.whatsapp.com/*", "*://mail.google.com/*"],
          "run_at": "document_end"
      } ],
      "background": {
          "page": "background.html"
      },
      "permissions": [
          "tabs",
          "http://*/*",
          "https://*/*",
          "contextMenus",
          "notifications",
          "unlimitedStorage",
          "storage"
      ],
      "icons": {
          "128": "assets/images/icon-v2-128px.png",
          "16": "assets/images/icon-v2-16px.png",
          "48": "assets/images/icon-v2-48px.png",
          "16": "assets/images/icon-v2-16px.png"
      },
      "web_accessible_resources": [
          "all required files here"
      ],
      "update_url": "https://clients2.google.com/service/update2/crx",
      "content_security_policy": "script-src 'self' 'unsafe-eval' https://ssl.google-analytics.com https://connect.facebook.net https://platform.twitter.com https://staticxx.facebook.com; object-src 'self'"
      }
      

      以下是附加控制器的 init.js

      export default class AddContactModule {
      constructor() {
          // Check for the webpage we are currently in (linkedin, facebook, twitter, naukri, whatsapp, mail.google)
          this.currentHost = window.location.host.split('.');
          this.app = 'extension-extension';
          // Variable to intialize parent of button we will add
          this.globalButtonVar = '';
          this.copyglobalButtonVar = '';
      
          this.globalFormTemplate = '';
          this.globalForm = '';
          this.isLoggedin = false;
      
          // Get ng-app param and check if its already present if it is then no need to intialize angular module again
          this.ifAppDirective = document.querySelector('[ng-app]');
      
          console.log("hello1");
          this.init();
      }
      
      init() {
          this.eventHandling();
          this.addContactModel();
          this.checkHost();
      
      }
      
      // Function to set the flag to check if user is logged in or not
      eventHandling() {
          if (!this.ifAppDirective && !$('.ng-scope').length) {
              // If ng-app is not initialized initialize ng-app
              $('body').attr('ng-app', this.app);
      
              this.miscellaneousEvent();
          }
      }
      
      // Function to call when user syncing images
      
      // Function to handle other events
      miscellaneousEvent() {
          $(document).on('click', '.dropdown-menu .input-group-btn, .dropdown-menu .checkbox, .dropdown-menu a', function (e) {
            e.stopPropagation();
          });
      
          // Cancel the fetch list dialoge box
          $('body').on('click', '#fetchContainer-step2 > .innerContainer .close-fetch, .close-intro, .close-dialog', function() {
              let thisEle = $(this).attr('id');
      
              let scope = angular.element($('.pane.pane-three')).scope();
              scope.$apply(function() {
                  scope.contact.cancelFetch(false, thisEle);
              });
          });
      
          // Close the notification of start sync message
          $('body').on('click', '#extension-close-notice', function(event) {
              let scope = angular.element($('#extension-close-notice')).scope();
      
              scope.$apply(function() {
                  scope.closeNotification();
              });
          });
      
          // If clicked on linkedin save button open call the trigger function to open contact details
          $('body').off('click', '#linkedin-save').on('click', '#linkedin-save', function() {
              let scope = angular.element($('#profile-wrapper')).scope();
      
              scope.$apply(function() {
                  scope.contact.linkedinFetch();
              });
          });
      
          // trigger save function for gmail
          $('body').off('click', '#gmail-save').on('click', '#gmail-save', function() {
              let scope = angular.element($('.nH.g.id')).scope();
              scope.$apply(function() {
                  scope.gmailFetch();
              });
          });
      }
      
      // Function to handle addcontactmodel open/close events
      addContactModel() {
          $('body').on('show.bs.modal', '#addContactModal', function () {
              $(this).after('<div class="modal-backdrop fade in"></div>');
          }).on('hide.bs.modal', '#addContactModal', function () {
              $('.modal-backdrop').remove();
          });
      
          $('body').off('shown.bs.modal', '#addContactModal').on('shown.bs.modal', '#addContactModal', (event) => {
              let openedBy = $(event.relatedTarget);
              this.loginStatus();
      
              // Check for the target popup is opened by whatzup from group
              if(openedBy.hasClass('whatsappGroupBtn') || openedBy.hasClass('whatsappGroupChat')) {
                  let contactObj = {
                      ContactMobile: openedBy.data('contactnumber'),
                      given: openedBy.data('name'),
                      profilePic: openedBy.data('profilepic'),
                  };
      
                  // Reduce size of modal for group detail page
                  if(openedBy.hasClass('whatsappGroupBtn')) {
                      contactObj.addFrom = 'wa_group';
                  } else {
                      contactObj.addFrom = 'wa_chat';
                  }
      
                  // Assigne all found details to form, for this call controller function
                  let scope = angular.element($('#addContactForm')).scope();
                  // Set form to its default state
                  scope.$apply(() => {
                      scope.contact.updateForm(contactObj);
                  });
              }
      
          }).off('hidden.bs.modal', '#addContactModal').on('hidden.bs.modal', '#addContactModal', () => {
              $('.arrowPointing').hide();
      
              // Assigne all found details to form, for this call controller function
              let scope = angular.element($('#addContactForm')).scope();
              // Set form to its default state
              scope.$apply(() => {
                  scope.contact.addNewContact = false;
                  // Helps to decide if the contact should be added to existing or new
                  scope.contact.addtoExisting = false;
                  scope.contact.noContact = false;
                  // Contains searched contact list
                  scope.contact.contactList = [];
                  // Search field model, this will have the searching text
                  scope.contact.searchKey = '';
                  scope.contact.searchCompleted = false;
                  scope.contact.globalTimeout = 0;
      
                  //lets you add another unknown contact when one unknown already has been added
                  scope.contact.contactAdded = false;
                  scope.contact.contactSaved = false;
              });
          });
      }
      
      // Hide/show login arrow depends on the login status
      
      // Depends on host call linkedin init or facebookinit
      checkHost() {
          switch(this.currentHost[1]) {
              case 'linkedin': {
                  this.initLinkedin();
                  break;
              }
              case 'google': {
                  this.initGmail();
                  break;
              }
              case 'whatsapp': {
                  this.initWhatsapp();
              } default: {}
          }
      }
      // Function to add template & button to linkedin
      initLinkedin() {
          // Variable to check if the button we are adding is in extra info section or not
          let ifTablist = 1;
          let linkedinForm = ''; 
          // Button template if adding to extra info section
          let buttonTemplate = '<span class="inline-block extension-save-button"><button type="button" id="linkedin-save" class="secondary top-card-action link-without-visited-state"><img src="https://d73xd4ooutekr.cloudfront.net/v4/img/logo-42.png"> Save Contact</a></span>';
          let buttonContainerLength = 0;
      
          // Get the parent
          $('body').on('DOMNodeInserted', (event) => {
              if(event.target.id == 'profile-wrapper' && !buttonContainerLength) {
                  this.globalButtonVar = this.copyglobalButtonVar = $('body').find('#profile-wrapper');
      
                  buttonContainerLength = this.globalButtonVar.find('.pv-top-card-section__actions').length;
      
                  // Check if the selected parent is not present 
                  if(buttonContainerLength) {
                      this.globalButtonVar.find('.pv-top-card-section__actions').append(buttonTemplate);
                      this.setController();
                  }
              }
          });
      }
      // Function to set ng-controller
      setController() {
          if(this.globalButtonVar.length) {
              this.globalButtonVar.attr('ng-controller', 'ContactModalController as contact');
          }
      }
      }
      

      以下是 controller.js 文件

      "use strict";
      import AddContactModule from './init'
      
      export default class ContactModalController {
      constructor($scope, $compile) {
          // Intializing dependencies to the scope variable
          this.$scope = $scope;
          this.$compile = $compile;
          /* Assign required objects which need to make everything works */
          }
      
      
      // Function to fetch linkedin details
      linkedinFetch() {
          // Show more button, this will show all contact details of current user
          let saveBtn = $('.contact-see-more-less')
      
          if(saveBtn.attr('data-control-name') == 'contact_see_more') {
              saveBtn.click();
          }
      
          this.resetModal();
      
          this.grabContactDetails.getLinkedinDetails(this.$scope, (response) => {
              this.contactObj = response;
              // default selected List
              this.contactObj.selectedList = [];
      
              $('#addContactModal').modal('show');
          });
      }
      }
      

1 个答案:

答案 0 :(得分:0)

我遇到了类似的问题,控制器没有初始化。

我在HTML标记上设置应用,并在正文标记上设置控制器。

根据我的问题,因为DOM元素的动态加载