项目作者: victorioferrario

项目描述 :
The repository is an experiment in Dynamic Component loading, and image preloading
高级语言: TypeScript
项目地址: git://github.com/victorioferrario/Angular-7-Dynamic-Component-Loading.git


Angular 7 Dynamic Image Loading

  1. ng g library projects --name manx-services

The repository is an experiement in dynamically loading components into the dom. In addition, I am also using a seperate service to dynamically add to the dom a spinner control from @angular/material.

Image of Yaktocat

Overview

ScrollViewComponent

The scroll-view component is driven by a Service called ScrollViewContext. ScrollViewContext contains the data payload of images. The first properties in ScrollViewContext are height & width. This controls the overall dimensions of the component, and its children. Implementation in the components takes advantage of using the HostBinding decorator.

  1. public height: BehaviorSubject<number>;
  2. public width: BehaviorSubject<string>;

@HostBinding and @HostListener are two decorators in Angular that can be really useful in custom directives and in components. @HostBinding lets you set properties on the element or component that hosts the directive, and @HostListener lets you listen for events on the host element or component.

  1. @HostBinding('style.width')
  2. viewWidth: string;
  3. @HostBinding('style.height.px')
  4. viewHeight: number;

Next in ScrollViewContext we have an array that contains our data, and set of Observables that enable the component to set through the items array, and view the images.

  1. public items: IDataItem[];
  2. public index: BehaviorSubject<number>;
  3. public hasNext: BehaviorSubject<boolean>;
  4. public hasPrev: BehaviorSubject<boolean>;
  5. public dispatch: EventEmitter<boolean>;

The scroll-view component works off of a payload:

  1. export const dataItems: IDataItem[] = [
  2. {
  3. path: serverPath + 'banner-06-1920X306.jpg',
  4. title: 'The elegant man',
  5. description: '',
  6. height: 306,
  7. index: 0
  8. },
  9. {
  10. path: serverPath + 'banner-05-1920X306.jpg',
  11. title: 'The regal begal',
  12. description: '',
  13. height: 306,
  14. index: 1
  15. },
  16. {
  17. path: serverPath + 'banner-04-1920X306.jpg',
  18. title: 'Some Dog',
  19. description: '',
  20. height: 306,
  21. index: 2
  22. },
  23. {
  24. path: serverPath + 'banner-03-1920X306.jpg',
  25. title: 'Dogs R Us',
  26. description: '',
  27. height: 306,
  28. index: 3
  29. },
  30. {
  31. path: serverPath + 'banner-02-1920x243.jpg',
  32. title: 'Legally dog',
  33. description: '',
  34. height: 306,
  35. index: 4
  36. },
  37. {
  38. path: serverPath + 'banner-01.jpg',
  39. title: 'Pupps R Us',
  40. description: '',
  41. height: 306,
  42. index: 5
  43. }
  44. ];

The scoll-view component template iterates through the array of items, and binds it the ui.

  1. <ul class="k-scrollview">
  2. <li *ngFor="let item of this.scrollContext.items; let i = index; let c = count" [item]="item" [index]="i"></li>
  3. </ul>
  4. <!-- These are the controls to move from image to image -->
  5. <manx-scroll-nav></manx-scroll-nav>

Important

I am overriding the native li element, and in the scroll-item selector.

  1. @Component({
  2. // tslint:disable-next-line:component-selector
  3. selector: 'li',
  4. templateUrl: './scroll-item.component.html',
  5. styleUrls: ['./scroll-item.component.scss']
  6. })
  7. export class ScrollItemComponent implements OnInit {

The template for ScrollItemComponent is very simple:

  1. <ng-container> <ng-template #dynamicImage></ng-template></ng-container>

and in the ngInit method, we subscribe each individual ScrollItemComponentcomponent to ScrollViewContext services index observable.

  1. ngOnInit() {
  2. const self = this;
  3. const tx = self.index === 0 ? 0 : 101;
  4. self.transform = 'translateX(' + tx + '%)';
  5. self.viewWidth = self.scrollContext.width.value;
  6. self.viewHeight = self.scrollContext.height.value;
  7. self.scrollContext.index.subscribe((index: number) => {
  8. if (index === self.index) {
  9. self.transform = 'translateX(' + 0 + '%)';
  10. } else {
  11. self.transform =
  12. index > self.index ? 'translateX(-100%)' : 'translateX(100%)';
  13. }
  14. });
  15. }

Dynamic Loading of Components

AppContextInjector

projects/manx-services/src/lib/app-injector.service.ts

The AppContextInjector Service removes all the verbose and complicated code of the components themselves, we just need to remeber to pass in a ViewContainerRef.

Initially I created a custom directive that was populating this refrenence for me and it worked really well. The challange became when I was adding a bunch of components to the dom at the same time, I needed individual instances of ViewContainerRef.

I also wanted to make sure this service did not need a reference to the component I was going to dynamically add to the dom, so I created the following component that also allows me to send in an interface type, to define so data that component may need.

projects/manx-services/src/lib/models/type.component.ts

  1. import { Type } from '@angular/core';
  2. export class TypeComponent<T> {
  3. constructor(public component: Type<any>, public data: T) {}
  4. }

The app-injector.service is available in the ScrollViewContext, and is called by ScrollViewComponent as follows:

  1. const image = new TypeComponent<IDataItem>(DynamicImageComponent, self.item);
  2. self.scrollContext.appInjector.loadComponent(self.containerImage, image);