AngularJS/Ionic promises - sometimes page loads forever - angularjs

I am using promises in my controller, and most of the times it works well. But sometimes it just loads forever and the WordPress.getAllCategories() function does not even get called.
This is my controller:
var mod = angular.module('app.controllers.home', []);
mod.controller('HomeCtrl', function ($scope, $q, $sce, $ionicPlatform, WordPress, Loading) {
console.log('HomeCtrl init');
$scope.$on('$ionicView.enter', function () {
Loading.show();
WordPress.getAllCategories()
.then(function (cats) {
console.info(angular.toJson(cats));
console.info('cats ^');
$q.all(cats.data.map(function (cat) {
var d = $q.defer();
console.error(cat.name);
WordPress.getLatestPostOfCategory(cat.id)
.then(function (post) {
console.debug(post.data.title.rendered);
WordPress.getMediaById(post.data.featured_media)
.then(function (media) {
console.log(media.data.source_url);
cat.firstPost = {};
cat.firstPost.id = post.data.id;
cat.firstPost.title = post.data.title.rendered;
cat.firstPost.content = post.data.content.rendered;
if (cat.firstPost.title.length > 50) {
cat.firstPost.title = cat.firstPost.title + '...';
}
if (cat.firstPost.content.length > 70) {
cat.firstPost.content = cat.firstPost.content.substr(0, 60) + '...';
}
cat.firstPost.thumbnail = media.data.source_url;
d.resolve(cat);
}, function (err) {
console.error(angular.toJson(err));
});
});
return d.promise;
})).then(function (cats) {
console.log('Loaded all articles and for main page.');
$scope.homeCategories = cats;
Loading.hide();
});
});
});
});
Is there anything wrong in my controller?
P.S. I also debug all the WordPress service functions and they work just fine, and provide the needed data.
EDIT:
Sometimes when it loads forever, I see the console.error(cat.name); debug message only logs 3 messages. But still proceeds to the next function...

This is how I solved it, by Bergi's advice.
Source for help: Promise anti pattern by Gorgi Kosev (bluebird)
var categories = [];
function sort() {
return WordPress.getAllCategories()
.then(function (cats) {
console.log('thens');
return $q.all(cats.data.map(function (cat) {
console.info('cat: ' + cat.name);
var category = {};
category.name = cat.name;
return WordPress.getLatestPostOfCategory(cat.id)
.then(function (post) {
var post = post.data;
category.post = {};
category.post.id = post.id;
category.post.title = post.title.rendered;
category.post.content = post.content.rendered;
console.log('ID: ' + category.post.id + ', title: ' + category.post.title);
return WordPress.getMediaById(post.featured_media);
}).then(function (media) {
category.post.thumbnail = media.data.source_url;
categories.push(category);
console.log('Pushed category "' + category.name + '"');
});
}));
}, function (err) {
console.error('ERR1');
console.error(angular.toJson(err));
});
}
sort()
.then(function () {
console.info('LOADED ALL CATEGORIES');
$scope.categories = categories;
}, function (err) {
console.error('err:' + angular.toJson(err));
});

Related

Creating new function in angularjs controller won't work

When creating new function in angularjs controller and assign it to button with ng-click and function is simple,
function toLogin() {
console.log("Entered function");
$state.go('login');
}
I tried with $scope and still does not work.
this is my controller:
'use strict';
angular.module('crudApp').controller('UserController',
['UserService', '$scope','$state', function( UserService, $scope, $state) {
var self = this;
self.user = {};
self.users=[];
self.user.enabled = false;
self.user.confirmationToken = '';
self.loggedUser = null;
self.submit = submit;
self.getAllUsers = getAllUsers;
self.createUser = createUser;
self.updateUser = updateUser;
self.removeUser = removeUser;
self.editUser = editUser;
self.reset = reset;
self.firstNamePattern=/^[A-Z][a-z]*\S$/;
self.lastNamePattern=/^[A-Z][a-z]*\S$/;
self.userNamePattern= /^\S*$/;
self.passwordPattern = /^\S*$/;
self.phonePattern = /^[0-9]+\S$/;
self.successMessage = '';
self.errorMessage = '';
self.done = false;
self.onlyIntegers = /^\d+$/;
self.onlyNumbers = /^\d+([,.]\d+)?$/;
function toLogin() {
console.log('Entered function');
// $state.go('login');
}
function submit() {
console.log('Submitting');
if (self.user.id === undefined || self.user.id === null) {
console.log('Saving New User', self.user);
createUser(self.user);
} else {
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
}
$scope.moje = function () {
console.log('Submitting');
if (self.user.id === undefined || self.user.id === null) {
console.log('Saving New User', self.user);
createUser(self.user);
} else {
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
}
function createUser(user) {
console.log('About to create user');
UserService.createUser(user)
.then(
function (response) {
console.log('User created successfully');
self.successMessage = 'User created successfully';
self.errorMessage='';
self.done = true;
self.user={};
$scope.registerForm.$setPristine();
$scope.registerForm.$setUntouched();
// $state.reload();
},
function (errResponse) {
console.error('Error while creating User');
self.errorMessage = 'Error while creating User: ' + errResponse.data.errorMessage;
self.successMessage='';
}
);
}
function updateUser(user, id){
console.log('About to update user');
UserService.updateUser(user, id)
.then(
function (response){
console.log('User updated successfully');
self.successMessage='User updated successfully';
self.errorMessage='';
self.done = true;
$scope.myForm.$setPristine();
},
function(errResponse){
console.error('Error while updating User');
self.errorMessage='Error while updating User '+errResponse.data;
self.successMessage='';
}
);
}
function removeUser(id){
console.log('About to remove User with id '+id);
UserService.removeUser(id)
.then(
function(){
console.log('User '+id + ' removed successfully');
},
function(errResponse){
console.error('Error while removing user '+id +', Error :'+errResponse.data);
}
);
}
function getAllUsers(){
return UserService.getAllUsers();
}
function editUser(id) {
self.successMessage='';
self.errorMessage='';
UserService.getUser(id).then(
function (user) {
self.user = user;
},
function (errResponse) {
console.error('Error while removing user ' + id + ', Error :' + errResponse.data);
}
);
}
function reset(){
self.successMessage='';
self.errorMessage='';
self.user={};
$scope.myForm.$setPristine(); //reset Form
}
}
]);
And and I using ui-routing and this is where I assign controller to my view:
}).state('success',{
url: '/success',
templateUrl: 'partials/successMessage',
controller: 'UserController',
controllerAs: 'sCtrl'
});
And the ftl part of code is here:
<button type="button" ng-click='sCtrl.toLogin()' class="btn btn-primary">Login</button>
It won't even print to console, but when assigning older function it works perfectly, although it is much more complicated. Ps. Sorry for bad clarification at first, I am new to community and still learning a proper way to ask question.
You're not registering the method to the controller, to do that.
Add this below in self.onlyNumbers
self.toLogin = toLogin;
You need to use $scope
$scope.toLogin = function(){
console.log("Entered function");
$state.go('login');
}

Delay loading data in Angular JS

I have code like this
(function (app) {
app.controller('productListController', productListController)
productListController.$inject = ['$scope', 'apiService', 'notificationService', '$ngBootbox', '$filter'];
function productListController($scope, apiService, notificationService, $ngBootbox, $filter) {
$scope.products = [];
$scope.page = 0;
$scope.pagesCount = 0;
$scope.getProducts = getProducts;
$scope.keyword = '';
$scope.search = search;
$scope.deleteProduct = deleteProduct;
$scope.selectAll = selectAll;
$scope.deleteMultiple = deleteMultiple;
function deleteMultiple() {
var listId = [];
$.each($scope.selected, function (i, item) {
listId.push(item.ID);
});
var config = {
params: {
checkedProducts: JSON.stringify(listId)
}
}
apiService.del('/api/product/deletemulti', config, function (result) {
notificationService.displaySuccess('Deleted successfully ' + result.data + 'record(s).');
search();
}, function (error) {
notificationService.displayError('Can not delete product.');
});
}
$scope.isAll = false;
function selectAll() {
if ($scope.isAll === false) {
angular.forEach($scope.products, function (item) {
item.checked = true;
});
$scope.isAll = true;
} else {
angular.forEach($scope.products, function (item) {
item.checked = false;
});
$scope.isAll = false;
}
}
$scope.$watch("products", function (n, o) {
var checked = $filter("filter")(n, { checked: true });
if (checked.length) {
$scope.selected = checked;
$('#btnDelete').removeAttr('disabled');
} else {
$('#btnDelete').attr('disabled', 'disabled');
}
}, true);
function deleteProduct(id) {
$ngBootbox.confirm('Are you sure to detele?').then(function () {
var config = {
params: {
id: id
}
}
apiService.del('/api/product/delete', config, function () {
notificationService.displaySuccess('The product hase been deleted successfully!');
search();
}, function () {
notificationService.displayError('Can not delete product');
})
});
}
function search() {
getProducts();
}
function getProducts(page) {
page = page || 0;
var config = {
params: {
keyword: $scope.keyword,
page: page,
pageSize: 20
}
}
apiService.get('/api/product/getall', config, function (result) {
if (result.data.TotalCount == 0) {
notificationService.displayWarning('Can not find any record.');
}
$scope.products = result.data.Items;
$scope.page = result.data.Page;
$scope.pagesCount = result.data.TotalPages;
$scope.totalCount = result.data.TotalCount;
}, function () {
console.log('Load product failed.');
});
}
$scope.getProducts();
}
})(angular.module('THTCMS.products'));
So my problem is when i loading data the application take me some time to load data.
I need load data as soon as
Is the any solution for this?
Since you are loading data via api call, there will be a delay. To handle this delay, you should display a loading screen. Once the data is loaded, the loading screen gets hidden and your main screen is visible. You can achieve this using $http interceptors.
See : Showing Spinner GIF during $http request in angular
The api-call is almost certainly causing the delay. Data may be received slowly via the api-call so you could display any sort of loading text/image to notify the use that the data is being loaded.
If u want the data ready at the time when controller inits, u can add a resolve param and pass the api call as a $promise in the route configuration for this route.

Ionic/Angular concurrent SQLite connections and writes failing

I'm fairly new to both Ionic and Angular. I'm trying to write to a SQLite database and it's intermittently successful. When I do a for loop and insert records rapidly, some succeed and some fail (without apparent error). The query execution uses promises, so multiple queries may be trying to execute concurrently. It seems that this causes a synchronization issue in SQLite - or the SQLite plugin. I've tried opening a new DB connection with every execute(), reopening the existing connection on every execute(), and I've also tried opening a connection globally in app.js once and reusing that connection. They all seem to behave the same.
A custom 'dbQuery' function is used to build queries and is chainable. The idea is that any place in my app with access to the DB service can execute a query and expect the results to flow into an "out" variable like:
var my_query_results = [];
DB.begin().select("*","table1").execute(my_query_results).then(function() {
console.log("Query complete");
});
That much works, but the problem comes from writes:
var records = [
{id:1, name:"Bob"},
{id:2, name:"John"},
{id:3, name:"Jim"},
];
for (var i = 0; i < records.length; i++) {
var obj = records[i];
var result = [];
DB.begin().debug(true).insert("table1", "(id,name)", "("+obj.id+","+ obj.name+")").execute(result).then(function () {
console.log("Inserted record", JSON.stringify(obj));
});
}
Sometimes it fails without any logged or apparent error, sometimes it succeeds. If I perform the inserts slowly over time, it seems to work without issue.
app.js
var db;
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.run(function ($ionicPlatform, $cordovaSQLite, appConfig, $q) {
$ionicPlatform.ready(function () {
db = $cordovaSQLite.openDB({
name: appConfig.sqlite_db,
location: appConfig.sqlite_db_location
});
dbQuery = function () {
this.bDebug = false;
this.query = "";
this.result = [];
this.params = [];
this.debug = function (value) {
this.bDebug = (value === true ? true : false);
return this;
};
this.rawQuery = function (query) {
this.query = query;
return this;
};
this.insert = function (table, fields, values) {
this.query = "INSERT INTO '" + table + "' (" + fields + ") VALUES (" + values + ")";
return this;
};
this.select = function (fields, table) {
this.query = "SELECT " + fields + " FROM " + table;
return this;
};
this.delete = function (query) {
this.query = "DELETE FROM " + query;
return this;
};
this.where = function (column, expression, value) {
expression = expression || "=";
this.query += " WHERE `" + column + "` " + expression + " ? ";
this.params[this.params.length] = value;
return this;
};
this.and = function (column, expression, value) {
expression = expression || "=";
this.query += " AND '" + column + "' " + expression + " ? ";
this.params[this.params.length] = value;
return this;
};
this.execute = function (out_var) {
var self = this;
this.result = out_var;
if (this.bDebug) {
console.log("Compiled query is", this.query);
}
var deferred = $q.defer();
db.open(function () {
console.log("Opened");
}, function () {
console.log("Failed");
});
//actually execute the query
$cordovaSQLite.execute(db, this.query, this.params).then(
function (res) {
for (var i = 0; i < res.rows.length; i++) {
self.result.push(res.rows.item(i));
console.log("Added row to set", JSON.stringify(res.rows.item(i)));
}
if (res.rows.length == 0 && self.bDebug === true) {
console.log("No results found ");
}
deferred.resolve();
}, function (err) {
console.error(JSON.stringify(err), this.query);
deferred.reject();
});
return deferred.promise;
}
services.js
.factory('DB', function ($ionicPlatform) {
return {
begin: function () {
return new dbQuery();
}
}
})
.factory('DbBootstrap', function ($cordovaSQLite, appConfig, $q, $state, DB) {
return {
wipe: function () {
DB.begin().rawQuery("DELETE FROM table1").execute().then(function () {
console.log("Purged records");
});
},
init: function () {
var result = []; //out variable
DB.begin().rawQuery("CREATE TABLE IF NOT EXISTS table1 (id integer primary key, name text)").execute(result).then(function () {
console.log("Schema create returned", JSON.stringify(result));
});
var records = [
{
id: 1, name:'Jim'
...
},
{
id: 2, name:'Bob'
...
},
{
id: 3, name:'John'
...
}
];
for (var i = 0; i < records.length; i++) {
var obj = records[i];
var result = [];
DB.begin().debug(true).insert("table1", "(id,name)", "(obj.id, obj.name).execute(result).then(function () {
console.log("Inserted record", JSON.stringify(obj));
});
}
}
})
I'm sure I'm missing something fundamental about angular, promises, and sqlite locking. If anyone has advice I'd really appreciate it.
I resolved this following the excellent advice here - Angular/Ionic and async SQLite - ensuring data factory initialised before return
The key issue being that I needed to wrap all my DB operations in promises and use them for orderly initialization and callbacks.
.factory('DB', function ($q, $cordovaSQLite, appConfig) {
//private variables
var db_;
// private methods - all return promises
var openDB_ = function (dbName, location) {
var q = $q.defer();
try {
db_ = $cordovaSQLite.openDB({
name: dbName,
location: location
});
q.resolve(db_);
} catch (e) {
q.reject("Exception thrown while opening DB " + JSON.stringify(e));
}
return q.promise;
};
var performQuery_ = function (query, params, out) {
var q = $q.defer();
params = params || [];
out = out || [];
//open the DB
openDB_(appConfig.sqlite_db, appConfig.sqlite_db_location)
.then(function (db) {
//then execute the query
$cordovaSQLite.execute(db, query, params).then(function (res) {
//then add the records to the out param
console.log("Query executed", JSON.stringify(query));
for (var i = 0; i < res.rows.length; i++) {
out.push(res.rows.item(i));
console.log("Added row to set", JSON.stringify(res.rows.item(i)));
}
if (res.rows.length == 0 && self.bDebug === true) {
console.log("No results found ");
}
}, function (err) {
console.log("Query failed", JSON.stringify(query));
q.reject();
});
db_.open(function () {
q.resolve("DB Opened")
}, function () {
q.reject("Failed to open DB");
});
}, function (err) {
console.log(JSON.stringify(err), this.query);
q.reject(err);
});
return q.promise;
};
// public methods
var execute = function (query, params, out) {
var q = $q.defer();
performQuery_(query, params, out).then(function () {
q.resolve([query, params]);
}, function (err) {
q.reject([query, params, err]);
});
return q.promise;
};
return {
execute: execute
};
})

How to lazy load Firebase data using ionic infinite scroll

I have a set of data within a users profile which holds the UID of all the posts they have liked. I am using a data snapshot to show these images within the users profile, but I would like to load it lazy or use ionic infinite scroll to load more items as the user scrolls.
I tried by making two duplicate snap shots. First one intialises the feed upon the page loading, and then I wanted the second one to load by using ionic infinite scroller.
But it doesn't work.
Does anyone know of a better way to do this?
service.js
getWardrobeFeed: function(){
var defered = $q.defer();
var user = User.getLoggedInUser();
var wardrobeKeyRef = new Firebase(FIREBASE_URL + '/userProfile/' + user.uid + '/wardrobe');
wardrobeKeyRef.orderByKey().limitToLast(2).on('child_added', function (snap) {
var imageId = snap.key();
userImageRef.child(imageId).on('value', function (snap) {
$timeout(function () {
if (snap.val() === null) {
delete userWardrobeFeed[imageId];
} else {
userWardrobeFeed[imageId] = snap.val();
}
});
});
});
wardrobeKeyRef.orderByKey().limitToLast(2).on('child_removed', function (snap) {
var imageId = snap.key();
$timeout(function () {
delete userWardrobeFeed[imageId];
});
});
defered.resolve(userWardrobeFeed);
return defered.promise;
},
getWardrobeItems: function(){
var defered = $q.defer();
var user = User.getLoggedInUser();
var wardrobeKeyRef = new Firebase(FIREBASE_URL + '/userProfile/' + user.uid + '/wardrobe');
wardrobeKeyRef.orderByKey().limitToFirst(2).on('child_added', function (snap) {
var imageId = snap.key();
userImageRef.child(imageId).on('value', function (snap) {
$timeout(function () {
if (snap.val() === null) {
delete userWardrobe[imageId];
} else {
userWardrobe[imageId] = snap.val();
}
});
});
});
wardrobeKeyRef.orderByKey().on('child_removed', function (snap) {
var imageId = snap.key();
$timeout(function () {
delete userWardrobe[imageId];
});
});
defered.resolve(userWardrobe);
return defered.promise;
},
controller.js
$scope.userWardrobe = [];
Service.getWardrobeFeed().then(function(items){
$scope.userWardrobe = items;
});
$scope.loadMore = function() {
Service.getWardrobeItems().then(function(items){
$scope.userWardrobe = $scope.userWardrobe.concat(items);
$scope.$broadcast('scroll.infiniteScrollComplete');
});
};

How to delay catch function in angularjs during routing to a page?

I'm using $rootscope and two other service inside a service to update the data based on the current car. CarsService helps with car selection and DataSelection point to the folder that keeps the data. When no car is in the car list user should be directed to thank page.
Here is the error: "Sometimes the processes between services takes long and no car detected. so user directed to thank page." How could I delay the .catch without affecting the total performance?
function DataSelection($rootScope, CarsService) {
var self = this;
function load() {
self.carslist = [];
return CarsService
.getCars()
.then(function (carslist) {
self.carslist = carslist;
})
.then(CarsService.getCurrent)
.then(function (data) {
self.currentCarIndex = data.index;
$rootScope.currentCarIndex = data.index;
self.car = data.current;
})
.catch(function () {
console.log('There is no car left, this should be disabled!');
location.assign('/#/thank');
});
}
self.loaded = load();
self.updateCars = function () {
return CarsService
.gotoNext()
.then(function (data) {
self.currentCarIndex = data.index;
$rootScope.currentCarIndex = data.index;
self.car = data.current;
})
.catch(function () {
console.log('There is no car left, this should be disabled!');
location.assign('/#/thank');
});
};
self.datalinks = function () {
return self
.loaded
.then(function () {
if (!self.car) {
location.assign('/#/thank');
}
var folderName = self.car.toLowerCase();
var jsonName = self.car.toLowerCase() + '-data.json';
var jsonIntroName = self.car.toLowerCase() + '.json';
var pictureB = '' + 'assets/' + self.car.toLowerCase() + '/' + self.car.toLowerCase() + '_b.jpg' + '';
var pictureL = '' + 'assets/' + self.car.toLowerCase() + '/' + self.car.toLowerCase() + '_l.jpg' + '';
return {
folderName: folderName,
jsonName: jsonName,
jsonIntroName: jsonIntroName,
pictureB: pictureB,
pictureL: pictureL
};
});
};
}

Resources