not - ¿Cómo puedo agregar algunas pequeñas funciones de utilidad a mi aplicación AngularJS?
init angular 1 (7)
Me gustaría agregar algunas funciones de utilidad a mi aplicación AngularJS. Por ejemplo:
$scope.isNotString = function (str) {
return (typeof str !== "string");
}
¿Es la mejor manera de hacer esto agregarlos como un servicio? Según lo que he leído, puedo hacer esto, pero me gustaría utilizarlos en mis páginas HTML, ¿aún es posible si están en un servicio? Por ejemplo, ¿puedo usar lo siguiente?
<button data-ng-click="doSomething()"
data-ng-disabled="isNotString(abc)">Do Something
</button>
¿Puede alguien darme un ejemplo de cómo podría agregar estos? ¿Debo crear un servicio o hay alguna otra forma de hacerlo? Lo más importante es que me gustaría que estas funciones de utilidad se encuentren en un archivo y no se combinen con otra parte de la configuración principal.
Entiendo que hay algunas soluciones, pero ninguna de ellas es tan clara.
Solución 1 - Propuesta de Urban
$scope.doSomething = ServiceName.functionName;
El problema aquí es que tengo 20 funciones y diez controladores. Si lo hiciera, significaría agregar una gran cantidad de código a cada controlador.
Solución 2 - Propuesto por mí
var factory = {
Setup: function ($scope) {
$scope.isNotString = function (str) {
return (typeof str !== "string");
}
La desventaja de esto es que al comienzo de cada controlador tendría una o más de estas llamadas de configuración para cada servicio que pasara el $ scope.
Solución 3 - Propuesto por Urban
La solución propuesta por Urban de crear un servicio genérico se ve bien. Aquí está mi configuración principal:
var app = angular
.module(''app'', [''ngAnimate'', ''ui.router'', ''admin'', ''home'', ''questions'', ''ngResource'', ''LocalStorageModule''])
.config([''$locationProvider'', ''$sceProvider'', ''$stateProvider'',
function ($locationProvider, $sceProvider, $stateProvider) {
$sceProvider.enabled(false);
$locationProvider.html5Mode(true);
¿Debo agregar el servicio genérico a esto y cómo podría hacerlo?
¿Entiendo correctamente que solo quiere definir algunos métodos de utilidad y ponerlos a disposición en plantillas?
No tiene que agregarlos a cada controlador. Simplemente defina un solo controlador para todos los métodos de utilidad y adjunte ese controlador a <html> o <body> (usando la directiva ngController). Cualquier otro controlador que adjunte en cualquier lugar en <html> (es decir, en cualquier lugar, punto) o <cuerpo> (en cualquier lugar menos <cabeza>) heredará ese $ scope y tendrá acceso a esos métodos.
Aquí hay un método simple, compacto y fácil de entender que uso.
Primero, agrega un servicio en tu js.
app.factory(''Helpers'', [ function() {
// Helper service body
var o = {
Helpers: []
};
// Dummy function with parameter being passed
o.getFooBar = function(para) {
var valueIneed = para + " " + "World!";
return valueIneed;
};
// Other helper functions can be added here ...
// And we return the helper object ...
return o;
}]);
Luego, en su controlador, inyecte su objeto auxiliar y use cualquier función disponible con algo como lo siguiente:
app.controller(''MainCtrl'', [
''$scope'',
''Helpers'',
function($scope, Helpers){
$scope.sayIt = Helpers.getFooBar("Hello");
console.log($scope.sayIt);
}]);
La forma más fácil de agregar funciones de utilidad es dejarlas en el nivel global:
function myUtilityFunction(x) { return "do something with "+x; }
Entonces, la forma más sencilla de agregar una función de utilidad (a un controlador) es asignarlo a $scope
, así:
$scope.doSomething = myUtilityFunction;
Entonces puedes llamarlo así:
{{ doSomething(x) }}
o así:
ng-click="doSomething(x)"
EDITAR:
La pregunta original es si la mejor manera de agregar una función de utilidad es a través de un servicio. Digo que no, si la función es lo suficientemente simple (como el ejemplo de isNotString()
proporcionado por el OP).
El beneficio de escribir un servicio es reemplazarlo por otro (mediante inyección) con el propósito de probarlo. Llevado al extremo, ¿necesita inyectar cada función de utilidad en su controlador?
La documentación dice que simplemente defina el comportamiento en el controlador (como $scope.double
): http://docs.angularjs.org/guide/controller
Por qué no utilizar la herencia del controlador, todos los métodos / propiedades definidos en el alcance de HeaderCtrl son accesibles en el controlador dentro de ng-view. $ scope.servHelper está disponible en todos sus controladores.
angular.module(''fnetApp'').controller(''HeaderCtrl'', function ($scope, MyHelperService) {
$scope.servHelper = MyHelperService;
});
<div ng-controller="HeaderCtrl">
<div ng-view=""></div>
</div>
Siguiendo este viejo hilo, quería enfatizar que
1 °) las funciones de utilidad pueden (¿debería?) Agregarse al ramoscopio a través de module.run. No hay necesidad de instanciar un controlador de nivel raíz específico para este propósito.
angular.module(''myApp'').run(function($rootScope){
$rootScope.isNotString = function(str) {
return (typeof str !== "string");
}
});
2 °) Si organiza su código en módulos separados, debe usar services angulares o de factory y luego insertarlos en la función pasada al bloque de ejecución, de la siguiente manera:
angular.module(''myApp'').factory(''myHelperMethods'', function(){
return {
isNotString: function(str) {
return (typeof str !== ''string'');
}
}
});
angular.module(''myApp'').run(function($rootScope, myHelperMethods){
$rootScope.helpers = myHelperMethods;
});
3 °) Tengo entendido que en las vistas, para la mayoría de los casos necesita estas funciones auxiliares para aplicar algún tipo de formato a las cadenas que visualiza. Lo que necesitas en este último caso es usar filters angulares
Y si ha estructurado algunos métodos auxiliares de bajo nivel en servicios angulares o de fábrica, simplemente inyéctelos dentro de su constructor de filtros:
angular.module(''myApp'').filter(''myFilter'', function(myHelperMethods){
return function(aString){
if (myHelperMethods.isNotString(aString)){
return
}
else{
// something else
}
}
);
Y en su opinión:
{{ aString | myFilter }}
También puede usar el servicio constante como tal. Definir la función fuera de la llamada constante también le permite ser recursivo.
function doSomething( a, b ) {
return a + b;
};
angular.module(''moduleName'',[])
// Define
.constant(''$doSomething'', doSomething)
// Usage
.controller( ''SomeController'', function( $doSomething ) {
$scope.added = $doSomething( 100, 200 );
})
;
EDITAR 01/07/15:
Escribí esta respuesta hace bastante tiempo y no he estado manteniendo un montón con angular por un tiempo, pero parece que esta respuesta todavía es relativamente popular, así que quería señalar que un par del punto @nicolas las marcas a continuación son buenas. Por un lado, inyectar $ rootScope y adjuntar los helpers evitará tener que agregarlos para cada controlador. Además, acepto que si lo que está agregando debe considerarse como filtros o servicios angulares, deben ser adoptados en el código de esa manera.
Además, a partir de la versión actual 1.4.2, Angular expone una API de "proveedor", que se permite inyectar en bloques de configuración. Vea estos recursos para más:
https://docs.angularjs.org/guide/module#module-loading-dependencies
Inyección de valor de dependencia AngularJS dentro de module.config
No creo que vaya a actualizar los bloques de códigos actuales a continuación, porque actualmente no estoy realmente usando Angular y realmente no quiero arriesgarme a una nueva respuesta sin sentirme cómoda de que en realidad se ajuste a la nueva mejoría prácticas. Si alguien más se siente a la altura, por supuesto, adelante.
EDITAR 2/3/14:
Después de pensar en esto y leer algunas de las otras respuestas, creo que prefiero una variación del método planteado por @Brent Washburne y @Amogh Talpallikar. Especialmente si está buscando utilidades como isNotString () o similar. Una de las claras ventajas aquí es que puede volver a utilizarlos fuera de su código angular y puede usarlos dentro de su función de configuración (que no puede hacer con los servicios).
Dicho esto, si está buscando una forma genérica de reutilizar lo que deberían ser servicios, la respuesta anterior creo que sigue siendo buena.
Lo que haría ahora es:
app.js:
var MyNamespace = MyNamespace || {};
MyNamespace.helpers = {
isNotString: function(str) {
return (typeof str !== "string");
}
};
angular.module(''app'', [''app.controllers'', ''app.services'']).
config([''$routeProvider'', function($routeProvider) {
// Routing stuff here...
}]);
controller.js:
angular.module(''app.controllers'', []).
controller(''firstCtrl'', [''$scope'', function($scope) {
$scope.helpers = MyNamespace.helpers;
});
Luego, en tu parcial puedes usar:
<button data-ng-click="console.log(helpers.isNotString(''this is a string''))">Log String Test</button>
Respuesta anterior a continuación:
Puede ser mejor incluirlos como un servicio. Si va a volver a utilizarlos en varios controladores, incluirlos como un servicio evitará que tenga que repetir el código.
Si desea utilizar las funciones de servicio en su html parcial, debe agregarlas al alcance de ese controlador:
$scope.doSomething = ServiceName.functionName;
Luego, en tu parcial puedes usar:
<button data-ng-click="doSomething()">Do Something</button>
Aquí hay una manera de mantener todo organizado y libre de demasiadas molestias:
Separe su controlador, servicio y código / configuración de enrutamiento en tres archivos: controllers.js, services.js y app.js. El módulo de la capa superior es "aplicación", que tiene app.controllers y app.services como dependencias. Luego, los controladores de aplicaciones y los servicios de aplicaciones pueden declararse como módulos en sus propios archivos. Esta estructura organizativa se acaba de tomar de Angular Seed :
app.js:
angular.module(''app'', [''app.controllers'', ''app.services'']).
config([''$routeProvider'', function($routeProvider) {
// Routing stuff here...
}]);
services.js:
/* Generic Services */
angular.module(''app.services'', [])
.factory("genericServices", function() {
return {
doSomething: function() {
//Do something here
},
doSomethingElse: function() {
//Do something else here
}
});
controller.js:
angular.module(''app.controllers'', []).
controller(''firstCtrl'', [''$scope'', ''genericServices'', function($scope, genericServices) {
$scope.genericServices = genericServices;
});
Luego, en tu parcial puedes usar:
<button data-ng-click="genericServices.doSomething()">Do Something</button>
<button data-ng-click="genericServices.doSomethingElse()">Do Something Else</button>
De esta forma, solo agrega una línea de código a cada controlador y puede acceder a cualquiera de las funciones de servicios allí donde se pueda acceder a dicho alcance.