简体   繁体   English

具有解析功能的 Angularjs ng-controller

[英]Angularjs ng-controller with resolve

I've ran into problem with ng-controller and 'resolve' functionality:我遇到了 ng-controller 和“解决”功能的问题:

I have a controller that requires some dependency to be resolved before running, it works fine when I define it via ng-route:我有一个控制器需要在运行之前解决一些依赖关系,当我通过 ng-route 定义它时它工作正常:

Controller code looks like this:控制器代码如下所示:

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

when I go to /someUrl, everything works.当我去 /someUrl 时,一切正常。

But I need to use this controller in other way(I need both ways in different places):但是我需要以其他方式使用这个控制器(我需要在不同的地方使用这两种方式):

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

And, of course, it fails, because 'data' dependency wasn't resolved.而且,当然,它失败了,因为“数据”依赖没有得到解决。 Is there any way to inject dependency into controller when I use 'ng-controller' or I should give up and load data inside controller?当我使用“ng-controller”或者我应该放弃并在控制器内加载数据时,有什么方法可以将依赖项注入控制器?

In the below, for the route resolve, we're resolving the promise and wrapping the return data in an object with a property.在下面,对于路由解析,我们正在解析承诺并将返回数据包装在具有属性的对象中。 We then duplicate this structure in the wrapper service ('dataService') that we use for the ng-controller form.然后,我们在用于 ng-controller 表单的包装器服务 ('dataService') 中复制此结构。

The wrapper service also resolves the promise but does so internally, and updates a property on the object we've already returned to be consumed by the controller.包装服务也解析承诺,但在内部这样做,并更新我们已经返回以供控制器使用的对象上的属性。

In the controller, you could probably put a watcher on this property if you wanted to delay some additional behaviours until after everything was resolved and the data was available.在控制器中,如果你想延迟一些额外的行为,直到一切都得到解决并且数据可用之后,你可能会在这个属性上放置一个观察者。

Alternatively, I've demonstrated using a controller that 'wraps' another controller;或者,我已经演示了使用“包装”另一个控制器的控制器; once the promise from Service is resolved, it then passes its own $scope on to the wrapped controller as well as the now-resolved data from Service.一旦来自 Service 的承诺被解析,它就会将自己的 $scope 传递给包装的控制器以及来自 Service 的现在解析的数据。

Note that I've used $timeout to provide a 1000ms delay on the promise return, to try and make it a little more clear what's happening and when.请注意,我已使用 $timeout 为承诺返回提供 1000 毫秒的延迟,以尝试使其更清楚发生什么以及何时发生。

 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>

Create a new module inside which you have the service to inject like seen below.创建一个新模块,您可以在其中注入服务,如下所示。

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

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

Inject newly created service module inside your app module在你的应用模块中注入新创建的服务模块

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

You can use the mechanism of the prototype.您可以使用原型的机制。

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

Now wherever you want to use现在无论你想使用

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

you need to ensure that there pr.data in the Scope of the calling controller.您需要确保调用控制器的 Scope 中有 pr.data。 As an example uib-modal以 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模态.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>

And now you can use $scope.data = $scope.pr.data;现在你可以使用 $scope.data = $scope.pr.data; in MyController在我的控制器中

pr.data is my style. pr.data 是我的风格。 You can rewrite the code without PR.您可以在没有 PR 的情况下重写代码。 the basic principle of working with ng-controller described in this video https://egghead.io/lessons/angularjs-the-dot本视频中描述的使用 ng-controller 的基本原理https://egghead.io/lessons/angularjs-the-dot

Presuming that Service.getData() returns a promise, MyController can inject that Service as well.假设 Service.getData() 返回一个承诺,MyController 也可以注入该服务。 The issue is that you want to delay running the controller until the promise resolves.问题是您想延迟运行控制器,直到承诺解决。 While the router does this for you, using the controller directly means that you have to build that 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;
    });
  }]
);

Now this works great when using the controller directly, but in your routing example, where you want to delay rendering a page until data is resolved, you are going to end up making two calls to Service.getData().现在这在直接使用控制器时效果很好,但是在您的路由示例中,您希望延迟渲染页面直到数据得到解析,您最终将两次调用 Service.getData()。 There are a few ways to work around this issue, like having Service.getData() return the same promise for all caller, or something like this might work to avoid the second call entirely:有几种方法可以解决这个问题,比如让 Service.getData() 为所有调用者返回相同的承诺,或者这样的事情可能会完全避免第二次调用:

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

I was trying to solve the problem using ng-init but came across the following warnings on angularjs.org我试图使用ng-init解决问题,但在 angularjs.org 上遇到以下警告

The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. ngInit 唯一合适的用途是为 ngRepeat 的特殊属性设置别名,如下面的演示所示。 Besides this case, you should use controllers rather than ngInit to initialize values on a scope.除了这种情况,您应该使用控制器而不是 ngInit 来初始化范围上的值。

So I started searching for something like ng-resolve and came across the following thread:所以我开始搜索类似ng-resolve东西,并遇到了以下线程:

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

The above link consists of a demo fiddle that have ng-resolve like functionality.上面的链接包含一个具有类似ng-resolve功能的演示小提琴。 I think ng-resolve can become a feature in the future versions of angular 1.x.我认为ng-resolve可以成为 angular 1.x 未来版本的一个功能。 For now we can work around with the directive mentioned in the above link.现在我们可以使用上面链接中提到的指令。

'data' from route resolve will not be available for injection to a controller activated other than route provider.路由解析中的“数据”将无法注入到路由提供者以外的激活控制器中。 it will be available only to the view configured in the route provider.它仅可用于路由提供程序中配置的视图。

if you want the data to the controller activated directly other than routeprovider activation, you need to put a hack for it.如果你想直接激活控制器的数据而不是路由提供者激活,你需要为它做一个黑客。

see if this link helps for it:看看这个链接是否有帮助:

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

Getting data in "resolve" attribute is the functionality of route (routeProvider) , not the functionality of controller.在“解析”属性中获取数据是路由 (routeProvider) 的功能,而不是控制器的功能。

Key( is your case : 'data') in resolve attribute is injected as service.键(是你的情况:'数据')在解析属性被注入作为服务。 That's why we are able fetch data from that service.这就是我们能够从该服务获取数据的原因。

But to use same controller in different place , you have fetch data in controller.但是要在不同的地方使用相同的控制器,您必须在控制器中获取数据。

Try this尝试这个

Service:服务:

(function() {

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

Controller:控制器:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM