【问题标题】:Angular 2+ one-time bindingAngular 2+ 一次性绑定
【发布时间】:2016-03-26 08:14:50
【问题描述】:

在 Angular 1 中,我们可以通过这种方式进行一次绑定:{{ ::myFunction() }}

在 Angular 2 中这是在抛出:

EXCEPTION: Template parse errors:
Parser Error: Unexpected token : at column 2 in [{{ ::consent(false, undefined, box) }}] in CookieConsent@5:29 ("ull-right" href="" (click)="consent(true, $event, box)">De acuerdo</a>
        <span class="hidden">[ERROR ->]{{ ::consent(false, undefined, box) }}</span>

我们如何在 angular2 中进行一次绑定?

【问题讨论】:

  • 看看ChangeDetectionStrategy.CheckOnce是不是你要找的东西:angular.io/docs/ts/latest/api/core/…
  • @EricMartinez 他的意思是“一次”绑定,而不是“一种方式”
  • @Miquel AFAIK 这个功能在 angular2 中不存在,而且很可能永远不会存在(参见this github issue
  • ` :: ` 代表 >> 一次渲染数据并让它持续存在而不受未来模型更新影响的能力。这是 angular 1 中的一个特征
  • 要遵循@ryanm 的建议,您可以在构造函数或 ngOnInit 中一次性计算结果(取决于它是否需要 Input 值),并将其存储在组件属性中。它使角度检查成为一个变量,而不是调用一个完整的方法。正如 ryanm 所说,这只是一个改进,它不如 "::" 好,但总比没有好;)

标签: javascript angular angular2-template


【解决方案1】:

目前,您无法使用 Angular 2 进行一次绑定。但是,您可以知道绑定何时更改并重置您的输入。

Angular 2 提供了相同的 OnChanges 生命周期钩子。您需要实现 OnChanges 接口以获取更改。

参见下面的代码示例,当调用 OnInit 时,我将数据绑定属性存储在私有变量中。

export class Footer implements OnInit, OnChanges {
  @Input() public name: string;
  private privname: string;

  constructor() { }

  ngOnInit() {
    this.privname = this.name;
  }


  ngOnChanges(changes: { [key: string]: SimpleChange }): void {
    if (!changes["name"].isFirstChange()) {
        this.name = this.privname;
    }
  }
}

稍后发生其他更改时,我将在后续更改中将该值设置为其旧值。

这种机制类似于一次性绑定

替代解决方案: 您还可以使用 setter 函数来捕获更改。

【讨论】:

【解决方案2】:

【讨论】:

  • 不,不是。如果您想以这种方式处理组件内部的所有绑定,则可能是这样。第一个 Angular 中的 :: 语法用于每个绑定,而不是整个控制器。
  • 如果您需要更多地控制您想要成为CheckOnce 和您不想要什么,那么您需要使用指令。通常,如果您需要一次更新某个组件以将其封装在单独的控制器/指令中,这将是有意义的。
  • 我仍然需要了解如何实际使用它。这里是。在您的组件声明中,添加:changeDetection: ChangeDetectionStrategy.OnPush
  • @LeFex 的 Angular 改变了 CheckDetectionStrategy?在提出这个问题时,CheckOnce 是正确的回答,但现在 angular 文档似乎令人困惑:OnPush means that the change detector's mode will be set to CheckOnce during hydration. 也许首先提出该解决方案的 @MarkRajcok 可以提供帮助
  • 我想用它来代替非常笨拙的 ngx-translate 或 Angular i18n。我的组件中有一个包含文本的字典引用,我只想将一次绑定到模板中的每个文本。有没有什么方法可以像在 AngularJs 中那样以简单的方式实现这一点?
【解决方案3】:

ChangeDetectionStrategy.CheckOnce 是这个问题的解决方案。

这已更新为OnPush,另请参阅代码中的注释:

export declare enum ChangeDetectionStrategy {
    /**
     * `OnPush` means that the change detector's mode will be set to `CheckOnce` during hydration.
     */
    OnPush = 0,
    /**
     * `Default` means that the change detector's mode will be set to `CheckAlways` during hydration.
     */
    Default = 1,
}

【讨论】:

    【解决方案4】:

    我在这里找到了一次在 Angular 2 中绑定的解决方案: https://github.com/angular/angular/issues/14033

    我创建了这个指令:

     import { Directive, TemplateRef, ViewContainerRef, NgZone } from "@angular/core";
    
    @Directive({
        selector: '[oneTime]',
    })
    export class OneTimeDirective {
        constructor(template: TemplateRef<any>, container: ViewContainerRef, zone: NgZone) {
            zone.runOutsideAngular(() => {
                const view = container.createEmbeddedView(template);
                setTimeout(() => view.detach());
            })
        }
    }
    

    并使用它:

      <some-selector *oneTime [somePropertyToOneTimeBinding]="someValueToOneTimeBinding"></some-selector>
    

    例如:

         <iframe *oneTime [src]="myUrl"></iframe>
    

    【讨论】:

      【解决方案5】:

      由于在 Angular 中默认情况下一次读取/绑定是不可能的,我认为编写一个公共 getter 函数将解决这个问题。

      例如

      public getValue():number {
       return mynumber ? mynumber : 25; // if mynumber is not undefined the mynumber else return 25
      }
      
      //In html template
      <input type="range" min="getValue()" max="100">
      

      如果 getter 函数能够在模板渲染发生之前回复,这将完美地工作。所以如果在 ngOnInit() 函数中完成 mynumber 的初始化会很棒

      【讨论】:

        【解决方案6】:

        大部分时间的最佳实践:ngOnInit()

        在 Angular 2+ 中,我们有 ngOnInit(),它通常只会运行一次,恰好在组件初始化的时候。这是解决一次性绑定问题的最简单且通常最好的解决方案。

        绑定到函数可能会导致对该函数进行数十次不必要的调用,从而降低您的应用速度。

        代替{{ ::myFunction() }},在组件上创建一个属性并将其值设置为ngOnInit()

        export class MyComponent implements OnInit {
          myValue: any;
        
          constructor() { }
        
          ngOnInit() {
        
            this.myValue = /* CALL FUNCTIONS OR CALCULATE VALUE HERE */
        
          }
        }
        

        然后在模板中,只需使用:

         {{ myValue }} 
        

        您的计算将只运行一次。 ?

        【讨论】:

          【解决方案7】:

          Angular 不提供任何语法糖来控制输入的绑定频率。

          但是,您可以控制输入更新时的反应时间。有两种方法,可以使用其中一种或两种方法来获得所需的行为:

          1. Input 使用 setter 并有条件地转发更新 到组件主体或组件的模板。
          2. 在 组件 onoff 使用 ChangeDetectorRef 当输入 满意。

          注意,关闭更改检测并不意味着不会更新输入。输入将始终更新,因此无论更改检测是 off 还是 on,都会调用 ngOnChanges。但是,如果更改检测关闭,模板将不会针对更新的输入进行更新。请参阅codesandbox 了解此效果。

          示例 1

          要解释上述第 1 点,请考虑以下几点:

            _input1: number;
            @Input() set input1(input: number) {
              if (this._input1 === undefined || this._input1 === null) {
                this._input1 = input;
              }
            }
          

          在模板中消费

            <div>Bound Input1: {{ _input1 }}</div>
          

          _input1 是组件的本地副本,仅在需要时更新,即在上述特定情况下,本地副本仅在之前为 nullundefined 时更新。 input1 是输入属性。这里我们假设一个 undefinednull 值永远不会被认为是一个有效值,除非 input1 被传递一个 non-null non-undefined 值,我们将认为“绑定一次”没有发生。

          示例 2

          ChangeDetectorRef 文档摘录:

          提供变更检测功能的基类。更改检测树收集要检查更改的所有视图。使用方法从树中添加和删除视图,启动更改检测...

          因此 ChangeDetectorRef 的方法detach 可用于从更改检测树中分离组件。为了演示上面的第 2 点,以下示例等待所有输入都满足,然后关闭更改检测:

          import {
            ChangeDetectorRef,
            Component,
            Input,
            OnChanges,
            SimpleChanges
          } from "@angular/core";
          
          @Component({
            selector: "bind-until-all",
            template: `
              <div>Input1: {{ input1 }}</div>
              <div>Input2: {{ input2 }}</div>
              <div>Input3: {{ input3 }}</div>
            `
          })
          export class BindUntillAllComponent implements OnChanges {
            /**
             * We assume that a null or undefined value is not an empty.
             * And until a non-empty value is passed to an input, it'll be considered as not-assigned.
             *
             * Based on the use case, define what is a suitable "empty" value
             * and assign that value as the default value to the inputs.
             */
            @Input() input1: number;
            @Input() input2: number;
            @Input() input3: number;
          
            private isDetached: boolean = false;
          
            constructor(private cdr: ChangeDetectorRef) {}
          
            ngOnChanges(changes: SimpleChanges) {
              // Check whether all inputs are satisfied i.e. they have non-empty values assigned
              // For our case, type === "number" satisfies that the value is non-empty
              const areAllInputsSatisfied = [this.input1, this.input2, this.input3].every(
                (n) => typeof n === "number"
              );
          
              // Stop change detection after triggering the manual change detection once
              if (areAllInputsSatisfied && !this.isDetached) {
                this.cdr.detectChanges();
                this.cdr.detach();
          
                this.isDetached = true;
              }
            }
          }
          
          

          上面提到的codesandbox中的实例。

          【讨论】:

            猜你喜欢
            • 2017-06-25
            • 1970-01-01
            • 2017-09-20
            • 2014-07-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多