如何在视图外评估 Aurelia 插值表达式?

Posted

技术标签:

【中文标题】如何在视图外评估 Aurelia 插值表达式?【英文标题】:How to evaluate an Aurelia interpolation expression outside a view? 【发布时间】:2017-04-28 14:48:58 【问题描述】:

在 Aurelia 中,假设我有一个包含插值表达式 "Today at $value | date: 'time'" 的字符串和一些表示此 value: new Date() 的绑定上下文的对象。 有什么办法可以在视图之外只获取该字符串和该对象,并获取生成的格式化字符串,即"Today at 13:44"

我查看了tests,但它们都涉及创建 html 元素、绑定和解除绑定 - 我想知道所有这些可能的性能开销是多少,以及是否有更简单的方法来实现这一点?如果有一种轻量级的方法来针对上下文对象评估这样的字符串,而无需设置和拆除绑定等,那将是非常棒的。

【问题讨论】:

【参考方案1】:

这是一个例子:https://gist.run?id=a12470f6e9f7e6a605b3dd002033fdc7

expression-evaluator.js

import inject from 'aurelia-dependency-injection';
import ViewResources from 'aurelia-templating';
import Parser, createOverrideContext from 'aurelia-binding';

@inject(Parser, ViewResources)
export class ExpressionEvaluator 
  constructor(parser, resources) 
    this.parser = parser;
    this.lookupFunctions = resources.lookupFunctions;
  

  evaluate(expressionText, bindingContext) 
    const expression = this.parser.parse(expressionText);
    const scope = 
      bindingContext,
      overrideContext: createOverrideContext(bindingContext)
    ;
    return expression.evaluate(scope, this.lookupFunctions);
  

app.js

import inject from 'aurelia-dependency-injection';
import ExpressionEvaluator from './expression-evaluator';

@inject(ExpressionEvaluator)
export class App 
  message = 'Hello World!';

  constructor(evaluator) 
    this.message = evaluator.evaluate('foo.bar.baz | test',  foo:  bar:  baz: 'it works'   );
  

编辑

我错过了您需要解析 interpolation 表达式,而不是正则绑定表达式...

aurelia-validation 中有一个这样的例子:https://github.com/aurelia/validation/blob/master/src/implementation/validation-message-parser.ts

【讨论】:

感谢杰里米,这是一个很大的帮助!我们最初尝试构建一个单一的表达式,类似于它在 aurelia-validation 中的实现方式,但我们遇到了一些麻烦 - 事实证明 BinaryConditional 在评估时不传递查找函数,这意味着它们不能与值转换器一起使用。我不确定这是一个错误还是故意的,但它让我们感到意外 - 不过幸运的是,我们可以通过一个更简单的解决方案来解决问题,在此处发布以供将来参考:-) 我会为此打开一个错误 - 似乎确实限制了我们编写任意表达式的能力【参考方案2】:

感谢 Jeremy 的回答,这成为了我们的最终解决方案:

import  autoinject, BindingLanguage, Expression, ViewResources, createOverrideContext  from "aurelia-framework";

// Represents the sequence of static and dynamic parts resulting from parsing a text.
type TemplateStringParts = (Expression | string)[];

// Cache containing parsed template string parts.
const cache = new Map<string, TemplateStringParts>();

/**
 * Represents an interpolation expression, such as "The price is $price | currency",
 * which when evaluated against a binding context results in a formatted string,
 * such as "The price is 42 USD".
 */
@autoinject
export class TemplateString

    private text: string;
    private viewResources: ViewResources;
    private bindingLanguage: BindingLanguage;

    /**
     * Creates a new instance of the TemplateString type.
     * @param text The text representing the interpolation expression.
     * @param viewResources The view resources to use when evaluating the interpolation expression.
     * @param bindingLanguage The BindingLanguage instance.
     */
    public constructor(text: string, viewResources: ViewResources, bindingLanguage: BindingLanguage)
    
        this.text = text;
        this.viewResources = viewResources;
        this.bindingLanguage = bindingLanguage;
    

    /**
     * Evaluates the interpolation expression against the specified context.
     * @param bindingContext The context against which expressions should be evaluated.
     * @param overrideContext The override context against which expressions should be evaluated.
     * @returns The string resulting from evaluating the interpolation expression.
     */
    public evaluate(bindingContext?: any, overrideContext?: any): string
    
        let parts = cache.get(this.text);

        if (parts == null)
        
            parts = (this.bindingLanguage as any).parseInterpolation(null, this.text) || [this.text];
            cache.set(this.text, parts);
        

        const scope =
        
            bindingContext: bindingContext || ,
            overrideContext: overrideContext || createOverrideContext(bindingContext)
        ;

        const lookupFunctions = (this.viewResources as any).lookupFunctions;

        return parts.map(e => e instanceof Expression ? e.evaluate(scope, lookupFunctions) : e).join("");
    

    /**
     * Gets the string representation of this template string.
     * @returns The string from which the template string was created.
     */
    public toString(): string
    
        return this.text;
    


/**
 * Represents a parser that parses strings representing interpolation expressions,
 * such as "The price is $price | currency".
 */
@autoinject
export class TemplateStringParser

    private resources: ViewResources;
    private bindingLanguage: BindingLanguage;

    /**
     * Creates a new instance of the TemplateStringParser type.
     * @param resources The view resources to use when evaluating expressions.
     * @param bindingLanguage The BindingLanguage instance.
     */
    public constructor(resources: ViewResources, bindingLanguage: BindingLanguage)
    
        this.resources = resources;
        this.bindingLanguage = bindingLanguage;
    

    /**
     * Parses the specified text as an interpolation expression.
     * @param text The text representing the interpolation expression.
     * @returns A TemplateString instance representing the interpolation expression.
     */
    public parse(text: string): TemplateString
    
        return new TemplateString(text, this.resources, this.bindingLanguage);
    

要在视图模型中使用它,只需注入 TemplateStringParser 并使用它来创建 TemplateString 实例,然后可以针对上下文对象对其进行评估。

【讨论】:

以上是关于如何在视图外评估 Aurelia 插值表达式?的主要内容,如果未能解决你的问题,请参考以下文章

vue中的插值表达式和指令

Vue 插值表达式的使用

三、插值表达式

Aurelia binding

python如何评估“is”表达式? [复制]

如何使用逻辑运算符评估前缀表达式