Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve View Encapsulation issue in Angular8?

I have parent component and child component. Child component created as a modal component. So i have included child component selector inside parent component and i have set view encapsulation is none so that it will take parent component css and all and it's working also but parent component has #paper id applying some third party(rappidjs) libary css(for SVG diagramming). Same like child component has #dataMapper id. but here the thirdparty css is not taking because child component set to 'encapsulation: ViewEncapsulation.None'. If i will remove encapsulation: ViewEncapsulation.None it's working but modal is not working. all the modals loading instead of onclick. How to solve this issue please advice me someone.

Coding:

Parent Component TS

@Component({
  selector: 'app-data-model',
  templateUrl: './data-model.component.html',
  styleUrls: ['./data-model.component.css']
})

export class DataModelComponent implements OnInit{

// Modal

openModal(id: string) {
  this.modalApp = true;
  this.modalService.open(id);
}

closeModal(id: string) {
  this.modalService.close(id);
}

Parent Component HTML

<div id="toolbar">
    <div class="tool-bar-section">
    <button class="btn" (click)="openModal('custom-modal-1');"><i class="fa fa-file-excel-o" style="font-size: 24px"></i></button>     
    </div>   
</div>
<div id="paper"> </div> ( this dom taking thirdparty css)

<app-modal id="custom-modal-1">           
    <h1 class="head-bar">Data Model - Import Excel  <a href="#" class="close-icon" (click)="closeModal('custom-modal-1');"><i class="fa fa-times-circle-o" aria-hidden="true"></i></a></h1>
    <div class="modal-content-section">
     <ul>
         <li><a href="#" (click)="closeModal('custom-modal-1');openModal('custom-modal-2');">Create New Schema</a></li>
         <li><a href="#" (click)="closeModal('custom-modal-1');openModal('custom-modal-3');">Import Data to existing schema</a></li>
     </ul>       
    </div>        
</app-modal>
<app-modal id="custom-modal-2">       
    <h1 class="head-bar">Data Model - Import Excel  <a href="#" class="close-icon" (click)="closeModal('custom-modal-2');"><i class="fa fa-times-circle-o" aria-hidden="true"></i></a></h1>
    <div class="modal-content-section">
        <div id="dataMapper"></div>       ( this dom is not taking thirdparty css)
        <p><a href="#" (click)="closeModal('custom-modal-2');openModal('custom-modal-4');">Data Mapping</a></p>
    </div>
</app-modal>

Child Component HTML

<div class="app-modal">
    <div class="app-modal-body">
        <ng-content></ng-content>
    </div>
</div>
<div class="app-modal-background"></div>

Child Component Ts

@Component({
  selector: 'app-modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css'],
  encapsulation: ViewEncapsulation.None
})
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 {
      // 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', el => {
          if (el.target.className === 'app-modal') {
              this.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 component is destroyed
  ngOnDestroy(): void {
      this.modalService.remove(this.id);
      this.element.remove();
  }

  // open modal
  open(): void {
      this.element.style.display = 'block';
      document.body.classList.add('app-modal-open');
  }

  // close modal
  close(): void {
      this.element.style.display = 'none';
      document.body.classList.remove('app-modal-open');
  }

}

Modal Service Code

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

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

  private modals: any[] = [];

  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
      const modal = this.modals.find(x => x.id === id);
      modal.open();
  }

  close(id: string) {
      // close modal specified by id
      const modal = this.modals.find(x => x.id === id);
      modal.close();
  }
}

Please let me know if more details required.

like image 734
bagya Avatar asked Mar 03 '20 10:03

bagya


People also ask

What is view encapsulation in Angular?

ViewEncapsulation.Emulated. Angular modifies the component's CSS selectors so that they are only applied to the component's view and do not affect other elements in the application, emulating Shadow DOM behavior.

What is view encapsulation and how many ways are there do to do it in Angular?

View Encapsulation in Angular defines how the styles defined in the template affect the other parts of the application. The angular uses three strategies while rendering the view ViewEncapsulation. Emulated , ViewEncapsulation. ShadowDOM and ViewEncapsulation.


1 Answers

Because of your modal. The modal is created on top of your app, (out of body tag if you look for the modal wrapper by inspecting it.)

It is the Modal cause the dom structure not in sync with ng component structure

So why the modal works with ng Encapsulation? Because your modal service can handle it.

But the 3rd party CSS file is not a part of your angular compile config (i.e. only sass, scss works). So it does ignore any .css even you import it properly.

To solve it, you can apply the .css globally. Or if you are afraid of that may overwriting other global styles, you can rename the css file to '_somefile.scss' (pay attention to the dash '_', it is important.)

Then under your project global style.scss file > create a selector matches your modal wrapper > under the selector: add a line that @import somefile (don't add extension)

style.scss

.modal-wrapper {
    @import somepath/somefile
}
like image 77
Vincent-cm Avatar answered Nov 14 '22 23:11

Vincent-cm