1

I am a ReactJS developer and have just entered the Angular4 club. I am using basic conditional statements with a super-simple condition just like this:

<div class="app-component">

    <app-country *ngIf="condition"></app-country>         
    <app-country  *ngIf="!condition"></app-country> 
    <div>condition is [{{condition}}]</div>
    <button (click)="condition=!condition">Toggle condition</button>

</div>

This is my app.component.ts file. And the other app-country is just ng g created components and contains only <p> hello country</p>. The condition in App.component.ts toggles every time. The problem is that the app-country re-constructs all the time when condition triggers instead of re-render. Eg for the first time condition turns true from undefined then app-country will get constructed and rendered. For the second time condition turns false and app-country gets constructed and rendered. But it should get re-rendered the last constructed component.


I don't know is it an problem or this is the way Angular works. My point is does Angular has any way to resolve this in the way I wanted it to behave? Just like keys in ReactJS that tells React that this the component key and React recognize that as an id of the component instance. Like this

<AppCountry key='app-country'/>

Any help will be appreciated. Thanks

shubham
  • 1,289
  • 2
  • 12
  • 26
  • To reuse the `app-country` component you typically pass an input property though bindings. In you example you will destroy the component and render a new one because you have two instances declared in the template. – cgatian Dec 30 '17 at 14:10
  • You can store the value in a shared service that you inject to the component, this way it doesn't matter when the component is re-created, otherwise see https://stackoverflow.com/questions/35578083/what-is-the-equivalent-of-ngshow-and-nghide-in-angular/35578093#35578093 – Günter Zöchbauer Dec 30 '17 at 14:28
  • Considering that you have 2 identical components, only one of which is rendered at time, component reinstantiation is the expected behaviour. If you don't need that, you don't need ngIf as well and possibly have XY problem. What is the objective? – Estus Flask Dec 30 '17 at 17:41

2 Answers2

3

You can use hidden attribute instead of *ngIf and change your code to :

<div class="app-component">

    <app-country [hidden]="condition"></app-country>         
    <app-country  [hidden]="!condition"></app-country> 
    <div>condition is [{{condition}}]</div>
    <button (click)="condition=!condition">Toggle condition</button>

</div>

This attribute will help you to hide and show your dom without triggering the constructor

Vala Khosravi
  • 2,352
  • 3
  • 22
  • 49
1

The Vala Khosravi answer is correct, but just to explain it for you.

NgIf directive is so called structural directive (like ngFor) which means that it changes the template. In ngIf case, it removes component from template or adds it to the templated (based on the condition). If you remove component from template in angular, you destroy it.

So you can either change its visibility as Vala Khosravi said or if you just want to show the same component but with different data, you can use an Input. For example object with configuration data and just update the input based on the condition.

So if you want to for example display different name and flag based on the condition, you have two options. 1. One input, object, with name and flag property. This can be easier to work with, but if you will need to detect changes in input, angular doesn't know that properties of the object changed, because it's the same reference. Easy fix is to spread the object or create new reference in any other way.

<app-country [countryData]="countryData"></app-country>

In AppComponent:

this.countryData = {
    name: 'Ukuleleland',
    flag: 'assets/images/ukuleleland.png',
};

In AppCountryComponent: (Input needs to be imported from @angular/core)

@Input() countryData;

and in template:

<div>
    <span>country name: {{countryData.name}}</span>
    <img [src]="countryData.flag">
</div>
  1. The same thing, but with two inputs:

You know the rest.

One more thing, pet peeve of mine: <div class="app-component"> isn't necessary. You will have in template:

<app-component>
    <div class="app-component"></div>
    ...
</app-component>

If you need to style it, you can use :host() {display:block;} selector. If you need to add class to it dynamically or what ever, you can use @HostBinding.

Hope it helps, good luck with angular.

Laker
  • 1,622
  • 4
  • 20
  • 32