首页 > 解决方案 > 嵌套的 JS 装饰器获取/设置,如何正确链接它们?

问题描述

ember框架积极地采用了装饰器。现在为了使用数据绑定,我必须装饰我的属性,@tracked每当我更改属性时,我都会使用它来更新我所有漂亮的 UI。

     @tracked username = 'dave';

这很好用,但是如果我需要在跟踪的装饰器之上添加自定义装饰器,我会遇到一些严重的问题。

    @typed(StateTrackMap)
    @tracked
    mapConfigsArray = [create(StateTrackMap)];

通过让我的@typed装饰器检查它是否在另一个装饰器之上,我可以让它工作。

export default function typed(classType) {
    let weak = new WeakMap();
    return function(object, property, descriptor) {
        return {
            get() {
                // Check if there is another decorator attached below us in the chain
                // i.e. "tracked"
                if (typeof descriptor.get == 'function') {
                    return descriptor.get.call(this);
                }
                // If we haven't initialized before but there is one ready, return that
                if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                    weak.set(this, descriptor.initializer.call(this));
                }
                return weak.get(this);
            },
            set(value) {
                // my set code which does the type checking/converting this descriptor is for

                                // Apply the converted value to the lower level object
                // This may be the object itself, or it may be another setter in the chain
                if (typeof descriptor.set == 'function') {
                    descriptor.set.call(this, typedValue);
                } else {
                    return weak.set(this, typedValue);
                }
            }
        }
    }
}

但这感觉很奇怪……而且看起来不像我见过的描述符的任何用法。主要是因为如果我改变装饰器的顺序,事情就会爆炸

    @tracked
    @typed(StateTrackMap)
    mapConfigsArray = [create(StateTrackMap)];
index.js:172 Uncaught Error: Assertion Failed: The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both.

所以我想我的问题是,链接具有get&的装饰器的正确方法是什么set?在我看来,装饰器的顺序决定了我是否可以上链/下链。在我看来,这种链接逻辑必须融入到每个装饰器中,否则它不起作用。有没有一些通用的方法可以将装饰器传递给其他装饰器?

我已经看到了一些示例,其中我返回了描述符引用,但这似乎对这里的问题没有帮助,因为我不太确定如何get/set在不擦除属性属性链或进入同一条船上的情况下仍然可以注入我的如上所述,我的代码必须设计为专门与其他描述符一起使用。

export default function typed(classType) {
    return function(object, property, descriptor) {
        const set = descriptor.set;
        const get = descriptor.get;
        const weak = new WeakMap();

        descriptor.get = function() {
            if (typeof get == 'function') {
                return get.call(this);
            }
            // If we haven't initialized before but there is one ready, return that
            if (!weak.has(this) && typeof descriptor.initializer == 'function') {
                weak.set(this, descriptor.initializer.call(this));
            }
            return weak.get(this);
        }
        descriptor.set = function(value) {
            // My type checking / conversion code

            // Apply the converted value to the lower level object
            // This may be the object itself, or it may be another setter in the chain
            if (typeof set == 'function') {
                set.call(this, typedValue);
            } else {
                return weak.set(this, typedValue);
            }
        }
        return descriptor;
    }
}

顺便说一句,这种方法给出了不同的爆炸。

Assertion Failed: You attempted to use @tracked on mapConfigsArray, but that element is not a class field.

标签: javascriptember.jsglimmer.jsjavascript-decorators

解决方案


推荐阅读