How to pass lists$ from list.component to ng-template

im提供的代码您可以看到函数调用的管道以及ng-模板如何传递到modal.compoent.html. ..................................................................................................................................................

List.component.html

<div class="list">
    <div class="header">
        <h3>{{list.name}}</h3>
        <span 
        style="
        display: flex; 
        width: 25%; 
        justify-content: space-between;">
            <h3>{{cards.length}}</h3>
            <button class="list-options-btn"></button>
        </span>
    </div>
    <div class="btn-container">
        <button class="card-add-btn" (click)="openModal(createCardTemplate)">Add new card</button>
    </div>
    <div class="cards-container" *ngIf="lists$ | async as lists">
        <app-card
        *ngFor="let card of cards" 
        [card]=card
        [lists]=lists
        ></app-card>
    </div>

    <ng-template #createCardTemplate>
        <div>
            <label for="name">Name</label><br>
            <input id="name" formControlName="name" type="text">
        </div>
        <div>
            <label for="description">Description</label><br>
            <input id="description" formControlName="description" type="text">
        </div>
        <div>
            <label for="dueDate">Due Date</label><br>
            <input id="dueDate" formControlName="dueDate" type="date">
        </div>
        <div>
            <label for="priority">Priority</label><br>
            <select id="priority" formControlName="priority">
                <option value="1">Low</option>
                <option value="2">Medium</option>
                <option value="3">High</option>
            </select>
        </div>
        <div>
            <label for="listId">List</label><br>
            <select id="listId" formControlName="listId">
                <option *ngFor="let list of lists$ | async" value={{ list.id }}>
                    {{ list.name }}
                 </option>
            </select>
        </div>
    </ng-template>
</div>

List.component.ts

import { Component, Input, TemplateRef } from '@angular/core';
import { CardDto, Priority } from 'src/Dtos/CardDto';
import { CardListDto } from 'src/Dtos/CardListDto';
import { Observable } from 'rxjs';
import { ListsService } from 'src/services/lists.service';
import { ModalService } from 'src/services/modal.service';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css']
})
export class ListComponent {
  @Input()
  list: CardListDto = new CardListDto;
  @Input()
  cards: CardDto[] = [];
  lists$: Observable<CardListDto[]> | null = null;

  constructor(
    private listsService: ListsService, 
    private modalService: ModalService){}

  openModal(modalTemplate: TemplateRef<any>) {
    this.modalService
      .open(modalTemplate, { title: 'New Card', data: this.lists$ })
      .subscribe((action) => {
        console.log('modalAction', action);
      });
  }

  ngOnInit(){
    this.lists$ = this.listsService.getLists();
    this.lists$.subscribe(lists => console.log(lists));
  }
}

Modal.sevice.ts

import { DOCUMENT } from '@angular/common';
import {
  ComponentFactoryResolver,
  Inject,
  Injectable,
  Injector,
  TemplateRef,
} from '@angular/core';
import { Subject } from 'rxjs';
import { ModalComponent } from 'src/app/modal/modal.component';

@Injectable()
export class ModalService {
  private modalNotifier?: Subject<string>;
  constructor(
    private resolver: ComponentFactoryResolver,
    private injector: Injector,
    @Inject(DOCUMENT) private document: Document
  ) {}

  open(content: TemplateRef<any>, options?: { size?: string; title?: string; data?: any }) {
    const modalComponentFactory = this.resolver.resolveComponentFactory(ModalComponent);
    const contentViewRef = content.createEmbeddedView(null);
    const modalComponent = modalComponentFactory.create(this.injector, [contentViewRef.rootNodes]);

    modalComponent.instance.size = options?.size;
    modalComponent.instance.title = options?.title;
    modalComponent.instance.data = options?.data; // Pass lists$ as a property of the ModalComponent
    modalComponent.instance.closeEvent.subscribe(() => this.closeModal());
    modalComponent.instance.submitEvent.subscribe(() => this.submitModal());

    modalComponent.hostView.detectChanges();

    this.document.body.appendChild(modalComponent.location.nativeElement);
    this.modalNotifier = new Subject();
    return this.modalNotifier?.asObservable();
  }

  closeModal() {
    this.modalNotifier?.complete();
  }

  submitModal() {
    this.modalNotifier?.next('confirm');
    this.closeModal();
  }
}

Modal.component.ts

import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
} from '@angular/core';
import { Observable } from 'rxjs';
import { CardListDto } from 'src/Dtos/CardListDto';

@Component({
  selector: 'modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css'],
})
export class ModalComponent{
  @Input() size? = 'md';
  @Input() title? = 'Modal title';
  @Input() data: any;
  

  @Output() closeEvent = new EventEmitter();
  @Output() submitEvent = new EventEmitter();

  constructor(private elementRef: ElementRef) {}

  ngOnInit(){
    (<Observable<CardListDto[]>>this.data).subscribe(
      data => {
        console.log(data)
      }
        
    )
  }

  close(): void {
    this.elementRef.nativeElement.remove();
    this.closeEvent.emit();
  }

  submit(): void {
    this.elementRef.nativeElement.remove();
    this.submitEvent.emit();
  }
}

**MODAL.COMPOMENT.html**MODAL.COMPOMENT.html **

<div class="modal {{ size }}">
  <div class="modal-header">
    {{ title }}
    <span class="modal-close" (click)="close()">✕</span>
  </div>
  <div class="modal-content">
    <ng-content></ng-content>
  </div>
  <div class="modal-footer">
    <button (click)="submit()">Submit</button>
  </div>
</div>

<div class="modal-backdrop" (click)="close()"></div>

推荐答案

在Angular 17中,而不是使用ComponentFactoryResolver,而是使用ViewContainerRef,这需要作为组件的参数传递.那么下面的代码块,将帮助您实现您想要的!

...
const contentViewRef = vcr.createEmbeddedView(
  content,
  { lists: options!.data }
  // {
  //   injector: this.injector,
  // }
);
const modalComponent = vcr.createComponent(ModalComponent, {
  projectableNodes: [contentViewRef.rootNodes],
  // environmentInjector: this.envInjector,
  // injector: this.injector,
});
modalComponent.setInput('size', options?.size);
modalComponent.setInput('title', options?.title);
modalComponent.setInput('data', options?.data);
...

我们可以将第二个参数传递为context,其中我们定义属性{ lists: options!.data, },还使用setInput内置函数来设置@Input个值.

我们还可以通过注射器(环境或正常,如果需要)

在HTML端,我们必须在模板上定义一个属性来存储这个值!

...
<ng-template #createCardTemplate let-lists="lists">
...

完整代码:完整代码:

主要

import { CommonModule } from '@angular/common';
import { Component, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { CardListDto, ModalComponent } from './app/modal/modal.component';
import { Observable, of } from 'rxjs';
import { ModalService } from './app/modal.service';
import { CardComponent } from './app/card/card.component';

export interface CardDto {}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, ModalComponent, CardComponent],
  template: `
    <div class="list">
        <div class="header">
            <h3>{{list.name}}</h3>
            <span 
            style="
            display: flex; 
            width: 25%; 
            justify-content: space-between;">
                <h3>{{cards.length}}</h3>
                <button class="list-options-btn"></button>
            </span>
        </div>
        <div class="btn-container">
            <button class="card-add-btn" (click)="openModal(createCardTemplate)">Add new card</button>
        </div>
        <div class="cards-container" *ngIf="lists$ | async as lists">
            <app-card
            *ngFor="let card of cards" 
            [card]=card
            [lists]=lists
            ></app-card>
        </div>

        <ng-template #createCardTemplate let-lists="lists">
            <div>
                <label for="name">Name</label><br>
                <input id="name" formControlName="name" type="text">
            </div>
            <div>
                <label for="description">Description</label><br>
                <input id="description" formControlName="description" type="text">
            </div>
            <div>
                <label for="dueDate">Due Date</label><br>
                <input id="dueDate" formControlName="dueDate" type="date">
            </div>
            <div>
                <label for="priority">Priority</label><br>
                <select id="priority" formControlName="priority">
                    <option value="1">Low</option>
                    <option value="2">Medium</option>
                    <option value="3">High</option>
                </select>
            </div>
            <div>
                <label for="listId">List</label><br>
                <select id="listId" formControlName="listId">
                    <option *ngFor="let list of lists | async" [value]="list.id">
                        {{ list.name }}
                    </option>
                </select>
            </div>
        </ng-template>
    </div>
  `,
})
export class App {
  @Input()
  list: CardListDto = new CardListDto();
  @Input()
  cards: CardDto[] = [];
  lists$: Observable<CardListDto[]> | null = of([
    { id: 1, name: 'one' },
    { id: 2, name: 'two' },
    { id: 3, name: 'three' },
  ]);

  constructor(
    private vcr: ViewContainerRef,
    private modalService: ModalService
  ) {}

  openModal(modalTemplate: TemplateRef<any>) {
    this.modalService
      .open(this.vcr, modalTemplate, { title: 'New Card', data: this.lists$ })
      .subscribe((action) => {
        console.log('modalAction', action);
      });
  }

  ngOnInit() {
    // this.lists$ = this.listsService.getLists();
    // this.lists$!.subscribe((lists) => console.log(lists));
  }
}

bootstrapApplication(App);

模态测试

import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
} from '@angular/core';
import { Observable } from 'rxjs';
export class CardListDto {
  name!: string;
}
@Component({
  selector: 'app-modal',
  standalone: true,
  imports: [],
  templateUrl: './modal.component.html',
  styleUrl: './modal.component.css',
})
export class ModalComponent {
  @Input() size? = 'md';
  @Input() title? = 'Modal title';
  @Input() data: any;

  @Output() closeEvent = new EventEmitter();
  @Output() submitEvent = new EventEmitter();

  constructor(private elementRef: ElementRef) {}

  ngOnInit() {
    (<Observable<CardListDto[]>>this.data).subscribe((data) => {
      console.log(data);
    });
  }

  close(): void {
    this.elementRef.nativeElement.remove();
    this.closeEvent.emit();
  }

  submit(): void {
    this.elementRef.nativeElement.remove();
    this.submitEvent.emit();
  }
}

模态 HTML

<div class="modal {{ size }}">
  <div class="modal-header">
    {{ title }}
    <span class="modal-close" (click)="close()">✕</span>
  </div>
  <div class="modal-content">
    <ng-content></ng-content>
  </div>
  <div class="modal-footer">
    <button (click)="submit()">Submit</button>
  </div>
</div>

<div class="modal-backdrop" (click)="close()"></div>

模态服务

import { DOCUMENT } from '@angular/common';
import {
  EnvironmentInjector,
  Inject,
  Injectable,
  Injector,
  TemplateRef,
  ViewContainerRef,
} from '@angular/core';
import { Subject, of } from 'rxjs';
import { ModalComponent } from './modal/modal.component';

@Injectable({
  providedIn: 'root',
})
export class ModalService {
  private modalNotifier?: Subject<string>;
  constructor(
    private envInjector: EnvironmentInjector,
    private injector: Injector,
    @Inject(DOCUMENT) private document: Document
  ) {}

  open(
    vcr: ViewContainerRef,
    content: TemplateRef<any>,
    options?: { size?: string; title?: string; data?: any }
  ) {
    const contentViewRef = vcr.createEmbeddedView(
      content,
      { lists: options!.data }
      // {
      //   injector: this.injector,
      // }
    );
    const modalComponent = vcr.createComponent(ModalComponent, {
      projectableNodes: [contentViewRef.rootNodes],
      // environmentInjector: this.envInjector,
      // injector: this.injector,
    });
    modalComponent.setInput('size', options?.size);
    modalComponent.setInput('title', options?.title);
    modalComponent.setInput('data', options?.data);
    modalComponent.instance.closeEvent.subscribe(() => this.closeModal());
    modalComponent.instance.submitEvent.subscribe(() => this.submitModal());

    modalComponent.hostView.detectChanges();

    this.document.body.appendChild(modalComponent.location.nativeElement);
    this.modalNotifier = new Subject();
    return this.modalNotifier?.asObservable();
  }

  closeModal() {
    this.modalNotifier?.complete();
  }

  submitModal() {
    this.modalNotifier?.next('confirm');
    this.closeModal();
  }
}

Stackblitz Demo

Angular相关问答推荐

Angular - Bootstrap 5 Carbon不工作

导入浏览器模块时出错已导入commonModule的insted,animationBrower也显示错误,当p打开时

如何在每次Angular 编译之前执行脚本(esbuild)

如何在HTML外部项目中使用Web组件(以17角创建)

带逻辑的组件decorator 中的Angular 主机侦听器属性

如何使用新的@for遍历Angular 为17的对象?

Angular 16-错误NG8002:无法绑定到NGModel,因为它不是输入的已知属性

Angular 17多内容投影错误

当包含react 性模板绑定时,NG-Bootstrap手风琴不会打开

如何在 Angular RxJS 中替换订阅中的 subscrib?

Angular 为什么延迟加载返回空路径?

Angular Signals 如何影响 RXJS Observables 以及在这种情况下变化检测会发生什么

IonicStorageModule似乎不是 NgModule 类

升级到 Webpack 5.79.0 后 NX Angular 项目构建失败

如何在 ngFor 循环中创建变量?

Angular-CLI 代理到后端不起作用

Angular为 4 的可扩展表格行,带有Angularmaterial

angular2 测试,我如何模拟子组件

RxJS - 发生错误时观察到的不会完成

ng-template 上的 *ngFor 不输出任何内容