【问题标题】:How to make a Modal reusable in Angular 2?如何使模态在 Angular 2 中可重用?
【发布时间】:2017-11-05 17:45:32
【问题描述】:

在编程时,可重用性非常重要,我们可以做任何减少代码重复的事情。

我必须在我的 Angular 2 项目中的许多地方使用模态弹出窗口向用户显示信息。我正在使用ng-bootstrap,所有这些模态框都具有相同的页眉和页脚,但在许多情况下正文会发生变化。有时,body 只是想替换一个占位符,而在其他时候,准备动态内容有一些复杂性。这些由不同的组件触发或管理。

ng-bootstrap 允许我们以两种方式将内容传递到 Modal。

  1. 作为模板。这里将整个 Modal html 包装在 <ng-template></ng-template>
  2. 作为一个组件

使用第一种方法,我必须为每个模式重复编写页眉、正文和页脚。

使用第二种方法,我可以将 HTML 包装在一个组件中,但需要放置占位符以使其动态化。那么我可以按如下方式传递值

  open() {
    const modalRef = this.modalService.open(NgbdModalContent);
    modalRef.componentInstance.name = 'World';
  } 

但灵活性仍然有限。

我想要实现的是使用 Content Projection [Transclusion] 制作一个重用的模态组件

所以,在我的 Common Modal 的主体中如下所示。我将<ng-content></ng-content> 作为Modal 主体的插槽。

  @Component({
  selector: 'common-modal',
  template: `
  <!-- Modal -->
  <div class="modal fade" id="common-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">{{title}}</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <ng-content></ng-content>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
  </div>
  `,

现在我希望我能像下面这样使用它。

<common-modal title="First Modal">
  <span>Welcome Jasnan!</span>
</common-modal>

在其他地方

<common-modal title="Second Modal">   
     //......
      <tr *ngFor="let student of pagedStudents">
        <td>
          {{student.name}}
        </td>
        <td>
          {{student.grade}}
        </td>
      </tr>
     //......
</common-modal>

我怎样才能做到这一点? ng-bootstrap 有没有办法做到这一点?感谢您帮助我解决这个问题。

【问题讨论】:

  • 我的回答 (stackoverflow.com/a/44430342/2335675) 能回答您的问题吗?
  • 到目前为止,您所看到的一切都非常好。这个实现有什么问题?你试过这个吗?
  • 您不能使用@input 值将输入传递给您的模态吗?
  • 你有没有找到一个好的解决方案。我正在尝试做同样的事情。我的问题是让主按钮工作。所有逻辑都在模态组件中,但按钮在基本模态组件中。
  • @IanHoar 希望下面的答案将来有时会对您有所帮助

标签: angular ng-bootstrap


【解决方案1】:

目前最好的解决方案是在共享模块(导出组件和服务的地方)中创建自定义可重用modal componentmodal service,以便可以在导入共享模块的任何其他模块中使用

shared.module.ts

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [
    ModalComponent,
  ],
  providers:[ 
    ModalService
   ],
  exports:[
    ModalComponent
  ]
})

modal.component.html

<div class="custom-modal">
  <div class="model-close-btn">
      <img class="close-image" src="assets/icon/png/close.png" alt="">
  </div>
  <ng-content></ng-content>
</div>

modal.component.ts

import { Component, OnInit, OnDestroy, ElementRef, Input } from '@angular/core';
import { ModalService } from '../services/modal.service';
import { element } from '@angular/core/src/render3';

@Component({
  selector: 'custom-modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.scss']
})
export class ModalComponent implements OnInit, OnDestroy {
  @Input() id: string;
  private element: any;

  constructor(private modalService: ModalService, private el: ElementRef) {
      this.element = el.nativeElement;
  }

  ngOnInit(): void {
      let modal = this;

      // ensure id attribute exists
      if (!this.id) {
          console.error('modal must have an id');
          return;
      }

      // move element to bottom of page (just before </body>) so it can be displayed above everything else
      document.body.appendChild(this.element);

      // close modal on background click
      this.element.addEventListener('click', function (e: any) {
          if (e.target.className === 'modal__overlay modal__overlay--toggle') {
              modal.close();
          }
      });

      this.element.addEventListener('click', function (e: any) {
        if (e.target.className === 'model-close-btn' || e.target.className === 'close-image' ) {
            modal.close();
        }
    });

      // add self (this modal instance) to the modal service so it's accessible from controllers
      this.modalService.add(this);
  }

  // remove self from modal service when directive is destroyed
  ngOnDestroy(): void {
      this.modalService.remove(this.id);
      this.element.remove();
  }

  // open modal
  open(): void {
    //console.log(this.element);
      this.element.style.display = 'block';

  }

  // close modal
  close(): void {
      this.element.style.display = 'none';
  }
}

modal.component.scss

/* MODAL STYLES
-------------------------------*/
:host(custom-modal) {
  /* modals are hidden by default */
  display: none;

}

.custom-modal-open {
  /* body overflow is hidden to hide main scrollbar when modal window is open */
  display: block !important;
}

.model-close-btn {
  position: fixed;
  width: 18px;
  height: 18px;
  right: 50px;
  top: 50px;
  z-index: 9999;
  background-color: #fff;
  border-radius: 50px;
  padding: 10px;
  cursor: pointer;

  img {
    width: 18px;
  }
}

modal.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ModalService {

  private modals: any[] = [];

  constructor() { }

  add(modal: any) {
    // add modal to array of active modals
    this.modals.push(modal);
  }

  remove(id: string) {
    // remove modal from array of active modals
    this.modals = this.modals.filter(x => x.id !== id);
  }

  open(id: string) {
    // open modal specified by id
    let modal: any = this.modals.filter(x => x.id === id)[0];
    modal.open();
  }

  close(id: string) {
    // close modal specified by id
    let modal: any = this.modals.filter(x => x.id === id)[0];
    modal.close();
  }

}

现在,如果您想在另一个模块的示例组件中使用该组件,请执行以下操作:

第1步:将共享模块导入您要使用custom-modal的示例模块

sample.module.ts

@NgModule({
  declarations: [
    SampleComponent,
  ],
  imports: [
    SharedModule
  ],
})

第二步:

sample.component.ts

  import { ModalService } from 'src/shared/services/modal.service';


  constructor(private modalService: ModalService){}

  //  call this function to open modal by passing modal id
  openModal(id: string) {
    this.modalService.open(id);
  }

  // just call this function to close modal by passing modal id
  closeModal(id: string) {
      this.modalService.close(id); 
  }

sample.component.html

<!-- modal popup started -->
  <custom-modal id="custom-modal-one">

   // add your any custom modal template code here and logic in sample.component.ts

  </custom-modal>
<!-- modalp popup ends -->

【讨论】:

  • @Jasnan 刚刚在此处发布了答案,以便将来对某人有所帮助。希望它能让人们的生活更美好
猜你喜欢
  • 2017-01-16
  • 1970-01-01
  • 2014-10-10
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
相关资源
最近更新 更多