Angular 2+ Modals

Although this article makes some valid points in building modals it doesn’t provide a working example.

So here is the working example on Stackblitz of what is explained. some things were renamed to make better sense.

I do not agree with everything that was done. Accessing the DOM directly in DomService is not the ideal and can come up with consequences.

The correct way to access elements is by using a template variable as below

 <input type="text" #testElem>

Then get the reference of the element

 @ViewChild('testElem') el:ElementRef;

Then access your native element as below

 this.el.nativeElement.style.background = "red";

More about it here

There are more elaborate and costomizable ways to create modals. This was just an exercise in rolling your own solution.

Center any Image in div while maintaining Aspect Ratio

This is very useful to keep things under control on the page.

div {
    float:left; 
    width: 50vh;
    height: 50vh;
    position:relative;
    border: 1px solid red;
}

img {
   position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    max-width: 100%;
    max-height: 100%;
    border: 1px solid green;
}

Angular 7 Progress Indicator Component

I came across the following progress indicator for jquery and wanted to try to recreate it as an angular component.

<ol class="ProgressBar">
    <li #lis class="ProgressBar-step" *ngFor="let step of steps; let i = index">
      <svg class="ProgressBar-icon"><use attr.href="{{svgUrl}}"/></svg>

      <span class="ProgressBar-stepLabel">{{step}}</span>
    </li>
</ol>

The only modifications done to the HTML were adding a template variable #lis and iterating on the steps to be displayed.

xlink:href has been deprecated so it was replaced with attr.href and the svg was saved in a file and the path to the file is passed to the component. Styling is pretty much identical.

import { Component, OnInit } from '@angular/core';
import { ElementRef, QueryList, ViewChildren, Renderer2} from '@angular/core';
import { Input } from '@angular/core';

@Component({
  selector: 'app-progress-indicator',
  templateUrl: './progress-indicator.component.html',
  styleUrls: ['./progress-indicator.component.scss'],
})
export class ProgressIndicatorComponent {

  @Input() public steps: string[];
  @Input() public svgUrl:string;

  @ViewChildren('lis') lis: QueryList<ElementRef>;

  private elems: ElementRef<any>[];

  constructor(private renderer: Renderer2) {
  }

  advance() {
    this.elems = this.lis.toArray();
    var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
    if (count > 0) {
      let index = 0;
      for (let i = 0; i < this.elems.length; i++) {
        if (this.elems[i].nativeElement.classList.contains('is-current')) {
          index = i;
          break;
        }
      }
      if (index < this.elems.length) {
        if(index <  this.elems.length-1){ // if last one done remove current
             this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
        }
        this.renderer.addClass(this.elems[index].nativeElement, "is-complete");
      } 
      if (index +1 < this.elems.length) {
        this.renderer.addClass(this.elems[++index].nativeElement, "is-current");
      }
    } else {
      this.renderer.addClass(this.lis.first.nativeElement, "is-current");
    }
  }

  previous(){
    this.elems = this.lis.toArray();
    var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
    if (count > 0) {
      let index = 0;
      for (let i = 0; i < this.elems.length; i++) {
        if (this.elems[i].nativeElement.classList.contains('is-current')) {
          index = i;
          break;
        }
      }
      if (index >= 0) {
         this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
         this.renderer.removeClass(this.elems[index].nativeElement, "is-complete");
      }
      if(index > 0){
         this.renderer.addClass(this.elems[--index].nativeElement, "is-current");
        this.renderer.removeClass(this.elems[index].nativeElement, "is-complete")
      }
    }
  }
}

The component uses @ViewChildren to access the li tags and Renderer2 to do safe modifications to the classes of each element.

 <app-progress-indicator 
     [steps]='steps'  
     [svgUrl]='"./assets/svg/checkmark-bold.svg#checkmark-bold"'>
</app-progress-indicator>

Usage is as above steps should be an array of strings.

  public steps = ["Test","Review","Result","Mistakes"]; 

Note that QueryList is not accessible by index nor one could iterate on it for ES5 so toarray() was used to get things done. ES6 makes it possible to iterate directly.