Angular2 @HostListener不适用于派生组件

我有一个基于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中,在DerivedTwo中,在UnloadHandler工作正常之前,它根本不接收调用.

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

还有一些说明:

如果我使用以下内容

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 {
}

它不会起作用.

最后,如果我在DerivedTwo和DerivedOne中使用@HostListener,它可以工作,但我想避免使用重复的代码.

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

更新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

But it still unclear why the solution with @HostListener didn’t work
in first place

特别是如果您在派生组件上有属性装饰器,它将无法工作.例如,假设我们有以下代码

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(),Object)
    ],DerivedOne.prototype,"test",void 0);
    DerivedOne = __decorate([
        core_1.Component({
            selector: 'derived-one',template: '<h2>derived-one</h2>'
        }),[])
    ],DerivedOne);
    return DerivedOne;
}(BaseClass));

我们对以下几行感兴趣:

__decorate([
    core_1.HostListener('window:beforeunload'),void 0)
 ],null);

 ... 
 __decorate([
   core_1.Input(),Object)
 ],void 0);

HostListener和Input是属性修饰符(propMetadata key).这种方式将定义两个元数据条目 – 在BaseClass和DerivedOne上

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

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

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

希望它能帮到你!

相关文章

AngularJS 是一个JavaScript 框架。它可通过 注:建议把脚本放在 元素的底部。这会提高网页加载速度,因...
angluarjs中页面初始化的时候会出现语法{{}}在页面中问题,也即是页面闪烁问题。出现这个的原因是:由于...
AngularJS 通过被称为指令的新属性来扩展 HTML。AngularJS 指令AngularJS 指令是扩展的 HTML 属性,带有...
AngularJS 使用表达式把数据绑定到 HTML。AngularJS 表达式AngularJS 表达式写在双大括号内:{{ expres...
ng-repeat 指令可以完美的显示表格。在表格中显示数据 {{ x.Name }} {{ x.Country }} 使用 CSS 样式为了...
$http是 AngularJS 中的一个核心服务,用于读取远程服务器的数据。读取 JSON 文件下是存储在web服务器上...