2016-07-14 7 views
5

Как мы можем получить доступ к данным из функции разрешения без обращения к контроллеру?доступ к данным из функции разрешения без обращения к контроллеру

В настоящее время мы работаем над проектом, который использует angular-ui-router. У нас есть два отдельных вида: слева - список родительских элементов, справа - элементы дочерних данных.

Если вы выберите родителя слева, мы разрешим его дочерние данные дочернему виду справа.

С целью не загружать контроллер childs (и просмотр) при выборе другого родительского элемента мы устанавливаем notify:false.

Нам удалось «повторно разрешить» данные дочерних контроллеров, не перезагружая контроллер и просматривать, но данные (область) не будут обновляться.

Мы сделали небольшую plunker, чтобы продемонстрировать наши проблемы here

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

ответ

1

Не так красиво, но рабочим решением будет использование событий. Ну, может быть, это не так уж плохо, по крайней мере, это не сложно. https://plnkr.co/edit/SNRFhaudhsWLKUNMFos6?p=preview

angular.module('app',[ 
    'ui.router' 
    ]) 
    .config(function($stateProvider) { 
    $stateProvider.state('parent', { 
     views:{ 
     'parent':{ 
      controller: 'parentCtrl', 
      template: '<div id="parent">'+ 
      '<button ng-click="go(1)">1</button><br>'+ 
      '<button ng-click="go(2)">2</button><br>'+ 
      '<button ng-click="go(3)">3</button><br>'+ 
      '</div>' 
     }, 
     }, 
     url: '' 
    }); 


    $stateProvider.state('parent.child', { 
     views:{ 
     '[email protected]':{ 
      controller: 'childCtrl', 
      template:'<b>{{ id }}</b>' 
     } 
     }, 
     url: '/:id/child', 
     resolve: { 
     detailResolver: function($http, $stateParams, $rootScope) { 
      return $http.get('file'+$stateParams.id+'.json')     
      .then(function(response) { 
       alert('response ' + response.data.id); 

       $rootScope.$broadcast('newData', response.data); 

       return response.data; 
      }); 
     } 
     } 
    }); 
    }) 
    .controller('parentCtrl', function ($log, $scope, $state) { 
    $log.info('parentCtrl'); 
    var notify = true; 
    $scope.go = function (id) { 
     $state.go('parent.child', {id: id}, {notify:notify}); 
     notify = false; 
    }; 
    }) 
    .controller('childCtrl', function ($scope, $log, detailResolver, $interval) { 
    /* 
    * some stuff happens here 
    */ 

    $log.info('childCtrl detailResolver.id == ' + detailResolver); 

    $scope.$on('newData', function (event, detailResolver) { 
     $scope.id = detailResolver; 
    }); 

    $scope.id = detailResolver; 
    $interval(function(){ 
     console.log(detailResolver.id) 
    },1000) 
    }) 
; 

EDIT: Немного более сложное решение, которое требует изменений функции обещания творца в наблюдаемые, но работает: https://plnkr.co/edit/1j1BCGvUXjtv3WhYN84T?p=preview

angular.module('app', [ 
    'ui.router' 
    ]) 
    .config(function($stateProvider) { 
    $stateProvider.state('parent', { 
     views: { 
     'parent': { 
      controller: 'parentCtrl', 
      template: '<div id="parent">' + 
      '<button ng-click="go(1)">1</button><br>' + 
      '<button ng-click="go(2)">2</button><br>' + 
      '<button ng-click="go(3)">3</button><br>' + 
      '</div>' 
     }, 
     }, 
     url: '' 
    }); 


    $stateProvider.state('parent.child', { 
     views: { 
     '[email protected]': { 
      controller: 'childCtrl', 
      template: '<b>{{ id }}</b>' 
     } 
     }, 
     url: '/:id/child', 
     resolve: { 
     detailResolver: turnToObservable(['$http', '$stateParams', function($http, $stateParams) { //Have to be decorated either be this or $inject 
      return $http.get('file' + $stateParams.id + '.json') 
      .then(function(response) { 
       alert('response ' + response.data.id); 
       return response.data; 
      }); 
     }]) 
     } 
    }); 
    }) 
    .controller('parentCtrl', function($log, $scope, $state) { 
    $log.info('parentCtrl'); 
    var notify = true; 
    $scope.go = function(id) { 
     $state.go('parent.child', {id: id}, {notify: notify}); 
     notify = false; 
    }; 
    }) 
    .controller('childCtrl', function($scope, $log, detailResolver, $interval) { 
    /* 
    * some stuff happens here 
    */ 

    $log.info('childCtrl detailResolver.id == ' + detailResolver); 

    detailResolver.addListener(function (id) { 
     $scope.id = id; 
    }); 
    }); 

function turnToObservable(promiseMaker) { 
    var promiseFn = extractPromiseFn(promiseMaker); 
    var listeners = []; 

    function addListener(listener) { 
    listeners.push(listener); 

    return function() { 
     listeners = listeners.filter(function(other) { 
     other !== listener; 
     }); 
    } 
    } 

    function fireListeners(result) { 
    listeners.forEach(function(listener) { 
     listener(result); 
    }); 
    } 

    function createObservable() { 
    promiseFn.apply(null, arguments).then(fireListeners); 

    return { 
     addListener: addListener 
    }; 
    } 

    createObservable.$inject = promiseFn.$inject; 

    return createObservable; 
} 

function extractPromiseFn(promiseMaker) { 
    if (angular.isFunction(promiseMaker)) { 
    return promiseMaker; 
    } 

    if (angular.isArray(promiseMaker)) { 
    var promiseFn = promiseMaker[promiseMaker.length - 1]; 
    promiseFn.$inject = promiseMaker.slice(0, promiseMaker.length - 1); 

    return promiseFn; 
    } 
} 
+0

Конечно, использование глобального мероприятия будет работать. Это будет наш план-б. Однако я надеялся найти более элегантную душу. Я буду ждать еще некоторое время, надеясь, что кто-то предоставит решение без спам-событий через rootScope. – nilsK

+1

@nilsK Это была довольно интересная проблема, поэтому я пришел с другим решением этой проблемы, она требует изменения вашей функции, которая возвращает обещания для функции, возвращающей наблюдаемый объект, но она работает. Хотя это зависит от некоторых доступных в глобальном масштабе функций, но вы, вероятно, могли бы взломать их, чтобы быть провайдерами, если вы этого хотите. – sielakos

+0

Это все еще, немного хаки и требует украшенных функций, но было здорово это сделать. – sielakos

2

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

Сервис

.service('dataLink', function() { 
    var storage = null; 

    function setData(data) { 
     storage = data; 
    } 

    function getData() { 
     return storage; 
    } 

    return { 
     setData: setData, 
     getData: getData 
    }; 
}) 

Ну, я должен использовать сервис в моей функции Resolve как так

Resolve функции

resolve: { 
    detailResolver: function($http, $stateParams, dataLink) { 
     return $http.get('file' + $stateParams.id + '.json') 
      .then(function(response) { 
       alert('response ' + response.data.id); 
       dataLink.setData(response.data); 
       return response.data; 
      }); 
    } 
} 

Уведомление линии dataLink.setData(response.data);. Он хранит данные из разрешения в службе, поэтому я могу получить доступ к нему из контроллера.

Контроллер

Я изменил немного контроллер. Я завернул всю инициализацию в функции, которую я могу выполнить при изменении данных. Вторая вещь, чтобы наблюдать возвращаемое значение dataLink.getData();

По состоянию https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope # $ часов $ сферы. $ Часы обеспечивает функциональные возможности для просмотра возвращаемых значений функций.

Вот некоторые Q & D Пример:

.controller('childCtrl', function($scope, $log, detailResolver, $interval, dataLink) { 
    initialise(); 
    /* 
    * some stuff happens here 
    */ 

    $interval(function() { 
     console.log(detailResolver.id) 
    }, 1000); 

    $scope.$watch(dataLink.getData, function(newData) { 
     detailResolver = newData; 
     initialise(); 
    }); 

    function initialise() { 
     $log.info('childCtrl detailResolver.id == ' + detailResolver); 
     $scope.id = detailResolver; 
    } 
}) 

Линия $scope.$watch(dataLink.getData, function(newData) { ... }); делает трюк. Каждый раз, когда данные в службе dataLink меняют обратный вызов, и заменяет старые данные новым. Ive создал плункер, чтобы вы могли его попробовать. https://plnkr.co/edit/xyZKQgENrwd4uEwS9QIM

Вам не нужно бояться утечек памяти, используя это решение, потому что угловые удаляют наблюдателей автоматически. См. https://stackoverflow.com/a/25114028/6460149 для получения дополнительной информации.

1

1) Для текущей задачи ng-view не требуется (IMHO). Если вам нужны две разные области, то перепроектируйте ng-views, чтобы стать директивами со своими собственными контроллерами. Это позволит предотвратить угловым перезагружать их

2), если вам необходимо обмениваться данными между областями, то услуга может быть использована для хранения данных (см helperService в the following code)

3), если мы говорим о текущем упрощении кода, то это можно было бы сделать так: использовать сервис от 2) и использовать только один контроллер:

(function() { 
    angular.module('app',[ 
    'ui.router' 
    ]); 
})(); 

(function() { 
    angular 
    .module('app') 
    .service('helperService', helperService); 

    helperService.$inject = ['$http', '$log']; 
    function helperService($http, $log) { 
    var vm = this; 

    $log.info('helperService'); 

    vm.data = { 
     id: 0 
    }; 
    vm.id = 0; 
    vm.loadData = loadData; 

    function loadData(id) { 
     vm.id = id; 

     $http 
     .get('file'+id+'.json') 
     .then(function(response) { 
      alert('response ' + response.data.id); 
      vm.data = response.data; 
     }); 
    } 
    } 
})(); 

(function() { 
    angular 
    .module('app') 
    .controller('AppController', ParentController); 

    ParentController.$inject = ['helperService', '$log']; 
    function ParentController(helperService, $log) { 
    var vm = this; 

    $log.info('AppController'); 

    vm.helper = helperService; 
    } 
})(); 

4) интервал, часы, трансляция, и т.д. не нужны, а

Полный код здесь: plunker

P.S. не забудьте про angularjs-best-practices/style-guide

Смежные вопросы