javascript 具有解析功能的 Angularjs ng-controller

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26928924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 06:41:55  来源:igfitidea点击:

Angularjs ng-controller with resolve

javascriptangularjs

提问by Rasalom

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”或者我应该放弃并在控制器内加载数据时,有什么方法可以将依赖项注入控制器?

回答by JcT

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>

回答by KanhuP2012

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

回答by Orlov S

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. As an example uib-modal

您需要确保调用控制器的 Scope 中有 pr.data。以 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; in MyController

现在你可以使用 $scope.data = $scope.pr.data; 在我的控制器中

pr.data is my style. You can rewrite the code without PR. the basic principle of working with ng-controller described in this video https://egghead.io/lessons/angularjs-the-dot

pr.data 是我的风格。您可以在没有 PR 的情况下重写代码。本视频中描述的使用 ng-controller 的基本原理https://egghead.io/lessons/angularjs-the-dot

回答by Remento

Presuming that Service.getData() returns a promise, MyController can inject that Service as well. 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.

假设 Service.getData() 返回一个承诺,MyController 也可以注入该服务。问题是您想延迟运行控制器,直到承诺解决。虽然路由器会为您完成此操作,但直接使用控制器意味着您必须构建该逻辑。

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(). 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()。有几种方法可以解决这个问题,比如让 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;
    });    
  }]
);

回答by I_Debug_Everything

I was trying to solve the problem using ng-initbut 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. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.

ngInit 唯一合适的用途是为 ngRepeat 的特殊属性设置别名,如下面的演示所示。除了这种情况,您应该使用控制器而不是 ngInit 来初始化范围上的值。

So I started searching for something like ng-resolveand 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-resolvelike functionality. I think ng-resolvecan become a feature in the future versions of angular 1.x. For now we can work around with the directive mentioned in the above link.

上面的链接包含一个具有ng-resolve类似功能的演示小提琴。我认为ng-resolve可以成为 angular 1.x 未来版本的一个功能。现在我们可以使用上面链接中提到的指令。

回答by Alagesan Palani

'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/

回答by Dextere

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

回答by Neeraj Kaplas

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.

但是要在不同的地方使用相同的控制器,您必须在控制器中获取数据。