I have a service to get my config settings from the db. I use this service to display various configs throughout the app. The problem I am facing is when I go to the configs page to update the configs the variables don't update until I refresh the page.
SERVICE:
app.factory('dataShare', function($rootScope, $log, $q, $http, Data) {
var configs= {};
configs.getconfigs=function(){
var deferred = $q.defer();
Data.get('config').then(function(data){
deferred.resolve(data.data);
});
return deferred.promise;
}
return configs;
});
CONTROLLER:
app.controller('authCtrl', function ($scope, $rootScope, $routeParams, $log, $location, $http, dataShare) {
dataShare.getconfigs().then(function(data){
$scope.configs = data;
});
});
HTML:
<div class="col-md-2"><span ng-if="configs[0].button_name"><a class="btn btn-sm btn-info navbar-btn" ng-href="{{configs[0].button_url}}">{{configs[0].button_name}}</a></span></div>
any help is greatly appreciated
You can put a "Watch" on the config object.
To do this , post first initialization of "config" register it to the watch service in the following manner :
$scope.config={};
$scope.$watch('config', function() {
alert('Config is updated');
//Do whatever you want to do when config gets updated
});
//Service call
You can read more about it over here https://docs.angularjs.org/api/ng/type/$rootScope.Scope
EDIT :
After going over the issue one more time , I think getting the updates on the "config" is not the issue you are facing.
If your problem is that your view is not updated even though the model is updated , you will have to use the $apply method :
dataShare.getconfigs().then(function(data){
$scope.configs = data;
$scope.$apply();
});
This would force the angular view to reflect the updated model.
Related
I am making an $http call using factory and sharing the data to all controllers.
Actually I am getting that data from config.json file, but first on load data is not getting broadcasted to all controllers.
I want data to be available before controller can make further calls inside.
My config.json has app_key, app_secret, base_url etc, here is my factory (JS code)
JS
.factory('UserService', function($http, $rootScope) {
$http.get('config.json').success(function(data) {
var app_key = data.app_key;
var app_secret = data.app_secret;
var base_url=data.base_url;
window.localStorage['app_key'] = app_key;
window.localStorage['app_secret'] = app_secret;
window.localStorage['base_url'] = base_url;
})
return {
app_key : window.localStorage['app_key'],
app_secret : window.localStorage['app_secret'],
base_url:window.localStorage['base_url'],
}
})
my config.json file looks like this
{
"app_key" : "my_app_key_here",
"app_secret" : "my_app_secret_here",
"base_url" : "http://base_url.com",
}
The reason I am returning app_key: window.localStorage['app_key'] is just because I need to access this service data like userService.app_key in controller.
ngoApp.controller('loginCtrl',['$scope','$http', '$localStorage', '$state','UserService','$rootScope', function($scope, $http, $localStorage, $state, UserService, $rootScope) {
//I want to access app_key as below format
$scope.app_key=UserService.app_key;
}]);
Can anyone please tell me what mistake I am making?
The right place where to load this dependencies is in your module run/config method.
Check documentation here: https://docs.angularjs.org/guide/module#module-loading-dependencies for example and more information...
I have a partial in which data is coming from multiple controllers, not the situation is those functions which are called in the controller,they are hitting the server for more than fifty times, and they keep hitting as long as they dont get the response from server. I dont know how to tackle this situation please guide me.
mainControllers.controller('AddProductController', ['$scope', '$http', '$routeParams', '$cookies', '$rootScope', 'Upload', '$timeout', '$uibModal', '$log', '$document', '$window', 'variantsService', 'toaster', '$route', '$rootScope', 'Lightbox', function ($scope, $http, $routeParams, $cookies, $rootScope, Upload, $timeout, $uibModal, $log, $document, $window, variantsService, toaster, $route, $rootScope, Lightbox) {
/*Currency dynamic*/
$scope.currency = function () {
$http.get('currencies',
{headers:
{'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': $rootScope.keyword_auth_token, 'Accept-Language': $cookies.get('type')}
})
.success(function (data) {
$scope.user_curr = data[0].code;
})
.error(function (data) {
console.log(data);
});
};
/*Currency dynamic ends here*/
$scope.currency();
}]);
Is there any way, any way, so that I can limit this thing?
It definitely is a bad idea to have multiple controllers for a single partial. You should consider using angular factories for maintaining data in such cases. But to provide you a short solution, you should remove the line $scope.currency(); from your controller (because it would make an api call as soon as your controller is initialized) and consider using ng-init built-in directive. So, basically in your partial where you are using ng-controller="AddProductController", you can add ng-init="currency()" (If you want to make an api call).
I always put the calls in a service, and then you can take full control. Something like this:
app.service("currencyService", function($q, $http) {
var _currencyPromise = null,
_currencies = null;
this.getCurrencies = function() {
var deferred = $q.defer();
// Check if the currencies are resolved before
// If so, immediately return these
if (_currencies) {
deferred.resolve(_currencies);
}
// Else check if the promise is already running
// If so, use that promise
else if (_currencyPromise) {
_currencyPromise.then(function(response) {
deferred.resolve(response.data);
});
}
// Else make the http call and assign to the promise
// So that the promise can be used instead of a new http call
else {
_currencyPromise = $http.get("..");
_currencyPromise.then(function(response) {
// Assign data to the currencies, so that it can be used
// by next calls immediately
_currencies = response.data;
deferred.resolve(_currencies);
}, function(error) {
// something went wrong
_currencyPromise = null;
deferred.reject();
});
}
return deferred.promise;
}
});
Then in your controllers you can always use this service, while the http call will only be made once:
app.controller("myCtrl", ["$scope", "currencyService", function($scope, currencyService) {
currencyService.getCurrencies().then(function(currencies) {
$scope.user_curr = currencies[0].code;
});
}]);
See this jsfiddle for reference. In the console you can see that the API is only called once.
I came up with a quite simple solution. For example I have a view like this
<div ng-controller="HomeController">
<div class="active tab-pane" ng-controller="AddProductController" ng-init="subcategories_id();currency();">
<p>{{user_curr}}</p>
</div><!--ends here->
<p>first controller {{abc}}</p>
</div>
I am using the nginitwhich works fine.
I have 2 controllers - ParentCtrl and ChildCtrl.
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
</div>
</div>
They using same service method to get data. Data is cached (using cache:true parameter of $http service).
angular.module('myApp')
.factory("myService", [
'$http',
function $http) {
return {
getMyData: function () {
return $http.get(url, {cache: true}).then(function (response) {
return data = response.
});
So the problem is that this 2 controllers begins to work simultaneously - this means myService#getMyData method can be called twice, i.e. 2 round trips to server. But I want only one round trip, so ChildCtrl will get result from cache.
How to solve this problem?
Thanks
Rather than doing some synchronization structure, I would add it to the routing as a prefetched data (viewModel):
$routeProvider
.when('/', {
templateUrl: 'index.html',
controller: homeController,
caseInsensitiveMatch: true,
resolve: {
sharedData: function($http, myService) {
var sharedPromise = myService.getMyData().then(function(results) {
return results;
});
}
}
});
Then in your controllers, at least in the root, you can reference the shared data just as if you had a reference to the service:
function homeController($scope, $http, sharedData) {}
Excuse the pseudo code, but it will allow you at least to be guaranteed that the reference data was fetch once.
I've created $http and REST API interface in AnguarJS service as a function that gets injected into different controllers like this:
// Global service to share between states
.service("appSharedService", ['$http', function($http) {
// Method: Returns list of all cities.
this.restCitiesGet = function() {
return $http.get('http://example/nkhorasaniec7/api/v0/city');
};
// Method:
this.citiesGet = function() {
this.restCitiesGet().success(function (data) {
console.log(data);
return data;
})
};
}])
console.log(data); returns the right json output when I call citiesGet() .
// Main controller that prints list of cities.
.controller('CityList', ['$scope', function($scope, appSharedService) {
$scope.cities = appSharedService.citiesGet();
console.log($scope.cities);
}]);
This is my controller injecting my service. console.log($scope.cities); here returns undefined.
$scope.cities value doesn't get changed after route calls this controller.
Is there something wrong with my setup?
Something interesting is that after I change route and come back to this controller again, this time $scope.cities have my REST data and everything's fine.
I think there's something wrong with timing or asynchronous functionality problem here that I'm not aware of.
EDIT:
I could have had $http in my controller and this works all well:
.controller('CityList', ['$scope', '$http', function($scope, $http, appSharedService) {
$http.get('http://localhost/nkhorasaniec7/api/v0/city').success(function (data) {
$scope.cities = data;
});
}]);
But I want to implement helper functions for this.
I would say that the common approach would be to return the promise directly to the controller, much like you have mentioned above by directly using the http request.
// Global service to share between states
.service("appSharedService", ['$http', function($http) {
// Method: Returning the promise
this.citiesGet = function() {
return $http.get('http://example/nkhorasaniec7/api/v0/city');
};
}])
Controller:
.controller('CityList', ['$scope', '$http', function($scope, $http, appSharedService) {
appSharedService.citiesGet().success(function (data) {
$scope.cities = data;
});
}]);
I think you are right about the timing issue. From what I understand, you are getting a promise, that at the moment you do console.log($scope.cities) is not yet resolved.
If you use $scope.cities inside your page, you should see the results as soon as they are loaded. Another option would be to use the promise then function if you really want to log.
$scope.cities = appSharedService.citiesGet().then(function(data) {
console.log(data);
return data;
};
Answering my own question:
I'm trying to make this happen in my a controller defined in my view using ng-controller, not a controller linked to a router (otherwise you could use resolve property like this Delaying AngularJS route change until model loaded to prevent flicker).
And I want to use REST using $http as a factory/service helper function for a cleaner code.
// Global service to share between states
.service("appSharedService", ['$http', '$q', function($http, $q) {
this.citiesGet = function() {
var deferred = $q.defer();
$http({method: 'GET', url: 'http://localhost/nkhorasaniec7/api/v0/city'}).success(function(data) {
deferred.resolve(data);
}).error(function(data, status) {
deferred.reject(data);
});
return deferred.promise;
};
}])
I used angular $q promise here.
// Our main controller that prints list of cities.
.controller('CityList', ['$scope', 'appSharedService', function($scope, appSharedService) {
var promise = appSharedService.citiesGet();
promise.then(
function(data){$scope.cities = data;}
,function(reason){alert('Failed: ' + reason);}
);
}])
And used then function to use that promise.
And now it always updates $scope.cities in any situation that template loads (not just in ng-view)
You can use $q service
.service("appSharedService", ['$http', '$q', function($http, $q) {
// Method: Returns list of all cities.
this.restCitiesGet = function() {
var deffered = $q.defer();
$http.get('http://example/nkhorasaniec7/api/v0/city').then(
//success
function(response){
deffered.resolve(response.data);},
//error
deffered.reject();
);
return deffered
};
and after that you can use promise in you controller
.controller('CityList', ['$scope', function($scope, appSharedService) {
$scope.cities = []
appSharedService.citiesGet().then(
//success
function(result){
angular.copy(result, $scope.cities)
console.log($scope.cities);
},
//error
function(){
console.log("load error");
});
}]);
I have a service in Angular which uses my API to get user information and provides it to my controllers. It's set up like this:
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
.service('userInfo', function($http, $cookies){
$http.get('/api/users/' + $cookies.id).
success(function(data) {
var userInfo = data.user[0];
return userInfo;
});
}). // other stuff comes after this
In my controllers, I include it like:
function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
$scope.user = userInfo;
console.log('user info is')
console.log(userInfo);
This is returning no data, while if I put the same service function in the controller itself it returns just fine. What am I missing here? Never used DI/Services in Angular before so might be a simple mistake somewhere.
I need to ensure that the service returns data before the controller loads. How can this be accomplished
You can make the factory to return a promise like this:
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
.service('userInfo', function ($http, $cookies) {
var promise = $http.get('/api/users/' + $cookies.id).
success(function (data) {
var userInfo = data.user[0];
return userInfo;
});
return promise;
}) // other stuff comes after this
And in your controller, do
function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
userInfo.then(function(data){
$scope.user = data;
});
}
This can guarantee that whenever you use the service, it always gives you the data synchronously, you don't have to necessarily load any data before loading the controller.
Your userInfo service has to return the promise returned by $http. That promise will then be injected into your controller, and the view will be updated as soon as the promise successfully resolves.
If you don't want the view to be rendered at all before userInfo resolves, you should set a resolve property on your route, and inject your service there:
$routeProvider.when('/profile', {
templateUrl: 'profile',
controller: userProfile,
resolve: {
userInfoData: function ($q, userInfo) {
return userInfo;
}
}
});
Then just inject userInfoData into your controller in place of the userInfo service.
You can assign the resource object to your variable like that
return $http.get(url)
I've faced the same problem in which I had to load data into a service using $resource and get that data into controllers $scope.
I didn't wanted to directly return the promise and then use a resolver in the controller (as #zsong wrote) but to do all the magic into the service once and use the ability to assign a promise directly to a $scope as #Joseph Silber point out, so here is what I did :
return function($scope){promise.then(function(data){$scope.user = data})};
And used it in the controller like this :
userInfo($scope);
Not much of an improvement but it allowed me a clearer syntax in my controllers, hope it'll help even three years later !