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 by
zone.js, so it is not in
ngZone`,你可以自己打补丁。
- 更新
zone.js
到最新版本0.8.18
- 添加以下代码
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']部分。
我在我的 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 by
zone.js, so it is not in
ngZone`,你可以自己打补丁。
- 更新
zone.js
到最新版本0.8.18 - 添加以下代码
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']部分。