如何在AngularJS中创建和聚焦新元素?

时间:2017-08-21 18:34:52

标签: javascript jquery html angularjs events

我有一个动态输入数量的表单,由AngularJS控制。

<body ng-app="mainApp" ng-controller="CreatePollController" ng-init="init(3)">
    <form id="createPollForm">
        <input class="create-input" ng-repeat="n in questions" id="q_{{$index}}" name="q_{{$index}}" type="text" ng-keypress="createInputKeypress($event);"/>
        <a href="javascript:void(0);" ng-click="addQuestion()">Add Question</a>
    </form>
</body>

这由以下角度代码控制:

app.controller('CreatePollController', function($scope) {
    $scope.questions = [];

    $scope.init = function(numOfInputs){
        for(var i = 0; i < numOfInputs; i++){
            $scope.questions.push({
                "questionText":""
            });
        }
    };

    $scope.addQuestion = function(){
        $scope.questions.push({
            "questionText":""
        });
    };

    $scope.createInputKeypress = function(e){
        if(e.keyCode === 13){
            e.preventDefault();

            var idx = Number(e.target.id.replace("q_", ""));
            if(idx === this.questions.length - 1){
                this.addQuestion();
            }

            // Wait for angular update ????

            var nextId = "#q_" + (++idx);
            $(nextId).focus();
        }
    };
});

目前,当用户在关注文本输入时点击Enter键时,将调用createInputKeypress函数,浏览器将下一个输入聚焦在表单中。但是,如果您当前关注表单中的最后一个元素,它会向questions数组添加一个新问题,这将导致在DOM中生成另一个输入。

但是,创建此新元素时,focus()调用无效。我怀疑这是因为angular不会立即添加新元素,所以尝试使用jQuery来定位和聚焦新元素是行不通的。

有没有办法等待更新DOM,然后关注新元素?

1 个答案:

答案 0 :(得分:1)

正如您可能已经知道的那样, javascript是基于回合的,这意味着浏览器将轮流(循环)执行JS代码。目前在下一个javascript周期中准备回调的方法是通过在超时的下一个周期设置我们想要运行的代码的回调,我们可以通过以{{1为间隔调用setTimeout来做到这一点。 miliseconds,在浏览器完成(从中获取)之后,将强制在下一个javascript转弯中调用给定的回调。

尝试保持简单,一个浏览器周期按给定顺序执行这些操作

  1. 脚本(JS转向发生)
  2. 渲染(HTML和DOM渲染)
  3. 绘画(在窗口中绘制渲染的DOM)
  4. 其他(内部浏览器的内容)
  5. 看一下这个例子:

    &#13;
    &#13;
    0
    &#13;
    &#13;
    &#13;

    即使知道没有间隔,也会在5和6之后打印

    3和4  console.log(1); console.log(2); setTimeout(function () { console.log(3); console.log(4); }, 0); console.log(5); console.log(6); /** prints in the console * 1 - in the current JS turn * 2 - in the current JS turn * 5 - in the current JS turn * 6 - in the current JS turn * 3 - in the next JS turn * 4 - in the next JS turn **/中的{0},因为setTimeout基本上只在当前javascript轮次结束后才会调用给定的回调。如果在下一回合中,当前时间与回调与setTimeout指令绑定的时间之间的差异低于setTimeout中传递的时间间隔,则不会调用回调,它将等待下一个转弯,重复该过程直到时间间隔低于该差异,然后才会调用回调!

    由于AngularJS是一个包装所有代码的框架,角度更新通常在我们的代码执行后发生,在每个javascript转向结束时,这意味着对HTML的角度更改只会发生在当前的javascript转为完成。

    AngularJS还内置了超时服务,它被称为$timeoutdifference between the native setTimeout and angular's $timeout service是最后一个是服务功能,恰好调用本机setTimeout通过角度内部回调,此回调轮流负责执行我们在setTimeout中传递的回调,然后确保我们在$timeout中所做的任何更改都会反映在其他地方!但是,由于在我们的案例中我们实际上并不想更新$scope,因此我们不需要使用此服务,简单的$scope恰好更有效!

    了解所有这些信息后,我们可以使用setTimeout来解决我们的问题。像这样:

    setTimeout

    为了使它更具语义,我们可以包装$scope.createInputKeypress = function(e){ if(e.keyCode === 13){ e.preventDefault(); var idx = Number(e.target.id.replace("q_", "")); if(idx === this.questions.length - 1){ this.addQuestion(); } // Wait for the next javascript turn setTimeout(function () { var nextId = "#q_" + (++idx); $(nextId).focus(); }, 0); } }; 逻辑 在具有更多上下文关联名称的函数中,例如setTimeout

    runAfterRender

    现在我们可以使用此函数在下一个javascript转弯中准备代码执行:

    function runAfterRender (callback) {
        setTimeout(function () {
            if (angular.isFunction(callback)) {
                callback();
            }
        }, 0);
    }