Hi I spent the last two days trying to make a view depending upon the response of an async http service. But it doesn't work for me. Can any one help me out?
Here there is the code for the basic idea, but the original one is a bit different:
var myApp = angular.module('app', []);
myApp.directive('dashboard',['service1',function(service1){
return {
templateUrl: 'sometemplate.html',
controller: function ($scope) {
$scope.data = {};
service1.get('keyXXX');
$scope.$watch(service1.data['keyXXX'],function(n,o){
//how to make this work???
//I always get null or it would not get executed at all
});
}
}
}])
.service('service1',['someAsyncservice',function(someAsyncservice){
var _s={
data:{},
get:function(key){
if(this.data[key]==undefined)
{
this.data[key]={status:"loading",data:{}};
}
someAsyncservice().then(function(result){
_s[key].data=result;
_s[key].status="success";
});
}
}
return _s;
}])
This is an extended example, using mostly your original code, showing how to completely go without $apply
or $watch
. Simply by using $q
you can bypass most of the problems the first two functions could generate:
(function (app, ng) {
'use strict';
app.controller('TestCtrl', ['$scope', 'RefreshViewService', function ($scope, RefreshViewService) {
$scope.key = 'key_A';
$scope.refresh = function refresh(key) {
$scope.busy = true;
RefreshViewService.refresh(key).then(function () {
$scope.busy = false;
});
};
}]);
app.directive('dashboard', ['service1', function(service1) {
return {
template: '<pre><strong>{{ key }}</strong>: {{ data|json }}</pre>',
scope: {
key: '@'
},
controller: function ($scope) {
// use currently provided data
$scope.data = service1.get($scope.key);
// load data
service1.load($scope.key).then(function (response) {
$scope.data = response;
});
}
};
}]);
app.service('service1', ['$q', 'someAsyncService', function($q, someAsyncService){
var data = {}, loading = {};
return {
/**
* returns the currently available data for `key`
*
* @param key
* @returns {*}
*/
get: function (key) {
if(data[key] === undefined) {
data[key] = {
status: 'loading',
data: {}
};
}
return data[key];
},
/**
* async load data for `key`
*
* @param key
* @returns {*}
*/
load: function(key){
// clear previous data
if (loading[key] === undefined) {
data[key].status = 'loading';
data[key].data = {};
}
return $q(function (resolve, reject) {
// only run if not already loading
if (loading[key] === undefined) {
loading[key] = someAsyncService(key).then(function(result) {
data[key].status = 'success';
data[key].data = result;
delete loading[key];
resolve(data[key]);
});
}
return loading;
});
}
};
}]);
/**
* mock refresh service
*/
app.service('RefreshViewService', ['service1', function(service1) {
return {
refresh: function (key) {
return service1.load(key);
}
};
}]);
/**
* mock async service
*/
app.service('someAsyncService', ['$q', '$timeout', function ($q, $timeout) {
return function(key) {
return $q(function (resolve, reject) {
$timeout(function () {
resolve({ 'requested key': key, 'foo': Math.floor(Math.random() * 100) });
}, 500 + Math.random() * 3000);
});
};
}]);
})(angular.module('app', []), angular);
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div data-ng-app="app" class="container">
<div data-ng-controller="TestCtrl">
<div class="radio">
<label>
<input type="radio" data-ng-model="key" data-ng-disabled="busy" value="key_A"> key A
</label>
<label>
<input type="radio" data-ng-model="key" data-ng-disabled="busy" value="key_B"> key B
</label>
</div>
<button class="btn btn-primary" data-ng-click="refresh(key)" data-ng-disabled="busy">Refresh</button>
<span data-ng-show="busy">loading...</span>
</div>
<hr>
<dashboard key="key_A"></dashboard>
<dashboard key="key_A"></dashboard>
<dashboard key="key_B"></dashboard>
Though there are probably other (better) ways, simply wrap your watched value in a function. E.g.:
$watch(function() { return service1.data['keyXXX']; }, ...
But note, that this will only work if someAsyncservice
actually triggers the digest cycle (e.g. by using $q
and similar)!