Angular2 @HostListener无法在派生组件

时间:2016-09-21 10:50:29

标签: angular typescript angular2-template

我有一个基于Angular2的客户端应用程序。 我有一个基类:

abstract class BaseClass {
    @HostListener('window:beforeunload') beforeUnloadHandler() {
        console.log('bla');
    }
}

和两个非常相似的派生类:

@Component({
    selector:  'derived-one',
    templateUrl:  './templates/app/+derived-one/derived-one.component.html'
})
export class DerivedOne extends BaseClass {
}

@Component({
    selector:  'derived-two',
    templateUrl:  './templates/app/+derived-two/derived-two.component.html'
})
export class DerivedTwo extends BaseClass {
}

问题在于,例如DerivedOne beforeUnloadHandlerDerivedTwo中工作正常,根本不会接到来电。

我知道很难找到为什么只是查看上面的信息,但也许有人可能怀疑可能导致这种奇怪行为的原因。

还有一些说明:

如果我使用以下内容:

abstract class BaseClass
    constructor(){
        window.onbeforeunload = function(){
            console.log('bla');
        }
    }
}

一切正常,但我仍然想找到一个基于Angular2的解决方案;

如果我写

abstract class BaseClass {
    beforeUnloadHandler() {
        console.log('bla');
    }
}

并在derived-two.component.html

<div (window.beforeunload)="beforeUnloadHandler()"></div>

一切都很好,但它看起来像一个丑陋的黑客;

再次,如果我写

abstract class BaseClass {
    beforeUnloadHandler() {
        console.log('bla');
    }
}

@Component({
    selector:  'derived-two',
    host: {'window:beforeunload': 'beforeUnloadHandler' }
    templateUrl:  './templates/app/+derived-two/derived-two.component.html'
})
export class DerivedTwo extends BaseClass {
}

它不起作用。

最后,如果我在@HostListenerDerivedTwo中使用DerivedOne,它会有效,但我希望避免使用重复的代码。

希望上面的信息足以与之合作(至少要猜测一下)。

1 个答案:

答案 0 :(得分:9)

更新2.3.0

您现在可以利用组件的对象继承。

您可以在此提交https://github.com/angular/angular/commit/f5c8e0989d85bc064f689fc3595207dfb29413f4

中看到更多详细信息

旧版

1)如果你有一个班级:

abstract class BaseClass {
  @HostListener('window:beforeunload') beforeUnloadHander() {
    console.log('bla');
  }
}

那么它会起作用

Plunker Example (在编辑器中放置空格并观看控制台)

但要小心,因为 Angular2不支持完全继承 - Issue with binding and @ViewChild

  

但仍然不清楚为什么@HostListener的解决方案不起作用   在第一位

具体来说,如果您在派生组件上有属性装饰器,那么它将无法工作。例如,我们说我们有以下代码:

abstract class BaseClass {
  @HostListener('window:beforeunload') beforeUnloadHander() {
    console.log(`bla-bla from${this.constructor.name}`);
  } 
} 

@Component({
    selector:  'derived-one',
    template:  '<h2>derived-one</h2>'
})
export class DerivedOne extends BaseClass {
   @Input() test;
}

<强> Plunker

它将转换为javascript,如:

var core_1 = require('@angular/core');
var BaseClass = (function () {
    function BaseClass() {
    }
    BaseClass.prototype.beforeUnloadHander = function () {
        console.log("bla-bla from" + this.constructor.name);
    };
    __decorate([
        core_1.HostListener('window:beforeunload'), 
        __metadata('design:type', Function), 
        __metadata('design:paramtypes', []), 
        __metadata('design:returntype', void 0)
    ], BaseClass.prototype, "beforeUnloadHander", null);
    return BaseClass;
}());
var DerivedOne = (function (_super) {
    __extends(DerivedOne, _super);
    function DerivedOne() {
        _super.apply(this, arguments);
    }
    __decorate([
        core_1.Input(), 
        __metadata('design:type', Object)
    ], DerivedOne.prototype, "test", void 0);
    DerivedOne = __decorate([
        core_1.Component({
            selector: 'derived-one',
            template: '<h2>derived-one</h2>'
        }), 
        __metadata('design:paramtypes', [])
    ], DerivedOne);
    return DerivedOne;
}(BaseClass));

我们对以下几行感兴趣:

 __decorate([
    core_1.HostListener('window:beforeunload'), 
      __metadata('design:type', Function), 
      __metadata('design:paramtypes', []), 
      __metadata('design:returntype', void 0)
 ], BaseClass.prototype, "beforeUnloadHander", null);

 ... 
 __decorate([
   core_1.Input(), 
   __metadata('design:type', Object)
 ], DerivedOne.prototype, "test", void 0);

HostListenerInput是属性修饰符(propMetadata键)。这种方式将定义两个元数据条目 - BaseClassDerivedOne enter image description here enter image description here

最后,当angular2从DerivedOne类中提取元数据时,它将只使用自己的元数据:

enter image description here

要获取所有元数据,您可以编写自定义装饰器,如:

function InheritPropMetadata() {
  return (target: Function) => {
    const targetProps = Reflect.getMetadata('propMetadata', target);

    const parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    const parentProps = Reflect.getMetadata('propMetadata', parentTarget);

    const mergedProps = Object.assign(targetProps, parentProps);

    Reflect.defineMetadata('propMetadata', mergedProps, target);
  };
};

@InheritPropMetadata()
export class DerivedOne extends BaseClass {

这是一个有效的 demo

2)如果你做了如下:

abstract class BaseClass
  constructor(){
    window.onbeforeunload = function(){
      console.log('bla');
    };
  }
}

然后它只会被调用一次,因为你每次都会覆盖window.onbeforeunload处理程序 您应该使用以下代码:

abstract class BaseClass {
 constructor(){
    window.addEventListener('beforeunload', () =>{
      console.log(`bla-bla from${this.constructor.name}`);
    })
  }
}  

<强> Plunker Example

3)最后,如果你有如下所示的基类:

abstract class BaseClass {
  beforeUnloadHander() {
     console.log(`bla-bla from${this.constructor.name}`);
  }
}

然后你必须使用正确的语法(你错过了 decorator属性中的括号

host: {'(window:beforeunload)': 'beforeUnloadHander()' }

<强> Plunker Example

希望它可以帮到你!