专注于新添加的输入元素

问题描述 投票:73回答:5

我有一个新的Angular 2应用程序,带有一个输入框列表。当用户点击返回键时,我会在他们当前正在编辑的输入框之后立即添加一个新输入框。或者更确切地说,我(异步)在模型中向数组添加一个新条目,这会导致Angular 2在不久的将来自动生成一个新的输入框。

如何使输入焦点自动更改为新添加的元素?

[edit]或者,我得到了一个引起DOM生成的模型对象的引用。从组件代码中,有没有办法搜索表示特定模型对象的DOM元素?

[编辑]这是我的代码,使这项工作。希望这对一些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);
                });
            }
        }
    });
angular
5个回答
95
投票

如果我被允许这样做,我将参与@Sasxa的答案并对其进行修改,使其更像您正在寻找的内容。

一些变化

  • 我将使用ngFor,因此angular2添加新输入而不是自己做。主要目的是让angular2迭代它。
  • 而不是ViewChild我将使用ViewChildren返回一个QueryList具有changes属性。此属性是一个Observable,它在更改后返回元素。

因为在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()

添加输入元素这是为了方便起见,输入数组没有真正的目的,只需重绘qazxsw poi。

ngFor

我们在数组上推送一个虚拟项目,以便重绘。

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

使用ES6 / TS的示例:http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex

Update 29/03/2016

时间已经过去,事情已经澄清,并且总是有最好的实践来学习/教学。我通过改变一些事情简化了这个答案

  • 我没有使用http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=preview订阅它,而是创建了一个指令,每次创建新输入时都会将其设置为instatiated
  • 我正在使用@ViewChildren使WebWorker安全。原始答案直接在Renderer上访问focus(),这是不鼓励的。
  • 现在我听nativeElement简化了关键事件,我没有必要检查keydown.enter值。

到了这一点。组件看起来像(下面的plnkrs上的简化,完整代码)

which

和指令

@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,而不是直接访问它。

这个版本比原版更清洁,更安全。

plnkrs更新为beta 12

使用ES5的示例:focus

使用ES6 / TS的示例:http://plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr

Update 2018

http://plnkr.co/edit/em18uMUxD84Z3CK53RRe已被弃用。使用Renderer2而不是Renderer。

给你的元素一个id,你可以使用invokeElementMethod

selectRootElement

40
投票

看看this.renderer2.selectRootElement('#myInput').focus(); ,这是一个ViewChild。这可能是你正在寻找的:

example

22
投票

使用内联角度代码,在条件绘制后聚焦:

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();
  }
}

9
投票

您可以实现一个简单的输入文本指令,这样无论何时创建新输入,它都会自动对焦。在视图完全初始化之后,在 <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> 组件生命周期钩子内部调用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

请注意以下事项:

  1. @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()); } } 事件用于检测何时 键是按下的
  2. (keyup.enter)用于重复ngFor数组中每个单词的输入元素
  3. words是绑定到局部变量的布尔值,当输入是最后一个时,该变量为true
  4. keyup事件绑定到表达式last。这可确保仅在添加新输入字段时添加 从最后一个输入按下键

last ? addNewWord() : 0


3
投票

为了在同一周期初始化多个指令时优先考虑哪个元素将成为焦点,请使用:

指示:

Demo Plnkr

用法:

@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">

© www.soinside.com 2019 - 2024. All rights reserved.