3

Right now, I have dead simple use case of the inheritance, but for the life of me, I cannot get it to work.

The use case

My use case is thus:

  • I have BaseComponent that has some LoaderComponent. It has methods showLoader() and hideLoader() for showing it and hiding it, respectively.
  • Every loadable component, or even every component, extends this BaseComponent. Then, when there is network request, or we need to wait for a lot of stuff to render, we can just showLoader(), and then hideLoader() when the work is done.

Implementation

My implementation of this is pretty straightforward, but for some obscure reason, it's not working.

base.component.ts

//...
export class BaseComponent implements AfterViewInit {
  @ViewChild(LoaderComponent) loader: LoaderComponent;

  constructor() {}

  ngAfterViewInit() {
    // caveman debugging, I know. Even worse is that I found myself placing breakpoint on this line of caveman debugging. SAD!
    console.log(this.loader);
  }

  showLoader() {
    this.loader.show();
  }

  hideLoader() {
    this.loader.hide();
  }
}

base.component.html

I use transclusion here, of course.

<ng-content> </ng-content>
<app-loader #loader></app-loader>

contact.component.ts

//...
@AutoUnsubscribe
export class ContactComponent extends BaseComponent implements OnInit {

constructor(
    private senderService: SenderService,
    private messageSentService: MessageSentService,
    private router: Router
  ) {
    super();
    // setup logic here...
  }

  ngOnInit() {}

  sendEmail() // or whatever...
  {
     this.showLoader();
     // send the email
     // do the request
    this.emailSender = this.senderService.send(this.emailMessage);
    this.emailSender.subscribe((res) => {
      this.hideLoader();
      // handling the request here....
  }

  //...
}

contact.component.html

<app-base>
  <!-- the contact page -->
</app-base>

When I fire this up, what I see, in the developer console, is :

enter image description here

.

When I place my breakpoint on the caveman debugging in BaseComponent.prototype.ngAfterViewInit(), it hit twice. On the first instance, I get this.constructor.name === "BaseComponent". However, on the second one, is the derived class : this.constructor.name === "ContactComponent".

The state of the decorated view child field loader isn't being passed down!!

How do I fix this, and without resorting to some bullshit design like making the derived class has-a base class?

7
  • contact.component.html doesn't have an <app-loader> as far as I can tell. Commented Jul 11, 2020 at 6:35
  • But contact component is-a base component, which has-a loader component. How do I say that in Angular? Commented Jul 11, 2020 at 6:38
  • A component cannot have two templates. I don't know how that would work exactly. Inheritance of decorators/component metadata is a bit wonky as it is but, regardless, a component cannot logically have multiple templates. How would they compose? Commented Jul 11, 2020 at 6:39
  • 3
    There are many ways to be DRY (something I strongly encourage) without inheritance. That said, you should consider that a) it doesn't make sense for a derived component to inherit its super's template and specify its own template, and b) Angular is a terrible framework for DRY. Angular is absolutely stuffed to the gills with requirements to write redundant boilerplate and often straight-up duplicate code. Commented Jul 11, 2020 at 6:52
  • 1
    Man, I wish I was writing in React+MobX now! At my last day job, we developed a system for this type of stuff! Commented Jul 11, 2020 at 6:54

1 Answer 1

6

Problem

From What I understand ur question is you are not only using just component inheritance but template composition as well

contact.component.html

<app-base>
  <!-- the contact page -->
</app-base>

Explanation

Thing is You can not use Parent Template while inherit from it. a component has only one template.

in contact.component.html <app-base> means an instance of base-component as part of ur template. so ur child component template is composed of base not inherit from it .

bcs Child Component template has not loader it it so its null.

Either use compose or inheritance

Simple Solution

while using component inheritance you have to copy all ur base template to child template

contact.component.html

<app-loader></app-loader>

Advance Solution

We can achieve almost what u want with composite pattern

start with just an interface

export interface ILoader{
  showLoading ():void
  hideLoading ():void
}

create a app-loader wrapper that implement ILoader

@Component({
  selector: 'app-loader-wrapper',
  template: `<ng-content> </ng-content>
            <app-loader></app-loader>`,
  styleUrls: ['./loader-wrapper.component.css']
})
export class LoaderWrapperComponent implements ILoader {

 @ViewChild(Loader) loader: Loader 
  showLoading (){
    this.loader.showLoading()
  }
  hideLoading (){
    this.loader.hideLoading()
  }
}

Create a baseComponent Class it will be base of all ur component which need loading note it has no template

class BaseComponent implements ILoader{
  @ViewChild(LoaderWrapperComponent) loader: LoaderWrapperComponent
  showLoading (){
    this.loader.showLoading();
  }
  hideLoading (){
    this.loader.hideLoading();
  }
}

now create components and inherit from it as use composition in template

@Component({
  selector: 'my-app',
  template: `<app-loader-wrapper>
            // rest of ur component 
              </app-loader-wrapper>`,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent extends BaseComponent {
  
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for this answer! I don't like the copy-and-paste implementation of showLoader(),hideLoader() methods, but I'll accept it!
why do you have implementation at two places. LoaderWrapperComponent & BaseComponent ??
How can we use @Input & @Output decorators in the base component
@Saurabh47g yes it can be used

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.