새로 추가 된 입력 요소에 집중


78

input상자 목록이있는 새로운 Angular 2 앱이 있습니다. 사용자가 리턴 키를 누르면 input현재 편집중인 상자 바로 뒤에 새 상자를 추가합니다 . 또는 오히려 (비동기 적으로) 모델의 배열에 새 항목을 추가하여 Angular 2가 input가까운 장래에 자동으로 새 상자를 생성합니다 .

어떻게이 정도로 할 수 input초점을 자동으로 새로 추가 된 요소로 변경?

편집 1 :
또는 DOM을 생성하는 모델 개체에 대한 참조를 얻습니다. 컴포넌트 코드에서 특정 모델 객체를 나타내는 DOM 요소를 검색하는 방법이 있습니까?

편집 2 :
다음은이 작업을 수행하는 코드입니다. 바라건대 이것은 일부 Angular 2 개발자에게 답장을 장려하기에 충분히 공격적입니다. :-)

app.WordComponent = ng.core
    .Component({
        selector: 'word-editor',
        template:'<input type="text" [value]="word.word" (input)="word.update($event.target.value)" (keydown)="keydown($event)"/>',
        styles:[
            ''
        ],
        properties:[
            'list:list',
            'word:word'
        ]
    })
    .Class({
        constructor:[
            function() {
            }
        ],
        keydown:function(e) {
            if(e.which == 13) {
                var ul = e.target.parentNode.parentNode.parentNode;
                var childCount = ul.childNodes.length;

                this.list.addWord("").then(function(word) {
                    var interval = setInterval(function() {
                        if(childCount < ul.childNodes.length) {
                            ul.lastChild.querySelector('input').focus();
                            clearInterval(interval);
                        }
                    }, 1);
                });
            }
        }
    });

1
당신은 setInterval가능성이 가장 높은 단지를해야한다 setTimeout.
Benjamin Gruenbaum

이 대답을 방문하는 사람은 누구나 이것을보십시오. github.com/spirosikmd/angular2-focus 사용하기 매우 쉽습니다. 당신이 * ngFor 사용하는 경우이 모든 쉽게 수행
Saiyaff 파 루크

너무 쉽게 외부 lib가 필요합니까 ??
Christophe Roussy

답변:


101

그렇게 할 수 있다면 @Sasxa 답변에 참여하여 찾고있는 것과 더 비슷하게 수정하겠습니다.

몇 가지 변경 사항

  • ngForangular2 를 사용하여 직접 수행하는 대신 새 입력을 추가합니다. 주된 목적은 angular2를 만들어 그것을 반복하는 것입니다.
  • 대신 속성 이있는 QueryList 를 반환하는 ViewChild것을 사용할 것 ViewChildren입니다 . 이 속성은 Observable이며 변경된 요소를 반환합니다.changes

ES5에서는 데코레이터가 없기 때문에 queries속성을 사용하여ViewChildren

구성 요소

Component({
    selector: 'cmp',
    template : `
        <div>
            // We use a variable so we can query the items with it
            <input #input (keydown)="add($event)" *ngFor="#input of inputs">
        </div>
    `,
    queries : {
        vc : new ng.core.ViewChildren('input')
    }
})

마지막 요소에 초점맞 춥니 다 .

ngAfterViewInit: function() {

    this.vc.changes.subscribe(elements => {
        elements.last.nativeElement.focus();
    });

}

앞서 말했듯이 ViewChildren은 changes속성 이 포함 된 QueryList를 반환 합니다. 변경할 때마다 구독하면 요소 목록이 반환됩니다. 목록 elements포함 last이 경우 마지막 요소를 반환, 우리가 사용하는 (다른 사람의 사이에서) 속성을 nativeElement마침내과focus()

입력 요소 추가 이것은 순수한 편의를위한 것이며, 입력 배열은 ngFor.

add: function(key) {
    if(key.which == 13) {
        // See plnkr for "this.inputs" usage
        this.inputs.push(this.inputs.length+1);
    }
}

배열에 더미 항목을 푸시하여 다시 그립니다.

ES5 사용 예 : http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex

ES6 / TS 사용 예 : http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=preview

2016 년 3 월 29 일 업데이트

시간이 지났고 상황이 명확 해졌으며 항상 배우고 가르 칠 모범 사례가 있습니다. 몇 가지를 변경하여이 답변을 단순화했습니다.

  • @ViewChildren그것을 사용 하고 구독하는 대신 새로운 입력이 생성 될 때마다 설치 될 지침을 만들었습니다.
  • RendererWebWorker를 안전하게 만들기 위해 사용 하고 있습니다. 원래 답변 은 권장되지 않는 focus()에 직접 액세스 합니다 nativeElement.
  • 이제 keydown.enter키 다운 이벤트를 단순화하는 것을 듣고 which값 을 확인할 필요가 없습니다 .

요점. 구성 요소는 다음과 같습니다 (아래 plnkr의 단순화 된 전체 코드).

@Component({
  template: `<input (keydown.enter)="add()" *ngFor="#input of inputs">`,
})

add() {
    this.inputs.push(this.inputs.length+1);
}

그리고 지침

@Directive({
  selector : 'input'
})
class MyInput {
  constructor(public renderer: Renderer, public elementRef: ElementRef) {}

  ngOnInit() {
    this.renderer.invokeElementMethod(
      this.elementRef.nativeElement, 'focus', []);
  }
}

보시다시피 요소에 직접 액세스하는 대신 invokeElementMethod트리거 focus를 호출 하고 있습니다.

이 버전은 원래 버전보다 훨씬 깨끗하고 안전합니다.

plnkrs가 베타 12로 업데이트되었습니다.

ES5 사용 예 : http://plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr

ES6 / TS 사용 예 : http://plnkr.co/edit/em18uMUxD84Z3CK53RRe

2018 업데이트

invokeElementMethod더 이상 사용되지 않습니다. Renderer 대신 Renderer2를 사용하세요.

요소에 ID를 부여하면 selectRootElement 를 사용할 수 있습니다 .

this.renderer2.selectRootElement('#myInput').focus();

1
매우 우아합니다. #input여러 요소에 동일한 로컬 템플릿 변수 이름 / 식별자를 사용할 수 있다는 사실에 놀랐습니다 .
Mark Rajcok

1
나는 이름을 바꿀 것입니다 ngFor그것이 다른 관련 것처럼 보이지 않는다 그래서, 다른 지역의 템플릿 변수 뭔가를 #input: *ngFor="#in of inputs".
Mark Rajcok

1
모든에 이벤트 핸들러를 추가하는 대신에를 input넣고 div이벤트가 버블 링되도록하는 것이 좋습니다 <div (keydown)="add($event)"> <input #input *ngFor="#in of inputs">..
Mark Rajcok

2018 업데이트의 경우 ID를 사용하는 대신 다음이 작동하는 것으로 나타났습니다.this.renderer2.selectRootElement(this.elementRef.nativeElement).focus();
Randy Chung

41

ViewChild를 살펴보십시오 . 여기에 예제가 있습니다. 이것은 당신이 찾고있는 것일 수 있습니다.

import {Component, ViewChild} from 'angular2/core'

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <input #name>
    </div>
  `,
  directives: []
})
export class App {

  @ViewChild('name') vc: ElementRef;

  ngAfterViewInit() {
    this.vc.nativeElement.focus();
  }
}

각도 6와 "하여 elementRef"ViewChild 형 선언을하지 않는 문제를 생략하거나 vscode의 린터에 여전히 좋은
stackuser83

26

인라인 각도 코드를 사용하여 조건부 페인트 후에 초점을 맞 춥니 다.

  <span *ngIf="editId==item.id">
    <input #taskEditText type="text" [(ngModel)]="editTask" (keydown.enter)="save()" (keydown.esc)="saveCancel()"/>
    <button (click)="save()">Save</button>
    <button (click)="saveCancel()">Cancel</button>
    {{taskEditText.focus()}}
  </span>

9

간단한 입력 텍스트 지시문을 구현하여 새 입력이 생성 될 때마다 자동으로 초점을 맞출 수 있습니다. 이 focus()메서드는 ngAfterViewInit()뷰가 완전히 초기화 된 후 구성 요소 수명주기 후크 내에서 호출됩니다 .

@Directive({
    selector: 'input[type=text]'
})
export class FocusInput implements AfterViewInit {
    private firstTime: bool = true;
    constructor(public elem: ElementRef) {
    }

    ngAfterViewInit() {
      if (this.firstTime) {
        this.elem.nativeElement.focus();
        this.firstTime = false;
      }
    }
}

FocusInput구성 요소 에서 지시문을 사용하십시오 .

@Component({
    selector: 'app',
    directives: [FocusInput],
    template: `<input type="text" (keyup.enter)="last ? addNewWord():0" 
                *ngFor="#word of words; #last = last" [value]="word.word" 
                #txt (input)="word.word = txt.value" />{{words |json}}`
})
export class AppComponent {
    words: Word[] = [];
    constructor() {
        this.addNewWord();
    }
    addNewWord() {
        this.words.push(new Word());
    }
}

다음 사항에 유의하십시오.

  1. (keyup.enter)이벤트는 <입력> 때를 감지하는 데 사용되는 키를 누를 때
  2. ngFor 배열의 각 단어에 대해 입력 요소를 반복하는 데 사용됩니다. words
  3. last 입력이 마지막 일 때 참인 지역 변수에 바인딩 된 부울입니다.
  4. keyup 이벤트는 표현식에 바인딩됩니다 last ? addNewWord() : 0. 이렇게하면 마지막 입력에서 <enter> 키를 눌렀을 때만 새 입력 필드가 추가됩니다.

데모 Plnkr


위의 모든 답변을 시도한 후 이것은 나를 위해 일했습니다! Anguler 5감사!!
Alfa Bravo

이것은 내 의견에서 가장 견고한 솔루션입니다
킬리안 페르 도모 Curbelo

3

동일한주기에서 여러 지시문을 초기화 할 때 어떤 요소에 초점을 맞출 지 우선 순위를 지정하려면 다음을 사용하십시오.

지령:

@Directive({
  selector: '[focusOnInit]'
})
export class FocusOnInitDirective implements OnInit, AfterViewInit {
  @Input('focusOnInit') priority: number = 0;

  static instances: FocusOnInitDirective[] = [];

  constructor(public renderer: Renderer, public elementRef: ElementRef) {
  }

  ngOnInit(): void {
    FocusOnInitDirective.instances.push(this)
  }

  ngAfterViewInit(): void {
    setTimeout(() => {
      FocusOnInitDirective.instances.splice(FocusOnInitDirective.instances.indexOf(this), 1);
    });

    if (FocusOnInitDirective.instances.every((i) => this.priority >= i.priority)) {
      this.renderer.invokeElementMethod(
        this.elementRef.nativeElement, 'focus', []);
    }
  }
}

용법:

<input type="text" focusOnInit="9">

https://plnkr.co/edit/T9VDPIWrVSZ6MpXCdlXF


1
대신 tabindex를 사용하는 것이 더 낫지 않을까요?
André Werlang 2017
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.