Bagaimana saya bisa menambahkan beberapa fungsi utilitas kecil ke aplikasi AngularJS saya?

146

Saya ingin menambahkan beberapa fungsi utilitas ke aplikasi AngularJS saya. Sebagai contoh:

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

Apakah cara terbaik untuk melakukan ini untuk menambahkannya sebagai layanan? Dari apa yang telah saya baca saya dapat melakukan ini tetapi kemudian saya ingin menggunakannya di halaman HTML saya jadi apakah masih mungkin jika mereka berada dalam layanan? Misalnya saya dapat menggunakan yang berikut ini:

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

Dapatkah seseorang memberi saya contoh bagaimana saya bisa menambahkan ini. Haruskah saya membuat layanan atau ada cara lain untuk melakukannya. Paling penting saya ingin fungsi utilitas ini dalam file dan tidak dikombinasikan dengan bagian lain dari pengaturan utama.

Saya mengerti ada beberapa solusi tetapi tidak satupun yang jelas.

Solusi 1 - Diusulkan oleh Urban

$scope.doSomething = ServiceName.functionName;

Masalahnya di sini adalah saya memiliki 20 fungsi dan sepuluh pengendali. Jika saya melakukan ini, itu berarti menambahkan banyak kode ke setiap kontroler.

Solusi 2 - Diusulkan oleh saya

    var factory = {

        Setup: function ($scope) {

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

Kerugian dari ini adalah bahwa pada awal setiap controller saya akan memiliki satu atau lebih dari panggilan Setup untuk setiap layanan yang melewati $ scope.

Solusi 3 - Diusulkan oleh Urban

Solusi yang diajukan oleh kota untuk menciptakan layanan generik terlihat bagus. Inilah pengaturan utama saya:

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

Haruskah saya menambahkan layanan umum ke ini dan bagaimana saya bisa melakukannya?

Alan2
sumber
periksa jawaban saya di sini stackoverflow.com/a/51464584/4251431
Basheer AL-MOMANI

Jawaban:

107

EDIT 7/1/15:

Saya menulis jawaban ini cukup lama dan belum banyak menjaga sudut dengan sudut pandang untuk sementara waktu, tetapi sepertinya jawaban ini masih relatif populer, jadi saya ingin menunjukkan bahwa beberapa titik @nicolas membuat di bawah ini bagus. Pertama, menyuntikkan $ rootScope dan melampirkan pembantu di sana akan membuat Anda tidak perlu menambahkannya untuk setiap pengontrol. Juga - Saya setuju bahwa jika apa yang Anda tambahkan harus dianggap sebagai layanan ATAU filter, mereka harus diadopsi ke dalam kode dengan cara itu.

Juga, pada versi 1.4.2 saat ini, Angular mengekspos API "Penyedia", yang diizinkan untuk disuntikkan ke blok konfigurasi. Lihat sumber daya ini untuk lebih banyak:

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

Injeksi ketergantungan nilai AngularJS di dalam module.config

Saya tidak berpikir saya akan memperbarui blok kode aktual di bawah ini, karena saya tidak benar-benar aktif menggunakan Angular hari ini dan saya tidak benar-benar ingin membahayakan jawaban baru tanpa merasa nyaman bahwa itu benar-benar sesuai dengan yang terbaik baru praktik. Jika orang lain merasa sanggup melakukannya, tentu saja lakukanlah.

EDIT 2/3/14:

Setelah memikirkan hal ini dan membaca beberapa jawaban lain, saya sebenarnya berpikir saya lebih suka variasi metode yang dibawakan oleh @Brent Washburne dan @Amogh Talpallikar. Terutama jika Anda mencari utilitas seperti isNotString () atau serupa. Salah satu keuntungan yang jelas di sini adalah Anda dapat menggunakannya kembali di luar kode sudut Anda dan Anda dapat menggunakannya di dalam fungsi konfigurasi Anda (yang tidak dapat Anda lakukan dengan layanan).

Yang sedang berkata, jika Anda mencari cara umum untuk menggunakan kembali apa yang seharusnya menjadi layanan, jawaban lama saya pikir masih bagus.

Apa yang akan saya lakukan sekarang adalah:

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

Kemudian di parsial Anda, Anda dapat menggunakan:

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

Jawaban lama di bawah:

Mungkin yang terbaik untuk memasukkan mereka sebagai layanan. Jika Anda akan menggunakannya kembali di banyak pengontrol, termasuk mereka sebagai layanan akan membuat Anda tidak perlu mengulangi kode.

Jika Anda ingin menggunakan fungsi layanan di sebagian html Anda, maka Anda harus menambahkannya ke lingkup pengontrol itu:

$scope.doSomething = ServiceName.functionName;

Kemudian di parsial Anda, Anda dapat menggunakan:

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

Inilah cara Anda dapat menjaga ini semua terorganisir dan bebas dari kerumitan terlalu banyak:

Pisahkan pengontrol, layanan, dan kode perutean / konfigurasi menjadi tiga file: controllers.js, services.js, dan app.js. Modul lapisan atas adalah "app", yang memiliki app.controllers dan app.services sebagai dependensi. Kemudian app.controllers dan app.services dapat dideklarasikan sebagai modul dalam file mereka sendiri. Struktur organisasi ini diambil dari Benih Angular :

app.js:

 angular.module('app', ['app.controllers', 'app.services']).                             
   config(['$routeProvider', function($routeProvider) {
     // Routing stuff here...
   }]);  

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

Kemudian di parsial Anda, Anda dapat menggunakan:

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

Dengan begitu Anda hanya menambahkan satu baris kode ke setiap pengontrol dan dapat mengakses fungsi layanan mana pun di mana ruang lingkup itu dapat diakses.

urban_raccoons
sumber
Saya mungkin memiliki dua puluh fungsi ini dan saya ingin menggunakannya di banyak pengontrol. Saya memikirkan hal ini tetapi tidak begitu praktis untuk memiliki kode seperti: $ scope.doSomething = ServiceName.functionName; di dalam setiap controller. Saya akan memperbarui pertanyaan saya dengan sedikit lebih detail. terima kasih
Alan2
ya itu masuk akal jika Anda perlu menambahkan baris untuk setiap fungsi dalam layanan, tetapi jika Anda dapat menambahkan seluruh layanan (dengan semua fungsi itu) ke ruang lingkup dalam satu baris, saya pikir itu masuk akal. Saya tidak terlalu jelas tentang bagaimana solusi yang Anda sebutkan mungkin bekerja?
urban_raccoons
1
@urban_racoons: Saya juga mulai dengan cara ini, tetapi sayangnya Anda tidak dapat menyuntikkan layanan seperti itu dalam konfigurasi. Saya ingin mengakses layanan auth_ser saya di dalam interseptor untuk menambahkan token ke header tetapi kemudian saya menyadari bahwa layanan tidak dapat disuntikkan dalam konfigurasi. hanya konstanta yang bisa. Saya pikir menambahkan fungsi ke konstanta harus menjadi pendekatan yang lebih baik.
Amogh Talpallikar
1
Saya hanya bertanya, karena saya bukan pria JS, tetapi apakah menggunakan namespace Anda sendiri akan menghasilkan salinan atau singleton? Jika Anda memiliki banyak modul, sepertinya buang-buang memori untuk memiliki salinan dari layanan yang sama, terutama jika Anda hanya ingin menggunakan satu pembantu.
Eric Keyte
3
@EricKeyte Namespace adalah objek literal, yang merupakan jenis singleton yang cukup umum di JS. Maaf atas balasan yang tertunda :)
urban_raccoons
32

Datang di utas lama ini saya ingin menekankan itu

1 °) fungsi utilitas dapat (harus?) Ditambahkan ke rootscope melalui module.run. Tidak perlu untuk instanciate pengendali level root spesifik untuk tujuan ini.

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

2 °) Jika Anda mengatur kode Anda ke dalam modul yang terpisah, Anda harus menggunakan layanan sudut atau pabrik dan kemudian menyuntikkannya ke dalam fungsi yang diteruskan ke run block, sebagai berikut:

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 °) Pemahaman saya adalah bahwa dalam pandangan, untuk sebagian besar kasus Anda memerlukan fungsi pembantu ini untuk menerapkan semacam pemformatan ke string yang Anda tampilkan. Yang Anda butuhkan dalam kasus terakhir ini adalah menggunakan filter sudut

Dan jika Anda telah menyusun beberapa metode pembantu tingkat rendah ke layanan sudut atau pabrik, cukup masukkan dalam konstruktor filter Anda:

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

Dan menurut Anda:

{{ aString | myFilter }}   
nicolas
sumber
Kedua solusi menyangkut runwaktu. Bagaimana dengan waktu konfigurasi? Bukankah kita membutuhkan utilitas di luar sana?
Cyril CHAPON
waktu konfigurasi Anda memerlukan penyedia (semacam layanan) checkout the angular js doc
nicolas
1
Solusi # 3 sepertinya yang terbaik bagi saya. Setelah filter terdaftar, Anda dapat menggunakannya di tempat lain. Saya menggunakannya untuk memformat mata uang saya & format tanggal.
Olantobi
6

Apakah saya mengerti dengan benar bahwa Anda hanya ingin mendefinisikan beberapa metode utilitas dan membuatnya tersedia dalam templat?

Anda tidak harus menambahkannya ke setiap pengontrol. Cukup tentukan satu pengontrol tunggal untuk semua metode utilitas dan pasang pengontrol itu ke <html> atau <body> (menggunakan arahan ngController). Kontroler lain yang Anda lampirkan di mana saja di bawah <html> (artinya di mana saja, titik) atau <body> (di mana saja kecuali <head>) akan mewarisi $ lingkup itu dan akan memiliki akses ke metode-metode itu.

Willis Blackburn
sumber
1
ini jelas cara terbaik untuk melakukan ini. Hanya memiliki pengontrol utilitas dan meletakkannya di pembungkus / wadah div seluruh proyek, semua pengontrol di dalamnya akan mewarisi: <div class="main-container" ng-controller="UtilController as util">kemudian dalam setiap tampilan interior:<button ng-click="util.isNotString(abc)">
Ian J Miller
4

Cara termudah untuk menambahkan fungsi utilitas adalah membiarkannya di tingkat global:

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

Kemudian, cara paling sederhana untuk menambahkan fungsi utilitas (ke pengontrol) adalah dengan menetapkannya $scope, seperti ini:

$scope.doSomething = myUtilityFunction;

Maka Anda dapat menyebutnya seperti ini:

{{ doSomething(x) }}

atau seperti ini:

ng-click="doSomething(x)"

EDIT:

Pertanyaan aslinya adalah apakah cara terbaik untuk menambahkan fungsi utilitas adalah melalui layanan. Saya bilang tidak, jika fungsinya cukup sederhana (seperti isNotString()contoh yang diberikan oleh OP).

Manfaat dari penulisan layanan adalah untuk menggantinya dengan layanan lain (melalui injeksi) untuk tujuan pengujian. Secara ekstrim, apakah Anda perlu menyuntikkan setiap fungsi utilitas tunggal ke dalam pengontrol Anda?

Dokumentasi mengatakan untuk mendefinisikan perilaku di controller (seperti $scope.double): http://docs.angularjs.org/guide/controller

Brent Washburne
sumber
Memiliki fungsi utilitas sebagai layanan memungkinkan Anda untuk mengaksesnya secara selektif di pengontrol Anda .. jika tidak ada pengontrol yang menggunakannya, maka layanan tersebut tidak akan dipakai.
StuR
Saya sebenarnya menyukai pendekatan Anda dan memasukkannya ke dalam jawaban yang diedit. Saya punya perasaan seseorang mungkin telah menurunkan Anda karena fungsi global (dan polusi namespace), tetapi saya memiliki perasaan bahwa Anda mungkin akan memasukkan pendekatan yang mirip dengan yang saya tuliskan jika Anda berpikir bahwa banyak memegang tangan diperlukan .
urban_raccoons
Secara pribadi, saya tidak dapat melihat masalah dengan membuat fungsi utilitas umum, kecil, dan global. Biasanya hal-hal yang Anda gunakan di seluruh basis kode Anda, sehingga siapa pun akan terbiasa dengan mereka dengan cepat. Lihat mereka sebagai ekstensi kecil ke bahasa.
Cornel Masson
Dalam hasil edit Anda, Anda menyebutkan "Dokumentasi mengatakan untuk mendefinisikan perilaku di controller (seperti $ scope.double)". Apakah Anda mengatakan bahwa dokumentasi menyarankan untuk menempatkan fungsi utilitas di dalam pengontrol?
losmescaleros
@losmescaleros Ya, baca bagian "Menambahkan Perilaku ke Objek Lingkup" dalam dokumentasi docs.angularjs.org/guide/controller
Brent Washburne
4

Ini adalah metode sederhana, ringkas dan mudah dipahami yang saya gunakan.
Pertama, tambahkan layanan di js Anda.

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;

    }]);

Kemudian, di controller Anda, menyuntikkan objek pembantu Anda dan menggunakan fungsi apa pun yang tersedia dengan sesuatu seperti berikut:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

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

}]);
Martin Brousseau
sumber
2
Ini jelas menunjukkan satu masalah mengapa saya kadang-kadang tidak suka Angular: mengatakan "tambahkan layanan" ... dan kemudian dalam kode membuat pabrik baru (). Dari pola desain, mereka bukan hal yang sama - pabrik biasanya digunakan untuk memproduksi objek baru, dan layanan, baik, untuk "melayani" beberapa fungsi atau sumber daya. Pada saat-saat seperti itu saya ingin mengatakan "WT *, Angular".
JustAMartin
1

Anda juga dapat menggunakan layanan konstan. Menentukan fungsi di luar panggilan konstan memungkinkannya menjadi rekursif juga.

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

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

Mengapa tidak menggunakan pewaris pengontrol, semua metode / properti yang didefinisikan dalam lingkup HeaderCtrl dapat diakses di pengontrol di dalam tampilan-ng. $ scope.servHelper dapat diakses di semua pengontrol Anda.

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


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
Kie
sumber