2016-07-14 66 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

相關問題