AngularJS: Khởi tạo dịch vụ với dữ liệu không đồng bộ


475

Tôi có một dịch vụ AngularJS mà tôi muốn khởi tạo với một số dữ liệu không đồng bộ. Một cái gì đó như thế này:

myModule.service('MyService', function($http) {
    var myData = null;

    $http.get('data.json').success(function (data) {
        myData = data;
    });

    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

Rõ ràng điều này sẽ không hoạt động bởi vì nếu có gì đó cố gắng gọi doStuff()trước khi myDataquay lại, tôi sẽ nhận được một ngoại lệ con trỏ null. Theo như tôi có thể nói khi đọc một số câu hỏi khác được hỏi ở đâyở đây tôi có một vài lựa chọn, nhưng không ai trong số chúng có vẻ rất sạch sẽ (có lẽ tôi đang thiếu một cái gì đó):

Dịch vụ cài đặt với "chạy"

Khi thiết lập ứng dụng của tôi, hãy làm điều này:

myApp.run(function ($http, MyService) {
    $http.get('data.json').success(function (data) {
        MyService.setData(data);
    });
});

Sau đó, dịch vụ của tôi sẽ như thế này:

myModule.service('MyService', function() {
    var myData = null;
    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

Điều này hoạt động đôi khi nhưng nếu dữ liệu không đồng bộ xảy ra mất nhiều thời gian hơn mọi thứ để được khởi tạo, tôi sẽ nhận được một ngoại lệ con trỏ null khi tôi gọi doStuff()

Sử dụng các đối tượng hứa

Điều này có thể sẽ làm việc. Nhược điểm duy nhất ở mọi nơi tôi gọi MyService tôi sẽ phải biết rằng doStuff () trả lại một lời hứa và tất cả các mã sẽ phải để chúng tôi thentương tác với lời hứa. Tôi chỉ muốn đợi cho đến khi myData trở lại trước khi tải ứng dụng của tôi.

Hướng dẫn sử dụng Bootstrap

angular.element(document).ready(function() {
    $.getJSON("data.json", function (data) {
       // can't initialize the data here because the service doesn't exist yet
       angular.bootstrap(document);
       // too late to initialize here because something may have already
       // tried to call doStuff() and would have got a null pointer exception
    });
});

Global Javascript Var Tôi có thể gửi JSON của mình trực tiếp đến một biến Javascript toàn cầu:

HTML:

<script type="text/javascript" src="data.js"></script>

data.js:

var dataForMyService = { 
// myData here
};

Sau đó, nó sẽ có sẵn khi khởi tạo MyService:

myModule.service('MyService', function() {
    var myData = dataForMyService;
    return {
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

Điều này cũng hoạt động, nhưng sau đó tôi có một biến javascript toàn cầu có mùi khó chịu.

Đây có phải là lựa chọn duy nhất của tôi? Là một trong những lựa chọn tốt hơn so với những người khác? Tôi biết đây là một câu hỏi khá dài, nhưng tôi muốn chứng tỏ rằng tôi đã cố gắng khám phá tất cả các lựa chọn của mình. Bất kỳ hướng dẫn sẽ rất được đánh giá cao.


angular - bootstrap không đồng bộ đi qua mã để lấy dữ liệu từ máy chủ $http, sau đó lưu dữ liệu trong dịch vụ, sau đó khởi động ứng dụng.
Steven Wexler

Câu trả lời:


327

Bạn đã xem chưa $routeProvider.when('/path',{ resolve:{...} ? Nó có thể làm cho cách tiếp cận lời hứa sạch sẽ hơn một chút:

Thực hiện một lời hứa trong dịch vụ của bạn:

app.service('MyService', function($http) {
    var myData = null;

    var promise = $http.get('data.json').success(function (data) {
      myData = data;
    });

    return {
      promise:promise,
      setData: function (data) {
          myData = data;
      },
      doStuff: function () {
          return myData;//.getSomeData();
      }
    };
});

Thêm vào resolvecấu hình tuyến đường của bạn:

app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
    resolve:{
      'MyServiceData':function(MyService){
        // MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
        return MyService.promise;
      }
    }})
  }):

Bộ điều khiển của bạn sẽ không được khởi tạo trước khi tất cả các phụ thuộc được giải quyết:

app.controller('MainCtrl', function($scope,MyService) {
  console.log('Promise is now resolved: '+MyService.doStuff().data)
  $scope.data = MyService.doStuff();
});

Tôi đã làm một ví dụ tại plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview


1
Cảm ơn bạn rất nhiều vì đã phản hồi! Nó sẽ hoạt động với tôi nếu tôi chưa có dịch vụ trong bản đồ giải quyết sử dụng MyService. Tôi đã cập nhật plunker của bạn với tình huống của tôi: plnkr.co/edit/465Cupaf5mtxljCl5NuF?p=preview . Có cách nào khiến MyOtherService đợi MyService được khởi tạo không?
nghiệm123

2
Tôi đoán rằng tôi sẽ xâu chuỗi các lời hứa trong MyOtherService - Tôi đã cập nhật trình duyệt với chuỗi và một số nhận xét - giao diện này như thế nào? plnkr.co/edit/Z7dWVNA9P44Q72sLiPjW?p=preview
joakimbl

3
Tôi đã thử điều này và vẫn gặp phải một số vấn đề vì tôi có chỉ thị và các bộ điều khiển khác (bộ điều khiển tôi sử dụng với $ routeProvider đang xử lý công cụ điều hướng chính, phụ ... đó là 'MyOtherService') cần phải đợi cho đến 'MyService ' được giải quyết. Tôi sẽ tiếp tục cố gắng và cập nhật điều này với bất kỳ thành công nào tôi có. Tôi chỉ ước có một cái móc ở góc mà tôi có thể đợi dữ liệu quay trở lại trước khi khởi tạo bộ điều khiển và chỉ thị của mình. Cảm ơn một lần nữa vì sự giúp đỡ của bạn. Nếu tôi có một bộ điều khiển chính bao bọc mọi thứ thì nó sẽ hoạt động.
nghiệm123

43
Một câu hỏi ở đây - làm thế nào bạn sẽ gán thuộc resolvetính cho một bộ điều khiển không được đề cập trong $routeProvider. Ví dụ , <div ng-controller="IndexCtrl"></div>. Ở đây, bộ điều khiển được đề cập rõ ràng và không được tải thông qua định tuyến. Trong trường hợp như vậy, làm thế nào một sự chậm trễ khởi tạo bộ điều khiển sau đó?
callmekatootie

19
Ummm nếu bạn không sử dụng định tuyến thì sao? Điều này gần giống như nói rằng bạn không thể viết một ứng dụng góc cạnh với dữ liệu không đồng bộ trừ khi bạn sử dụng định tuyến. Cách được đề xuất để nhận dữ liệu vào một ứng dụng là tải nó không đồng bộ, nhưng ngay khi bạn có nhiều hơn một bộ điều khiển và bạn ném vào các dịch vụ, thì KHÔNG THỂ.
dây_in

88

Dựa trên giải pháp của Martin Atkins, đây là một giải pháp Angular thuần túy hoàn chỉnh, súc tích:

(function() {
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  $http.get('/config.json').then(
    function (response) {
      angular.module('config', []).constant('CONFIG', response.data);

      angular.element(document).ready(function() {
          angular.bootstrap(document, ['myApp']);
        });
    }
  );
})();

Giải pháp này sử dụng chức năng ẩn danh tự thực thi để nhận dịch vụ $ http, yêu cầu cấu hình và đưa nó vào một hằng số được gọi là CONFIG khi nó khả dụng.

Sau khi hoàn thành, chúng tôi đợi cho đến khi tài liệu sẵn sàng và sau đó khởi động ứng dụng Angular.

Đây là một cải tiến nhỏ so với giải pháp của Martin, đã hoãn việc tìm nạp cấu hình cho đến khi tài liệu sẵn sàng. Theo tôi biết, không có lý do gì để trì hoãn cuộc gọi $ http cho điều đó.

Kiểm tra đơn vị

Lưu ý: Tôi đã phát hiện ra giải pháp này không hoạt động tốt khi kiểm tra đơn vị khi mã được bao gồm trong app.jstệp của bạn . Lý do cho điều này là đoạn mã trên chạy ngay lập tức khi tệp JS được tải. Điều này có nghĩa là khung kiểm tra (Jasmine trong trường hợp của tôi) không có cơ hội cung cấp một triển khai giả $http.

Giải pháp của tôi, điều mà tôi không hoàn toàn hài lòng, là chuyển mã này vào index.htmltệp của chúng tôi , vì vậy cơ sở hạ tầng kiểm tra đơn vị Grunt / Karma / Jasmine không thấy nó.


1
Quy tắc như 'không gây ô nhiễm phạm vi toàn cầu' chỉ nên được tuân theo trong phạm vi họ làm cho mã của chúng tôi tốt hơn (ít phức tạp hơn, dễ bảo trì hơn, an toàn hơn, v.v.). Tôi không thể thấy giải pháp này tốt hơn việc tải dữ liệu vào một biến toàn cục duy nhất. Tôi đang thiếu gì?
david004

4
Nó cho phép bạn sử dụng hệ thống tiêm phụ thuộc của Angular để truy cập vào hằng số 'CONFIG' trong các mô-đun cần nó, nhưng bạn không có nguy cơ mắc kẹt các mô-đun khác không có. Ví dụ: nếu bạn đã sử dụng biến 'config' toàn cầu, có khả năng mã bên thứ 3 khác cũng có thể đang tìm kiếm cùng một biến.
JBCP

1
Tôi là một người mới góc cạnh, sau đây là một số lưu ý về cách tôi đã phụ thuộc mô-đun cấu hình giải quyết trong ứng dụng của tôi: gist.github.com/dsulli99/0be3e80db9b21ce7b989 ref: tutorials.jenkov.com/angularjs/... Cảm ơn bạn cho giải pháp này.
dps

7
Nó được đề cập trong một bình luận trong một trong những giải pháp bootstrap thủ công khác bên dưới nhưng là một người mới góc cạnh không phát hiện ra, tôi có thể chỉ ra rằng bạn cần xóa chỉ thị ng-app trong mã html của mình để nó hoạt động chính xác - nó đang thay thế bootstrap tự động (thông qua ng-app) bằng phương pháp thủ công này. Nếu bạn không gỡ ứng dụng ng ra, ứng dụng có thể thực sự hoạt động nhưng bạn sẽ thấy nhiều lỗi nhà cung cấp không xác định khác nhau trong bảng điều khiển.
IrishDubGuy

49

Tôi đã sử dụng một cách tiếp cận tương tự như cách được mô tả bởi @XMLilley nhưng muốn có khả năng sử dụng các dịch vụ AngularJS như $http tải cấu hình và thực hiện khởi tạo thêm mà không cần sử dụng API hoặc jQuery cấp thấp.

Sử dụng resolvetrên các tuyến đường cũng không phải là một tùy chọn vì tôi cần các giá trị khả dụng dưới dạng hằng số khi ứng dụng của tôi được khởi động, ngay cả trongmodule.config() các khối.

Tôi đã tạo một ứng dụng AngularJS nhỏ tải cấu hình, đặt chúng làm hằng số trên ứng dụng thực tế và khởi động nó.

// define the module of your app
angular.module('MyApp', []);

// define the module of the bootstrap app
var bootstrapModule = angular.module('bootstrapModule', []);

// the bootstrapper service loads the config and bootstraps the specified app
bootstrapModule.factory('bootstrapper', function ($http, $log, $q) {
  return {
    bootstrap: function (appName) {
      var deferred = $q.defer();

      $http.get('/some/url')
        .success(function (config) {
          // set all returned values as constants on the app...
          var myApp = angular.module(appName);
          angular.forEach(config, function(value, key){
            myApp.constant(key, value);
          });
          // ...and bootstrap the actual app.
          angular.bootstrap(document, [appName]);
          deferred.resolve();
        })
        .error(function () {
          $log.warn('Could not initialize application, configuration could not be loaded.');
          deferred.reject();
        });

      return deferred.promise;
    }
  };
});

// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');

// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {

  bootstrapper.bootstrap('MyApp').then(function () {
    // removing the container will destroy the bootstrap app
    appContainer.remove();
  });

});

// make sure the DOM is fully loaded before bootstrapping.
angular.element(document).ready(function() {
  angular.bootstrap(appContainer, ['bootstrapModule']);
});

Xem nó trong hành động (sử dụng $timeoutthay vì $http) ở đây: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=preview

CẬP NHẬT

Tôi khuyên bạn nên sử dụng phương pháp được mô tả dưới đây của Martin Atkins và JBCP.

CẬP NHẬT 2

Bởi vì tôi cần nó trong nhiều dự án, tôi vừa phát hành một mô-đun Bower đảm nhiệm việc này: https://github.com/philippd/angular-deferred-bootstrap

Ví dụ tải dữ liệu từ back-end và đặt hằng số được gọi là APP_CONFIG trên mô-đun AngularJS:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: function ($http) {
      return $http.get('/api/demo-config');
    }
  }
});

11
deferredBootstrapper là cách để đi
fatlinesofcode

44

Trường hợp "bootstrap thủ công" có thể có quyền truy cập vào các dịch vụ Angular bằng cách tạo thủ công trước khi bootstrap. Trình tiêm ban đầu này sẽ đứng độc lập (không được gắn với bất kỳ phần tử nào) và chỉ bao gồm một tập hợp con của các mô-đun được tải. Nếu tất cả những gì bạn cần là các dịch vụ Angular cốt lõi, thì chỉ cần tải ng, như thế này:

angular.element(document).ready(
    function() {
        var initInjector = angular.injector(['ng']);
        var $http = initInjector.get('$http');
        $http.get('/config.json').then(
            function (response) {
               var config = response.data;
               // Add additional services/constants/variables to your app,
               // and then finally bootstrap it:
               angular.bootstrap(document, ['myApp']);
            }
        );
    }
);

Ví dụ, bạn có thể sử dụng module.constantcơ chế để cung cấp dữ liệu cho ứng dụng của mình:

myApp.constant('myAppConfig', data);

Điều này myAppConfighiện có thể được tiêm giống như bất kỳ dịch vụ nào khác và đặc biệt là nó có sẵn trong giai đoạn cấu hình:

myApp.config(
    function (myAppConfig, someService) {
        someService.config(myAppConfig.someServiceConfig);
    }
);

hoặc, đối với một ứng dụng nhỏ hơn, bạn chỉ có thể đưa cấu hình toàn cầu trực tiếp vào dịch vụ của mình, với chi phí truyền bá kiến ​​thức về định dạng cấu hình trong toàn bộ ứng dụng.

Tất nhiên, vì các hoạt động không đồng bộ ở đây sẽ chặn bootstrap của ứng dụng và do đó chặn quá trình biên dịch / liên kết của mẫu, nên sử dụng ng-cloakchỉ thị để ngăn mẫu không được hiển thị trong quá trình làm việc. Bạn cũng có thể cung cấp một số loại chỉ báo tải trong DOM, bằng cách cung cấp một số HTML chỉ được hiển thị cho đến khi AngularJS khởi chạy:

<div ng-if="initialLoad">
    <!-- initialLoad never gets set, so this div vanishes as soon as Angular is done compiling -->
    <p>Loading the app.....</p>
</div>
<div ng-cloak>
    <!-- ng-cloak attribute is removed once the app is done bootstrapping -->
    <p>Done loading the app!</p>
</div>

Tôi đã tạo một ví dụ hoàn chỉnh, hoạt động của phương pháp này trên Plunker, tải cấu hình từ tệp JSON tĩnh làm ví dụ.


Tôi không nghĩ bạn cần trì hoãn $ http.get () cho đến khi tài liệu sẵn sàng.
JBCP

@JBCP có, bạn đúng rằng nó cũng hoạt động tốt nếu bạn trao đổi các sự kiện để chúng tôi không chờ tài liệu sẵn sàng cho đến khi phản hồi HTTP được trả về, với lợi thế là có thể bắt đầu HTTP yêu cầu nhanh hơn. Chỉ có cuộc gọi bootstrap cần đợi cho đến khi DOM sẵn sàng.
Martin Atkins

2
Tôi đã tạo một mô-đun Bower với cách tiếp cận của bạn: github.com/philippd/angular-deferred-bootstrap
philippd 18/03/2016

@MartinAtkins, tôi chỉ thấy rằng cách tiếp cận tuyệt vời của bạn không hoạt động với Angular v1.1 +. Có vẻ như các phiên bản ban đầu của Angular chỉ không hiểu "sau đó" cho đến khi ứng dụng được khởi động. Để xem nó trong Plunk thay thế URL Angular bằng code.angularjs.org/1.1.5/angular.min.js
vkelman

16

Tôi có cùng một vấn đề: Tôi yêu resolveđối tượng, nhưng điều đó chỉ hoạt động cho nội dung của ng-view. Điều gì xảy ra nếu bạn có bộ điều khiển (đối với điều hướng cấp cao nhất, giả sử) tồn tại bên ngoài chế độ xem ng và cần được khởi tạo với dữ liệu trước khi định tuyến thậm chí bắt đầu xảy ra? Làm thế nào để chúng ta tránh mucking xung quanh phía máy chủ chỉ để làm cho nó hoạt động?

Sử dụng bootstrap thủ công và hằng số góc . Một XHR ngây thơ lấy cho bạn dữ liệu của bạn và bạn bootstrap góc cạnh trong cuộc gọi lại của nó, giải quyết các vấn đề không đồng bộ của bạn. Trong ví dụ dưới đây, bạn thậm chí không cần tạo biến toàn cục. Dữ liệu được trả về chỉ tồn tại trong phạm vi góc dưới dạng tiêm và thậm chí không có trong bộ điều khiển, dịch vụ, v.v. trừ khi bạn tiêm dữ liệu. (Nhiều như bạn sẽ tiêm đầu ra của bạnresolve đối tượng vào bộ điều khiển để xem định tuyến.) Nếu sau đó bạn muốn tương tác với dữ liệu đó như một dịch vụ, bạn có thể tạo một dịch vụ, tiêm dữ liệu và không ai sẽ là người khôn ngoan hơn .

Thí dụ:

//First, we have to create the angular module, because all the other JS files are going to load while we're getting data and bootstrapping, and they need to be able to attach to it.
var MyApp = angular.module('MyApp', ['dependency1', 'dependency2']);

// Use angular's version of document.ready() just to make extra-sure DOM is fully 
// loaded before you bootstrap. This is probably optional, given that the async 
// data call will probably take significantly longer than DOM load. YMMV.
// Has the added virtue of keeping your XHR junk out of global scope. 
angular.element(document).ready(function() {

    //first, we create the callback that will fire after the data is down
    function xhrCallback() {
        var myData = this.responseText; // the XHR output

        // here's where we attach a constant containing the API data to our app 
        // module. Don't forget to parse JSON, which `$http` normally does for you.
        MyApp.constant('NavData', JSON.parse(myData));

        // now, perform any other final configuration of your angular module.
        MyApp.config(['$routeProvider', function ($routeProvider) {
            $routeProvider
              .when('/someroute', {configs})
              .otherwise({redirectTo: '/someroute'});
          }]);

        // And last, bootstrap the app. Be sure to remove `ng-app` from your index.html.
        angular.bootstrap(document, ['NYSP']);
    };

    //here, the basic mechanics of the XHR, which you can customize.
    var oReq = new XMLHttpRequest();
    oReq.onload = xhrCallback;
    oReq.open("get", "/api/overview", true); // your specific API URL
    oReq.send();
})

Bây giờ, NavDataliên tục của bạn tồn tại. Đi trước và tiêm nó vào một bộ điều khiển hoặc dịch vụ:

angular.module('MyApp')
    .controller('NavCtrl', ['NavData', function (NavData) {
        $scope.localObject = NavData; //now it's addressable in your templates 
}]);

Tất nhiên, việc sử dụng một đối tượng XHR trần sẽ loại bỏ một số chi tiết $httphay JQuery sẽ chăm sóc cho bạn, nhưng ví dụ này hoạt động không có phụ thuộc đặc biệt, ít nhất là đơn giản get. Nếu bạn muốn có thêm một chút năng lượng cho yêu cầu của mình, hãy tải lên một thư viện bên ngoài để giúp bạn giải quyết. Nhưng tôi không nghĩ rằng nó có thể truy cập vào góc$http các công cụ hoặc các công cụ khác trong bối cảnh này.

( Bài liên quan đến SO )


8

Những gì bạn có thể làm là trong .config cho ứng dụng của bạn là tạo đối tượng giải quyết cho tuyến đường và trong hàm truyền trong $ q (đối tượng lời hứa) và tên của dịch vụ mà bạn phụ thuộc và giải quyết lời hứa trong chức năng gọi lại cho $ http trong dịch vụ như vậy:

CONFIG ROUTE

app.config(function($routeProvider){
    $routeProvider
     .when('/',{
          templateUrl: 'home.html',
          controller: 'homeCtrl',
          resolve:function($q,MyService) {
                //create the defer variable and pass it to our service
                var defer = $q.defer();
                MyService.fetchData(defer);
                //this will only return when the promise
                //has been resolved. MyService is going to
                //do that for us
                return defer.promise;
          }
      })
}

Angular sẽ không kết xuất mẫu hoặc làm cho bộ điều khiển khả dụng cho đến khi defer.resolve () được gọi. Chúng tôi có thể làm điều đó trong dịch vụ của chúng tôi:

DỊCH VỤ

app.service('MyService',function($http){
       var MyService = {};
       //our service accepts a promise object which 
       //it will resolve on behalf of the calling function
       MyService.fetchData = function(q) {
             $http({method:'GET',url:'data.php'}).success(function(data){
                 MyService.data = data;
                 //when the following is called it will
                 //release the calling function. in this
                 //case it's the resolve function in our
                 //route config
                 q.resolve();
             }
       }

       return MyService;
});

Bây giờ MyService có dữ liệu được gán cho thuộc tính dữ liệu của nó và lời hứa trong đối tượng giải quyết tuyến đường đã được giải quyết, bộ điều khiển của chúng tôi cho tuyến đường đi vào cuộc sống và chúng tôi có thể gán dữ liệu từ dịch vụ cho đối tượng điều khiển của chúng tôi.

KIỂM SOÁT

  app.controller('homeCtrl',function($scope,MyService){
       $scope.servicedata = MyService.data;
  });

Bây giờ tất cả các ràng buộc của chúng tôi trong phạm vi của bộ điều khiển sẽ có thể sử dụng dữ liệu bắt nguồn từ MyService.


Tôi sẽ cho nó một shot khi tôi có nhiều thời gian hơn. Điều này trông giống như những gì người khác đã cố gắng làm trong ngModules.
nghiệm123

1
Tôi thích cách tiếp cận này và tôi đã sử dụng nó trước đây nhưng hiện tại tôi đang cố gắng tìm ra cách thực hiện điều này một cách sạch sẽ khi tôi có một số tuyến đường, mỗi tuyến có thể hoặc không phụ thuộc vào dữ liệu được tìm nạp trước. Bạn có suy nghĩ gì về điều đó không?
ivarni

Tuy nhiên, tôi đang nghiêng về việc mỗi dịch vụ yêu cầu dữ liệu được tìm nạp trước sẽ đưa ra yêu cầu khi được khởi tạo và trả lại lời hứa và sau đó thiết lập các đối tượng giải quyết với các dịch vụ theo yêu cầu của các tuyến khác nhau. Tôi chỉ muốn có một cách ít dài dòng hơn.
ivarni

1
@dewd Đó là những gì tôi đang hướng tới, nhưng tôi rất thích nếu có cách nào đó để nói "tìm nạp tất cả nội dung này trước bất kể tuyến đường nào được tải" mà không phải lặp lại các khối giải quyết của tôi. Họ đều có một cái gì đó họ phụ thuộc vào. Nhưng nó không phải là một vấn đề lớn, nó sẽ chỉ cảm thấy KHÔ hơn một chút :)
ivarni

2
đây là con đường cuối cùng tôi đi trừ khi tôi phải tạo resolvemột vật thể với một thuộc tính là hàm. vì vậy nó đã kết thúcresolve:{ dataFetch: function(){ // call function here } }
aron.duby

5

Vì vậy, tôi tìm thấy một giải pháp. Tôi đã tạo một dịch vụ angularJS, chúng tôi sẽ gọi nó là MyDataRep repository và tôi đã tạo một mô-đun cho nó. Sau đó tôi phục vụ tệp javascript này từ bộ điều khiển phía máy chủ của mình:

HTML:

<script src="path/myData.js"></script>

Phía máy chủ:

@RequestMapping(value="path/myData.js", method=RequestMethod.GET)
public ResponseEntity<String> getMyDataRepositoryJS()
{
    // Populate data that I need into a Map
    Map<String, String> myData = new HashMap<String,String>();
    ...
    // Use Jackson to convert it to JSON
    ObjectMapper mapper = new ObjectMapper();
    String myDataStr = mapper.writeValueAsString(myData);

    // Then create a String that is my javascript file
    String myJS = "'use strict';" +
    "(function() {" +
    "var myDataModule = angular.module('myApp.myData', []);" +
    "myDataModule.service('MyDataRepository', function() {" +
        "var myData = "+myDataStr+";" +
        "return {" +
            "getData: function () {" +
                "return myData;" +
            "}" +
        "}" +
    "});" +
    "})();"

    // Now send it to the client:
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/javascript");
    return new ResponseEntity<String>(myJS , responseHeaders, HttpStatus.OK);
}

Sau đó tôi có thể tiêm MyDataRep repository bất cứ khi nào tôi cần:

someOtherModule.service('MyOtherService', function(MyDataRepository) {
    var myData = MyDataRepository.getData();
    // Do what you have to do...
}

Điều này làm việc rất tốt cho tôi, nhưng tôi sẵn sàng nhận mọi phản hồi nếu có ai có. }


Tôi thích cách tiếp cận mô-đun của bạn. Tôi đã thấy rằng $ routeScope có sẵn cho dịch vụ yêu cầu dữ liệu và bạn có thể gán dữ liệu cho nó trong cuộc gọi lại $ http.success. Tuy nhiên, việc sử dụng $ routeScope cho các mục không toàn cầu sẽ tạo ra mùi và dữ liệu nên thực sự được gán cho bộ điều khiển $ scope. Thật không may, tôi nghĩ rằng cách tiếp cận của bạn, trong khi đổi mới, không lý tưởng (mặc dù tôn trọng việc tìm kiếm thứ gì đó phù hợp với bạn). Tôi chỉ chắc chắn rằng phải có câu trả lời phía khách hàng bằng cách nào đó chờ dữ liệu và cho phép gán phạm vi. Cuộc tìm kiếm vẫn tiếp tục!
sương

Trong trường hợp nó hữu ích với ai đó, gần đây tôi đã thấy một số cách tiếp cận khác nhau khi xem các mô-đun mà người khác đã viết và thêm vào trang web ngModules. Khi tôi có thêm thời gian, tôi sẽ phải bắt đầu sử dụng một trong những thứ đó, hoặc tìm hiểu những gì họ đã làm và thêm nó vào công cụ của tôi.
nghiệm123

2

Ngoài ra, bạn có thể sử dụng các kỹ thuật sau để cung cấp dịch vụ của mình trên toàn cầu, trước khi các bộ điều khiển thực tế được thực thi: https://stackoverflow.com/a/27050497/1056679 . Chỉ cần giải quyết dữ liệu của bạn trên toàn cầu và sau đó chuyển dữ liệu đến dịch vụ của bạn theo runkhối.


1

Bạn có thể sử dụng JSONPđể tải dữ liệu dịch vụ không đồng bộ. Yêu cầu JSONP sẽ được thực hiện trong quá trình tải trang ban đầu và kết quả sẽ có sẵn trước khi ứng dụng của bạn bắt đầu. Bằng cách này, bạn sẽ không phải làm mờ định tuyến của mình với các độ phân giải dự phòng.

Bạn html sẽ trông như thế này:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>

function MyService {
  this.getData = function(){
    return   MyService.data;
  }
}
MyService.setData = function(data) {
  MyService.data = data;
}

angular.module('main')
.service('MyService', MyService)

</script>
<script src="/some_data.php?jsonp=MyService.setData"></script>

-1

Cách dễ nhất để tìm nạp bất kỳ khởi tạo sử dụng thư mục ng-init.

Chỉ cần đặt phạm vi div ng-init nơi bạn muốn tìm nạp dữ liệu init

index.html

<div class="frame" ng-init="init()">
    <div class="bit-1">
      <div class="field p-r">
        <label ng-show="regi_step2.address" class="show-hide c-t-1 ng-hide" style="">Country</label>
        <select class="form-control w-100" ng-model="country" name="country" id="country" ng-options="item.name for item in countries" ng-change="stateChanged()" >
        </select>
        <textarea class="form-control w-100" ng-model="regi_step2.address" placeholder="Address" name="address" id="address" ng-required="true" style=""></textarea>
      </div>
    </div>
  </div>

index.js

$scope.init=function(){
    $http({method:'GET',url:'/countries/countries.json'}).success(function(data){
      alert();
           $scope.countries = data;
    });
  };

LƯU Ý: bạn có thể sử dụng phương pháp này nếu bạn không có cùng mã hơn một địa điểm.


Không nên sử dụng ngInit theo các tài liệu: docs.angularjs.org/api/ng/directive/ngInit
fodma1
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.