在指令中访问 Angular NgForOfContext

rsl*_*mos 5 user-interface user-experience inplace-editing ngfor angular

我正在开发一个 Angular 指令来检测(并删除)列表中的空项目*ngFor

我不是添加一个X将触发删除的按钮(每个项目),而是更倾向于基于内容的方法:如果用户完全删除或只是删除某个项目的某些关键字段,它将立即不存在当用户关注文档的另一部分时。

我成功地做到了(令我惊讶的是,它一点也不难)。我将在这里强调关键方面:

使用指令

模板

  <div *ngFor="let item of data; index as i"
    [appInPlaceDelete]="isEmpty"
    (delete)="onDelete(i)"
  >
Run Code Online (Sandbox Code Playgroud)

成分

  onDelete(i: number) {
    // code to erase ith item
  }

  private isEmpty(item): boolean {
    // logic to answer if item is empty or not
  }
Run Code Online (Sandbox Code Playgroud)

执行

@Directive({
  selector: '[appInPlaceDelete]'
})
export class InPlaceDeleteDirective<T> implements OnInit, DoCheck {
  @Input() appInPlaceDelete: (t: T) => boolean;
  @Output() delete: EventEmitter<undefined> = new EventEmitter();

  // ...

  private $implicit: T;
  private focusstream = new Subject<string>();

  constructor(private containerRef: ViewContainerRef) {
  }

  ngOnInit() {
    const context: NgForOfContext<T> = this.containerRef['_view'].context;
    this.$implicit = context.$implicit

    // code forwarding 'focusstream' to 'delete' output
  }

  // code that gives visual indication that the item will be deleted

  // ...
  // a bunch of @HostListeners feeding the focusstream
  // ...
}
Run Code Online (Sandbox Code Playgroud)

现在,我对我所做的非常满意,除了那一行:

    const context: NgForOfContext<T> = this.containerRef['_view'].context;
Run Code Online (Sandbox Code Playgroud)

因为访问该_view属性不是平台可移植的(也许该属性仅存在于platformBrowserDynamic)。

问题来了:

  1. 有什么方法可以捕获context平台无关的信息吗?

NgForOf独立地将这个上下文填充到平台上,如其源代码所示。也许有(或应该有)一种可移植地检索该上下文的方法。我知道,开发人员(我)仍然有责任知道该上下文包含什么(其类型)。等等,这是下一个问题。

  1. 我可以selector更加严格地限制仅在主机内部挂钩*ngFor吗?

对于这个,我知道我们应该记住,角度将原始模板转换为:

<ng-template ngFor let-item [ngForOf]="data" let-i="index">
  <div
    [appInPlaceDelete]="isEmpty"
    (delete)="onDelete(i)"
  >
</ng-template>
Run Code Online (Sandbox Code Playgroud)

因此,指令实际divs挂钩的是由NgForOf指令动态创建的嵌入视图。

我想知道是否有某种方法可以强制执行此操作(我尝试过selector: '[ngFor] > [appInPlaceDelete]'但无济于事)。

  1. 有解决方法吗?

输出delete显然使用了上下文变量i(指令本身一无所知i;它只是通过通道发出一个事件delete)。

我尝试过[appInPlaceDelete]="isEmpty(item)",尽管我事先知道它会绑定falseappInPlaceDelete.

我还尝试[appInPlaceDelete]="() => isEmpty(item)"...="function() { return isEmpty(item); }"期望 Angular 会创建一个闭包抓取item(and isEmpty) 并传递该闭包。也没有成功。

虽然有点难看,但这比不可移植的指令要好。

  1. 如果没有其他方法可以正面回答第一个问题,并且第三个问题也没有解决方法,我怎样才能至少覆盖最常见的平台?

虽然不可取,但也许我可以通过某种方式来确定我的指令在哪个平台上运行并context专门访问。

在这件事上,我必须承认,我不知道如何使用(甚至更少调试)除platformBrowserDynamic.

  1. 如果您已经走到这一步,还有其他完全不同的方法吗?

这篇文章的前两段描述了我在这里想要完成的任务。其他一切只是我的解决方案(以及它带来的问题)。

也许有一种完全不同的方法。也许有人已经做了/打包/发布了更好的解决方案。