Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to realize website with hundreds of pages in Angular2

I am preparing SPA website containing hundreds of article-like pages (apart from eCommerce, login etc.). Every article has its own URL. I want to realize it using Angular2. The only solution I found so far is:

1. to prepare hundreds of Agular2 components, one component for every article...

...with templateUrl pointing to article markup. So I will need hundreds of components similar to:

@core.Component({
  selector: 'article-1',
  templateUrl: 'article1.html'
})
export class Article1 {}

2. to display an article using AsyncRoute

see Lazy Loading of Route Components in Angular2

@core.Component({
  selector: 'article-wrapper',
  template: '<router-outlet></router-outlet>'
})
@router.RouteConfig([
  new router.AsyncRoute({
    path: '/article/:id',
    loader: () => {
      switch (id) {
        case 1: return Article1;
        case 2: return Article2;
          //... repeat it hundreds of times
      }
    },
    name: 'article'
  })
])
class ArticleWrapper { }

In Angular1 there was ngInclude directive, which is missing in Angular2 due to the security issues (see here).

[Edit 1] There is not only problem with the code itself. Problem is also with static nature of this solution. If I need website with sitemap and dynamic page structure - adding a single page needs recompilation of the whole ES6 JavaScript module.

[Edit 2] The concept "markup x html as data" (where markup is not only static HTML but also HTML with active components) is basic concept of whole web (every CMS has its markup data in database). If there does not exist Angular2 solution for it, it denies this basic concept. I believe that there must exist some trick.

like image 422
Pavel Zika Avatar asked Mar 15 '16 10:03

Pavel Zika


1 Answers

All following solutions are tricky. Official Angular team support issue is here.

Thanks to @EricMartinez for pointing me to @alexpods solution:

this.laoder.loadIntoLocation(
  toComponent(template, directives), 
  this.elementRef,
  'container'
);

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

  return FakeComponent;
}

And another similar (from @jpleclerc):

@RouteConfig([
  new AsyncRoute({
    path: '/article/:id',
    component: ArticleComponent,
    name: 'article'
  })
])
...

@Component({ selector: 'base-article', template: '<div id="here"></div>', ... })
class ArticleComponent {
    public constructor(private params: RouteParams, private loader: DynamicComponentLoader, private injector: Injector){

    }

    ngOnInit() {
      var id = this.params.get('id');
      @Component({ selector: 'article-' + id, templateUrl: 'article-' + id + '.html' })
      class ArticleFakeComponent{}

      this.loader.loadAsRoot(
          ArticleFakeComponent, 
          '#here'
          injector
      );
    }
}

A bit different (from @peter-svintsitskyi):

// Faking class declaration by creating new instance each time I need.
        var component = new (<Type>Function)();
        var annotations = [
            new Component({
                selector: "foo"
            }),
            new View({
                template: text,
                directives: [WordDirective]
            })
        ];

        // I know this will not work everywhere
        Reflect.defineMetadata("annotations", annotations, component);

        // compile the component
        this.compiler.compileInHost(<Type>component).then((protoViewRef: ProtoViewRef) => {
            this.viewContainer.createHostView(protoViewRef);
        });
like image 118
Pavel Zika Avatar answered Nov 03 '22 09:11

Pavel Zika