angular4、zone.js和一个javascript库的自定义事件

angular 4, zone.js and a custom event of a javascript library

我在我的 angular 4 项目中包含了 cropper.js。 在我使用 cropper.js 的组件中,我像这样注册了它的 ready 事件:

this.cropperOptions = {
    // omitted options which are not importent
    cropend: () => {
        this.changedOrTouched();
    },
    ready: () => {
        URL.revokeObjectURL(this.cropperImage.nativeElement.src);
        this.photoReady.emit();
        this.changedOrTouched();
    }
};

发出的 ready 事件由父组件使用,父组件本身通知服务

父组件

photoReady(): void {
        this.loaderService.setLoaderStatus(false);
    }

服务

export class LoaderService {
    public loaderStatus: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

    /**
     * indicates wheter something in the application is loading or not. Internal it calls next() on the loaderStatus BehaviorSubject.
     * This triggers any subscription to that BehaviorSubject which then can act.
     * @param value true if application is loading, false if not.
     */
    setLoaderStatus(value: boolean) {
        this.loaderStatus.next(value);
    }
}

app.component 订阅的非常简单的服务

app.component

this.loaderService.loaderStatus.subscribe((val: boolean) => {
            this.isLoading = val;
            //this is here because of iOS
            this.changeDetector.detectChanges();
        });

它的作用是显示或隐藏微调器。

几乎每个浏览器都按预期工作,执行 IE10,它不会在被 cropper.js 就绪事件触发时隐藏微调器。

虽然 zone.js 对我来说仍然是个谜,但有时我确实(有点)理解它是如何工作的。

我已经遇到过类似的问题,我(或其他人)通常可以通过使用 ChangeDetectorRef.detectChanges()NgZone.run() ApplicationRef.tick 或在 setTimeout 中包装调用来解决.

已经在各个地方试过了,但我不明白为什么上面的 none 解决了这个问题。 也许 cropper.js 以 zone.js 无法处理的方式添加事件? 也许有办法从外部 js 库猴子修补自定义事件?

有什么想法吗?

下面是 cropper.js 添加事件的方法 cropper.js - 添加事件

function addListener(element, type, _handler, once) {
  var types = trim(type).split(REGEXP_SPACES);
  var originalHandler = _handler;

  if (types.length > 1) {
    each(types, function (t) {
      addListener(element, t, _handler);
    });
    return;
  }

  if (once) {
    _handler = function handler() {
      for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
        args[_key4] = arguments[_key4];
      }

      removeListener(element, type, _handler);

      return originalHandler.apply(element, args);
    };
  }

  if (element.addEventListener) {
    element.addEventListener(type, _handler, false);
  } else if (element.attachEvent) {
    element.attachEvent('on' + type, _handler);
  }
}

cropper.js - 调度事件

function dispatchEvent(element, type, data) {
  if (element.dispatchEvent) {
    var event = void 0;

    // Event and CustomEvent on IE9-11 are global objects, not constructors
    if (isFunction(Event) && isFunction(CustomEvent)) {
      if (isUndefined(data)) {
        event = new Event(type, {
          bubbles: true,
          cancelable: true
        });
      } else {
        event = new CustomEvent(type, {
          detail: data,
          bubbles: true,
          cancelable: true
        });
      }
    } else if (isUndefined(data)) {
      event = document.createEvent('Event');
      event.initEvent(type, true, true);
    } else {
      event = document.createEvent('CustomEvent');
      event.initCustomEvent(type, true, true, data);
    }

    // IE9+
    return element.dispatchEvent(event);
  } else if (element.fireEvent) {
    // IE6-10 (native events only)
    return element.fireEvent('on' + type);
  }

  return true;
}

cropper.js - 调用就绪事件

// Call the "ready" option asynchronously to keep "image.cropper" is defined
      self.completing = setTimeout(function () {
        if (isFunction(options.ready)) {
          addListener(element, EVENT_READY, options.ready, true);
        }

        dispatchEvent(element, EVENT_READY);
        dispatchEvent(element, EVENT_CROP, self.getData());

        self.complete = true;
      }, 0);

cropper.js 不能与 zone.js 一起使用的原因是 cropper.js addEventListener/removeEventListenernot patched byzone.js, so it is not inngZone`,你可以自己打补丁。

  1. 更新zone.js到最新版本0.8.18
  2. 添加以下代码
Zone.__load_patch("cropper", function(global, Zone, api) {
  // check cropper loaded or not
  if (!global["cropper"]) {
    return;
  }

  api.patchEventTarget(global, [global["cropper"].prototype], {
    addEventListenerFnName: "addListener",
    removeEventListenerFnName: "removeListener"
  });
});

注意:代码假定cropper是一个全局对象,如果不是,请获取cropper Type并替换global['cropper']部分。