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.
Related
I've spent a few hours trying to debug this with no real breakthroughs. My console.logs correctly output the load order.
- app
- factory
- controller
I'm annotating my dependencies (even though I'm not minifying at the moment).
Is there anything obviously wrong here that I am missing?
error
app
(function () {
'use strict';
console.log("running app");
var app = angular.module('InventoryProductApp', []).config(function ($logProvider) {
$logProvider.debugEnabled(true);
});
angular.element(document).ready(function () {
var app = document.getElementById('InventoryProductApp');
angular.bootstrap(angular.element(app), ['InventoryProductApp']);
});
})();
controller
(function () {
'use strict';
angular.module('InventoryProductApp').controller("LocationsController", ['$scope', '$log', 'LocationsFactory'
, function ($scope, $http, $log, LocationsFactory) {
console.log("running controller");
$scope.locations = null;
$scope.loading = false;
//private methods -------------------------------------------------------------------------------------------------------------------------------------------------------------
var fetchLocationData = function (inventoryId) {
$scope.loading = true;
console.log(LocationsFactory);
var promise = LocationsFactory.getLocationData(inventoryId);
promise.then(function (data) {
$scope.loading = false;
if (data.success) {
$scope.locations = data.locations;
}
else
{
$log.error('There was an error getting location data');
}
}, function (data) {
$scope.loading = false;
$log.error('There was an error getting location data');
});
}
//end private methods ---------------------------------------------------------------------------------------------------------------------------------------------------------
//public methods --------------------------------------------------------------------------------------------------------------------------------------------------------------
var init = function (inventoryId) {
console.log('inventoryId', inventoryId);
fetchLocationData(inventoryId);
}
//end public methods ----------------------------------------------------------------------------------------------------------------------------------------------------------
init(inventoryId); // inventoryId is found in the partialView _inventoryLocationDistribution
}]);
})();
factory
(function () {
'use strict';
angular.module('InventoryProductApp').factory('LocationsFactory', ['$http', '$q', '$log', function ($http, $q, $log) {
console.log("running factory");
return {
getLocationData: function (inventoryId) {
var def = $q.defer();
$http.get('/butthead', {
params: {
inventoryId: inventoryId
}
}).then(function (response) {
def.resolve({
success: true,
locations: data.locations
});
}, function (response) {
$log.error('failed to fetch data', response);
def.resolve({
success: false,
redirect: response.redirect
});
});
return def.promise;
}
}
}]);
})();
script load order
<script src="~/theme/modern/assets/global/plugins/angularjs/angular.min.js"></script>
<script src="~/App/Inventory/Product/ProductApp.js"></script>
<script src="~/App/Inventory/Product/LocationsFactory.js"></script>
<script src="~/App/Inventory/Product/LocationsController.js"></script>
In your controller:
angular.module('InventoryProductApp').controller("LocationsController",
['$scope', '$log', 'LocationsFactory', function ($scope, $http, $log, LocationsFactory) {
You're missing '$http' in your dependency injections, which means the LocationsFactory argument isn't filled at all.
I have data in one controller and now I want to share it with another but both controller has different modules. I have used $rootscope but it didn't work. I have used service it also didn't work. link here Service
Is there any other way to do. I have spent one week for this please help me.
toolbar.controler
(function ()
{
'use strict';
angular
.module('app.toolbar')
.controller('ToolbarController', ToolbarController);
function ToolbarController($rootScope, $mdSidenav, msNavFoldService, $translate, $mdToast, $location, $localStorage, $http, $scope)
{
var vm = this;
vm.name = $localStorage.name;
vm.userId = $localStorage._id;
vm.readNotifications = function(notifId){
$http({
url: 'http://192.168.2.8:7200/api/readNotification',
method: 'POST',
data: {notificationId: notifId, userId: vm.userId}
}).then(function(res){
vm.rslt = res.data.result1;
console.log(vm.rslt);
vm.refresh();
$location.path('/sharedwishlistdetails');
}, function(error){
alert(error.data);
})
}
}
})();
The data stored here in vm.reslt.
toolbar.module.js
(function ()
{
'use strict';
angular
.module('app.toolbar', [])
.config(config);
/** #ngInject */
function config($stateProvider, $translatePartialLoaderProvider)
{
$translatePartialLoaderProvider.addPart('app/toolbar');
}
})();
Now I want that result for this controller.
sharedwishlistdetails.controller.js
(function ()
{
'use strict';
angular
.module('app.sharedwishlistdetails')
.controller('SharedWishlistDetailsController', SharedWishlistDetailsController);
/** #ngInject */
//NotificationsController.$inject = ['$http', '$location'];
function SharedWishlistDetailsController($http, $location, $localStorage, $rootScope, $scope)
{
var vm = this;
vm.uid = $localStorage._id;
}
})();
shareddata.service.js
(function ()
{
'use strict';
angular
.module('app.core')
.factory('shareData', shareDataService);
/** #ngInject */
function shareDataService($resource,$http) {
var shareData = {};
return shareData;
}
})();
write a service in 'app.toolbar' module
angular.module('app.toolbar').service('ServiceA', function() {
this.getValue = function() {
return this.myValue;
};
this.setValue = function(newValue) {
this.myValue = newValue;
}
});
In your toolbarController , inject ServiceA and set data -
vm.readNotifications = function(notifId){
$http({
url: 'http://192.168.2.8:7200/api/readNotification',
method: 'POST',
data: {notificationId: notifId, userId: vm.userId}
}).then(function(res){
vm.rslt = res.data.result1;
ServiceA.setValue(vm.rslt);
console.log(vm.rslt);
vm.refresh();
$location.path('/sharedwishlistdetails');
}, function(error){
alert(error.data);
})
}
Now write another service for 'app.sharedwishlistdetails' module -
angular.module('app.sharedwishlistdetails',['app.toolbar']).service('ServiceB', function(ServiceA) {
this.getValue = function() {
return ServiceA.getValue();
};
this.setValue = function() {
ServiceA.setValue('New value');
}
});
Now inject ServiceB in your SharedWishlistDetailsController controller and access data -
var sharedData = ServiceB.getValue();
How could $rootScope failed in your code it would be appreciated if you paste your code: never mind here is an example that will help you out:
All applications have a $rootScope which is the scope created on the HTML element that contains the ng-app directive.
The rootScope is available in the entire application.If a variable has the same name in both the current scope and in the rootScope, the application use the one in the current scope.
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.test = new Date();
})
.controller('myCtrl', function($scope, $rootScope) {
$scope.change = function() {
$scope.test = new Date();
};
$scope.getOrig = function() {
return $rootScope.test;
};
})
.controller('myCtrl2', function($scope, $rootScope) {
$scope.change = function() {
$scope.test = new Date();
};
$scope.changeRs = function() {
$rootScope.test = new Date();
};
$scope.getOrig = function() {
return $rootScope.test;
};
});
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);
});
}
}
})();
I have a demo application where I have a controller which has a factory as dependency and factory itself depends on another service. My code is as follows:
var app = angular.module('sampleApp', ['ui.router']);
app.service("someServ", function(){
this.sendMsg = function(name){
return "Hello " + name;
}
})
app.factory("appFactory", function ($http, someServ) {
function getData(url) {
return $http.get(url);
}
function foo(){
var text = someServ.sendMsg("Monotype");
alert(text);
}
return {
getData : getData,
foo : foo
}
})
var productsController = function ($scope, $http, appFactory) {
var pct = this;
pct.url = "http://mysafeinfo.com/api/data?list=englishmonarchs&format=json";
var jsonDataPromise = appFactory.getData(pct.url);
jsonDataPromise
.then(function (response) {
pct.jsonData = response.data;
}, function (err) {
console.log("Error is: " + error);
});
pct.profun = function(){
appFactory.foo();
}
};
app.controller("productsController", productsController);
productsController.$inject = ['$scope', '$http', 'appFactory'];
I have to test with karma using Jasmine 2.4 as testing framework. I have tried many online tutorials but getting totally confused as everyone tries to do something different. Some use $provide to mock the service, some simply inject the actual service/factory and use a reference, some do not give any example of passing arguments to services.
Can someone please tell me how to do unit testing in simple terms. I have already tried doing something like this:
describe('unit testing of service and controller', function(){
beforeEach(module('sampleApp'));
var prodCtrl, $prodScope, mockfactory, mockservice;
beforeEach(function(){
mockservice = {
sendMsg : function(name){
return name;
}
}
module(function($provide){
$provide.value("someServ", mockservice);
});
inject(function($rootScope, $controller, $http, $q, appFactory){
appFactory = appFactory;
spyOn(appFactory, 'getData');
spyOn(appFactory, 'foo');
$prodScope = $rootScope.$new();
prodCtrl = $controller('productsController', {
$scope: $prodScope, $http: $http, appFactory:appFactory
});
});
});
it('appFactory has method getData and foo', function(){
appFactory.getData();
appFactory.foo();
expect(appFactory.getData).toHaveBeenCalled();
expect(appFactory.foo).toHaveBeenCalled();
})
it('productsController gets a promise back from getData', function(){
var url = "sample url";
var myPromise = prodCtrl.getData(url);
myPromise.then(function(){console.log("Promise returned");})
})
it('foo calls service method sendMsg', function(){
prodCtrl.profun();
expect(mockservice.sendMsg).toHaveBeenCalled();
})
});
I was finally able to solve this issue. My code looks like this:
var app = angular.module('sampleApp', []);
app.service("someServ", function(){
this.sendMsg = function(name){
return "Hello " + name;
}
})
app.factory("appFactory", function ($q, someServ) {
function getData() {
var defer = $q.defer();
defer.resolve("Success message");
return defer.promise;
}
function foo(){
var text = someServ.sendMsg("Monotype");
alert(text);
}
return {
getData : getData,
foo : foo
}
})
app.controller("mainController", ['$scope', '$http','appFactory', function($scope, $http, appFactory){
var mct = this;
mct.printData = function(){
var myPromise = appFactory.getData();
myPromise
.then(function(data){
alert("Promise returned successfully. Data : " + data);
}, function(error){
alert("Something went wrong.... Error: " + error);
})
}
mct.showMsg = function(){
appFactory.foo();
}
}]);
The test case looked like this:
describe('unit testing', function(){
var jsonData = {
name: "Aosis",
id: 12345
}
beforeEach(module('sampleApp'));
beforeEach(module(function($provide){
$provide.service("someServ", function(){
//this.sendMsg = function(param){}
this.sendMsg = jasmine.createSpy('sendMsg').and.callFake(function(param){})
});
$provide.factory("appFactory", function(someServ, $q){
function getData(){
var defer = $q.defer();
defer.resolve("Success message");
return defer.promise;
}
function foo(){
var facParam = "some text";
someServ.sendMsg(facParam);
}
return {
getData : getData,
foo : foo
}
});
}));
var $scope, mainController, appFactoryMock, someServMock;
beforeEach(inject(function($rootScope, $controller, $http, $q, appFactory, someServ){
appFactoryMock = appFactory;
someServMock = someServ;
$scope = $rootScope.$new();
mainController = $controller("mainController", {
$scope : $scope,
$http : $http,
appFactory : appFactoryMock
});
}));
// Tests go here....
});
Here, I have mocked service method as jasmine spy and specified the function that should get executed usingand.callFake(function(){.....}). A fake factory has been created and its methods have been spied upon. I tried to create fake factory similar to service using jasmine.createSpy but return {
getData : getData,
foo : foo
} was giving error. Hence, I did that.
Anyone, else if has better solution or some other explanation, please share.
We have a project based on John Papa's HotTowel SPA Using Angular, Breeze and UI-Bootstrap.
We are running in to problems using clientside caching to load Drop downs from SQL Lookup tables. The view loads before the data is cached resulting in empty drop downs when the view first loads. If we refresh the view, the drops downs then populate. We realize that this is sequencing and routing issue but cannot figure out how to make it work.
The issue seems to center around the use of promises and how to recognize when they return successfully. We use the prime function to load the cached data from the database at startup but the search view is loaded before the data is accessible in the getActionDomain() function.
We would appreciate any pointers or ideas.
Thanks,
JG
app.run within app.js is the starting point
(function () {
'use strict';
var serviceId = 'app';
var app = angular.module('app', [
// Angular modules
'ngAnimate', // animations
'ngRoute', // routing
'ngSanitize', // sanitizes html bindings (ex: sidebar.js)
// Custom modules
'common', // common functions, logger, spinner
//'common.bootstrap', // bootstrap dialog wrapper functions
// 3rd Party Modules
'ui.bootstrap' // ui-bootstrap (ex: carousel, pagination, dialog)
]);
// Handle routing errors and success events
app.run(['$route', '$rootScope', '$location', '$http', 'Auth', 'datacontext', 'common', function ($route, $rootScope, $location, $http, Auth, datacontext, common) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(serviceId);
var logError = getLogFn(serviceId, 'error');
var logSuccess = getLogFn(serviceId, 'success');
var $q = common.$q;
//breeze.core.extendQ($rootScope, $q);
primeData();
function primeData() {
return datacontext.prime()
.then(startRouting)
.then(querySucceeded, _queryFailed, null);
function querySucceeded(data) {
log('Retrieved [Lookups] from remote data source', data, true);
return true;
}
}
function startRouting() {
$rootScope.$on('$routeChangeStart', function (event, next, current) {
$rootScope.error = null;
if ($rootScope.user) {
return true;
} else {
$rootScope.user = {};
var defered = $q.defer();
checkRouting($q, $rootScope, $location);
return defered.promise;
}
});
var checkRouting = function ($q, $rootScope, $location) {
var defered = $q.defer();
Auth.getCurrentUser()
.then(function (data) {
$rootScope.user.isInUserGroup = data.data.IsInUserGroup;
$rootScope.user.firstName = data.data.FirstName.replace(/\"/g, "");
$rootScope.user.lastName = data.data.LastName.replace(/\"/g, "");
$rootScope.user.userName = data.data.UserName.replace(/\"/g, "");
});
return defered.promise;
};
}
function _queryFailed(error) {
var msg = config.appErrorPrefix + 'Error priming data.' + error.message;
logError(msg, error);
throw error;
}
}]);
})();
The prime function is found in the datacontext.js module:
function prime() {
if (primePromise) return primePromise;
var deferred = $q.defer();
primePromise = $q.all([getLookupLists()])
.then(extendMetadata)
.then(success);
function success() {
setLookups();
dataPrimed = true;
//apps.startRouting();
log('Primed the data');
};
function extendMetadata() {
var metadataStore = manager.metadataStore;
var types = metadataStore.getEntityTypes();
types.forEach(function (type) {
if (type instanceof breeze.EntityType) {
set(type.shortName, type);
}
});
function set(resourceName, entityName) {
metadataStore.setEntityTypeForResourceName(resourceName, entityName);
}
}
deferred.promise = primePromise;
return deferred.promise;
}
function setLookups() {
service.lookupCachedData = {
actions: _getAllLocal('ActionDomain', 'sortorder'),
statusCodes: _getAllLocal('StatusDomain', 'sortorder')
}
}
function _getAllLocal(resource, ordering) {
return EntityQuery.from(resource)
.orderBy(ordering)
.using(manager)
.executeLocally();
}
function getLookupLists() {
return EntityQuery.from('Lookups')
.using(manager).execute()
.then(querySucceeded, _queryFailed);
function querySucceeded(data) {
log('Retrieved [Lookups] from remote data source', data, false);
return true;
}
}
The code is called in the search.js view controller module
function activate() {
common.activateController([getActionDomain(), getStatusDomain(), getCpuLog()], controllerId)
.then(function () { log('Activated search View', null, false); });
}
function getActionDomain() {
if (datacontext.lookupCachedData && datacontext.lookupCachedData.actions) {
vm.actions.push({ value: 0 });
datacontext.lookupCachedData.actions.forEach(function (actionItem) {
vm.actions.push(actionItem);
})
}
}
function getStatusDomain() {
if (datacontext.lookupCachedData && datacontext.lookupCachedData.statusCodes) {
vm.statusList.push({ value: 0 });
datacontext.lookupCachedData.statusCodes.forEach(function (statusItem) {
vm.statusList.push(statusItem);
})
}
}
If you want to wait that the promise is resolved before display the view you can use the resolve property in your service $routeProvider when you configure the app module.
There an example:
$routeProvider.when('/staff_profil/edit', {
templateUrl: 'app/app/assets/partials/profil-edit.html',
controller: 'ProfilEditController'
resolve: {
'currentUser':function( UserService){
return UserService.getCurrentUser();
}
});
You have to return a promise in the resolve!!
In this example, I wait to get the currentUser before display my profil-edit page. The UserService.getCurrentUser() is a promise create by the $http service in my UserService.
Moreover you can use this promise resolve in my controller ProfilEditController by injecte the 'currentUser' in my controller like if it is a service and then you can use currentUser.name
in your controller and view.
I hope this will help you!