IT TIP

Angular 2의 동적 템플릿 URL

itqueen 2020. 11. 5. 20:00
반응형

Angular 2의 동적 템플릿 URL


저는 지난 며칠 동안 Angular 2를 가지고 놀았고 데코레이터에 다이내믹 templateUrl제공 할 수 있는지 궁금했습니다 @View.

함수를 전달하고 문자열을 반환하려고 시도했지만 전체 함수가 문자열로 바뀝니다.

나는 전에 Angular 1.x를 실제로 사용하지 않았으므로 잘못된 방식으로 진행 중인지 모르겠지만 이것이 가능합니까? 아니면 동적 뷰를 만드는 더 좋은 방법이 있습니까?

예를 들어 사용자가 로그인하지 않은 경우 양식을 표시하고 로그인 한 경우 텍스트 메시지를 표시 할 수 있습니다.

다음과 같은 것이 작동하지 않습니다.

@Component({
  selector: 'my-component'
})
@View({
  // This doesn't work
  templateUrl: function() {
    return this.isLoggedIn ? 'logged-in.html' : 'logged-out.html';
  }
})
class MyComponent {
  constructor() {
    this.loggedIn = false;
  }
}

어떤 도움을 주시면 감사하겠습니다.


가장 우아한 솔루션은 아니지만 DynamicComponentLoader 및 ElementRef를 사용하여 템플릿 값을 구성 요소에 동적으로 할당했습니다. 사실 저는 여러 사용자 지정 구성 요소를 자리 표시 자에 추가 할 수있는 솔루션을 찾고있었습니다.

shmck에서 설명한대로 함수에 서비스 주입을 시도했습니다. 템플릿 함수가 호출 될 때 아직 서비스를 사용할 수 없기 때문에 작동하지 않습니다. 실제로 thisWindow 개체를 참조합니다.

내가 사용한 솔루션에 대한 참조 URL은 다음에서 찾을 수 있습니다. Angular2에서 ComponentResolver 및 ngFor를 사용하여 동적 anchorName / Components 생성

이 방법을 Plnkr1Plnkr2라고도 합니다.

Dartdocs 사이트는 TypeScript 에도 적용 할 수있는 Angular 2 DynamicComponentLoader 클래스에 대한 멋진 문서를 제공합니다.

요컨대 :

사용할 템플릿으로 간단한 구성 요소

@Component({
  selector: 'dt2-simple-block',
  properties: ["idx"],
  template: `<h1>Simple block for  {{ idx }} </h1>`,
  directives: []
})
class dt2SimpleBlock {
  constructor() {
  }
}

추가 할 모든 구성 요소를 보유하는 구성 요소의 생성자 (내 앱에는 여러 하위 항목이 포함되어야합니다.

 constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {

  //iterate
  for (var i = 0; i < toSomething; i++) {
      // build the template
      var blockdirective = 'dt2-simple-block'
      var template = '<' + blockdirective + 
                     ' idx="' + this.userBlocks.userHomePanelBlocks[i] +
                     '"></' + blockdirective + '>';
      console.log(template);   // debugging purpose
      var directives = [dt2SimpleBlock];
        loader.loadNextToLocation(toComponent(template, directives), elementRef);
    }

그리고 어딘가에 util로 넣을 도우미 함수

function toComponent(template, directives = []) {
  @Component({ selector: 'fake-component' })
  @View({ template, directives })
  class FakeComponent { }

  return FakeComponent;
}

내 솔루션 :

Angular 2.0 ViewResolver 클래스

class myViewResolver extends ViewResolver{
    resolve(component: Type): ViewMetadata {        
        var view =  super.resolve(component);
        // TODO: Write logic here:-)
        view.templateUrl = 'app/app.html';
        return view;
    }
}
bootstrap(App,[
    provide(ViewResolver , {useClass:myViewResolver})
]);

요청한 내용은 아니지만 다음과 같이 언급 할 가치가 있습니다.

대부분의 사용 사례에서 작동하는 또 다른 간단한 솔루션은 다음과 같이 템플릿 자체에 로직을 넣는 것입니다.

@Component({
  selector: 'my-component'
})
@View({
// Note1: Here, I use template instead of templateUrl.
// Note2: I use ES6 string interpolation + require() to embed/load the other templates, but you can do it however you like.
  template: `
    <div [ngSwitch]="loggedIn">
      <template [ngSwitchCase]="true"> ${require('./logged-in.html')} </template>
      <template ngSwitchDefault> ${require('./logged-out.html')} </template>
    </div>`
})
class MyComponent {
  constructor() {
    this.loggedIn = false;
  }
}

이 솔루션의 단점은 제공된 js 파일에 두 템플릿이 모두 포함되어 있으므로 큰 템플릿의 경우 문제가 될 수 있다는 것입니다 (하지만 실제로는 하나의 템플릿 만 렌더링되고 대부분의 경우 js 크기 오버 헤드가 허용됨).


내 솔루션 :( 이에 대한 아름다움은 html 및 css 파일에 대한 지연 로딩입니다.)

이것은 home.componenet.ts입니다.

import { Component } from '@angular/core';
import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive';
import { TranslateService, LangChangeEvent } from 'ng2-translate/ng2-translate';

@Component({
  selector: 'lib-home',
  templateUrl: './app/content/home/home.component.html',
  directives: [DynamicHTMLOutlet]
})
export class HomeComponent {
  html_template = `./app/content/home/home_`;
  html: string;
  css: string;
  constructor(translate: TranslateService) {
        this.html = this.html_template + translate.currentLang;
        this.css = './app/content/home/home.component.css';
    translate.onLangChange.subscribe((event: LangChangeEvent) => {
          this.html = this.html_template + translate.currentLang;
          this.css = './app/content/home/home.component.css';
    });
  }

 }

내가 사용하고 몇 가지 변경 한 지시문 : 이것은 home.componenet.html에 있습니다.

<dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>

다음은 동적 구성 요소에 대한 지시문입니다.

import {
  Component,
  Directive,
  ComponentFactory,
  ComponentMetadata,
  ComponentResolver,
  Input,
  ReflectiveInjector,
  ViewContainerRef,

} from '@angular/core';
import { TranslatePipe } from 'ng2-translate/ng2-translate';
declare var $:any;

export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
    const cmpClass = class DynamicComponent {};
    const decoratedCmp = Component(metadata)(cmpClass);
    return resolver.resolveComponent(decoratedCmp);
}

@Directive({
    selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
  @Input() htmlPath: string;
  @Input() cssPath: string;

  constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
  }

  ngOnChanges() {
    if (!this.htmlPath) return;
    $('dynamic-html') && $('dynamic-html').remove();
    const metadata = new ComponentMetadata({
        selector: 'dynamic-html',
        templateUrl: this.htmlPath +'.html',
        styleUrls:  [this.cssPath],
        pipes: [TranslatePipe]
    });
    createComponentFactory(this.resolver, metadata)
      .then(factory => {
        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
        this.vcRef.createComponent(factory, 0, injector, []);
      });
  }
}

@Eyal Vardi의 답변 업데이트 ( ViewResolver더 이상 사용되지 않음) :

import { Directive, Type, Component } from '@angular/core';
import { DirectiveResolver } from '@angular/compiler';

class myViewUrlResolver extends DirectiveResolver {
    resolve(type: Type<any>, throwIfNotFound?: boolean): Directive {        
        let view = <any>super.resolve(type, throwIfNotFound);
        if (typeof view["templateUrl"] !== "undefined") {
            console.log("Yay!");
            let originalUrl = (<Component>view).templateUrl;
            (<Component> view).templateUrl = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.html");
        }
        if (typeof view["styleUrls"] !== "undefined") {
            console.log("Yay2!");
            let originalUrls = (<Component>view).styleUrls;
            originalUrls.forEach((originalUrl, at) => (<Component>view).styleUrls[at] = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.css"));
        }
        return view;
    }
}

platformNativeScriptDynamic().bootstrapModule(AppModule,{ 
  providers: [
    { provide: DirectiveResolver, useClass: myViewUrlResolver } 
  ]
});

"ng serve --aot"를 사용하여 애플리케이션을 컴파일하십시오.

export let DEFAULT_PREFIX :string= './app.component';
//or localStorage.getItem('theme')
export function getMySuperTemplate(template: string) {
  return DEFAULT_PREFIX + template + '.html';
}

@Component({
  selector: 'app-root',
  templateUrl: getMySuperTemplate('2'),
  styleUrls:['./app.component.css']
})

보안 문제로 인해 Angular 2에서는 이러한 동적 템플릿 생성 방법을 사용할 수 없습니다. 안타깝게도 Angular 1에서 이전 응용 프로그램이 이러한 방식으로 동적으로 구동되었습니다.

For Angular 2 - This could be a differeny way of doing the same (link example below). By updating the template html files to be components in the application, then injecting them into (the place where you were trying to create the templateUrl with a string etc) view component template parameter as elements (using DynamicComponentLoader).

https://angular.io/docs/js/latest/api/core/DynamicComponentLoader-class.html


Hope, github example for you will help you! There are example to compile dynamic html. So, you can load HTML by any of your Service and then compile it.


1- install this library

npm i -D html-loader

============================================================

2- In webpack.config use html-loader for html files

 { test: /\.html$/,  loaders: ['html-loader']   }

============================================================

3- If you use ionic , you can copy webpack.config.js from the path "node_modules/@ionic/app-scripts/config/webpack.config.js" then add the html loader to it

=============================================================

4-If you use ionic In package.json add these lines

"config": { 
    "ionic_bundler": "webpack",
    "ionic_webpack": "webpack.config.ionic.js" 
  },

=============================================================

5-Then you can use it as below

@Component({
  selector: 'page-login',
 // templateUrl:"./login.html"

   template:     function(){
    if(globalVariables.test==2) {

      return require("./login2.html")
    }
    else
    {
      return require("./login.html")
    }
  }(),
})

======================================

6-If there is unresolved error with require function you can put it in declarations.d.ts file as the below :

declare var require: any;

참고URL : https://stackoverflow.com/questions/31692416/dynamic-template-urls-in-angular-2

반응형