ฉันจะเพิ่มฟังก์ชั่นยูทิลิตี้เล็ก ๆ ให้กับแอพพลิเคชั่น 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");
            }

ข้อเสียของสิ่งนี้คือในตอนเริ่มต้นของคอนโทรลเลอร์ทุกตัวฉันจะมีการตั้งค่าเหล่านี้หนึ่งรายการขึ้นไปสำหรับแต่ละบริการที่ผ่าน $ scope

โซลูชันที่ 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
Basheer AL-MOMANI

คำตอบ:


107

แก้ไข 7/1/15:

ฉันเขียนคำตอบนี้เมื่อนานมาแล้วและไม่ได้รักษาอะไรมากมายกับมุมในขณะที่ แต่ดูเหมือนว่าคำตอบนี้ยังคงได้รับความนิยมดังนั้นฉันอยากจะชี้ให้เห็นว่า @nicolas สองประเด็น ทำให้ด้านล่างเป็นสิ่งที่ดี สำหรับหนึ่งการฉีด $ rootScope และแนบผู้ช่วยที่นั่นจะทำให้คุณไม่ต้องเพิ่มพวกเขาสำหรับทุกตัวควบคุม นอกจากนี้ - ฉันยอมรับว่าหากสิ่งที่คุณกำลังเพิ่มควรถูกมองว่าเป็นบริการเชิงมุมหรือตัวกรองแองกูลาร์พวกเขาควรจะนำไปใช้ในรหัสในลักษณะนั้น

นอกจากนี้ในเวอร์ชั่นปัจจุบัน 1.4.2 Angular ยังเปิดเผย "ผู้ให้บริการ" API ซึ่งได้รับอนุญาตให้ฉีดเข้าไปในบล็อกปรับแต่ง ดูแหล่งข้อมูลเหล่านี้เพิ่มเติม:

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

AngularJS การพึ่งพาการฉีดค่าภายใน module.config

ฉันไม่คิดว่าฉันจะอัปเดตบล็อคโค้ดจริงด้านล่างเพราะฉันไม่ได้ใช้แองกูลาร์อย่างจริงจังในวันนี้และฉันไม่ต้องการเสี่ยงกับคำตอบใหม่โดยไม่รู้สึกว่ามันสอดคล้องกับสิ่งที่ดีที่สุด การปฏิบัติ หากคนอื่นรู้สึกถึงมันโดยทั้งหมดไปสำหรับมัน

แก้ไข 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>

นี่คือวิธีที่คุณสามารถจัดการให้เป็นระเบียบและปลอดจากความยุ่งยากมากเกินไป:

แยกคอนโทรลเลอร์ของคุณบริการและรหัสเส้นทาง / กำหนดค่าออกเป็นสามไฟล์: controllers.js, services.js และ app.js โมดูลเลเยอร์บนสุดคือ "แอพ" ซึ่งมีแอพควบคุมและแอพการบริการเป็นการอ้างอิง จากนั้น 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

ใช่ที่เหมาะสมถ้าคุณต้องการเพิ่มบรรทัดสำหรับแต่ละฟังก์ชั่นในบริการ แต่ถ้าคุณสามารถเพิ่มบริการทั้งหมด (ด้วยฟังก์ชั่นทั้งหมด) ในขอบเขตในหนึ่งบรรทัดฉันคิดว่ามันสมเหตุสมผล ฉันไม่ชัดเจนเกินไปเกี่ยวกับวิธีที่โซลูชันที่คุณกล่าวถึงอาจทำงานได้อย่างไร
urban_raccoons

1
@urban_racoons: ฉันก็เริ่มต้นด้วยวิธีนี้ แต่ไม่ได้เปิดเผยว่าคุณไม่สามารถฉีดบริการดังกล่าวในการกำหนดค่า ฉันต้องการเข้าถึง auth_service ของฉันภายในตัวสกัดกั้นเพื่อเพิ่มโทเค็นไปยังส่วนหัว แต่แล้วฉันก็รู้ว่าบริการไม่สามารถถูกฉีดในการกำหนดค่า ค่าคงที่เท่านั้นที่สามารถทำได้ ฉันคิดว่าการเพิ่มฟังก์ชั่นให้คงที่ควรเป็นวิธีที่ดีกว่า
Amogh Talpallikar

1
ฉันแค่ถามเพราะฉันไม่ได้เป็นคนที่แต่งตัวประหลาด JS แต่จะใช้เนมสเปซของคุณเองผลิตสำเนาหรือซิงเกิลตัน? หากคุณมีโมดูลมากมายดูเหมือนว่าหน่วยความจำเหลือน้อยที่จะมีสำเนาของบริการเดียวกันโดยเฉพาะอย่างยิ่งถ้าคุณต้องการใช้ผู้ช่วยเพียงคนเดียว
Eric Keyte

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เวลา แล้วเวลา config ล่ะ เราไม่ต้องการสาธารณูปโภคที่นั่นใช่ไหม
Cyril CHAPON

กำหนดค่าเวลาที่คุณต้องการผู้ให้บริการ (ชนิดของบริการ) ชำระเงินที่มุมเชิงมุม js doc
ลัส

1
ทางออก # 3 ดูเหมือนดีที่สุดสำหรับฉัน เมื่อลงทะเบียนตัวกรองแล้วคุณสามารถใช้ตัวกรองได้ทุกที่ ฉันใช้สำหรับการจัดรูปแบบสกุลเงินและการจัดรูปแบบวันที่
Olantobi

6

ฉันเข้าใจถูกต้องหรือไม่ว่าคุณเพียงต้องการกำหนดวิธีการยูทิลิตี้บางอย่างและทำให้ใช้ได้ในเทมเพลต

คุณไม่จำเป็นต้องเพิ่มมันลงในคอนโทรลเลอร์ทุกตัว เพียงแค่กำหนดคอนโทรลเลอร์เดียวสำหรับวิธีการยูทิลิตี้ทั้งหมดและแนบคอนโทรลเลอร์นั้นกับ <html> หรือ <body> (โดยใช้คำสั่ง ngController) ตัวควบคุมอื่น ๆ ที่คุณแนบไว้ที่ใดก็ได้ภายใต้ <html> (หมายถึงที่ใดก็ได้ช่วงเวลา) หรือ <body> (ที่ใดก็ได้ยกเว้น <head>) จะสืบทอดขอบเขต $ นั้นและจะสามารถเข้าถึงวิธีการเหล่านั้นได้


1
นี่เป็นวิธีที่ดีที่สุดในการทำสิ่งนี้ เพียงแค่มีตัวควบคุมสาธารณูปโภคและใส่ไว้ในเสื้อคลุม div / ภาชนะบรรจุของโครงการทั้งควบคุมทั้งหมดที่อยู่ภายในจะได้รับมรดก: <div class="main-container" ng-controller="UtilController as util">แล้วในมุมมองการตกแต่งภายใน:<button ng-click="util.isNotString(abc)">
เอียนเจมิลเลอร์

4

วิธีที่ง่ายที่สุดในการเพิ่มฟังก์ชั่นยูทิลิตี้คือการปล่อยให้อยู่ในระดับสากล:

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

จากนั้นวิธีที่ง่ายที่สุดในการเพิ่มฟังก์ชั่นยูทิลิตี้ (ให้กับคอนโทรลเลอร์) คือการกำหนดให้กับ$scopeสิ่งนี้:

$scope.doSomething = myUtilityFunction;

จากนั้นคุณสามารถเรียกมันว่า:

{{ doSomething(x) }}

หรือเช่นนี้

ng-click="doSomething(x)"

แก้ไข:

คำถามเดิมคือถ้าวิธีที่ดีที่สุดในการเพิ่มฟังก์ชั่นยูทิลิตี้คือผ่านบริการ ฉันบอกว่าไม่ถ้าฟังก์ชั่นนั้นง่ายพอ (เช่นisNotString()ตัวอย่างที่มีให้โดย OP)

ประโยชน์ของการเขียนบริการคือการแทนที่ด้วยอีก (ผ่านการฉีด) เพื่อวัตถุประสงค์ในการทดสอบ คุณจำเป็นต้องฉีดทุกฟังก์ชั่นยูทิลิตี้ลงในคอนโทรลเลอร์ของคุณหรือไม่?

เอกสารอธิบายว่าเพียงแค่กำหนดพฤติกรรมในตัวควบคุม (เช่น$scope.double): http://docs.angularjs.org/guide/controller


การมีฟังก์ชั่นยูทิลิตี้เป็นบริการช่วยให้คุณสามารถเลือกได้ในตัวควบคุมของคุณ .. หากไม่มีตัวควบคุมที่ใช้งานอยู่บริการจะไม่ถูกยกตัวอย่าง
StuR

ฉันชอบวิธีการของคุณจริงๆและรวมไว้ในคำตอบที่แก้ไขของฉัน ฉันมีความรู้สึกว่ามีบางคนอาจทำให้คุณผิดหวังเพราะฟังก์ชั่นทั่วโลก (และมลพิษของ namespace) แต่ฉันมีความรู้สึกว่าคุณน่าจะรวมวิธีการคล้ายกับที่ฉันเขียนไว้ถ้าคุณคิดว่าจำเป็นต้องใช้มือถือมาก .
urban_raccoons

โดยส่วนตัวแล้วฉันไม่เห็นปัญหาในการสร้างฟังก์ชั่นยูทิลิตี้ทั่วไปขนาดเล็กทั่วโลก โดยทั่วไปจะเป็นสิ่งที่คุณใช้ทั่ว codebase ดังนั้นทุกคนจะคุ้นเคยกับมันอย่างรวดเร็ว เห็นพวกเขาเป็นส่วนขยายเล็ก ๆ กับภาษา
Cornel Masson

ในการแก้ไขของคุณคุณพูดถึง "เอกสารบอกว่าเพียงแค่กำหนดพฤติกรรมในตัวควบคุม (เช่น $ scope.double)" คุณกำลังบอกว่าเอกสารแนะนำการวางฟังก์ชั่นยูทิลิตี้ในตัวควบคุม?
losmescaleros

@losmescaleros ใช่อ่านส่วน "การเพิ่มพฤติกรรมไปยังวัตถุขอบเขต" ในเอกสารdocs.angularjs.org/guide/controller
Brent Washburne

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
สิ่งนี้แสดงให้เห็นถึงปัญหาหนึ่งอย่างชัดเจนว่าทำไมบางครั้งฉันไม่ชอบแองกูลาร์: พูดว่า "เพิ่มบริการ" ... และจากนั้นในรหัสที่สร้างโรงงานใหม่ () จากรูปแบบการออกแบบพวกมันไม่เหมือนกัน - โดยปกติแล้วโรงงานจะใช้สำหรับการผลิตวัตถุใหม่และการบริการก็เป็นเรื่องที่ดีสำหรับ "การให้บริการ" ฟังก์ชั่นหรือทรัพยากรบางอย่าง ในช่วงเวลาดังกล่าวฉันต้องการพูดว่า "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>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.