2016-07-14 59 views
5

如何在不关闭控制器的情况下从解析函数访问数据?从解析函数访问数据而不关闭控制器

我们目前正在研究一个使用angular-ui-router的项目。 我们有两个分离的视图:左侧是父元素列表,右侧是元素子数据。

如果选择左侧的父级,我们将其子数据解析为右侧的子视图。

为了不要实时加载孩子控制器(和视图),当选择不同的父元素时,我们设置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) 
    }) 
; 

编辑: 多一点点复杂的解决方案,这需要改变承诺的创造者功能分为观测,但工程: 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

当然,使用全局事件将工作。这将是我们的计划-b。尽管如此,我希望能找到更优雅的灵魂。我会再等一会儿,希望有人能通过rootScope提供一个无垃圾邮件事件的解决方案。 – nilsK

+1

@nilsK这是一个相当有趣的问题,所以我带着其他解决方案来解决这个问题,它需要改变你的函数,返回承诺函数返回可观察对象,但它的工作原理。虽然它取决于一些全球可用的功能,但如果您希望这样做,您可能会将其篡改为提供商。 – sielakos

+0

这仍然是一个有点古怪,需要装饰的功能,但这样做很有趣。 – sielakos

2

基于sielakos答案用特殊的服务,我来了采用这种解决方案。 首先,我需要一个额外的服务,它保留了resovle的数据参考。

服务

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

    function setData(data) { 
     storage = data; 
    } 

    function getData() { 
     return storage; 
    } 

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

好吧,我必须使用该服务在我的决心功能,像这样

解析功能

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#$ $表范围。$表提供的功能来观看的函数的返回值。

下面是一些问答& 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服务中的数据都会更改回调函数,并用新数据替换旧数据。 我创建了一个plunker,所以你可以试试https://plnkr.co/edit/xyZKQgENrwd4uEwS9QIM

你不必害怕使用这个解决方案的内存泄漏,因为角度是自动移除观察者。有关更多信息,请参阅https://stackoverflow.com/a/25114028/6460149

1

1)不需要当前任务ng-view(恕我直言)。如果你需要两个不同的范围,那么重新设计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