关注新添加的input元素
我有一个新的Angular 2应用程序与input框列表。 当用户点击返回键时,我在他们正在编辑的那个之后立即添加一个新的input框。 或者说,我(asynchronous)向模型中的数组添加一个新条目,这将导致Angular 2在不久的将来自动生成一个新的input框。
我怎样才能使input焦点自动改变到新添加的元素?
或者,我得到一个引起DOM生成的模型对象的引用。 从组件代码中,有没有一种方法来search代表特定模型对象的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); }); } } });
如果允许我这样做,我会参与@Sasxa答案,并修改它,使其更像你正在寻找的东西。
一些变化
- 我将使用
ngFor
所以angular2添加新的input,而不是自己做。 主要目的是使angular2迭代它。 - 而不是
ViewChild
我将使用ViewChildren
返回一个具有changes
属性的QueryList 。 这个属性是一个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()
添加input元素这是纯粹的方便,input数组没有真正的目的, 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
更新29/03/2016
时间已经过去,事情已经澄清,总是有最好的实践来学习/教导。 我通过改变一些事情简化了这个答案
- 而不是使用
@ViewChildren
并订阅它,我做了一个指令,每当创build一个新的input时就会被安装 - 我正在使用
Renderer
来使WebWorker安全。 原始答案直接访问nativeElement
focus()
是不鼓励的nativeElement
。 - 现在我听
keydown.enter
,它简化了按键事件,我不必检查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
来触发元素的focus
,而不是直接访问元素。
这个版本比原版更清洁,更安全。
plnkrs更新至beta 12
使用ES5的示例: http : //plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr
使用ES6 / TS的示例: http : //plnkr.co/edit/em18uMUxD84Z3CK53RRe
看看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(); } }
您可以实现一个简单的input文本指令,以便每当创build一个新的input时,它将自动自动对焦。 在视图完全初始化之后, 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()); } }
请注意以下几点:
-
(keyup.enter)
事件用于检测何时按<enter>键 -
ngFor
用于为单词数组中的每个单词重复input元素 -
last
是一个布尔值,它绑定到一个局部variables,当input是最后一个时,该variables是true - keyup事件绑定到expression式
last ? addNewWord() : 0
last ? addNewWord() : 0
。 这确保了只有在从上一个input按下<enter>键时才会添加新的input字段
演示Plnkr
使用内联angular码,在条件画图之后重点:
<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>
为了在初始化同一周期中的几个指令时优先考虑哪个元素将获得焦点,请使用:
指示:
@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">