Estou trabalhando em um aplicativo front-end com Angular 5 e preciso ocultar uma caixa de pesquisa, mas, ao clicar em um botão, a caixa de pesquisa deve ser exibida e focada.
Eu tentei algumas maneiras encontradas no StackOverflow com diretiva ou algo assim, mas não consegui.
Aqui está o código de exemplo:
@Component({
selector: 'my-app',
template: `
<div>
<h2>Hello</h2>
</div>
<button (click) ="showSearch()">Show Search</button>
<p></p>
<form>
<div >
<input *ngIf="show" #search type="text" />
</div>
</form>
`,
})
export class App implements AfterViewInit {
@ViewChild('search') searchElement: ElementRef;
show: false;
name:string;
constructor() {
}
showSearch(){
this.show = !this.show;
this.searchElement.nativeElement.focus();
alert("focus");
}
ngAfterViewInit() {
this.firstNameElement.nativeElement.focus();
}
A caixa de pesquisa não está definida para o foco.
Como eu posso fazer isso?
angular
angular5
angular-forms
Prumo
fonte
fonte
setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
Você deve usar o foco automático html para isso:
Nota: se o seu componente for persistido e reutilizado, ele só fará o foco automático na primeira vez que o fragmento for anexado. Isso pode ser superado por ter um ouvinte global de dom que verifica o atributo autofocus dentro de um fragmento de dom quando ele é anexado e, em seguida, reaplica-o ou focus via javascript.
fonte
Esta diretiva irá instantaneamente focar e selecionar qualquer texto no elemento assim que ele for exibido. Isso pode exigir um setTimeout para alguns casos, ele não foi muito testado.
E no HTML:
fonte
Vou ponderar sobre isso (solução Angular 7)
import {AfterViewInit, Directive, ElementRef, Input,} from '@angular/core'; @Directive({ selector: 'input[appFocus]', }) export class FocusDirective implements AfterViewInit { @Input('appFocus') private focused: boolean = false; constructor(public element: ElementRef<HTMLElement>) { } ngAfterViewInit(): void { // ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. if (this.focused) { setTimeout(() => this.element.nativeElement.focus(), 0); } } }
fonte
Isso está funcionando no Angular 8 sem setTimeout:
Explicação: Ok, então isso funciona por causa de: Detecção de alterações. É a mesma razão que setTimout funciona, mas ao executar um setTimeout em Angular, ele ignorará Zone.js e executará todas as verificações novamente, e funciona porque quando setTimeout for concluído, todas as alterações serão concluídas. Com o gancho de ciclo de vida correto (AfterContentChecked), o mesmo resultado pode ser alcançado, mas com a vantagem de que o ciclo extra não será executado. A função será acionada quando todas as alterações forem verificadas e aprovadas e será executada após os ganchos AfterContentInit e DoCheck. Se eu estiver errado aqui, por favor me corrija.
Mais um ciclo de vida e detecção de mudança em https://angular.io/guide/lifecycle-hooks
ATUALIZAÇÃO : descobri uma maneira ainda melhor de fazer isso usando Angular Material CDK, o pacote a11y. Primeiro importe A11yModule no módulo declarando o componente em que você tem o campo de entrada. Em seguida, use as diretivas cdkTrapFocus e cdkTrapFocusAutoCapture e use assim em html e defina tabIndex na entrada:
Tivemos alguns problemas com nossos menus suspensos em relação ao posicionamento e capacidade de resposta e começamos a usar o OverlayModule do cdk, e este método usando A11yModule funciona perfeitamente.
fonte
cdkTrapFocusAutoCapture
attribute, mas mudei seu primeiro exemplo em meu código. Por razões desconhecidas (para mim), ele não funcionou com o gancho de ciclo de vida AfterContentChecked, mas apenas com OnInit. Em particular com AfterContentChecked não posso mudar o foco e mover (com o mouse ou teclado) para outra entrada sem aquela diretiva no mesmo formulário.No Angular, dentro do próprio HTML, você pode definir o foco para a entrada com o clique de um botão.
fonte
mat-select
selectionChanged
evento que acontece antes de outras coisas da interface do usuário, portanto, puxar o foco naquele ponto não funcionou. Em vez disso eu tive que escrever um métodosetFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }
e chamá-lo a partir do manipulador de eventos:<mat-select (selectionChanged)="setFocus(myInput)">
. Não é tão bom, mas simples e funciona bem. THX!Para fazer a execução após o booleano ser alterado e evitar o uso de tempo limite, você pode fazer:
fonte
Há também um atributo DOM chamado
cdkFocusInitial
que funciona para mim nas entradas. Você pode ler mais sobre isso aqui: https://material.angular.io/cdk/a11y/overviewfonte
Apenas usando modelo angular
fonte
html do componente:
<input [cdkTrapFocusAutoCapture]="show" [cdkTrapFocus]="show">
controlador de componente:
showSearch() { this.show = !this.show; }
..e não se esqueça de importar A11yModule de @ angular / cdk / a11y
import { A11yModule } from '@angular/cdk/a11y'
fonte
Estou tendo o mesmo cenário, funcionou para mim, mas não estou tendo o recurso "ocultar / mostrar" que você tem. Então, talvez você possa primeiro verificar se obtém o foco quando tem o campo sempre visível e, em seguida, tentar resolver por que não funciona quando você altera a visibilidade (provavelmente é por isso que você precisa aplicar um sono ou uma promessa)
Para definir o foco, esta é a única mudança que você precisa fazer:
sua entrada de tapete Html deve ser:
em sua classe TS, referência como esta na seção de variáveis (
Seu botão está bom, chamando:
e é isso. Você pode verificar esta solução neste post que encontrei, então, obrigado a -> https://codeburst.io/focusing-on-form-elements-the-angular-way-e9a78725c04f
fonte
A maneira mais fácil também é fazer isso.
fonte