Подтвердить что ты не робот

Простое манипулирование dom в AngularJS - нажмите кнопку, затем установите фокус на входной элемент

У меня есть этот angular код:

<div class="element-wrapper" ng-repeat="element in elements">
  <div class="first-wrapper">
     <div class="button" ng-click="doSomething(element,$event)">{{element.name}}</div>   
  </div>
  <div class="second-wrapper">
    <input type="text" value="{{element.value}}">    
  </div>
</div>

Что я хочу:, когда пользователь нажимает кнопку - элемент ввода будет сфокусирован.

Как найти элемент ввода после щелчка элемента кнопки и сфокусировать его?

Я могу сделать функцию, которая выглядит так:

function doSomething(element,$event) {
  //option A - start manipulating in the dark:
  $event.srcElement.parentNode.childNodes[1]

  //option B - wrapping it with jQuery:
   $($event.srcElement).closest('.element-wrapper').find('input').focus();
}

Ни один из них не работает - есть ли более приятный способ angular сделать это? Использование таких функций, как .closest() и .find(), как в jQuery?

Update:

Я нашел этот хак для работы (но он все еще не кажется правильным решением):

function doSomething(element,$event) {
   setTimeout(function(){
     $($event.srcElement).closest('.element-wrapper').find('input').focus();
   },0)
}

Я обматываю его с помощью setTimeout, после того как angular завершает все свои манипуляции, он фокусируется на элементе ввода.

4b9b3361

Ответ 1

DOM-манипуляция должна быть в директиве вместо контроллера. Я бы определил директиву focusInput и использовал ее на кнопке:

<div class="button" focus-input>{{element.name}}</div>   

Директива

app.directive('focusInput', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      element.bind('click', function() {
        $timeout(function() {
          element.parent().parent().find('input')[0].focus();
        });
      });
    }
  };
});

Plunker

Так как jqLite довольно ограничен с точки зрения методов обхода DOM, мне пришлось использовать parent().parent(). Вы можете использовать jQuery или некоторые методы JavaScript.

Как вы уже выяснили, требуется $timeout, чтобы метод focus() вызывался после рендеринга браузера (т.е. завершает обработку события click).

find('input')[0] дает нам доступ к элементу DOM, позволяя нам использовать метод JavaScript focus() (а не find('input').focus(), который потребует jQuery).

Ответ 2

Недавно я взглянул на AngularJS и столкнулся с подобной ситуацией.

Я работал над обновлением приложения примера Todo с главной страницы angular, чтобы добавить режим редактирования при двойном щелчке по тодовому элементу.

Мне удалось решить мою проблему, используя модельный/государственный подход. Если ваше приложение работает аналогичным образом (вы хотите установить фокус на поле, когда какое-либо условие для модели истинно), то это может сработать и для вас.

Мой подход заключается в том, чтобы установить свойство model.editing на true, когда пользователь дважды щелкает по метке todo - это показывает редактируемый ввод и скрывает обычную не редактируемую метку и флажок. У нас также есть настраиваемая директива под названием focusInput, которая имеет часы с тем же свойством model.editing и будет устанавливать фокус на текстовое поле при изменении значения:

<li ng-repeat="todo in todos">

    <div>
        <!-- Regular display view. -->
        <div ng-show="todo.editing == false">
            <label class="done-{{todo.done}}" ng-dblclick="model.editing = true">
                <input type="checkbox" ng-model="todo.done"/>{{todo.text}}
            </label>
        </div>

        <!-- Editable view. -->
        <div ng-show="todo.editing == true">
            <!--
                - Add the `focus-input` directive with the statement "todo.editing == true".
                  This is the element that will receive focus when the statement evaluates to true.

                - We also add the `todoBlur` directive so we can cancel editing when the text field loses focus.
            -->
            <input type="text" ng-model="todo.text" focus-input="todo.editing == true" todo-blur="todo.editing = false"/>
        </div>
    </div>

</li>

Вот директива focusInput, которая будет фокусировать внимание на текущем элементе, когда какое-либо условие оценивается как true:

angular.module('TodoModule', [])
    // Define a new directive called `focusInput`.
    .directive('focusInput', function($timeout){
        return function(scope, element, attr){

            // Add a watch on the `focus-input` attribute.
            // Whenever the `focus-input` statement changes this callback function will be executed.
            scope.$watch(attr.focusInput, function(value){
                // If the `focus-input` statement evaluates to `true`
                // then use jQuery to set focus on the element.
                if (value){
                    $timeout(function(){
                        element.select();
                    });
                }
            });

        };
    })
    // Here is the directive to raise the 'blur' event.
    .directive('todoBlur', [
        '$parse', function($parse){
            return function(scope, element, attr){

                var fn = $parse(attr['todoBlur']);
                return element.on('blur', function(event){

                    return scope.$apply(function(){
                        return fn(scope, {
                            $event: event
                        });
                    });

                });

            };
        }
    ]);

Ответ 3

Вот директива, которая инициирует событие фокусировки на целевом элементе dom:

Директива AngularJs:

app.directive('triggerFocusOn', function($timeout) {
    return {
        link: function(scope, element, attrs) {
            element.bind('click', function() {
                $timeout(function() {
                    var otherElement = document.querySelector('#' + attrs.triggerFocusOn);

                    if (otherElement) {
                        otherElement.focus();
                    }
                    else {
                        console.log("Can't find element: " + attrs.triggerFocusOn);
                    }
                });
            });
        }
    };
});

html:

<button trigger-focus-on="targetInput">Click here to focus on the other element</button>
<input type="text" id="targetInput">

Живой пример в Plunker

Ответ 4

Мне нужно было создать учетную запись, чтобы обеспечить легкий ответ.

//Add a bool to your controller scope that indicates if your element is focused
... //ellipsis used so I don't write the part you should know
$scope.userInputActivate = false;
...
//Add a new directive to your app stack
...
.directive('focusBool', function() { 
    return function(scope, element, attrs) {
        scope.$watch(attrs.focusBool, function(value) {
            if (value) $timeout(function() {element.focus();});
        });
    }
})
...

<!--Now that our code is watching for a scope boolean variable, stick that variable on your input element using your new directive, and manipulate that variable as desired.-->
...
<div class="button" ng-click="userInputActivate=true">...</div>
...
<input type="text" focus-Bool="userInputActivate">
...

Обязательно reset эту переменную, если вы не используете вход. Вы можете добавить директиву ng-blur достаточно легко, чтобы изменить его, или другое событие ng-click, которое сбрасывает его на false. Установив его в false, он будет готов в следующий раз. Вот пример директивы ng-blur, который я нашел, если у вас есть проблемы с поиском.

.directive('ngBlur', ['$parse', function($parse) {
    return function(scope, element, attr) {
        var fn = $parse(attr['ngBlur']);
        element.bind('blur', function(event) {
        scope.$apply(function() {
            fn(scope, {$event:event});
        });
    });
    }
}]);

Ответ 5

Вот что я придумал. Я начал с решения Mark Rajcok выше, а затем переехал, чтобы упростить повторное использование. Он настраивается и не требует никакого кода в вашем контроллере. Фокус - это чистый аспект представления и не требует кода контроллера

HTML:

 <div id="focusGroup">
     <div>
         <input type="button" value="submit" pass-focus-to="focusGrabber" focus-parent="focusGroup">
     </div>
     <div>
         <input type="text" id="focusGrabber">
     </div> 
 </div>

директива:

chariotApp.directive('passFocusTo', function ($timeout) {
    return {
        link: function (scope, element, attrs) {
            element.bind('click', function () {
                $timeout(function () {
                    var elem = element.parent();
                    while(elem[0].id != attrs.focusParent) {
                        elem = elem.parent();
                    }
                    elem.find("#"+attrs.passFocusTo)[0].focus();
                });
            });
        }
    };
});

предположение:

  • Ваш даритель и помощник рядом.
  • при использовании этого многократного использования на одном идентификаторе страницы используются уникальные, или данные и получатели находятся в изолированной ветки DOM.

Ответ 6

Для использования метода .closest() я предлагаю вам применить механизм наследования прототипа fox expand angular. Точно так же:

angular.element.prototype.closest = (parentClass)->
  $this = this
  closestElement = undefined
  while $this.parent()
    if $this.parent().hasClass parentClass
      closestElement = $this.parent()
      break
    $this = $this.parent()
  closestElement

Разметка:

<span ng-click="removeNote($event)" class="remove-note"></span>

Использование:

 $scope.removeNote = ($event)->
    currentNote = angular.element($event.currentTarget).closest("content-list_item") 
    currentNote.remove()

Ответ 7

Чтобы найти вход, добавьте его Id <input id="input{{$index}}" .. /> и передайте индекс ngRepeat как параметр в функцию ng-click="doSomething(element,$event, $index)"

 <div class="element-wrapper" ng-repeat="element in elements">
  <div class="first-wrapper">
     <div class="button" ng-click="doSomething(element,$event, $index)">{{element.name}}</div>   
  </div>
  <div class="second-wrapper">
    <input id="input{{$index}}" type="text" value="{{element.value}}">    
  </div>
</div>   

В функции используйте $timeout с нулевой задержкой, чтобы подождать до конца рендеринга DOM. Затем вход можно найти в getElementById в $timeout. Не забудьте добавить $timeout в контроллер.

.controller("MyController", function ($scope, $timeout)
{
  $scope.doSomething = function(element,$event, index) {
    //option A - start manipulating in the dark:
    $event.srcElement.parentNode.childNodes[1]

    $timeout(function () 
    {
      document.getElementById("input" + index).focus();
    });
  }
});