Làm cách nào tôi có thể thêm một số chức năng tiện ích nhỏ vào ứng dụng AngularJS của mình?


146

Tôi muốn thêm một số chức năng tiện ích vào ứng dụng AngularJS của mình. Ví dụ:

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

Là cách tốt nhất để làm điều này để thêm chúng như một dịch vụ? Từ những gì tôi đã đọc, tôi có thể làm điều này nhưng sau đó tôi muốn sử dụng những thứ này trong các trang HTML của mình để vẫn có thể nếu chúng ở trong một dịch vụ? Ví dụ tôi có thể sử dụng như sau:

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

Ai đó có thể cho tôi một ví dụ về cách tôi có thể thêm những thứ này. Tôi nên tạo ra một dịch vụ hay có một số cách khác để làm điều đó. Quan trọng nhất tôi muốn các chức năng tiện ích này trong một tệp và không kết hợp với một phần khác của thiết lập chính.

Tôi hiểu có một vài giải pháp nhưng không có giải pháp nào rõ ràng như vậy.

Giải pháp 1 - Đề xuất của đô thị

$scope.doSomething = ServiceName.functionName;

Vấn đề ở đây là tôi có 20 chức năng và mười bộ điều khiển. Nếu tôi làm điều này có nghĩa là thêm rất nhiều mã vào mỗi bộ điều khiển.

Giải pháp 2 - Đề xuất của tôi

    var factory = {

        Setup: function ($scope) {

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

Nhược điểm của điều này là khi bắt đầu mọi bộ điều khiển, tôi sẽ có một hoặc nhiều cuộc gọi Thiết lập này cho mỗi dịch vụ vượt qua phạm vi $.

Giải pháp 3 - Đề xuất của đô thị

Các giải pháp được đề xuất bởi đô thị tạo ra một dịch vụ chung có vẻ tốt. Đây là thiết lập chính của tôi:

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

Tôi có nên thêm dịch vụ chung vào đây không và làm cách nào để thực hiện?


kiểm tra câu trả lời của tôi ở đây stackoverflow.com/a/51464584/4251431
Basheer AL-MOMANI

Câu trả lời:


107

EDIT 7/1/15:

Tôi đã viết câu trả lời này cách đây khá lâu và đã không theo kịp góc cạnh trong một thời gian, nhưng có vẻ như câu trả lời này vẫn còn tương đối phổ biến, vì vậy tôi muốn chỉ ra rằng một vài điểm của @nicolas làm cho dưới đây là tốt. Đối với một, tiêm $ rootScope và đính kèm các trợ giúp ở đó sẽ giúp bạn không phải thêm chúng cho mọi bộ điều khiển. Ngoài ra - Tôi đồng ý rằng nếu những gì bạn thêm vào nên được coi là bộ lọc HOẶC dịch vụ Angular, thì chúng nên được chấp nhận vào mã theo cách đó.

Ngoài ra, kể từ phiên bản 1.4.2 hiện tại, Angular hiển thị API "Nhà cung cấp", được phép đưa vào các khối cấu hình. Xem các tài nguyên này để biết thêm:

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

AngularJS tiêm phụ thuộc giá trị bên trong module.config

Tôi không nghĩ rằng tôi sẽ cập nhật các khối mã thực tế bên dưới, bởi vì tôi không thực sự tích cực sử dụng Angular trong những ngày này và tôi không thực sự muốn mạo hiểm một câu trả lời mới mà không cảm thấy thoải mái vì nó thực sự phù hợp với điều tốt nhất mới thực hành. Nếu ai đó cảm thấy như vậy, bằng mọi cách hãy làm điều đó.

EDIT 2/3/14:

Sau khi suy nghĩ về điều này và đọc một số câu trả lời khác, tôi thực sự nghĩ rằng tôi thích một biến thể của phương pháp được đưa ra bởi @Brent Washburne và @Amogh Talpallikar. Đặc biệt nếu bạn đang tìm kiếm các tiện ích như isNotString () hoặc tương tự. Một trong những lợi thế rõ ràng ở đây là bạn có thể sử dụng lại chúng bên ngoài mã góc của bạn và bạn có thể sử dụng chúng bên trong chức năng cấu hình của mình (điều mà bạn không thể làm với các dịch vụ).

Điều đó đang được nói, nếu bạn đang tìm kiếm một cách chung để sử dụng lại những gì nên là dịch vụ, thì câu trả lời cũ tôi nghĩ vẫn là một cách tốt.

Những gì tôi sẽ làm bây giờ là:

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

control.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', function($scope) {
    $scope.helpers = MyNamespace.helpers;
  });

Sau đó, một phần bạn có thể sử dụng:

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

Câu trả lời cũ dưới đây:

Có thể tốt nhất là bao gồm chúng như một dịch vụ. Nếu bạn sẽ sử dụng lại chúng trên nhiều bộ điều khiển, bao gồm cả chúng như một dịch vụ sẽ giúp bạn không phải lặp lại mã.

Nếu bạn muốn sử dụng các hàm dịch vụ trong phần html của mình, thì bạn nên thêm chúng vào phạm vi của bộ điều khiển đó:

$scope.doSomething = ServiceName.functionName;

Sau đó, một phần bạn có thể sử dụng:

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

Đây là một cách bạn có thể giữ tất cả những thứ này ngăn nắp và không gặp quá nhiều rắc rối:

Tách bộ điều khiển, dịch vụ và mã định tuyến / cấu hình của bạn thành ba tệp: controls.js, services.js và app.js. Mô-đun lớp trên cùng là "ứng dụng", có app.controllers và app.service làm phụ thuộc. Sau đó, app.controllers và app.service có thể được khai báo dưới dạng các mô-đun trong các tệp riêng của chúng. Cơ cấu tổ chức này chỉ được lấy từ 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
       }
    });

control.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', 'genericServices', function($scope, genericServices) {
    $scope.genericServices = genericServices;
  });

Sau đó, một phần bạn có thể sử dụng:

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

Bằng cách đó, bạn chỉ thêm một dòng mã cho mỗi bộ điều khiển và có thể truy cập bất kỳ chức năng dịch vụ nào ở bất kỳ phạm vi nào có thể truy cập được.


Tôi có thể có hai mươi chức năng này và tôi muốn sử dụng chúng trong nhiều bộ điều khiển. Tôi đã nghĩ về điều này nhưng nó không thực tế để có mã như: $ scope.doSthing = ServiceName.feftName; bên trong mỗi bộ điều khiển. Tôi sẽ cập nhật câu hỏi của tôi với một chút chi tiết. cảm ơn
Alan2

vâng, điều đó hợp lý nếu bạn cần thêm một dòng cho từng chức năng trong các dịch vụ, nhưng nếu bạn có thể thêm toàn bộ dịch vụ (với tất cả các chức năng của nó) vào phạm vi trong một dòng, tôi nghĩ nó có ý nghĩa. Tôi không quá rõ về cách giải pháp 2 mà bạn đề cập có thể hoạt động?
Urban_raccoons

1
@urban_racoons: Tôi cũng đã bắt đầu theo cách này, nhưng thật không may là bạn không thể tiêm các dịch vụ như vậy trong cấu hình. Tôi muốn truy cập auth_service của mình bên trong một bộ chặn để thêm mã thông báo vào tiêu đề nhưng sau đó tôi nhận ra dịch vụ không thể được chèn trong cấu hình. chỉ hằng số có thể. Tôi nghĩ rằng việc thêm các hàm vào hằng số sẽ là một cách tiếp cận tốt hơn.
Amogh Talpallikar

1
Tôi chỉ đang hỏi, bởi vì tôi không phải chủ yếu là một chàng trai JS, nhưng việc sử dụng không gian tên của riêng bạn sẽ tạo ra một bản sao hoặc một bản sao? Nếu bạn có rất nhiều mô-đun, có vẻ như lãng phí bộ nhớ để có các bản sao của cùng một dịch vụ, đặc biệt nếu bạn chỉ muốn sử dụng một trình trợ giúp.
Eric Keyte

3
@EricKeyte Không gian tên là một đối tượng theo nghĩa đen, là một loại đơn lẻ trong đó khá phổ biến trong JS. Xin lỗi vì đã trả lời chậm trễ :)
Urban_raccoons

32

Đến với chủ đề cũ này tôi muốn nhấn mạnh rằng

1 °) các chức năng tiện ích có thể (nên?) Được thêm vào rootscope thông qua module.run. Không cần thiết phải cung cấp một bộ điều khiển cấp gốc cụ thể cho mục đích này.

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

2 °) Nếu bạn sắp xếp mã của mình thành các mô-đun riêng biệt, bạn nên sử dụng các dịch vụ góc hoặc nhà máy và sau đó đưa chúng vào hàm được truyền cho khối chạy, như sau:

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 °) Theo tôi hiểu là trong các khung nhìn, trong hầu hết các trường hợp, bạn cần các hàm trợ giúp này để áp dụng một số loại định dạng cho chuỗi bạn hiển thị. Những gì bạn cần trong trường hợp cuối cùng này là sử dụng các bộ lọc góc

Và nếu bạn đã cấu trúc một số phương thức của trình trợ giúp cấp thấp vào các dịch vụ góc hoặc nhà máy, chỉ cần đưa chúng vào hàm tạo bộ lọc của bạn:

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

Và theo quan điểm của bạn:

{{ aString | myFilter }}   

Cả hai giải pháp quan tâm- runthời gian. Còn thời gian cấu hình thì sao? Chúng ta không cần các tiện ích ngoài đó?
Cyril CHAPON

cấu hình thời gian bạn cần một nhà cung cấp (một loại dịch vụ) kiểm tra tài liệu js góc
nicolas

1
Giải pháp số 3 có vẻ như là tốt nhất với tôi. Khi một bộ lọc được đăng ký, bạn có thể sử dụng nó ở bất cứ nơi nào khác. Tôi đã sử dụng nó cho định dạng tiền tệ và định dạng ngày.
Olantobi

6

Tôi có hiểu chính xác rằng bạn chỉ muốn xác định một số phương thức tiện ích và làm cho chúng có sẵn trong các mẫu không?

Bạn không cần phải thêm chúng vào mọi bộ điều khiển. Chỉ cần xác định một bộ điều khiển duy nhất cho tất cả các phương thức tiện ích và đính kèm bộ điều khiển đó vào <html> hoặc <body> (sử dụng lệnh ngControll). Bất kỳ bộ điều khiển nào khác mà bạn đính kèm ở bất cứ đâu trong <html> (có nghĩa là bất cứ nơi nào, giai đoạn) hoặc <body> (bất cứ nơi nào trừ <head>) sẽ thừa hưởng phạm vi $ đó và sẽ có quyền truy cập vào các phương thức đó.


1
đây chắc chắn là cách tốt nhất để làm điều này Chỉ cần có một bộ điều khiển tiện ích và đặt nó trong div bọc / container của toàn bộ dự án, tất cả các bộ điều khiển bên trong sẽ kế thừa: <div class="main-container" ng-controller="UtilController as util">sau đó trong bất kỳ chế độ xem bên trong nào:<button ng-click="util.isNotString(abc)">
Ian J Miller

4

Cách dễ nhất để thêm các chức năng tiện ích là để chúng ở cấp độ toàn cầu:

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

Sau đó, cách đơn giản nhất để thêm chức năng tiện ích (vào bộ điều khiển) là gán nó cho $scope, như thế này:

$scope.doSomething = myUtilityFunction;

Sau đó, bạn có thể gọi nó như thế này:

{{ doSomething(x) }}

hoặc như thế này:

ng-click="doSomething(x)"

BIÊN TẬP:

Câu hỏi ban đầu là nếu cách tốt nhất để thêm chức năng tiện ích là thông qua một dịch vụ. Tôi nói không, nếu chức năng đủ đơn giản (như isNotString()ví dụ do OP cung cấp).

Lợi ích của việc viết một dịch vụ là thay thế nó bằng một dịch vụ khác (thông qua tiêm) cho mục đích thử nghiệm. Được đưa đến mức cực đoan, bạn có cần phải tiêm mọi chức năng tiện ích vào bộ điều khiển của mình không?

Tài liệu nói đơn giản là xác định hành vi trong bộ điều khiển (như $scope.double): http://docs.angularjs.org/guide/cont điều khiển


Có các chức năng tiện ích như một dịch vụ cho phép bạn truy cập có chọn lọc chúng trong bộ điều khiển của mình .. nếu không có bộ điều khiển nào sử dụng chúng, thì dịch vụ sẽ không được khởi tạo.
StuR

Tôi thực sự giống như cách tiếp cận của bạn và kết hợp nó vào câu trả lời đã được chỉnh sửa của tôi. Tôi có cảm giác ai đó có thể đã hạ thấp bạn vì chức năng toàn cầu (và ô nhiễm không gian tên), nhưng tôi có cảm giác rằng bạn có thể đã kết hợp một cách tiếp cận tương tự như cách tôi đã viết nếu bạn nghĩ rằng việc cầm tay nhiều là cần thiết .
Urban_raccoons

Cá nhân, tôi không thể thấy một vấn đề với việc làm cho các chức năng chung, nhỏ, tiện ích trở nên toàn cầu. Nó thường là những thứ mà bạn sử dụng trên toàn bộ cơ sở mã của mình, vì vậy bất kỳ ai cũng sẽ làm quen với chúng khá nhanh. Xem chúng như các phần mở rộng nhỏ cho ngôn ngữ.
Cornel Masson

Trong phần chỉnh sửa của bạn, bạn đề cập đến "Tài liệu nói đơn giản là xác định hành vi trong bộ điều khiển (như $ scope.double)". Bạn đang nói rằng tài liệu đề nghị đưa các chức năng tiện ích vào bộ điều khiển?
losmescaleros

@losmescaleros Có, hãy đọc phần "Thêm hành vi vào đối tượng phạm vi" trong tài liệu docs.angularjs.org/guide/contcont
Brent Washburne

4

Đây là một phương pháp đơn giản, nhỏ gọn và dễ hiểu mà tôi sử dụng.
Đầu tiên, thêm một dịch vụ trong js của bạn.

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;

    }]);

Sau đó, trong bộ điều khiển của bạn, tiêm đối tượng trợ giúp của bạn và sử dụng bất kỳ chức năng có sẵn nào với nội dung như sau:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

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

}]);

2
Điều này cho thấy rõ một vấn đề tại sao đôi khi tôi không thích Angular: nói "thêm dịch vụ" ... và sau đó trong mã tạo ra một nhà máy mới (). Từ các mẫu thiết kế, chúng không giống nhau - nhà máy thường được sử dụng để sản xuất các đối tượng mới và dịch vụ là để "phục vụ" một số chức năng hoặc tài nguyên. Trong những khoảnh khắc như vậy tôi muốn nói "WT *, Angular".
JustAMartin

1

Bạn cũng có thể sử dụng dịch vụ liên tục như vậy. Xác định chức năng bên ngoài cuộc gọi liên tục cũng cho phép nó được đệ quy.

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

Tại sao không sử dụng kế thừa bộ điều khiển, tất cả các phương thức / thuộc tính được xác định trong phạm vi của HeaderCtrl đều có thể truy cập được trong bộ điều khiển bên trong ng-view. $ scope.servHelper có thể truy cập được trong tất cả các bộ điều khiển của bạn.

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


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.