Подтвердить что ты не робот

Angularjs ng-контроллер с разрешением

У меня возникла проблема с функциями ng-controller и "разрешить":

У меня есть контроллер, который требует некоторой зависимости, которую нужно разрешить перед запуском, он отлично работает, когда я определяю его через ng-route:

Код контроллера выглядит следующим образом:

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

когда я перехожу к /someUrl, все работает.

Но мне нужно использовать этот контроллер другим способом (мне нужны оба пути в разных местах):

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

И, конечно же, он терпит неудачу, потому что зависимость данных не была решена. Есть ли способ ввести зависимость в контроллер, когда я использую "ng-controller", или я должен отказаться и загрузить данные внутри контроллера?

4b9b3361

Ответ 1

В приведенном ниже примере для решения маршрута мы решим обещание и обертываем возвращаемые данные в объект с свойством. Затем мы дублируем эту структуру в службе обертки ('dataService'), которую мы используем для формы ng-controller.

Служба обёртки также устраняет обещание, но делает это внутренне и обновляет свойство объекта, который мы уже вернули, чтобы его потреблял контроллер.

В контроллере вы могли бы поместить наблюдателя в это свойство, если хотите отложить некоторые дополнительные действия до тех пор, пока все не будет разрешено и данные не будут доступны.

В качестве альтернативы, я продемонстрировал использование контроллера, который "обертывает" другой контроллер; после того, как обещание от Сервиса будет разрешено, оно затем передаст свой собственный $scope на обернутый контроллер, а также данные, разрешенные сейчас, из Сервиса.

Обратите внимание, что я использовал $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>

Ответ 2

Создайте новый модуль, внутри которого у вас есть служба для инъекций, как показано ниже.

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

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

Внедрение вновь созданного сервисного модуля внутри вашего модуля приложения

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

Ответ 3

Вы можете использовать механизм прототипа.

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

Теперь, где вы хотите использовать

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

вам нужно убедиться, что в области видимости вызывающего контроллера есть pr.data. В качестве примера uib-модальный

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>

И теперь вы можете использовать $scope.data = $scope.pr.data; в MyController

pr.data - мой стиль. Вы можете переписать код без PR. основной принцип работы с ng-контроллером, описанный в этом видео https://egghead.io/lessons/angularjs-the-dot

Ответ 4

Предполагая, что 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;
    });
  }]
);

Теперь это отлично работает при непосредственном использовании контроллера, но в вашем примере маршрутизации, где вы хотите отложить показ страницы до тех пор, пока данные не будут решены, вы в конечном итоге выполните два вызова 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;
    });    
  }]
);

Ответ 5

Я пытался решить проблему, используя ng-init, но набрал следующие предупреждения на angularjs.org

Единственное соответствующее использование ngInit - для специальных свойств сглаживания ngRepeat, как показано в демонстрации ниже. Помимо этого случая, вы должны используйте контроллеры вместо ngInit для инициализации значений в области.

Итак, я начал искать что-то вроде ng-resolve и наткнулся на следующий поток:

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

Вышеупомянутая ссылка состоит из демонстрационной скрипты, имеющей функциональные возможности ng-resolve. Я думаю, что ng-resolve может стать функцией в будущих версиях angular 1.x. Пока мы можем работать с директивой, упомянутой в приведенной выше ссылке.

Ответ 6

"данные" из разрешения маршрута не будут доступны для впрыска на контроллер, активированный, кроме поставщика маршрута. он будет доступен только для представления, настроенного поставщиком маршрута.

если вы хотите, чтобы данные на контроллере были активированы напрямую, кроме активации маршрутизатора, вам нужно сделать взломать его.

посмотрите, помогает ли эта ссылка:

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

Ответ 7

Получение данных в атрибуте "Разрешить" - это функция маршрута (routeProvider), а не функциональность контроллера.

Ключ (ваш случай: "данные" ) в атрибуте разрешения вводится как служба. Вот почему мы можем извлекать данные из этой службы.

Но для использования того же контроллера в другом месте вы получаете данные в контроллере.

Ответ 8

Попробуйте это

Услуги:

(function() {

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

Контроллер:

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