通过指令 [PrimeNG] 插入复选框图标 TemplateRef

问题描述 投票:0回答:1

PrimeNG 更改了 ui 组件的图标机制。我想要我的自定义复选框图标。

触摸 html 不是一个选项,而是使用指令。

我想通过指令插入复选框的图标模板

我正在尝试这样的事情

<p-checkbox checkboxIcon>
  <!-- this is not an OPTION
    <ng-template pTemplate="icon">
      <custom-icon></custom-icon>
    </ng-template>
  -->
</p-checkbox>

我不知何故想要在指令中创建TemplateRef并插入它

@Directive({
  selector: '[checkboxIcon]',
})
export class IconDirective implements OnInit {
  constructor(
    private element: ElementRef,
    @Self() private checkbox: Checkbox
  ) {}

  @HostListener('click', ['$event']) onModelChange(event): void {
    this.appendIconTemplate();
  }

  appendIconTemplate(): void {
    const iconTemplate = document.createElement('ng-template');
    iconTemplate.setAttribute('pTemplate', 'icon');
    iconTemplate.innerHTML = '<custom-icon></custom-icon>';
    this.element.nativeElement.appendChild(iconTemplate);
  }
}

我有一个问题的最小重现这里

发现了带有

@Self()
ComponentFactoryResolver
的东西。但从来没有这样做过,有人可以帮忙吗?

我认为这些问题这里这里想要实现类似的目标。

angular primeng
1个回答
0
投票

因此,根据您的需求需要考虑一些事情,首先,没有简单干净的方法使用指令添加子组件,我们可以使用

TemplateRef
ViewContainerRef
但该组件将作为同级组件添加.

在我们的例子中,我们也无法使用 HostListener,因为在结构指令上使用它时,它不会具有预期的行为。

所以我们必须采取不同的方法,因为你分享的 StackBlitz 是在 Angular 17 中,我使用了信号。

首先,我们需要一个新的

Component
来封装自定义图标。

@Component({
  selector: 'appCustomIcon',
  template: `
    <custom-icon></custom-icon>
  `,
})
export class CustomIconContainer {}

然后我们可以修改指令以便能够在其嵌入视图中创建我们的

CustomIconContainer
:

@Directive({
  selector: '[checkboxIcon]',
})
export class IconDirective {
  // We will now us an input to know if the icon must be shown
  checkboxIcon = input<boolean>(false);

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) {
    // This effect will listen on the changes in our input.
    effect(() => {
      // Using the signal will mark it to be used by the effect.
      const showIcon = this.checkboxIcon();

      // We run the rest of the logic in the untracked function to avoid
      // side effects
      untracked(() => {
        if (showIcon) this.appendIconTemplate();
        else this.resetView();
      });
    });
  }

  appendIconTemplate(): void {
    this.viewContainer.clear();
    const embeddedView = this.viewContainer.createEmbeddedView(
      this.templateRef
    );
    const component = this.viewContainer.createComponent(CustomIconContainer);

    // This is the "hack" that allows us to create the component
    // as a child of the element
    embeddedView.rootNodes[0].appendChild(component.location.nativeElement);
  }

  resetView(): void {
    this.viewContainer.clear();
    this.viewContainer.createEmbeddedView(this.templateRef);
  }
}

为了避免依赖

HostListener
,我们将监听复选框本身的点击,这是新的
ChboxDemo
类:

export class ChboxDemo {
  protected showCustomIcon = false;
  // The checked boolean is used to keep the state of the checkbox
  // between the view container refreshes
  protected checked = false;

  protected onClick() {
    this.showCustomIcon = !this.showCustomIcon;
  }
}

<div class="card">
  <p-checkbox
    binary="true"
    label="label"

    (click)="onClick()"
    *checkboxIcon="showCustomIcon"
    [(ngModel)]="checked"
  >
  </p-checkbox>
</div>

还有一个小问题,图标无法显示,为了解决这个问题,我简单地为 svg 元素添加了一个宽度:

<svg style="width: 20px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path
    d="M14.83,12,18,8.82A1,1,0,0,0,18,7.4L16.6,6a1,1,0,0,0-1.42,0L12,9.17,8.82,6A1,1,0,0,0,7.4,6L6,7.4A1,1,0,0,0,6,8.82L9.17,12,6,15.18A1,1,0,0,0,6,16.6L7.4,18a1,1,0,0,0,1.42,0L12,14.83,15.18,18a1,1,0,0,0,1.42,0L18,16.6a1,1,0,0,0,0-1.42Z"
  />
</svg>

这是您的分叉 StackBlitz 及其工作解决方案:

https://stackblitz.com/edit/btpn8a-as1ppw?file=src%2Fapp%2Fdemo%2Fchbox-demo.html

我希望这对您有所帮助,如果有任何其他问题,我可以回答。

© www.soinside.com 2019 - 2024. All rights reserved.