2014-11-14 15 views
17

Tôi đã chạy vào vấn đề với ng-điều khiển và 'quyết tâm' chức năng:Angularjs ng-điều khiển với quyết tâm

Tôi đã một bộ điều khiển đòi hỏi một số phụ thuộc được giải quyết trước khi chạy, nó hoạt động tốt khi tôi định nghĩa nó qua ng-tuyến đường:

điều khiển mã trông như thế này:

angular.module('myApp') 
    .controller('MyController', ['$scope', 'data', function ($scope, data) { 
     $scope.data = data; 
    } 
    ] 
); 

Routing:

... 
.when('/someUrl', { 
     templateUrl : 'some.html', 
     controller : 'MyController', 
     resolve : { 
      data: ['Service', function (Service) { 
      return Service.getData(); 
      }] 
     } 
}) 
... 

khi tôi truy cập/someUrl, mọi thứ đều hoạt động.

Nhưng tôi cần phải sử dụng điều khiển này theo cách khác (tôi cần cả hai cách ở những nơi khác nhau):

<div ng-controller="MyController">*some html here*</div> 

Và, tất nhiên, nó không thành công, bởi vì sự phụ thuộc 'dữ liệu' đã không được giải quyết. Có cách nào để tiêm phụ thuộc vào bộ điều khiển khi tôi sử dụng 'ng-điều khiển' hoặc tôi nên bỏ và tải dữ liệu bên trong bộ điều khiển?

+0

Tôi có thể viết cho bạn một ví dụ sau. Nhưng trong trường hợp của bạn, có Dịch vụ trả về một lời hứa chứ không phải là dữ liệu thực tế. Khi nó đã được giải quyết, hãy truy xuất dữ liệu trong MyController của bạn, điều đó có nghĩa là Dịch vụ của bạn phải có một phương thức hoặc thuộc tính trả về dữ liệu được lưu trong bộ nhớ cache. Tôi hy vọng rằng sẽ giúp. – Lance

+0

Tôi đã đưa ra điều này cho cùng một vấn đề http://jsfiddle.net/cnst530p/6/ –

Trả lời

3

Tạo mô-đun mới bên trong mà bạn có dịch vụ để tiêm như được thấy bên dưới.

var module = angular.module('myservice', []); 

module.service('userService', function(Service){ 
    return Service.getData(); 
}); 

Tiêm mới được tạo ra mô-đun dịch vụ bên trong mô-đun ứng dụng của bạn

angular.module('myApp') 
    .controller('MyController', ['$scope', 'myservice', function ($scope, myservice) { 
     $scope.data = data; 
    // now you can use new dependent service anywhere here. 
    } 
    ] 
); 
+0

Wow, điều này trông giống như một hack, nhưng một đẹp, và nó hoạt động. Cảm ơn rất nhiều. – Rasalom

+1

Hoặc không. 'myservice' chỉ tải dữ liệu sau khi mã bộ điều khiển bắt đầu. Nhưng ý tưởng 'giải quyết' là tải dữ liệu trước khi mã bộ điều khiển bắt đầu. Dù sao thì cũng đã cố gắng. * xin lỗi, đã xem xét sai mã sau khi chỉnh sửa * – Rasalom

+0

@Rasalom bạn đã giải quyết nó chưa? Tôi có cùng một vấn đề – fabricioflores

0

Bắt dữ liệu trong "giải quyết" thuộc tính là các chức năng của tuyến đường (routeProvider), không phải là chức năng của bộ điều khiển.

Khóa (trường hợp của bạn: 'dữ liệu') trong thuộc tính giải quyết được tiêm dưới dạng dịch vụ. Đó là lý do tại sao chúng tôi có thể tìm nạp dữ liệu từ dịch vụ đó.

Nhưng để sử dụng cùng một bộ điều khiển ở vị trí khác nhau, bạn đã tìm nạp dữ liệu trong bộ điều khiển.

2

Giả sử Service.getData() trả về một lời hứa, MyController cũng có thể tiêm Dịch vụ đó. Vấn đề là bạn muốn trì hoãn chạy bộ điều khiển cho đến khi lời hứa giải quyết. Trong khi router thực hiện điều này cho bạn, bằng cách sử dụng bộ điều khiển trực tiếp có nghĩa là bạn phải xây dựng logic đó.

angular.module('myApp') 
    .controller('MyController', ['$scope', 'Service', function ($scope, Service) { 
    $scope.data = {}; // default values for data 
    Service.getData().then(function(data){ 
     // data is now resolved... do stuff with it 
     $scope.data = data; 
    }); 
    }] 
); 

Bây giờ công trình này tuyệt vời khi sử dụng bộ điều khiển trực tiếp, nhưng trong ví dụ định tuyến của bạn, nơi bạn muốn trì hoãn render một trang cho đến khi dữ liệu đã được giải quyết, bạn sẽ kết thúc lên làm cho hai cuộc gọi đến Service.getData (). Có một số cách để làm việc xung quanh vấn đề này, giống như có Service.getData() trả lại lời hứa tương tự cho tất cả người gọi, hoặc một cái gì đó như thế này có thể làm việc để tránh những cuộc gọi thứ hai hoàn toàn:

angular.module('myApp') 
    .controller('MyController', ['$scope', '$q', 'Service', function ($scope, $q, Service) { 
    var dataPromise, 
     // data might be provided from router as an optional, forth param 
     maybeData = arguments[3]; // have not tried this before 
    $scope.data = {}; //default values 
    // if maybeData is available, convert it to a promise, if not, 
    // get a promise for fetching the data 
    dataPromise = !!maybeData?$q.when(maybeData):Service.getData(); 
    dataPromise.then(function(data){ 
     // data is now resolved... do stuff with it 
     $scope.data = data; 
    });  
    }] 
); 
+0

Tôi không nghĩ rằng bạn hiểu những gì cần thiết ở đây. Tôi muốn sử dụng chức năng 'resolve' từ tuyến mà không định tuyến, điều này là không thể. Nhưng ở đây bạn mô tả bây giờ '$ q' hoạt động. – Rasalom

11

Trong dưới đây, để giải quyết tuyến đường, chúng tôi sẽ giải quyết lời hứa và gói dữ liệu trả lại vào một đối tượng có thuộc tính. Sau đó chúng ta sao chép cấu trúc này trong dịch vụ trình bao bọc ('dataService') mà chúng ta sử dụng cho biểu mẫu ng-controller.

Dịch vụ trình bao bọc cũng giải quyết lời hứa nhưng thực hiện nội bộ và cập nhật thuộc tính trên đối tượng mà chúng tôi đã quay trở lại để được bộ điều khiển tiêu thụ.

Trong bộ điều khiển, bạn có thể đặt người quan sát trên thuộc tính này nếu bạn muốn trì hoãn một số hành vi bổ sung cho đến khi mọi thứ đã được giải quyết và dữ liệu có sẵn.

Hoặc, tôi đã chứng minh bằng cách sử dụng bộ điều khiển 'kết thúc tốt' một bộ điều khiển khác; một khi lời hứa từ Dịch vụ được giải quyết, nó sau đó vượt qua phạm vi $ của riêng nó trên bộ điều khiển được bao bọc cũng như dữ liệu được giải quyết ngay từ Dịch vụ.

Lưu ý rằng tôi đã sử dụng $ timeout để cung cấp độ trễ 1000ms khi trả lời hứa, để thử và làm cho nó rõ ràng hơn một chút về những gì đang diễn ra và thời điểm.

angular.module('myApp', ['ngRoute']) 
 
    .config(function($routeProvider) { 
 
    $routeProvider 
 
     .when('/', { 
 
     template: '<h1>{{title}}</h1><p>{{blurb}}</p><div ng-controller="ResolveController">Using ng-controller: <strong>{{data.data}}</strong></div>', 
 
     controller: 'HomeController' 
 
     }) 
 
     .when('/byResolve', { 
 
     template: '<h1>{{title}}</h1><p>{{blurb}}</p><p>Resolved: <strong>{{data.data}}</strong></p>', 
 
     controller: "ResolveController", 
 
     resolve: { 
 
      dataService: ['Service', 
 
      function(Service) { 
 
       // Here getData() returns a promise, so we can use .then. 
 
       // I'm wrapping the result in an object with property 'data', so we're returning an object 
 
       // which can be referenced, rather than a string which would only be by value. 
 
       // This mirrors what we return from dataService (which wraps Service), making it interchangeable. 
 
       return Service.getData().then(function(result) { 
 
       return { 
 
        data: result 
 
       }; 
 
       }); 
 
      } 
 
      ] 
 
     } 
 
     }) 
 
     .when('/byWrapperController', { 
 
     template: '<h1>Wrapped: {{title}}</h1><p>{{blurb}}</p><div ng-controller="WrapperController">Resolving and passing to a wrapper controller: <strong>{{data.data ? data.data : "Loading..."}}</strong></div>', 
 
     controller: 'WrapperController' 
 
     }); 
 
    }) 
 
    .controller('HomeController', function($scope) { 
 
    $scope.title = "ng-controller"; 
 
    $scope.blurb = "Click 'By Resolve' above to trigger the next route and resolve."; 
 
    }) 
 
    .controller('ResolveController', ['$scope', 'dataService', 
 
    function($scope, dataService) { 
 
     $scope.title = "Router and resolve"; 
 
     $scope.blurb = "Click 'By ng-controller' above to trigger the original route and test ng-controller and the wrapper service, 'dataService'."; 
 
     $scope.data = dataService; 
 
    } 
 
    ]) 
 
    .controller('WrapperController', ['$scope', '$controller', 'Service', 
 
    function($scope, $controller, Service) { 
 
     $scope.title = "Resolving..."; //this controller could of course not show anything until after the resolve, but demo purposes... 
 
     Service.getData().then(function(result) { 
 
     $controller('ResolveController', { 
 
      $scope: $scope, //passing the same scope on through 
 
      dataService: { 
 
      data: result 
 
      } 
 
     }); 
 
     }); 
 
    } 
 
    ]) 
 
    .service('Service', ['$timeout', 
 
    function($timeout) { 
 
     return { 
 
     getData: function() { 
 
      //return a test promise 
 
      return $timeout(function() { 
 
      return "Data from Service!"; 
 
      }, 1000); 
 
     } 
 
     }; 
 
    } 
 
    ]) 
 
    // our wrapper service, that will resolve the promise internally and update a property on an object we can return (by reference) 
 
    .service('dataService', function(Service) { 
 
    // creating a return object with a data property, matching the structure we return from the router resolve 
 
    var _result = { 
 
     data: null 
 
    }; 
 
    Service.getData().then(function(result) { 
 
     _result.data = result; 
 
     return result; 
 
    }); 
 
    return _result; 
 
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js"></script> 
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-route.min.js"></script> 
 
<div ng-app="myApp"> 
 
    <a href="#/">By ng-controller</a> | 
 
    <a href="#/byResolve">By Resolve</a> | 
 
    <a href="#/byWrapperController">By Wrapper Controller</a> 
 
    <div ng-view /> 
 
</div>

+0

Thú vị lưu ý rằng trong phiên bản 'By ng-controller', sau lần đầu tiên nó giải quyết, dịch vụ trình bao bọc không còn cần phải giải quyết nữa ngay cả khi bạn thay đổi lại tuyến đường (đó là một singleton vừa khởi tạo một lần). Hai người còn lại kích hoạt mọi thứ một lần nữa. 'By Wrapper Controller' có thể sử dụng dịch vụ bao bọc thay vì trực tiếp sử dụng Dịch vụ để tránh việc giải quyết liên tục ... – JcT

+0

Ngoài ra, bạn có thể làm chỉ thị bằng cách sử dụng khái niệm tương tự với bộ điều khiển được bọc, có thể mạnh hơn và Linh hoạt. – JcT

1

Tôi đã cố gắng để giải quyết vấn đề sử dụng ng-init nhưng đi qua những lời cảnh báo sau trên angularjs.org

Việc sử dụng thích hợp duy nhất của ngInit là dành cho răng cưa thuộc tính đặc biệt của ngRepeat, như đã thấy trong bản demo bên dưới. Bên cạnh trường hợp này, bạn nên sử dụng bộ điều khiển thay vì ngInit để khởi tạo giá trị trên phạm vi.

Vì vậy, tôi bắt đầu tìm kiếm một cái gì đó giống như ng-resolve và đã xem qua các chủ đề sau:

https://github.com/angular/angular.js/issues/2092

Liên kết ở trên bao gồm một fiddle demo mà có ng-resolve như chức năng. Tôi nghĩ rằng ng-resolve có thể trở thành một tính năng trong các phiên bản tương lai của góc 1.x. Bây giờ chúng ta có thể làm việc xung quanh với chỉ thị được đề cập trong liên kết ở trên.

1

'dữ liệu' từ giải quyết tuyến đường sẽ không có sẵn để tiêm cho bộ điều khiển được kích hoạt ngoài nhà cung cấp tuyến đường. nó sẽ chỉ có sẵn cho chế độ xem được định cấu hình trong nhà cung cấp tuyến đường.

nếu bạn muốn dữ liệu cho bộ điều khiển được kích hoạt trực tiếp ngoài kích hoạt routeprovider, bạn cần phải đặt hack cho nó.

xem liên kết này giúp cho nó:

http://www.johnpapa.net/route-resolve-and-controller-activate-in-angularjs/

0

Hãy thử dịch vụ này

:

(function() { 

var myService = function($http) { 
    var getData = function() { 
     //return your result 
    }; 
    return { 
     getData:getData 
    }; 
}; 
var myApp = angular.module("myApp"); 
myApp.factory("myService", myService); 
}()); 

Bộ điều khiển:

(function() { 
var myApp = angular.module("myApp"); 
myApp.controller('MyController', [ 
    '$scope', 'myService', function($scope, myService) { 
     $scope.data = myService.getData(); 
    } 
]); 

//Routing 
.when('/someUrl', { 
    templateUrl : 'some.html', 
    controller : 'MyController', 
    resolve : { 
     data: $scope.data, 
    } 
}) 
}()); 
3

Y Bạn có thể sử dụng cơ chế của nguyên mẫu.

.when('/someUrl', { 
    template : '<div ng-controller="MyController" ng-template="some.html"></div>', 
    controller: function (data) { 
     var pr = this; 
     pr.data = data; 
    }, 
    controllerAs: 'pr', 
    resolve : { 
     data: ['Service', function (Service) { 
      return Service.getData(); 
     }] 
    } 
}) 

angular.module('myApp') 
    .controller('MyController', ['$scope', function ($scope) { 
     $scope.data = $scope.pr.data; //magic 
    } 
    ] 
); 

Bây giờ bất cứ nơi nào bạn muốn sử dụng

'<div ng-controller="MyController"></div>' 

bạn cần phải đảm bảo rằng có pr.data trong Phạm vi điều khiển gọi.Là một UIB-modal

var modalInstance = $modal.open({ 
    animation: true, 
    templateUrl: 'modal.html', 
    resolve: { 
     data: ['Service', function (Service) { 
      return Service.getData(); 
     }] 
    }, 
    controller: function ($scope, $modalInstance, data) { 
     var pr = this; 
     pr.data = data; 
     pr.ok = function() { 
      $modalInstance.close(); 
     }; 
    }, 
    controllerAs:'pr', 
    size:'sm' 
}); 

modal.html

<script type="text/ng-template" id="modal.html"> 
    <div class="modal-body"> 
     <div ng-include="some.html" ng-controller="MyController"></div> 
    </div> 
    <div class="modal-footer"> 
     <button class="btn btn-primary pull-right" type="button" ng-click="pr.ok()">{{ 'ok' | capitalize:'first'}}</button> 
    </div> 
</script> 

dụ Và bây giờ bạn có thể sử dụng $ scope.data = $ scope.pr.data; trong MyController

pr.data là kiểu của tôi. Bạn có thể viết lại mã mà không cần PR. nguyên tắc cơ bản khi làm việc với bộ điều khiển ng được mô tả trong video này https://egghead.io/lessons/angularjs-the-dot

Các vấn đề liên quan