专注于新添加的输入元素 [英] Focus on newly added input element

查看:78
本文介绍了专注于新添加的输入元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个新的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返回一个

    在最后一个元素上

    关注.

     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 use ViewChildren that returns a QueryList which has a changes 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 accesses focus() directly on the nativeElement which is discouraged.
    • Now I listen to keydown.enter which simplifies the key down event, I don't have to check which 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();
    

    这篇关于专注于新添加的输入元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆