Facile manipolazione di dom in AngularJS: fare clic su un pulsante, quindi impostare lo stato attivo su un elemento di input

Ho questo codice angular:

{{element.name}}

Cosa voglio che succeda: quando l’utente fa clic sul pulsante, l’elemento di input sarà focalizzato.

Come trovo l’elemento di input dopo aver fatto clic sull’elemento del pulsante e averlo messo a fuoco?

Posso fare una funzione simile a questa:

 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(); } 

Nessuno dei due funziona – Esiste un modo angular migliore per farlo? Usando funzioni come .closest() e .find() come in jQuery?

Aggiornare:

Ho trovato questo hack funzionare (ma non sembra ancora la soluzione corretta) :

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

Lo sto avvolgendo con setTimeout, quindi dopo che Angular ha terminato tutte le sue manipolazioni si concentra sull’elemento input.

La manipolazione del DOM dovrebbe essere in una direttiva anziché nel controller. Definirei una direttiva focusInput e usarla sul pulsante:

 
{{element.name}}

Direttiva:

 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

Poiché jqLite è piuttosto limitato in termini di metodi di attraversamento DOM, ho dovuto usare parent().parent() . Potresti voler usare jQuery o alcuni metodi JavaScript.

Come hai già scoperto, $timeout è necessario in modo che il metodo focus() venga chiamato dopo il rendering del browser (cioè, termina la gestione dell’evento click).

find('input')[0] ci dà accesso all’elemento DOM, permettendoci di usare il metodo JavaScript focus() (piuttosto che find('input').focus() che richiederebbe jQuery).

Di recente ho dato un’occhiata ad AngularJS e mi sono imbattuto in una situazione simile.

Stavo lavorando per aggiornare l’applicazione di esempio Todo dalla pagina angular principale per aggiungere una modalità di “modifica” quando si fa doppio clic su un object todo.

Sono stato in grado di risolvere il mio problema utilizzando un approccio basato su modello / stato. Se la tua applicazione funziona in modo simile (vuoi mettere a fuoco un campo quando alcune condizioni del modello sono vere), allora potrebbe funzionare anche per te.

Il mio approccio è impostare la proprietà model.editing su true quando l’utente fa doppio clic sull’etichetta todo: mostra l’input modificabile e nasconde l’etichetta e la casella di controllo non modificabili. Abbiamo anche una direttiva personalizzata chiamata focusInput che ha un orologio sulla stessa proprietà model.editing e imposterà il focus sul campo di testo quando il valore cambia:

 
  • Ecco la direttiva focusInput che imposta lo stato attivo sull’elemento corrente quando alcune condizioni 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 }); }); }); }; } ]); 

    Ecco una direttiva che triggers un evento focus su un elemento dom target:

    Direttiva angular:

     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); } }); }); } }; }); 

    L’html:

       

    Un esempio dal vivo su Plunker

    Ho dovuto creare un account solo per fornire la risposta facile.

     //Add a bool to your controller's 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();}); }); } }) ...  ... 
    ...
    ... ...

    Assicurati di resettare questa variabile quando non stai usando l’input. È ansible aggiungere una direttiva ng-blur abbastanza semplice da cambiarla o un altro evento di ng-clic che lo reimposta su falso. Impostandolo su false, è appena pronto per la prossima volta. Ecco un esempio di direttiva ng-blur che ho trovato nel caso in cui abbiate difficoltà a trovarne uno.

     .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}); }); }); } }]); 

    Ecco cosa ho scoperto. Ho iniziato con la soluzione di Mark Rajcok in alto e poi mi sono trasferito per renderlo facile da riutilizzare. È configurabile e non richiede alcun codice nel controller. Il focus è puro aspetto di presentazione e non dovrebbe richiedere il codice del controller

    html:

      

    direttiva:

     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(); }); }); } }; }); 

    assunzione:

    • Il tuo donatore e il tuo acquirente sono nelle vicinanze.
    • quando si utilizza questo più volte su una pagina, gli ID utilizzati sono univoci o i beneficiari si trovano in un ramo isolato del DOM.

    Per l’utilizzo del metodo .closest () suggerisco di applicare il meccanismo di ereditarietà del prototipo per espandere le opportunità angolari. Proprio come questo:

     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 

    markup:

      

    Uso:

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

    Per trovare input, aggiungilo Id e passa ngRepeat index come parametro per funzionare ng-click="doSomething(element,$event, $index)"

      
    {{element.name}}

    In funzione usa $timeout con zero delay per aspettare fino alla fine del rendering DOM. Quindi l’input può essere trovato da getElementById nella funzione $timeout . Non dimenticare di aggiungere $timeout al controller.

     .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(); }); } });