问题描述
我有一个新的Angular 2应用,其中包含input
框列表.当用户按下返回键时,我会在他们当前正在编辑的框之后立即添加一个新的input
框.或者,我(异步)向模型中的数组添加了一个新条目,这导致Angular 2在不久的将来自动生成一个新的input
框.
如何使input
焦点自动更改为新添加的元素?
或者,我得到对导致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);
});
}
}
});
如果可以的话,我将参加@Sasxa答案并对其进行修改,使其更符合您的需求./p>
一些更改
- 我将使用
ngFor
,因此angular2会添加新输入,而不是自己进行输入.主要目的只是使angular2对其进行迭代. - 我将使用
ViewChildren
而不是ViewChild
返回一个查询列表,其中具有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()
添加输入元素这纯粹是为了方便起见,input数组除了重绘ngFor
之外没有其他实际用途.
add: function(key) {
if(key.which == 13) {
// See plnkr for "this.inputs" usage
this.inputs.push(this.inputs.length+1);
}
}
我们将 dummy 项目推入数组,以便重新绘制.
使用ES5的示例: http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex
使用ES6/TS的示例: http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=预览
更新29/03/2016
时间过去了,事情已经弄清楚了,总有一些最佳的学习/教学方法.我通过更改一些内容来简化了此答案
- 我没有使用
@ViewChildren
并订阅它,而是制定了一条指令,该指令在每次创建新输入时都会被置为无效 - 我正在使用
Renderer
使WebWorker安全.不鼓励使用原始答案直接在nativeElement
上访问focus()
. - 现在我听
keydown.enter
,它简化了按键事件,我不必检查which
值.
要点.该组件看起来像(下面的plnkrs上的简化的完整代码)
@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
更新2018年
invokeElementMethod
已过时.使用Renderer2代替Renderer.
为您的元素提供ID,然后可以使用 selectRootElement :
this.renderer2.selectRootElement('#myInput').focus();
I have a new Angular 2 app with a list of input
boxes. When the user hits the return key, I add a new input
box immediately after the one they're currently editing. Or rather, I (asynchronously) add a new entry to the array in the model, which causes Angular 2 to automatically generate a new input
box in the near future.
How can I make it so that input
focus changes to the newly added element automatically?
EDIT 1:
Alternatively, I get a reference to the model object that's causing the DOM to be generated. From the component code, is there a way to search for a DOM element representing a particular model object?
EDIT 2:
Here's my code to just make this work. Hopefully this is offensive enough to some Angular 2 devs to encourage a reply :-)
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);
});
}
}
});
If I'm allowed to do so, I will take part of @Sasxa answer and modify it to make it more like what you're looking for.
A few changes
- I will use a
ngFor
so angular2 adds the new input instead of doing it myself. The main purpose is just to make angular2 to iterate over it. - Instead of
ViewChild
I'm going to useViewChildren
that returns a QueryList which has achanges
property. This property is an Observable and it returns the elements after they've changed.
Since in ES5, we have no decorators we have to use the queries
property to use ViewChildren
Component
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')
}
})
Focus on the last element.
ngAfterViewInit: function() {
this.vc.changes.subscribe(elements => {
elements.last.nativeElement.focus();
});
}
Like I said before, ViewChildren returns a QueryList which contains changes
property. When we subscribe to it everytime it changes it will return the list of elements. The list elements
contains a last
property (among others) that in this case returns the last element, we use nativeElement
on it and finally focus()
Add input elements This is for pure convenince, the inputs array has no real purpose more than redraw the ngFor
.
add: function(key) {
if(key.which == 13) {
// See plnkr for "this.inputs" usage
this.inputs.push(this.inputs.length+1);
}
}
We push a dummy item on the array so it redraws.
Example using ES5 : http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex
Example using ES6/TS : http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=preview
Update 29/03/2016
Time has passed, things have been clarified and there are always best practices to learn/teach. I've simplified this answer by changing a few things
- Instead of using
@ViewChildren
and subscribing to it I made a Directive that will be instatiated everytime a new input is created - I'm using
Renderer
to make it WebWorker safe. The original answer accessesfocus()
directly on thenativeElement
which is discouraged. - Now I listen to
keydown.enter
which simplifies the key down event, I don't have to checkwhich
value.
To the point. The component looks like (simplified, full code on the plnkrs below)
@Component({
template: `<input (keydown.enter)="add()" *ngFor="#input of inputs">`,
})
add() {
this.inputs.push(this.inputs.length+1);
}
And the directive
@Directive({
selector : 'input'
})
class MyInput {
constructor(public renderer: Renderer, public elementRef: ElementRef) {}
ngOnInit() {
this.renderer.invokeElementMethod(
this.elementRef.nativeElement, 'focus', []);
}
}
As you can see I'm calling invokeElementMethod
to trigger focus
on the element instead of accessing it directly.
This version is much cleaner and safer than the original one.
plnkrs updated to beta 12
Example using ES5 : http://plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr
Example using ES6/TS : http://plnkr.co/edit/em18uMUxD84Z3CK53RRe
Update 2018
invokeElementMethod
is deprecated. Use Renderer2 instead of Renderer.
Give your element an id, and you can use selectRootElement:
this.renderer2.selectRootElement('#myInput').focus();
这篇关于专注于新添加的输入元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!