My service populates all items using $http and a cache when the controller is activated. If I create a new item, by doing an $http.post(), what is the best way to refresh the cache?
The problem with the example below is the cached getAll call will return an outdated array:
(function () {
'use strict';
angular
.module('myapp')
.factory('items', itemsService)
.controller('Items', itemsController);
// myCache was created using angular-cache
itemsService.$inject = ['$http', 'myCache'];
function itemsService ($http, myCache) {
var service = {
getAll : function () {
return $http.get('/api/item', myCache);
},
createNew : function (item) {
return $http.post('/api/item', item);
}
};
return service;
}
itemsController.$inject = ['items'];
function itemsController (items) {
var vm = this;
vm.items = [];
vm.item = {};
activate();
function activate() {
items.getAll().then(function(response){
vm.items = response.data || [];
});
}
function createNew() {
items.createNew(vm.item).then(function(response){
vm.items.push(response.data);
});
}
}
})();
Edit #1 : Invalidating Cache
I've modified the code to invalidate the cache when a new item is created. The addition of the $q service, and manually rejecting or resolveing the calls seems very tedious and bloated.
Is there a better way?
(function () {
'use strict';
angular
.module('myapp')
.factory('items', itemsService)
.controller('Items', itemsController);
itemsService.$inject = ['$q', '$http', 'CacheFactory'];
function itemsService ($q, $http, CacheFactory) {
var _cache = CacheFactory.get('items') || CacheFactory('items');
var service = {
getItems : function(refresh) {
var d = $q.defer();
if (refresh) { _cache.invalidate(); }
$http.get('/api/item', _cache).then(function(response){
d.resolve(response.data);
}, function(err){ d.reject(err); });
return d.promise;
},
createNew : function(info){
var d = $q.defer();
$http.post('/api/item', info).then(function(response){
_cache.invalidate();
d.resolve(response.data);
}, function(err){ d.reject(err); });
return d.promise;
}
};
return service;
}
itemsController.$inject = ['items'];
function itemsController (items) {
var vm = this;
vm.items = [];
vm.item = {};
activate();
function activate() {
items.getAll().then(function(response){
vm.items = response.data || [];
});
}
function createNew() {
items.createNew(vm.item).then(function(response){
vm.items.push(response.data);
});
}
}
})();
Related
I'm testing the communication of angular and SQLite. I need to get the ID and NAME of the selected company from database when the User access the page. I'm using the ion-autcomplete to select the company in the CRUD page.
Service: sqlite.js
(function () {
'use strict';
angular
.module('Test')
.service('$sqliteService', $sqliteService);
$sqliteService.$inject = ['$q', '$cordovaSQLite'];
function $sqliteService($q, $cordovaSQLite) {
var self = this;
var _db;
self.db = function () {
if (!_db) {
if (window.sqlitePlugin !== undefined) {
_db = window.sqlitePlugin.openDatabase({ name: "my.db", location: 2, createFromLocation: 1 });
} else {
// For debugging in the browser
_db = window.openDatabase("my.db", "1", "Database", 200000);
}
}
return _db;
};
self.getFirstItem = function (query, parameters) {
var deferred = $q.defer();
self.executeSql(query, parameters).then(function (res) {
if (res.rows.length > 0)
return deferred.resolve(res.rows.item(0));
else
return deferred.reject("There aren't items matching");
}, function (err) {
return deferred.reject(err);
});
return deferred.promise;
};
}
})();
Factory: CompanyService.js
(function () {
'use strict';
angular
.module('Test')
.factory('CompanyService', CompanyService);
CompanyService.$inject = ['$q', '$sqliteService'];
function CompanyService($q, $sqliteService) {
return {
getId: function (Id) {
var query = "Select * FROM Company WHERE ID = ?";
var values = [Id];
return $q.when($sqliteService.getFirstItem(query, values));
}
};
}
})();
Controller: CompanyController.js
(function() {
'use strict';
angular
.module('Test')
.controller('CompanyEditController', CompanyEditController);
CompanyEditController.$inject = ['$scope', '$q', '$stateParams', '$state', '$cordovaCamera', '$cordovaImagePicker', '$ionicPopup', 'CompanyService'];
function OcorrenciaEditController($scope, $q, $stateParams , $state, $cordovaCamera, $cordovaImagePicker, $ionicPopup, CompanyService) {
var vm = $scope;
vm.modelToItemMethod = function (modelValue) {
var d = $q.defer();
CompanyService.getId(modelValue)
.then(function(data) {
console.log('My first promise succeeded', JSON.stringify(data));
$q.resolve(data);
}, function(error) {
console.log('My first promise failed', error.message);
});
return d.promise;
};
})();
Company.html
<input ion-autocomplete ng-model="company.IdCompany" type="text" name="fieldEmpresa" placeholder="Empresa" readonly="readonly" class="ion-autocomplete" autocomplete="off" max-selected-items="1" required
item-value-key="Id"
item-view-value-key="CompanyName"
items-method="getTestItems(query)"
cancel-label="Cancel"
items-removed-method="itemsRemoved()"
loader-icon="spinner"
external-model="company"
model-to-item-method="modelToItemMethod(modelValue)"/>
I don't undestand why I need to use de "$q.defer" inside the controller if i'm using inside de Factory and Service. If I don't use, controller can't return the value to ion-aucomplete. Am i missing something? Or the code is right?
You are binding this method to auto complete; as ajax call is asynchronous, you gotta return a primise. Thats the reason why you ended up using $q.defer.
If you dont want to use $q, then instead of using $q.defer , you can just do return CompanyService.getId(modalValue); in your VM.modelToItemMethod which inturn returns a deferred object.
I'm trying to write a unit test using karma and jasmine for an Angular controller that depends on a service
storesController.js
(function () {
var app = angular.module('storesController', ['storesService']);
app.controller('StoresListController', function ($scope, StoresService) {
$scope.getStores = function () {
StoresService.getStores().then(function (data) {
$scope.stores = data.data;
});
};
$scope.getStores();
$scope.deleteStore = function (id) {
StoresService.deleteStore(id).then(function () {
$scope.getStores();
});
};
});
storesService.js
(function () {
var app = angular.module('storesService', []);
app.factory('StoresService', ['$http', function ($http) {
var stores = [];
stores.getStores = function () {
return $http.get(/api/getStores');
};
stores.deleteStore = function (storeID) {
return $http.delete(/api/deleteStore/'+storeID);
};
return stores;
}]);
})();
And the test,
controllers.spec.js
describe('StoresController', function () {
beforeEach(module('storesController'));
var scope;
var storesServiceMock;
var controller;
beforeEach(inject(function ($controller, $rootScope) {
storesServiceMock = {
getStores: function() {
},
deleteStores: function() {
}
};
spyOn(storesServiceMock, 'getStores').and.returnValue({name : 'TestName', country : 'TestCountry'})
scope = $rootScope.$new();
controller = $controller('StoresListController', {
$scope: scope, StoresService: storesServiceMock
});
}));
it('scope.stores should be defined', function () {
expect(scope.stores).toBeDefined;
});
});
And I'm getting
TypeError: StoresService.getStores(...).then is not a function at n.$scope.getStores
I've also tried width httpBackend but I'm not be able to make it work, any clue about what I'm doing wrong?
Have the spy return a promise.
With ES2015:
spyOn(storesServiceMock, 'getStores').and.returnValue(Promise.resolve({name : 'TestName', country : 'TestCountry'}));
With $q:
spyOn(storesServiceMock, 'getStores').and.callFake(function() {
var deferred = $q.defer();
deferred.resolve({name : 'TestName', country : 'TestCountry'}));
return deferred.promise;
});
So I created with angular a small factory to get my local json file now I wanna pass that data to my controller but it can't find the factory name and says 'unresolved variable'.
Here is the snippet of my code what I guess is relevant for now.
(function () {
var app = angular.module('locatieTool', ['ngRoute']);
app.controller('teamController', function ($scope) {
function init () {
dataFactory.getTeams().success(function(data) {
$scope.teams = data
});
}
init();
console.log($scope.teams);
});
// factory
app.factory('dataFactory', function($http) {
var team = {};
//get local data
team.getTeams = function() {
return $http.get ('http://localhost:4040/');
};
return team;
});
})();
My goal is just to console log the $scope.teams, than I can do more with the data.
you should include "dataFactory" inside your controller
(function () {
var app = angular.module('locatieTool', ['ngRoute']);
app.controller('teamController', function ($scope, dataFactory) {
function init () {
dataFactory.getTeams().success(function(data) {
$scope.teams = data
});
}
init();
console.log($scope.teams);
});
// factory
app.factory('dataFactory', function($http) {
var team = {};
//get local data
team.getTeams = function() {
return $http.get ('http://localhost:4040/');
};
return team;
}); })();
I believe you need to pass your factory into the controller:
app.controller('teamController', function ($scope, dataFactory) {
function init () {
dataFactory.getTeams().success(function(data) {
$scope.teams = data
});
}
init();
console.log($scope.teams);
});
I have an Angular service that looks like this:
(function () {
'use strict';
var serviceId = 'currentUserService';
angular.module('app').factory(serviceId, ['common', 'datacontext', currentUserService]);
function currentUserService(common, datacontext) {
var $q = common.$q;
var getLogFn = common.logger.getLogFn;
var logError = getLogFn(serviceId, "error");
var user = {};
var service = {
user: user,
doesUserHaveFeature: doesUserHaveFeature
};
activate();
return service;
function activate() {
var promises = [getCurrentUser()];
$q.all(promises);
}
function getCurrentUser() {
var deferred = $q.defer();
datacontext.getLoginInformation().then(function (data) {
user = data;
deferred.resolve(data);
});
return deferred.promise;
}
function doesUserHaveFeature(featureName) {
debugger;
var feature = featureName.toLowerCase();
var result = _.filter(user.features, function(item) {
var featureString = item.toLowerCase();
return feature == featureString;
});
if (result) {
return true;
}
return false;
}
}
})();
The service is injected into my Controller and my controller calls the doesUserHaveFeature() method.
However, the doesUserHaveFeature method is called before the promise in the activate method is resolved. Therefore, the user variable is still an empty object.
How can I ensure that the promise in the activate method is returned before the doesUserHaveFeature method is called?
Thanks!
Jeremy
As the user data is retrieved asynchronously, your factory methods are going to have to return promises. Here's how I'd do it...
.factory('currentUserService', function(datacontext) {
var userPromise = datacontext.getLoginInformation();
return {
user: userPromise,
doesUserHaveFeature: function(featureName) {
return userPromise.then(function(user) {
return _.some(user.features, function(feature) {
return feature.toLowerCase() === featureName.toLowerCase();
});
});
}
};
});
I'm very new to AngilarJS. I am trying to write a service in angularJS.
<script>
var module = angular.module("myapp", []);
module.service('BrandService', function ($http) {
var brands = [];
this.getBrands = function()
{
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
.then(function(response)
{
brands = response.brands;
alert (brands);
});
}
//simply returns the brands list
this.list = function ()
{
return brands;
}
});
module.controller("brandsController", function($scope, BrandService) {
$scope.brandlist = BrandService.list();
alert ($scope.brandlist);
});
</script>
The statement "alert (brands);" is not getting called. What is the issue with this code. Is m missing any thing in implementation?
$http calls are always async. Meaning, even you do a .then at your service, there is no way it will properly the resolved data back into your controller. You will have to write it in your controller.
Your Service:
module.service('BrandService', function($http) {
var brands = [];
this.getBrands = function() {
//do not need the dot then.
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
}
//simply returns the brands list
this.list = function() {
return brands;
}
});
In your controller:
module.controller("brandsController", function($scope, BrandService) {
BrandService.list()
.then(function(response) {
$scope.brandlist = response.brands;
alert($scope.brandlist);
});
});
In service:
this.getBrands = function() {
$http.get('http://admin.localhost/cgi-bin/brand.pl').then(function(response) {
brands = response.brands;
alert(brands);
return brands;
});
}
In controller:
$scope.brandlist = BrandService.getBrands();
alert($scope.brandlist);