Как я могу добавить несколько небольших служебных функций в мое приложение AngularJS?


146

Я хотел бы добавить некоторые служебные функции в мое приложение AngularJS. Например:

$scope.isNotString = function (str) {
    return (typeof str !== "string");
}

Это лучший способ сделать это, чтобы добавить их в качестве службы? Из того, что я прочитал, я могу сделать это, но затем я хотел бы использовать их на своих HTML-страницах, так что все еще возможно, если они находятся в службе? Например, я могу использовать следующее:

 <button data-ng-click="doSomething()"
         data-ng-disabled="isNotString(abc)">Do Something
 </button>

Может кто-нибудь дать мне пример того, как я мог бы добавить это. Должен ли я создать сервис или есть какой-то другой способ сделать это. Самое главное, чтобы эти служебные функции были в файле, а не в сочетании с другой частью основной настройки.

Я понимаю, что есть несколько решений, но ни одно из них не так ясно.

Решение 1 - Предложено Urban

$scope.doSomething = ServiceName.functionName;

Проблема в том, что у меня 20 функций и десять контроллеров. Если бы я сделал это, это означало бы добавление большого количества кода для каждого контроллера.

Решение 2 - предложено мной

    var factory = {

        Setup: function ($scope) {

            $scope.isNotString = function (str) {
                return (typeof str !== "string");
            }

Недостатком этого является то, что при запуске каждого контроллера у меня будет один или несколько из этих вызовов установки для каждой службы, которая прошла область $.

Решение 3 - Предложено Urban

Городское решение по созданию универсального сервиса выглядит хорошо. Вот моя основная установка:

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

Должен ли я добавить общий сервис к этому и как я могу это сделать?


проверьте мой ответ здесь stackoverflow.com/a/51464584/4251431
Башир АЛЬ-МОМАНИ

Ответы:


107

РЕДАКТИРОВАТЬ 7/1/15:

Я написал этот ответ довольно давно и не слишком долго следил за углом, но кажется, что этот ответ все еще относительно популярен, поэтому я хотел бы отметить, что пара моментов @nicolas делает ниже хороши. Например, добавление $ rootScope и добавление туда помощников избавит вас от необходимости добавлять их для каждого контроллера. Кроме того - я согласен с тем, что если то, что вы добавляете, следует рассматривать как фильтры ИЛИ Angular, они должны быть включены в код таким образом.

Кроме того, начиная с текущей версии 1.4.2, Angular предоставляет API «Провайдер», который разрешено вставлять в блоки конфигурации. Смотрите эти ресурсы для получения дополнительной информации:

https://docs.angularjs.org/guide/module#module-loading-dependencies

Внедрение зависимости AngularJS в значение внутри module.config

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

РЕДАКТИРОВАТЬ 2/3/14:

Подумав об этом и прочитав некоторые другие ответы, я действительно предпочитаю вариант метода, предложенного @Brent Washburne и @Amogh Talpallikar. Особенно, если вы ищете утилиты, такие как isNotString () или аналогичные. Одно из явных преимуществ заключается в том, что вы можете повторно использовать их вне своего углового кода и использовать их внутри своей функции конфигурации (чего нельзя сделать со службами).

При этом, если вы ищете общий способ повторно использовать то, что должно быть должным образом сервисами, я думаю, что старый ответ все еще хорош.

Что бы я сделал сейчас:

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

Тогда в своей части вы можете использовать:

<button data-ng-click="console.log(helpers.isNotString('this is a string'))">Log String Test</button>

Старый ответ ниже:

Возможно, было бы лучше включить их в качестве службы. Если вы собираетесь повторно использовать их на нескольких контроллерах, включая их в качестве службы, вам не придется повторять код.

Если вы хотите использовать сервисные функции в html-части, то вы должны добавить их в область действия этого контроллера:

$scope.doSomething = ServiceName.functionName;

Тогда в своей части вы можете использовать:

<button data-ng-click="doSomething()">Do Something</button>

Вот способ, которым вы могли бы сохранить все это организованным и без лишних хлопот:

Разделите ваш контроллер, сервис и код маршрутизации / config на три файла: controllers.js, services.js и app.js. Модуль верхнего уровня - это «приложение», в котором зависимости app.controllers и app.services являются зависимостями. Затем app.controllers и app.services могут быть объявлены как модули в своих собственных файлах. Эта организационная структура только что взята из 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;
  });

Тогда в своей части вы можете использовать:

<button data-ng-click="genericServices.doSomething()">Do Something</button>
<button data-ng-click="genericServices.doSomethingElse()">Do Something Else</button>

Таким образом, вы добавляете только одну строку кода к каждому контроллеру и можете получить доступ к любой из функций служб, где бы ни была доступна эта область.


У меня может быть двадцать из этих функций, и я хочу использовать их в нескольких контроллерах. Я думал об этом, но не очень удобно иметь такой код: $ scope.doSomething = ServiceName.functionName; внутри каждого контроллера. Я обновлю свой вопрос с более подробной информацией. спасибо
Alan2

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

1
@urban_racoons: я тоже так начал, но, к сожалению, вы не можете внедрить такие сервисы в конфиге. Я хотел получить доступ к своему auth_service внутри перехватчика, чтобы добавить токен в заголовок, но потом я понял, что сервис не может быть введен в config. могут только константы. Я думаю, что добавление функций к константам должно быть лучшим подходом.
Амог Талпалликар

1
Я просто спрашиваю, потому что я в первую очередь не парень JS, но с помощью собственного пространства имен получу копию или синглтон? Если у вас есть тонна модулей, то копирование одной и той же службы кажется пустой тратой памяти, особенно если вы хотите использовать только один помощник.
Эрик Кейт

3
@EricKeyte Пространство имен - это объектный литерал, который является своего рода синглтоном, который довольно распространен в JS. Извините за задержку с ответом :)
urban_raccoons

32

Возвращаясь к этой старой теме, я хотел бы подчеркнуть, что

1 °) служебные функции могут (должны?) Быть добавлены в корневую область через module.run. Для этого нет необходимости создавать конкретный контроллер корневого уровня.

angular.module('myApp').run(function($rootScope){
  $rootScope.isNotString = function(str) {
   return (typeof str !== "string");
  }
});

2 °) Если вы организуете свой код в отдельные модули, вы должны использовать угловые сервисы или фабрику, а затем внедрить их в функцию, переданную в блок выполнения, следующим образом:

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 °) Насколько я понимаю, в представлениях в большинстве случаев вам нужны эти вспомогательные функции для применения какого-либо форматирования к отображаемым строкам. В этом последнем случае вам нужно использовать угловые фильтры

И если вы структурировали некоторые вспомогательные методы низкого уровня в угловые сервисы или фабрики, просто внедрите их в свой конструктор фильтра:

angular.module('myApp').filter('myFilter', function(myHelperMethods){ 
  return function(aString){
    if (myHelperMethods.isNotString(aString)){
      return 
    }
    else{
      // something else 
    }
  }
);

И по вашему мнению:

{{ aString | myFilter }}   

Оба решения касаются runвремени. Как насчет времени конфигурации? Разве нам не нужны коммунальные услуги там?
Кирилл ЧАПОН

Конфиг время вам нужен провайдер (вид услуг) оформить заказ угловой JS документ
Николас

1
Решение № 3 кажется мне лучшим. Как только фильтр зарегистрирован, вы можете использовать его где угодно. Я использовал его для форматирования своей валюты и форматирования даты.
Олантоби

6

Правильно ли я понимаю, что вы просто хотите определить некоторые служебные методы и сделать их доступными в шаблонах?

Вам не нужно добавлять их в каждый контроллер. Просто определите один контроллер для всех служебных методов и подключите этот контроллер к <html> или <body> (используя директиву ngController). Любые другие контроллеры, которые вы присоединяете в любом месте под <html> (имеется в виду где угодно, точка) или <body> (где угодно, кроме <head>), будут наследовать этот $ scope и иметь доступ к этим методам.


1
это определенно лучший способ сделать это. Просто имейте служебный контроллер и поместите его в div-оболочку / контейнер всего проекта, все контроллеры внутри будут наследовать: <div class="main-container" ng-controller="UtilController as util">тогда в любых внутренних видах:<button ng-click="util.isNotString(abc)">
Ian J Miller

4

Самый простой способ добавить служебные функции - оставить их на глобальном уровне:

function myUtilityFunction(x) { return "do something with "+x; }

Затем самый простой способ добавить служебную функцию (к контроллеру) - назначить ее $scopeследующим образом:

$scope.doSomething = myUtilityFunction;

Тогда вы можете назвать это так:

{{ doSomething(x) }}

или вот так:

ng-click="doSomething(x)"

РЕДАКТИРОВАТЬ:

Первоначальный вопрос заключается в том, является ли лучший способ добавить служебную функцию через службу. Я говорю нет, если функция достаточно проста (как в isNotString()примере, представленном ОП).

Преимущество написания службы состоит в том, чтобы заменить ее на другую (посредством внедрения) для целей тестирования. В крайности, нужно ли вам вводить каждую функцию в свой контроллер?

Документация говорит, чтобы просто определить поведение в контроллере (например $scope.double): http://docs.angularjs.org/guide/controller


Наличие служебных функций в качестве службы позволяет вам выборочно обращаться к ним в ваших контроллерах ... если их не используют никакие контроллеры, то служба не будет создана.
StuR

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

Лично я не вижу проблемы в том, чтобы сделать общие, маленькие, вспомогательные функции глобальными. Обычно это вещи, которые вы используете по всей своей кодовой базе, поэтому любой может ознакомиться с ними довольно быстро. Рассматривайте их как небольшие расширения языка.
Корнел Массон

В вашем редакторе вы упоминаете «Документация говорит просто определить поведение в контроллере (например, $ scope.double)». Вы говорите, что документация предлагает поместить служебные функции в контроллеры?
losmescaleros

@losmescaleros Да, прочитайте раздел «Добавление поведения к объекту области» в документации docs.angularjs.org/guide/controller
Брент Уошборн,

4

Вот простой, компактный и легкий для понимания метод, который я использую.
Сначала добавьте сервис в свой 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;

    }]);

Затем в вашем контроллере внедрите ваш вспомогательный объект и используйте любую доступную функцию с чем-то вроде следующего:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

    $scope.sayIt = Helpers.getFooBar("Hello");
    console.log($scope.sayIt);

}]);

2
Это ясно показывает одну проблему, почему мне иногда не нравится Angular: сказать «добавить службу» ... а затем в коде создать новую фабрику (). От шаблонов проектирования, это не одно и то же - фабрика обычно используется для производства новых объектов, а сервис - это «обслуживание» некоторой функциональности или ресурса. В такие моменты я хочу сказать "WT *, Angular".
JustAMartin

1

Вы также можете использовать постоянный сервис как таковой. Определение функции вне константного вызова позволяет также быть рекурсивным.

function doSomething( a, b ) {
    return a + b;
};

angular.module('moduleName',[])
    // Define
    .constant('$doSomething', doSomething)
    // Usage
    .controller( 'SomeController', function( $doSomething ) {
        $scope.added = $doSomething( 100, 200 );
    })
;

0

Почему бы не использовать наследование контроллера, все методы / свойства, определенные в области действия HeaderCtrl, доступны в контроллере внутри ng-view. $ scope.servHelper доступен во всех ваших контроллерах.

    angular.module('fnetApp').controller('HeaderCtrl', function ($scope, MyHelperService) {
      $scope.servHelper = MyHelperService;
    });


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.