AngularJS uygulamama bazı küçük yardımcı program işlevlerini nasıl ekleyebilirim?


147

AngularJS uygulamama bazı yardımcı fonksiyonlar eklemek istiyorum. Örneğin:

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

Bunları hizmet olarak eklemenin en iyi yolu bu mu? Okuduklarımdan bunu yapabilirim ama sonra bunları HTML sayfalarımda kullanmak istiyorum, bu yüzden bir hizmette olsalar hala mümkün mü? Örneğin aşağıdakileri kullanabilir miyim:

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

Bunları nasıl ekleyebileceğime dair bir örnek bana verebilir mi? Bir hizmet oluşturmalı mıyım yoksa bunu yapmanın başka bir yolu var mı? En önemlisi, bu yardımcı program işlevlerinin bir dosyada olmasını ve ana kurulumun başka bir bölümüyle birleştirilmesini istemem.

Birkaç çözüm olduğunu anlıyorum ama hiçbiri o kadar net değil.

1. Çözüm - Kent tarafından önerilen

$scope.doSomething = ServiceName.functionName;

Buradaki sorun benim 20 fonksiyonum ve on kontrol cihazım olması. Bunu yaparsam, her bir denetleyiciye çok fazla kod eklemek anlamına gelir.

2.Çözüm - Tarafımdan önerildi

    var factory = {

        Setup: function ($scope) {

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

Bunun dezavantajı, her denetleyicinin başlangıcında, $ kapsamını geçen her hizmete bu Kurulum çağrılarından bir veya daha fazlasına sahip olmamdır.

3. Çözüm - Urban tarafından önerildi

Urban tarafından genel bir hizmet yaratmanın önerdiği çözüm iyi görünüyor. İşte ana kurulumum:

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

Buna jenerik hizmeti eklemeli miyim ve bunu nasıl yapabilirim?


Yanıtlar:


108

01.07.2015 DÜZENLE:

Bu cevabı çok uzun zaman önce yazmıştım ve bir süredir açısal ile pek uymuyordum, ancak bu cevap hala nispeten popüler gibi görünüyor, bu yüzden @nicolas'ın birkaç noktasını belirtmek istedim. aşağıdaki markalar iyidir. Birincisi, $ rootScope enjekte etmek ve yardımcıları oraya eklemek, sizi her denetleyici için eklemek zorunda kalmayacaktır. Ayrıca - Eklediğiniz şeyin Angular hizmetleri VEYA filtreleri olarak düşünülmesi gerekiyorsa, bu şekilde koda uyarlanması gerektiğini kabul ediyorum.

Ayrıca, mevcut sürüm 1.4.2'den itibaren, Angular, yapılandırma bloklarına enjekte edilmesine izin verilen bir "Sağlayıcı" API'sini açığa çıkarmaktadır. Daha fazlası için şu kaynaklara bakın:

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

Module.config içindeki değerin AngularJS bağımlılığı enjeksiyonu

Aşağıdaki gerçek kod bloklarını güncelleyeceğimi sanmıyorum, çünkü bugünlerde Angular'ı gerçekten aktif olarak kullanmıyorum ve yeni bir cevabı gerçekten en iyiye uyduğundan emin olmadan tehlikeye atmak istemiyorum uygulamalar. Bir başkası kendini iyi hissederse, elbette onun peşinden git.

2/3/14 DÜZENLE:

Bunu düşündükten ve diğer cevapların bazılarını okuduktan sonra, @Brent Washburne ve @Amogh Talpallikar'ın ortaya koyduğu yöntemin bir çeşitlemesini tercih ettiğimi düşünüyorum. Özellikle isNotString () veya benzeri yardımcı programlar arıyorsanız. Buradaki açık avantajlardan biri, bunları açısal kodunuzun dışında yeniden kullanabilmeniz ve bunları yapılandırma işlevinizin (hizmetlerle yapamayacağınız) içinde kullanabilmenizdir.

Bununla birlikte, hizmetleri düzgün bir şekilde yeniden kullanmanın genel bir yolunu arıyorsanız, eski cevabın hala iyi olduğunu düşünüyorum.

Şimdi yapacağım şey:

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

Daha sonra kısmi olarak şunları kullanabilirsiniz:

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

Aşağıdaki eski cevap:

Bunları bir hizmet olarak dahil etmek en iyisi olabilir. Bunları birden çok denetleyicide yeniden kullanacaksanız, bir hizmet olarak da dahil olmak üzere, kodu tekrarlamak zorunda kalmayacaksınız.

Kısmi html'nizdeki hizmet işlevlerini kullanmak istiyorsanız, bunları denetleyicinin kapsamına eklemelisiniz:

$scope.doSomething = ServiceName.functionName;

Daha sonra kısmi olarak şunları kullanabilirsiniz:

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

İşte tüm bunları organize ve çok fazla güçlükten uzak tutmanın bir yolu:

Denetleyicinizi, hizmet ve yönlendirme kodunuzu / yapılandırmanızı üç dosyaya ayırın: controllers.js, services.js ve app.js. Üst katman modülü, bağımlılık olarak app.controllers ve app.services içeren "app" dir. Ardından app.controllers ve app.services kendi dosyalarında modüller olarak bildirilebilir. Bu organizasyon yapısı sadece Angular Seed'den alınmıştır :

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

Daha sonra kısmi olarak şunları kullanabilirsiniz:

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

Bu şekilde, her bir denetleyiciye yalnızca bir satır kod eklersiniz ve kapsamın erişilebilir olduğu her yerde hizmet işlevlerinden herhangi birine erişebilirsiniz.


Bu işlevlerden belki yirmi tane var ve bunları birden çok denetleyicide kullanmak istiyorum. Bunu düşündüm ama şu gibi bir koda sahip olmak o kadar pratik değil: $ kapsam.doSomething = HizmetAdı.functionName; her denetleyicinin içinde. Sorumu biraz daha ayrıntıyla güncelleyeceğim. teşekkürler
Alan2

evet, bu, hizmetlerdeki her işlev için bir satır eklemeniz gerekiyorsa mantıklıdır, ancak tüm hizmeti (tüm işlevleriyle birlikte) kapsama bir satırda ekleyebilirseniz, bunun mantıklı olduğunu düşünüyorum. Bahsettiğiniz çözüm 2'nin nasıl işe yarayacağı konusunda çok net değilim.
urban_raccoons

1
@urban_racoons: Ben de bu şekilde başladım, ama ne yazık ki bu tür hizmetleri config'de enjekte edemezsiniz. Başlığa belirteç eklemek için bir engelleyici içindeki auth_service hizmetime erişmek istedim, ancak daha sonra hizmetin yapılandırma içinde enjekte edilemeyeceğini fark ettim. sadece sabitler yapabilir. Sabitlere fonksiyon eklemenin daha iyi bir yaklaşım olması gerektiğini düşünüyorum.
Amogh Talpallikar

1
Sadece soruyorum, çünkü öncelikle bir JS çalışanı değilim, ancak kendi ad alanınızı kullanmak bir kopya mı yoksa bir singleton oluşturur mu? Bir ton modülünüz varsa, özellikle tek bir yardımcı kullanmak istiyorsanız, aynı hizmetin kopyalarına sahip olmak bellek kaybı gibi görünür.
Eric Keyte

3
@EricKeyte İsim alanı, JS'de oldukça yaygın olan bir tür singleton olan bir nesne değişmezidir. Geciken cevap için özür dilerim :)
urban_raccoons

32

Bu eski konuya gelince, bunu vurgulamak istedim

1 °) yardımcı program işlevleri, module.run aracılığıyla kök kapsama eklenebilir (gerekir mi?). Bu amaç için belirli bir kök düzey denetleyiciye gerek yoktur.

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

2 °) Kodunuzu ayrı modüller halinde düzenlerseniz, açısal hizmetler veya fabrika kullanmalı ve ardından bunları aşağıdaki gibi çalıştırma bloğuna iletilen işleve eklemelisiniz :

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 °) Anladığım kadarıyla, görünümlerde, çoğu durumda, görüntülediğiniz dizelere bir tür biçimlendirme uygulamak için bu yardımcı işlevlere ihtiyacınız var. Bu son durumda ihtiyacınız olan şey açısal filtreler kullanmaktır

Ve bazı düşük seviyeli yardımcı yöntemleri açısal servislere veya fabrikaya yapılandırdıysanız, bunları filtre yapıcınıza enjekte edin:

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

Ve size göre:

{{ aString | myFilter }}   

Her iki çözüm de runzamanla ilgilidir. Yapılandırma zamanı ne olacak? Orada yardımcı programlara ihtiyacımız yok mu?
Cyril CHAPON

config time bir sağlayıcıya ihtiyacınız var (bir tür hizmet) angular js doc
nicolas

1
3 numaralı çözüm bana en iyisi gibi görünüyor. Bir filtre kaydedildikten sonra onu başka bir yerde kullanabilirsiniz. Para birimi biçimlendirmem ve tarih biçimlendirmem için kullandım.
Olantobi

6

Sadece bazı yardımcı program yöntemlerini tanımlamak ve bunları şablonlarda kullanılabilir hale getirmek istediğinizi doğru anlıyor muyum?

Bunları her denetleyiciye eklemeniz gerekmez. Tüm yardımcı program yöntemleri için tek bir denetleyici tanımlayın ve bu denetleyiciyi <html> veya <body> 'ye ekleyin (ngController yönergesini kullanarak). <html> (herhangi bir yer, nokta anlamına gelir) veya <body> (<head> dışında herhangi bir yer) altında herhangi bir yere eklediğiniz diğer denetleyiciler, bu $ kapsamını devralır ve bu yöntemlere erişebilir.


1
bu kesinlikle bunu yapmanın en iyi yoludur. Sadece bir yardımcı program denetleyicisine sahip olun ve onu tüm projenin sarmalayıcı / kapsayıcı bölmesine koyun, içindeki tüm denetleyiciler miras alır: <div class="main-container" ng-controller="UtilController as util">o zaman herhangi bir iç görünümde:<button ng-click="util.isNotString(abc)">
Ian J Miller

4

Yardımcı program işlevleri eklemenin en kolay yolu, bunları küresel düzeyde bırakmaktır:

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

Ardından, bir yardımcı program işlevi (bir denetleyiciye) eklemenin en basit yolu, onu aşağıdaki $scopegibi atamaktır :

$scope.doSomething = myUtilityFunction;

O zaman şöyle diyebilirsiniz:

{{ doSomething(x) }}

veya bunun gibi:

ng-click="doSomething(x)"

DÜZENLE:

Asıl soru, bir yardımcı program işlevi eklemenin en iyi yolunun bir hizmet aracılığıyla olup olmadığıdır. İşlev yeterince basitse ( isNotString()OP tarafından sağlanan örnek gibi) hayır diyorum .

Bir hizmet yazmanın yararı, test amacıyla onu başka biriyle (enjeksiyon yoluyla) değiştirmektir. Uç noktalarda, her bir yardımcı program işlevini kontrol cihazınıza eklemeniz gerekiyor mu?

Dokümantasyon, denetleyicideki (gibi $scope.double) davranışı basitçe tanımlamayı söylüyor : http://docs.angularjs.org/guide/controller


Hizmet olarak yardımcı program işlevlerine sahip olmak, onlara denetleyicilerinizden seçmeli olarak erişmenizi sağlar .. eğer hiçbir denetleyici bunları kullanmazsa, hizmet başlatılmaz.
StuR

Aslında yaklaşımınızı beğendim ve onu düzenlenmiş cevabıma dahil ettim. Birisinin küresel işlev (ve ad alanı kirliliği) nedeniyle sizi olumsuz oylamış olabileceğini düşünüyorum, ancak bu kadar el tutmanın gerekli olduğunu düşünürseniz muhtemelen yazdığıma benzer bir yaklaşımı dahil edeceğinize dair bir his var. .
urban_raccoons

Şahsen, genel, küçük, yardımcı program işlevlerini küresel yapmakta bir sorun göremiyorum. Tipik olarak kod tabanınızın her yerinde kullandığınız şeyler olur, böylece herkes bunlara oldukça hızlı bir şekilde aşina olur. Bunları dilin küçük uzantıları olarak görün.
Cornel Masson

Düzenlemenizde "Belgeler denetleyicideki davranışı basitçe tanımlamayı söylüyor ($ kapsam.double gibi)". Belgelerin kontrolörlere yardımcı fonksiyonlar koymayı önerdiğini mi söylüyorsunuz?
losmescaleros


4

İşte kullandığım basit, kompakt ve anlaşılması kolay bir yöntem.
Önce js'nize bir hizmet ekleyin.

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;

    }]);

Ardından, denetleyicinizde yardımcı nesnenizi enjekte edin ve aşağıdaki gibi herhangi bir kullanılabilir işlevi kullanın:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

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

}]);

2
Bu, neden bazen Angular'ı sevmediğim bir sorunu açıkça gösteriyor: "bir hizmet ekle" demek ... ve sonra kodda yeni bir fabrika () oluşturmak. Tasarım modellerine bakıldığında, bunlar aynı şeyler değildir - fabrika genellikle yeni nesneler üretmek için kullanılır ve hizmet de bazı işlevler veya kaynaklar "hizmet etmek" içindir. Böyle anlarda "WT *, Angular" demek istiyorum.
JustAMartin

1

Sabit hizmeti de bu şekilde kullanabilirsiniz. Sabit çağrının dışında işlevi tanımlamak, aynı zamanda özyinelemeli olmasına izin verir.

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

Neden denetleyici kalıtım kullanılmıyorsa, HeaderCtrl kapsamında tanımlanan tüm yöntemlere / özelliklere ng-view içindeki denetleyiciden erişilebilir. $ kapsam.servHelper'a tüm denetleyicilerinizden erişilebilir.

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


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.