Как отловить потерю фокуса с компонента и изменить значение при выборе не только в компоненте, а и в привязке?

Есть компонент улучшенный dropbox и он выводит данные и потом я хочу передать выбранное значение вверх и при потере фокуса скрыть выпадающий список. Вот код

import { Component, Input, HostListener } from '@angular/core';

@Component({
  selector: 'app-dropbox-comp',
  templateUrl: './dropbox.component.html',
  styleUrls: ['./dropbox.component.scss']
})
export class DropboxComponent{
  @Input() selected: object;
  @Input() array: [];

  public isOpen = false;

  constructor() { }

  toggle(){
    this.isOpen = !this.isOpen;
  }

  change_value(item){
    this.selected = item;
  }

  @HostListener("blur") //Не работает
  lostfocus(){
    this.isOpen = false;
  }

}

Разметка

<div class="dropdown selectDropdown" [ngClass]="{'open': isOpen}" (click)="toggle()" (blur)="lostfocus()">
  <ul (blur)="lostfocus()">
    <li *ngFor="let item of array; let i = index" (click)="change_value(item)"><a>{{item.name}}</a></li>
  </ul>
  <span>{{selected['name']}}</span>
</div>

подключение

    <app-dropbox-comp [array]="store['sortCarsParameters']['data']"  
 [selected]="store['sortCarsParameters']['selected']"></app-dropbox-comp>

Вопрос:
1) Если я уйду с компонента - я не могу его "закрыть"
2) Выбранное значение остаётся выбрано внутри компонента, но не выходит наверх по типу двойного связывания и [selected]="store['sortCarsParameters']['selected']" остаётся тем же. Как изменить это значение?


Ответы (1 шт):

Автор решения: Yugofx

Во-первых, @HostListener не отработает на компоненте, который не может получить фокус. Поэтому и на div и на ul он не сработает. Можете на желаемый элемент установить tabIndex=0 в html, или повесить его сразу на хоста. Внизу приведу пример с установкой атрибута на компонент.

Во-вторых, двойное связывание предполагает исходящий ивент. У вас компонент только принимает значения с помощью директивы @Input() и квадратных скобок в темплейте.

Поэтому добавляем в компонент @Output.

import { Attribute, Component, Input, HostListener, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-dropbox-comp',
  templateUrl: './dropbox.component.html',
  styleUrls: ['./dropbox.component.scss']
})
export class DropboxComponent{
  @Input() selected: object;
  @Input() array: [];

  @Output() selectedChange = new EventEmitter<object>();

  public isOpen = false;

  constructor(@Attribute('tabindex') public tabIndex: number = 0) {}

  toggle(){
    this.isOpen = !this.isOpen;
  }

  change_value(item){
    this.selectedChange.emit(item);
  }

  @HostListener("blur") //Не работает
  lostfocus(){
    this.isOpen = false;
  }

}

В подключении можно сделать теперь двухсторонний биндинг [(...)].

<app-dropbox-comp [array]="store.sortCarsParameters.data" 
                  [(selected)]="store.sortCarsParameters.selected">
</app-dropbox-comp>

Или с разделением биндингов:

<app-dropbox-comp [array]="store.sortCarsParameters.data" 
                  [selected]="store.sortCarsParameters.selected"
                  (selectedChange)="updateSelected(/*здесь обрабатывайте изменение данных*/)">
</app-dropbox-comp>
→ Ссылка