Angular component Inheritance and inheriting subscriptions

折月煮酒 提交于 2020-07-08 09:31:13

问题


Let below is a component which I will be extending in various other components ti reuse some code..

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

@Component ({
   selector: 'my-app',
   template: ` <div>
      <h1>{{appTitle}}</h1>
      <div>To Tutorials Point</div>
   </div> `,
})

export class AppComponent {
   appTitle: string = 'Welcome';
   ngOnInit(){
      this.registerSomeSubscriptions();
   }
   registerSomeSubscriptions(){
      this.subscribeSomething.subscribe((data)=>{
         performSomeAction();
      })
   }


}

I can extend it like below

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

@Component ({
   selector: 'my-app',
   template: ` <div>
      <h1>{{appTitle}}</h1>
      <div>To Tutorials Point</div>
   </div> `,
})

export class ChildComponent extends AppComponent {
   
}

While I know that public members of the component will be available in the child component.

My Question

  1. Do I still have to use separate html files for both the components or is thier some of code style or problem solving technique to reuse the same html template along with some child specific implementation by some technique I am not aware of.

回答1:


The Child components can either use the same html template as the parent Component or they can have their own html files, in which case the html of the parent component will not be used.

If you want to use the parent component's html with some modification in the Child components you can reuse the parent component instead of inheriting it.

  • make your parent component as a reusable component
  • in the child/feature component's template, add the parent component using its selector
    // add something specific to child/feature component here
    <app-reusable-component></app-reusable-component>
    // add something specific to child/feature component here

The reusable component's html will be available along with the additional html that you add in the feature component's html.

  • to pass the data between the reusable component and the feature component use component interaction - Input and Output.

  • Input - Pass data from feature component to reusable component with input binding

reusable-component.ts:
export class ReusableComponent implements OnInit {
   @Input() dataIn: Observable<object>;
   ngOnInit() {
     dataIn.subscribe(data => {
       // display data
     });
   }
}

feature-component.html:
...
<app-reusable-component [dataIn]="dataToDisplay$"></app-reusable-component>
...

feature-component.ts:
export class FeatureComponent implements OnInit {
  public dataToDisplay$: Observable<object>;
  ngOnInit() {
    this.dataToDisplay$ = this.http.get(url);
  }
}
  • Output - Send data/events from reusable component to feature component
reusable-component.ts:
export class ReusableComponent implements OnInit {
   @Input() dataIn: Observable<object>;
   @Output() eventOut: EventEmitter<object> = new EventEmitter<object>();
   ngOnInit() {
     dataIn.subscribe(data => {
       // display data
     });
   }
   userInput(value) {
      this.eventOut.emit(value);
   }
}

feature-component.html:
...
<app-reusable-component [dataIn]="dataToDisplay$" (eventOut) ="handleUserData($event)"></app-reusable-component>
...

feature-component.ts:
export class FeatureComponent implements OnInit {
  public dataToDisplay$: Observable<object>;
  ngOnInit() {
    this.dataToDisplay$ = this.http.get(url);
  }
  handleUserData(value) {
    this.http.post(url, value);
  }
}

In this approach you can reuse the template of the parent component in all your child/feature components, and you will be able add additional html content in your child/feature components and pass data between them.




回答2:


If you just want to reuse the same template for child and parent components, then you just have to put that template in a html file and reference it with templateUrl. Your subscriptions will be inherited, and you'll be able to access public protected and protected variables from the child component.

Now, if you want to inherit the parent template but be able to make template modifications, this is a bit harder, because angular does not provide template inheritance.

A possible alternative is to use a templating engine to generate the html. In one of our projects, we used nunjucks to work around this limitation.

So basically, you specify your parent component's html using a nunjucks template

parent.component.html.njk

<div>

Parent template

{% block specific %}
{% endblock %}

</div>

parent.component.ts

@Component ({
   selector: 'app-parent',
   templateUrl: `parent.component.html`, //Reference compiled template (not .njk)
})
export class ParentComponent
{
    //...
    

And then you also define your child component as a nunjucks template which inherits from parent template

child.component.html.njk

{% extends '/path/to/parent/parent.component.html.njk' %}

{% block specific %}
  Child content: {{childContent}}
{%endblock %}

child.component.ts

@Component ({
   selector: 'app-child',
   templateUrl: `child.component.html`, //reference .html, not .njk
})
export class ChildComponent extends ParentComponent
{
  public childContent = "hello";

  //Access to public/protected members and methods from parent componenent
   
  

On top of that, we have a basic watch script that monitors .njk file changes and generate the corresponding html

watch.js

const nunjucks = require('nunjucks');

//Nunjuck config to use <$ and $> for tags, instead of {{ and }}, to avoid conflicting with angular
const nunjucksEnv = nunjucks.configure(
  {
    noCache: true,

    tags:
      {
        variableStart: '<$',
        variableEnd: '$>',
      }
  }
);

//Add watch loop here. Whenever a njk file changes, call the code below
nunjucks.render(njkTemplateFileThatJustChanged, {}, function (err, html)
{
  if (!err)
  {
    let compiledPath = njkTemplateFileThatJustChanged.replace('.njk', '');
    fs.writeFileSync(compiledPath, html);
  }
}



回答3:


if the template is in file, you can reuse it. @Component is a decorator, which assigns some "meta" data for a "component", so you will not inherit it from the extended class. If the template is in separate file you can use it in templateUrl: property of @Component decorator.




回答4:


You may not be able to extend htmls, but you can utilize the templates in multiple ways:

  1. Set the TemplateRef of the base template in a service, use a directive to use it later anywhere.

    A: A drawback of this is that the base component needs to be instantiated manually and will be in the memory till the container is in view. [see example - BaseOneComponent]

    B. If the template does not have too many bindings, you can initialize it in the root (AppComponent) itself.

  2. Create a common component for a placeholder BaseTwoComponent, and use <ng-content> to project the data from the consumer component [DerivedTwoComponent]. You can also create multiple placeholder for <ng-content> and use them with selectors. Check this Angular Content Projection Guide on how to use multiple slots for content projection.

Stackblitz: Template extension




回答5:


https://plnkr.co/edit/HNG0ndIErlOfVnMl?preview

Have just used the selector of the parent component in the template of the child component. Check the code I have added in app.ts.



来源:https://stackoverflow.com/questions/62424608/angular-component-inheritance-and-inheriting-subscriptions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!