angularjs - imagen - Establecer el foco del elemento en forma angular
ng-bind-html unsafe (6)
Después de buscar ejemplos de cómo establecer elementos de enfoque con angular, vi que la mayoría de ellos usan alguna variable para observar y luego establecer el enfoque, y la mayoría de ellos usan una variable diferente para cada campo que desean establecer. En una forma, con muchos campos, eso implica muchas variables diferentes.
Con JQuery en mente, pero queriendo hacerlo de manera angular, hice una solución en la que fijamos el foco en cualquier función usando la identificación del elemento, por lo que, como soy muy nuevo en angular, me gustaría obtener algunas opiniones si de esa manera es correcto, tengo problemas, lo que sea, cualquier cosa que pueda ayudarme a hacer esto de la mejor manera en angular.
Básicamente, creo una directiva que observa un valor de ámbito definido por el usuario con la directiva, o el elemento de enfoque predeterminado, y cuando ese valor es el mismo que la identificación del elemento, ese elemento establece el foco en sí mismo.
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();
})
}
};
});
Una entrada que necesita ser enfocada por alguna razón, lo hará de esta manera
<input my-focus id="input1" type="text" />
Aquí cualquier elemento para establecer el foco:
<a href="" ng-click="clickButton()" >Set focus</a>
Y la función de ejemplo que establece el foco:
$scope.clickButton = function() {
$scope.focusElement = "input1";
}
¿Es esa una buena solución en angular? ¿Tiene problemas que con mi mala experiencia aún no veo?
Acerca de esta solución, podríamos crear una directiva y adjuntarla al elemento DOM que debe enfocarse cuando se cumple una determinada condición. Al seguir este enfoque, evitamos el acoplamiento del controlador a las ID del elemento DOM.
Directiva de código de muestra:
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();
});
}
});
}
};
}
]);
Un posible uso
.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">
El problema con su solución es que no funciona bien cuando está vinculado a otras directivas que crean un nuevo alcance, por ejemplo,
ng-repeat
.
Una solución mejor sería simplemente crear una función de servicio que le permita enfocar elementos imperativamente dentro de sus controladores o enfocar elementos declarativamente en el html.
JAVASCRIPT
Servicio
.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();
});
};
});
Directiva
.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);
});
};
});
Controlador
.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>
Me gusta evitar las búsquedas DOM, los relojes y los emisores globales siempre que sea posible, por lo que utilizo un enfoque más directo. Use una directiva para asignar una función simple que se centre en el elemento de la directiva. Luego llame a esa función siempre que sea necesario dentro del alcance del controlador.
Aquí hay un enfoque simplificado para adjuntarlo al alcance. Vea el fragmento completo para manejar la sintaxis del controlador.
Directiva:
app.directive(''inputFocusFunction'', function () {
''use strict'';
return {
restrict: ''A'',
link: function (scope, element, attr) {
scope[attr.inputFocusFunction] = function () {
element[0].focus();
};
}
};
});
y en html:
<input input-focus-function="focusOnSaveInput" ng-model="saveName">
<button ng-click="focusOnSaveInput()">Focus</button>
o en el controlador:
$scope.focusOnSaveInput();
angular.module(''app'', [])
.directive(''inputFocusFunction'', function() {
''use strict'';
return {
restrict: ''A'',
link: function(scope, element, attr) {
// Parse the attribute to accomodate assignment to an object
var parseObj = attr.inputFocusFunction.split(''.'');
var attachTo = scope;
for (var i = 0; i < parseObj.length - 1; i++) {
attachTo = attachTo[parseObj[i]];
}
// assign it to a function that focuses on the decorated element
attachTo[parseObj[parseObj.length - 1]] = function() {
element[0].focus();
};
}
};
})
.controller(''main'', function() {});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<body ng-app="app" ng-controller="main as vm">
<input input-focus-function="vm.focusOnSaveInput" ng-model="saveName">
<button ng-click="vm.focusOnSaveInput()">Focus</button>
</body>
Editado para proporcionar más explicaciones sobre el motivo de este enfoque y para extender el fragmento de código para el uso del controlador.
Otra opción sería utilizar la arquitectura de pub-sub incorporada de Angular para notificar a su directiva para enfocarse. Similar a los otros enfoques, pero no está directamente relacionado con una propiedad, y en cambio está escuchando en su alcance una clave particular.
Directiva:
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" />
Controlador:
//Assume this is within your controller
//And you''ve hit the point where you want to focus the input:
$scope.$broadcast("focusTextInput");
Preferí usar una expresión. Esto me permite hacer cosas como enfocarme en un botón cuando un campo es válido, alcanza una cierta longitud y, por supuesto, después de la carga.
<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">
En una forma compleja, esto también reduce la necesidad de crear variables de alcance adicionales para fines de enfoque.
Ver https://.com/a/29963695/937997
Puedes probar
angular.element(''#<elementId>'').focus();
por ej.
angular.element(''#txtUserId'').focus();
Me está funcionando.