Angular AoT自定义装饰器错误遇到静态解析符号值 [英] Angular AoT Custom Decorator Error encountered resolving symbol values statically

查看:114
本文介绍了Angular AoT自定义装饰器错误遇到静态解析符号值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个装饰器来帮助我处理桌面/移动事件

I created a decorator to help me with handling desktop/mobile events

import { HostListener } from '@angular/core';

type MobileAwareEventName =
  | 'clickstart'
  | 'clickmove'
  | 'clickend'
  | 'document:clickstart'
  | 'document:clickmove'
  | 'document:clickend'
  | 'window:clickstart'
  | 'window:clickmove'
  | 'window:clickend';

export const normalizeEventName = (eventName: string) => {
  return typeof document.ontouchstart !== 'undefined'
    ? eventName
        .replace('clickstart', 'touchstart')
        .replace('clickmove', 'touchmove')
        .replace('clickend', 'touchend')
    : eventName
        .replace('clickstart', 'mousedown')
        .replace('clickmove', 'mousemove')
        .replace('clickend', 'mouseup');
};

export const MobileAwareHostListener = (
  eventName: MobileAwareEventName,
  args?: string[],
) => {
  return HostListener(normalizeEventName(eventName), args);
};

问题是当我尝试使用--prod进行编译时,出现以下错误

The problem with that is when I try to compile with --prod, I get the following error

typescript error
Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing
the function or lambda with a reference to an exported function (position 26:40 in the original .ts file),
resolving symbol MobileAwareHostListener in
.../event-listener.decorator.ts, resolving symbol HomePage in
.../home.ts

Error: The Angular AoT build failed. See the issues above

怎么了?我该如何解决?

What is wrong? How can I fix that?

推荐答案

这表示错误的确切含义.在执行函数的地方不支持函数调用.不支持Angular内置装饰器的行为扩展. .

This means exactly what the error says. Function calls aren't supported in a place where you're doing them. The extension of the behaviour of Angular built-in decorators isn't supported.

AOT编译(通过--prod选项触发)允许静态分析现有代码,并用其评估的预期结果替换部分代码.这些地方的动态行为意味着AOT无法用于该应用程序,这是该应用程序的主要缺点.

AOT compilation (triggered by --prod option) allows to statically analyze existing code and replace some pieces with expected results of their evaluation. Dynamic behaviour in these places means that AOT cannot be used for the application, which is a major drawback for the application.

如果您需要自定义行为,则不应该使用HostListener.由于它基本上是在元素上设置一个侦听器,因此应使用渲染器提供程序手动完成,这比DOM更好.Angular抽象.

If you need custom behaviour, HostListener shouldn't be used. Since it basically sets up a listener on the element, this should be done manually with renderer provider, which is preferable Angular abstraction over DOM.

这可以通过自定义装饰器解决:

This can be solved with custom decorator:

interface IMobileAwareDirective {
  injector: Injector;
  ngOnInit?: Function;
  ngOnDestroy?: Function;
}

export function MobileAwareListener(eventName) {
  return (classProto: IMobileAwareDirective, prop, decorator) => {
    if (!classProto['_maPatched']) {
      classProto['_maPatched'] = true;
      classProto['_maEventsMap'] = [...(classProto['_maEventsMap'] || [])];

      const ngOnInitUnpatched = classProto.ngOnInit;
      classProto.ngOnInit = function(this: IMobileAwareDirective) {
        const renderer2 = this.injector.get(Renderer2);
        const elementRef = this.injector.get(ElementRef);
        const eventNameRegex = /^(?:(window|document|body):|)(.+)/;

        for (const { eventName, listener } of classProto['_maEventsMap']) {
          // parse targets
          const [, eventTarget, eventTargetedName] = eventName.match(eventNameRegex);
          const unlisten = renderer2.listen(
            eventTarget || elementRef.nativeElement,
            eventTargetedName,
            listener.bind(this)
          );
          // save unlisten callbacks for ngOnDestroy
          // ...
        }

        if (ngOnInitUnpatched)
          return ngOnInitUnpatched.call(this);
      }
      // patch classProto.ngOnDestroy if it exists to remove a listener
      // ...
    }

    // eventName can be tampered here or later in patched ngOnInit
    classProto['_maEventsMap'].push({ eventName, listener:  classProto[prop] });
  }
}

并按如下方式使用:

export class FooComponent {
  constructor(public injector: Injector) {}

  @MobileAwareListener('clickstart')
  bar(e) {
    console.log('bar', e);
  }

  @MobileAwareListener('body:clickstart')
  baz(e) {
    console.log('baz', e);
  }  
}

IMobileAwareDirective界面在这里起着重要的作用.它强制一个类具有injector属性,并且这种方式可以访问其注入器并拥有自己的依赖项(包括ElementRef,它是本地的,显然在根注入器上不可用).此约定是装饰器与类实例依赖项进行交互的首选方式.也可以添加class ... implements IMobileAwareDirective来提高表达力.

IMobileAwareDirective interface plays important role here. It forces a class to have injector property and this way has access to its injector and own dependencies (including ElementRef, which is local and obviously not available on root injector). This convention is the preferable way for decorators to interact with class instance dependencies. class ... implements IMobileAwareDirective can also be added for expressiveness.

MobileAwareListenerHostListener的不同之处在于,后者接受参数名称列表(包括神奇的$event),而后者仅接受事件对象并绑定到类实例.可以在需要时更改.

MobileAwareListener differs from HostListener in that the latter accepts a list of argument names (including magical $event), while the former just accepts event object and is bound to class instance. This can be changed when needed.

这是一个演示.

这里还应解决几个问题.事件侦听器应在ngOnDestroy中删除.类继承可能存在潜在的问题,需要对此进行额外的测试.

There are several concerns that should be addressed additionally here. Event listeners should be removed in ngOnDestroy. There may be potential problems with class inheritance, this needs to be additionally tested.

这篇关于Angular AoT自定义装饰器错误遇到静态解析符号值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆