ตั้งโฟกัสองค์ประกอบในลักษณะเชิงมุม


113

หลังจากหาตัวอย่างวิธีตั้งค่าองค์ประกอบโฟกัสด้วยเชิงมุมแล้วฉันเห็นว่าส่วนใหญ่ใช้ตัวแปรบางตัวเพื่อดูแล้วตั้งโฟกัสและส่วนใหญ่ใช้ตัวแปรที่แตกต่างกันหนึ่งตัวแปรสำหรับแต่ละฟิลด์ที่ต้องการกำหนดโฟกัส ในรูปแบบที่มีฟิลด์จำนวนมากนั่นหมายถึงตัวแปรต่างๆมากมาย

ด้วยวิธี jquery ในใจ แต่ต้องการทำแบบนั้นในเชิงมุมฉันได้สร้างวิธีแก้ปัญหาที่เราตั้งโฟกัสในฟังก์ชั่นใด ๆ โดยใช้ id ขององค์ประกอบดังนั้นเนื่องจากฉันยังใหม่มากในเชิงมุมฉันต้องการรับความคิดเห็นบางอย่างถ้า วิธีนั้นถูกต้องมีปัญหาอะไรก็ได้ที่ช่วยให้ฉันทำวิธีนี้ได้ดีขึ้นในเชิงมุม

โดยพื้นฐานแล้วฉันสร้างคำสั่งที่เฝ้าดูค่าขอบเขตที่กำหนดโดยผู้ใช้ด้วยคำสั่งหรือ focusElement ของค่าเริ่มต้นและเมื่อค่านั้นเหมือนกับ id ขององค์ประกอบองค์ประกอบนั้นจะตั้งโฟกัสเอง

angular.module('appnamehere')
  .directive('myFocus', function () {
    return {
      restrict: 'A',
      link: function postLink(scope, element, attrs) {
        if (attrs.myFocus == "") {
          attrs.myFocus = "focusElement";
        }
        scope.$watch(attrs.myFocus, function(value) {
          if(value == attrs.id) {
            element[0].focus();
          }
        });
        element.on("blur", function() {
          scope[attrs.myFocus] = "";
          scope.$apply();
        })        
      }
    };
  });

ข้อมูลที่จำเป็นต้องได้รับการโฟกัสด้วยเหตุผลบางประการจะดำเนินการในลักษณะนี้

<input my-focus id="input1" type="text" />

องค์ประกอบที่จะกำหนดโฟกัส:

<a href="" ng-click="clickButton()" >Set focus</a>

และฟังก์ชั่นตัวอย่างที่กำหนดโฟกัส:

$scope.clickButton = function() {
    $scope.focusElement = "input1";
}

เป็นทางออกที่ดีในเชิงมุมหรือไม่? มันมีปัญหาไหมกับประสบการณ์ที่ไม่ดีของฉันฉันยังไม่เห็น?

คำตอบ:


173

ปัญหาเกี่ยวกับการแก้ปัญหาของคุณคือว่ามันไม่ได้ทำงานได้ดีเมื่อผูกลงไปสั่งอื่น ๆ ng-repeatที่สร้างขอบเขตใหม่เช่น ทางออกที่ดีกว่าคือการสร้างฟังก์ชันการบริการที่ช่วยให้คุณสามารถโฟกัสองค์ประกอบที่จำเป็นภายในคอนโทรลเลอร์ของคุณหรือเพื่อโฟกัสองค์ประกอบอย่างเปิดเผยใน html

การสาธิต

JAVASCRIPT

บริการ

 .factory('focus', function($timeout, $window) {
    return function(id) {
      // timeout makes sure that it is invoked after any other event has been triggered.
      // e.g. click events that need to run before the focus or
      // inputs elements that are in a disabled state but are enabled when those events
      // are triggered.
      $timeout(function() {
        var element = $window.document.getElementById(id);
        if(element)
          element.focus();
      });
    };
  });

คำสั่ง

  .directive('eventFocus', function(focus) {
    return function(scope, elem, attr) {
      elem.on(attr.eventFocus, function() {
        focus(attr.eventFocusId);
      });

      // Removes bound events in the element itself
      // when the scope is destroyed
      scope.$on('$destroy', function() {
        elem.off(attr.eventFocus);
      });
    };
  });

ตัวควบคุม

.controller('Ctrl', function($scope, focus) {
    $scope.doSomething = function() {
      // do something awesome
      focus('email');
    };
  });

HTML

<input type="email" id="email" class="form-control">
<button event-focus="click" event-focus-id="email">Declarative Focus</button>
<button ng-click="doSomething()">Imperative Focus</button>

ฉันชอบโซลูชันนี้มาก อย่างไรก็ตามคุณสามารถอธิบายเหตุผลในการใช้ $ timeout อีกเล็กน้อยได้หรือไม่? สาเหตุที่คุณใช้เนื่องจาก "Angular Thing" หรือ "DOM Thing"?
user1821052

ตรวจสอบให้แน่ใจว่ามันทำงานหลังจากรอบการย่อยใด ๆ ที่เชิงมุมทำ แต่สิ่งนี้ไม่รวมรอบการย่อยที่ได้รับผลกระทบหลังจากการกระทำแบบอะซิงโครนัสที่ดำเนินการหลังจากหมดเวลา
ryeballar

3
ขอบคุณ! สำหรับผู้ที่สงสัยว่าสิ่งนี้อ้างอิงถึงที่ใดในเอกสารเชิงมุมนี่คือลิงค์ (พาฉันไปหาตลอด)
user1821052

@ryeballar ขอบคุณ!. วิธีง่ายๆที่ดี แค่คำถามว่า ฉันสามารถใช้โรงงานที่สร้างขึ้นผ่านแอตทริบิวต์แทนที่จะรอให้เหตุการณ์บางอย่างเกิดขึ้นได้หรือไม่?
Pratik Gaikwad

4
มันบ้างานที่จำเป็นในเชิงมุมเพียงเพื่อโฟกัสอินพุต
Bruno Santos

19

เกี่ยวกับโซลูชันนี้เราสามารถสร้างคำสั่งและแนบเข้ากับองค์ประกอบ DOM ที่ต้องได้รับโฟกัสเมื่อเงื่อนไขที่กำหนดเป็นที่พอใจ ด้วยการปฏิบัติตามวิธีนี้เราจะหลีกเลี่ยงตัวควบคุมการมีเพศสัมพันธ์กับรหัสองค์ประกอบ DOM

คำสั่งรหัสตัวอย่าง:

gbndirectives.directive('focusOnCondition', ['$timeout',
    function ($timeout) {
        var checkDirectivePrerequisites = function (attrs) {
          if (!attrs.focusOnCondition && attrs.focusOnCondition != "") {
                throw "FocusOnCondition missing attribute to evaluate";
          }
        }

        return {            
            restrict: "A",
            link: function (scope, element, attrs, ctrls) {
                checkDirectivePrerequisites(attrs);

                scope.$watch(attrs.focusOnCondition, function (currentValue, lastValue) {
                    if(currentValue == true) {
                        $timeout(function () {                                                
                            element.focus();
                        });
                    }
                });
            }
        };
    }
]);

การใช้งานที่เป็นไปได้

.controller('Ctrl', function($scope) {
   $scope.myCondition = false;
   // you can just add this to a radiobutton click value
   // or just watch for a value to change...
   $scope.doSomething = function(newMyConditionValue) {
       // do something awesome
       $scope.myCondition = newMyConditionValue;
  };

});

HTML

<input focus-on-condition="myCondition">

1
จะเกิดอะไรขึ้นเมื่อmyConditionตัวแปร $ ขอบเขตถูกตั้งค่าเป็นจริงแล้วจากนั้นผู้ใช้เลือกที่จะโฟกัสไปที่องค์ประกอบอื่นคุณยังสามารถกำหนดโฟกัสใหม่ได้หรือไม่เมื่อmyConditionเป็นจริงแล้วรหัสของคุณจะเฝ้าดูการเปลี่ยนแปลงของแอตทริบิวต์focusOnConditionแต่จะไม่ทริกเกอร์เมื่อ ค่าที่คุณพยายามเปลี่ยนยังคงเหมือนเดิม
ryeballar

ฉันจะอัปเดตตัวอย่างในกรณีของเราเรามีปุ่มตัวเลือกสองปุ่มและเราสลับการตั้งค่าสถานะเป็นจริงหรือเท็จขึ้นอยู่กับค่าคุณสามารถเปลี่ยนค่าสถานะ myCondition เป็นจริงหรือเท็จ
Braulio

ดูเหมือนจะเป็นวิธีแก้ปัญหาทั่วไป ดีกว่าขึ้นอยู่กับรหัส ฉันชอบมัน.
Mortb

ในกรณีที่มีใครลองใช้แล้ว แต่ไม่ได้ผลฉันต้องเปลี่ยน element.focus (); ไปยังองค์ประกอบ [0] .focus ();
Adrian Carr

1
วิธีนี้เป็น 'วิธีเชิงมุม' มากกว่าการแฮ็กที่ใช้รหัสข้างต้น
setec

11

ฉันชอบที่จะหลีกเลี่ยงการค้นหา DOM นาฬิกาและตัวปล่อยสัญญาณทั่วโลกเมื่อทำได้ดังนั้นฉันจึงใช้แนวทางที่ตรงกว่า ใช้คำสั่งเพื่อกำหนดฟังก์ชันง่ายๆที่เน้นองค์ประกอบคำสั่ง จากนั้นเรียกใช้ฟังก์ชันนั้นทุกที่ที่ต้องการภายในขอบเขตของคอนโทรลเลอร์

ต่อไปนี้เป็นแนวทางที่เรียบง่ายสำหรับการแนบเข้ากับขอบเขต ดูข้อมูลโค้ดแบบเต็มสำหรับการจัดการไวยากรณ์ในฐานะคอนโทรลเลอร์

Directive:

app.directive('inputFocusFunction', function () {
    'use strict';
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            scope[attr.inputFocusFunction] = function () {
                element[0].focus();
            };
        }
    };
});

และใน html:

<input input-focus-function="focusOnSaveInput" ng-model="saveName">
<button ng-click="focusOnSaveInput()">Focus</button>

หรือในคอนโทรลเลอร์:

$scope.focusOnSaveInput();

แก้ไขเพื่อให้คำอธิบายเพิ่มเติมเกี่ยวกับเหตุผลของแนวทางนี้และเพื่อขยายข้อมูลโค้ดสำหรับการใช้งานคอนโทรลเลอร์


เป็นเรื่องที่ดีมากและทำงานได้ดีสำหรับฉัน แต่ตอนนี้ฉันมีชุดอินพุตที่ใช้ng-repeatและฉันต้องการตั้งค่าฟังก์ชันโฟกัสสำหรับอันแรกเท่านั้น มีความคิดอย่างไรที่ฉันจะตั้งค่าฟังก์ชันโฟกัส<input>ตามเงื่อนไข$indexตัวอย่างเช่น
Garret Wilson

ดีใจที่มีประโยชน์ เชิงมุม 1 ของฉันเป็นสนิมเล็กน้อย แต่คุณควรจะสามารถเพิ่มแอตทริบิวต์อื่นให้กับอินพุตได้เช่นassign-focus-function-if="{{$index===0}}"จากนั้นเป็นบรรทัดแรกของคำสั่งออกก่อนกำหนดฟังก์ชันหากไม่เป็นความจริง: if (attr.assignFocusFunctionIf===false) return; หมายเหตุฉันกำลังตรวจสอบว่า มันชัดเจนfalseและไม่ใช่แค่เท็จดังนั้นคำสั่งจะยังคงใช้งานได้หากไม่ได้กำหนดแอตทริบิวต์นั้น
cstricklan

Controller-as นั้นง่ายกว่ามากด้วย lodash _.set(scope, attributes.focusOnSaveInput, function() { element.focus(); }).
Atomosk

9

คุณสามารถลอง

angular.element('#<elementId>').focus();

สำหรับเช่น

angular.element('#txtUserId').focus();

มันทำงานให้ฉัน


4
หมายเหตุ: สิ่งนี้จะใช้ได้เฉพาะเมื่อใช้ jQuery แบบเต็มแทนที่จะใช้ jqLite ที่ฝังอยู่ใน Angular ดูdocs.angularjs.org/api/ng/function/angular.element
John Rix

4
นี่คือวิธี jQuery ในการทำสิ่งนี้ไม่ใช่วิธีเชิงมุม คำถามนี้ถามเป็นพิเศษสำหรับวิธีการทำในลักษณะเชิงมุม
อภัย

4

อีกทางเลือกหนึ่งคือการใช้สถาปัตยกรรม Pub-Sub ในตัวของ Angular เพื่อแจ้งคำสั่งให้โฟกัส คล้ายกับวิธีการอื่น ๆ แต่ไม่ได้เชื่อมโยงโดยตรงกับคุณสมบัติและแทนที่จะรับฟังขอบเขตของคีย์เฉพาะ

Directive:

angular.module("app").directive("focusOn", function($timeout) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$on(attrs.focusOn, function(e) {
        $timeout((function() {
          element[0].focus();
        }), 10);
      });
    }
  };
});

HTML:

<input type="text" name="text_input" ng-model="ctrl.model" focus-on="focusTextInput" />

ควบคุม:

//Assume this is within your controller
//And you've hit the point where you want to focus the input:
$scope.$broadcast("focusTextInput");

3

ฉันชอบที่จะใช้นิพจน์ สิ่งนี้ช่วยให้ฉันทำสิ่งต่างๆเช่นโฟกัสที่ปุ่มเมื่อฟิลด์ถูกต้องถึงความยาวที่กำหนดและแน่นอนหลังจากโหลด

<button type="button" moo-focus-expression="form.phone.$valid">
<button type="submit" moo-focus-expression="smsconfirm.length == 6">
<input type="text" moo-focus-expression="true">

ในรูปแบบที่ซับซ้อนสิ่งนี้ยังช่วยลดความจำเป็นในการสร้างตัวแปรขอบเขตเพิ่มเติมเพื่อวัตถุประสงค์ในการโฟกัส

ดูhttps://stackoverflow.com/a/29963695/937997

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.