1
votes

I have a component which toggles the component's template based on client device size. Component code is:

import {Component} from '@angular/core';
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';

@Component({
    selector: 'ui-switcher',
    template: `
        <ng-content *ngIf="isSmall" select="mobile"></ng-content>
        <ng-content *ngIf="!isSmall" select="web"></ng-content>
`
})
export class UiSwitcherComponent {
    public isSmall: boolean;

    constructor(breakpointObserver: BreakpointObserver) {
        breakpointObserver.observe([Breakpoints.Small, Breakpoints.XSmall]).subscribe(result => {
            this.isSmall = result.matches;
        });
    }    
}

I use it like this:

<ui-switcher>
    <web>
        <!-- some commented details -->
        <input class="form-control mr-2" #searchInput 
        type="text" (keyup)="this.search(searchInput.value)">
    </web>

    <mobile>
        <!-- some commented details -->
        <input class="form-control" #searchInput 
        type="text" (keyup)="this.search(searchInput.value)">
    </mobile>
</ui-switcher>

In the mobile size, everything works correctly but in desktop size the value passed to search(value) function is always an empty string.

When I debug the app, it seems that #searchInput templateref is not working correctly (value of the element it refers to is always empty).

Why templateref doesn't work correctly?

1
I don't believe you can have two elements with the same template ref name, the angular template compiler will always take the first element and ignore the 2ndAviad P.
But now it seems that the first is skipped, because in mobile everything works correctlyFartab
I don't know how it should behave, this is undocumented behavior at best...Aviad P.

1 Answers

7
votes

In angular template reference variables should be unique per view.

Views can be two types View and EmbeddedView. Templates, that we write within structural directives(inside ng-template tag or *ngFor), represent embedded views. This way we can have the same name of template reference variable in different ng-templates.

For an example see

Let's imagine we have AppComponent and wrote in template:

<ui-switcher>
    <web>
        <!-- some commented details -->
        <input class="form-control mr-2" #searchInput 
        type="text" (keyup)="this.search(searchInput.value)">
    </web>

    <mobile>
        <!-- some commented details -->
        <input class="form-control" #searchInput 
        type="text" (keyup)="this.search(searchInput.value)">
    </mobile>
</ui-switcher>

Angular treats it as one AppComponentView because there is no any structural directives in this template. Both inputs belong to the same view.

Now when Angular compiler parses this template it creates one ViewBuilder per view with refNodeIndices property:

private refNodeIndices: {[refName: string]: number} = Object.create(null);

that holds all references in current template.

Let's reproduce your case: enter image description here

We can see that second template reference variable overrides previous.

And as result Angular handles click event on the same element:

enter image description here