ngFor内部的自定义结构指令,在ngFor之前进行更新 [英] Custom structural directive inside ngFor updates before ngFor

查看:90
本文介绍了ngFor内部的自定义结构指令,在ngFor之前进行更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个小型应用程序,该应用程序使用ngFor显示具有名称,姓氏和年龄字段的人的列表.该应用程序具有搜索字段,可以在其中输入查询,然后根据该查询,服务器将列表替换为新的实体.

I am creating a small application, which display a list of People with Name, Surname and Age fields using ngFor. The application has search field, where one can enter a query, and then the list will get replaced with new Entities from the server, based on that query.

我创建了一条指令,该指令突出显示ngFor行内查询的字母.

I created a directive, that highlights letters from the query inside the ngFor row.

例如,如果我在数据库中有一个人的名字是David,并且在查询中输入"Dav",则只有ngFor的实体会被从服务器中加载,而带有"Dav"的实体将被从服务器中加载,而"Dav"字母将被从服务器中加载.突出显示,而"id"则不会.如果我有David和Davin,两个实体都将突出显示.

For example, if I have a person in database that its name is David, and I enter "Dav" inside my query, only entities conatining "Dav" will be loaded from the server in ngFor, and "Dav" letters will be highlighted and "id" will be not. If I have David and Davin, both entities will be highlighted.

仅当我使用人工setTimeout()时,该指令才能按预期工作,以确保在该指令执行操作之前将加载新列表.还有其他方法可以使这项工作吗?

The directive works as expected only if I am using an artifical setTimeout(), to make sure that the new list will load before the Directive takes action. Is there any other way to make this work?

指令:

import { Directive, Input, ElementRef } from '@angular/core';
import { SimpleChanges, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appQueryHighlight]'
})
export class QueryHighlightDirective {

  @Input('appQueryHighlight') query: string;
  queryPos: number;
  paragraphElement: HTMLParagraphElement;

  constructor(private element: ElementRef, private renderer: Renderer2) {
    this.paragraphElement = (<HTMLParagraphElement>this.element.nativeElement);
  }

  ngOnChanges(changes: SimpleChanges){
    // Temporary timeout solution
    setTimeout(()=>{

      var childCount = this.paragraphElement.childElementCount;
      var text: string = "";

      // If paragraph contain SPANS, we need to flat them to innerHTML
      if(childCount > 1) {
        for(var i = 0; i < childCount; i++) {
          text += (<HTMLSpanElement>this.paragraphElement.childNodes[0]).innerHTML;
          console.log("SPAN" + (<HTMLSpanElement>this.paragraphElement.childNodes[0]).innerHTML);
          this.paragraphElement.removeChild(this.paragraphElement.childNodes[0]);
        }
        console.log("Text=" + text)
        this.paragraphElement.innerHTML = text;
      }

      console.log('Directive ngOnChanges: query=' + this.query + ", paragraph=" + this.paragraphElement.innerHTML);

      this.queryPos = this.paragraphElement.innerHTML.toUpperCase().indexOf(this.query.toUpperCase());
      if(this.query!="" && this.queryPos >= 0) {
        //this.paragraphElement.style.backgroundColor = 'yellow';

        //First span, containing pre-colored text
        var span1 = this.renderer.createElement('span');
        var text1 = this.renderer.createText(this.paragraphElement.innerHTML.substring(0,this.queryPos));
        this.renderer.appendChild(span1, text1);

        //Colored text span, containing query
        var span2 = this.renderer.createElement('span');
        var text2 = this.renderer.createText(this.paragraphElement.innerHTML.substr(this.queryPos, this.query.length));
        this.renderer.setStyle(span2, 'color', "red");
        this.renderer.setStyle(span2, 'text-decoration', "underline");
        this.renderer.appendChild(span2, text2);

        //Third span, containing text after query
        var span3 = this.renderer.createElement('span');
        var text3 = this.renderer.createText(this.paragraphElement.innerHTML.substring(this.queryPos + this.query.length));
        this.renderer.appendChild(span3, text3);

        this.paragraphElement.innerHTML = "";
        this.renderer.appendChild(this.paragraphElement, span1);
        this.renderer.appendChild(this.paragraphElement, span2);
        this.renderer.appendChild(this.paragraphElement, span3);
      }
      else {
        //this.paragraphElement.style.color = 'black';
      }
    }, 15);
  }
}

LIST-COMPONENT.TS:

LIST-COMPONENT.TS:

ngOnChanges(changes: SimpleChanges) { 
  this.debtsService.getFilteredDebts(this.query)
    .subscribe(
      (data) => {
        this.debtsList = data;
        this.afterFilteringQuery = this.query;
      },
      (err) => console.log("Error occured: " + err)
    );
}

LIST-COMPONENT.HTML:

LIST-COMPONENT.HTML:

  <app-person-item 
    *ngFor="let person of personList;" 
    [query]="afterFilteringQuery">
  </app-person-item>

推荐答案

您可以尝试使用BehaviorSubject方法来代替ngOnChanges方法.我不确定,但是Observable的.next()调用应保证有一个额外的事件循环周期,这对于您的情况是必需的,因为我们可以看到每个有效的setTimeout调用为零.

Instead of ngOnChanges approach you may try to apply BehaviorSubject approach . I'm not sure but Observable's .next() call should guarantee an additional event loop cycle which is necessary in your case as we can see per working zero setTimeout call.

import { Directive, Input, ElementRef, Renderer2 } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Directive({
  selector: '[appQueryHighlight]'
})
export class QueryHighlightDirective {

  private _query = new BehaviorSubject<string>('');

  @Input('appQueryHighlight')
  set query(value: string) {
    this._query.next(value);
  };
  get query(): string {
    return this._query.getValue();
  }

  ngOnInit() {
    this._query.subscribe((query: string) => {
      // on query change handler
      // ... ngOnChanges-setTimeout previous code with 'query' instead of 'this.query'
    });
  }

}

这篇关于ngFor内部的自定义结构指令,在ngFor之前进行更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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