Como posso fechar uma lista suspensa ao clicar fora?

Gostaria de fechar meu menu suspenso de login quando o usuário clicar em qualquer lugar fora desse menu suspenso e gostaria de fazer isso com o Angular2 e com a "abordagem" do Angular2 ...

Eu implementei uma solução, mas realmente não me sinto confiante com ela. Eu acho que deve haver uma maneira mais fácil de obter o mesmo resultado, então se você tiver alguma idéia ... vamos discutir :)!

Aqui está a minha implementação:

O componente suspenso:

Este é o componente do meu menu suspenso:

Sempre que esse componente é definido como visível (por exemplo: quando o usuário clica em um botão para exibi-lo), ele assina um assunto rxjs "global"menu do usuário armazenado dentro doAssuntosServiço.E toda vez que está oculto, cancela a inscrição neste assunto.Cada clique em qualquer lugardentro o modelo deste componente aciona oonClick () método, que apenas interrompe a subida do evento até o topo (e o componente do aplicativo)

Aqui está o código

export class UserMenuComponent {

    _isVisible: boolean = false;
    _subscriptions: Subscription<any> = null;

    constructor(public subjects: SubjectsService) {
    }

    onClick(event) {
        event.stopPropagation();
    }

    set isVisible(v) {
        if( v ){
            setTimeout( () => {
this._subscriptions =  this.subjects.userMenu.subscribe((e) => {
                       this.isVisible = false;
                       })
            }, 0);
        } else {
            this._subscriptions.unsubscribe();
        }
        this._isVisible = v;
    }

    get isVisible() {
        return this._isVisible;
    }
}
O componente do aplicativo:

Por outro lado, existe o componente de aplicativo (que é o pai do componente suspenso):

Este componente captura todos os eventos de clique e emite no mesmo assunto rxjs (menu do usuário)

Aqui está o código:

export class AppComponent {

    constructor( public subjects: SubjectsService) {
        document.addEventListener('click', () => this.onClick());
    }
    onClick( ) {
        this.subjects.userMenu.next({});
    }
}
O que me incomoda:Não me sinto muito à vontade com a ideia de ter um Assunto global que atue como o conector entre esses componentes.osetTimeout: Isso é necessário porque é o que acontece caso contrário, se o usuário clicar no botão que mostra o menu suspenso:O usuário clica no botão (que não faz parte do componente suspenso) para mostrar o menu suspenso.O menu suspenso é exibido eele se inscreve imediatamente no assunto userMenu.O evento click clica no componente do aplicativo e é capturadoO componente de aplicativo emite um evento nomenu do usuário sujeitoO componente suspenso captura essa ação emmenu do usuário e oculte o menu suspenso.No final, o menu suspenso nunca é exibido.

Esse tempo limite definido atrasa a assinatura até o final do código JavaScript atual, que resolve o problema, mas de uma maneira muito elegante na minha opinião.

Se você souber soluções mais limpas, melhores, mais inteligentes, mais rápidas ou mais fortes, entre em contato :)!

questionAnswers(18)

yourAnswerToTheQuestion