I'm looking for solution for load data from sqlite in cordova bootstrap, for more specifically in $ionicPlatform.ready but until here I don't have a solution.
See a piece of my code (I'm using ionic with $cordovaSQLite):
app.js file
...
app.run(function($DB, $log, params...){
$ionicPlatform.ready(function() {
...
$DB.init().then(function(res){
// here I need load a user
$rootScope.user = res;
$log.debug('1 step = user loaded');
});
$rootScope.$on('$stateChangeStart', function(event, toState, toParams){
$log.debug('2 step = use user loaded!');
var _requireAuth = toState.data.requireAuth;
if(_requireAuth && !$rootScope.user){
event.preventDefault();
$state.go('login');
}
});
});
...
DB.js file
app.factory('$DB', function($q, $cordovaSQLite, params...){
...
_self.init = function(){
var deferred = $q.defer();
$cordovaSQLite.execute($ndDB.open(), "SELECT * FROM user LIMIT 1", [])
.then(function(res){
if(res.rows.length > 0){
var _user = {
id: res.rows.item(0).id,
name: res.rows.item(0).name,
...
};
deferred.resolve(_user);
}
}, function(err){
deferred.reject(err);
});
return deferred.promise;
...
};
...
});
config.js file
app.config(function($urlRouterProvider, params...){
...
$urlRouterProvider.otherwise('/home');
...
});
The problem is, I need $rootScope.user before all, because I need authenticate and redirect, but always I go direct to home, because $rootScope.user is already late and $stateChangeStart no apply redir. If put it in $DB.init() callback, the behaviour is the same because default route is /home and stateChangeStart load after Db.init().
Now I'm using a bad method, in home I check $rootScope.user and redirect to /login, but it's so bad because first we see the transition between home and login.
If I use the localStorage, it works fine, but I really need to use the sqlite.
What the best approach for this?
Thanks
Related
I am new to angularjs and started implementing login/logout for my assignments.
What i am doing now is-
$rootScope.$on('$stateChangeStart', function (event, toState, toParams,fromState) {
var token = SessionService.get('token')
LoginService.isLoggedIn({token:token}).$promise.then(function(response){
if(!response.error){
var isLoggedInOnServer = response.object
var requireLogin = toState.data.requireLogin
if(!isLoggedInOnServer && requireLogin){
console.log("1....")
event.preventDefault()
$state.go('user.signin');
}
console.log(toState.data.title,['signin','signup','forget'].indexOf(toState.data.title),isLoggedInOnServer,requireLogin)
if(isLoggedInOnServer && !requireLogin && ['signin','signup','forget'].indexOf(toState.data.title) > -1){
console.log("2....")
$state.go('app.dashboard')
}
}else{
console.log("3....")
event.preventDefault();
$state.go('user.signin')
}
})
The problem with this is when i hit a secure page, it first got there and show that page in flicker then return to login page if i am not login.
If i am not login this should immediately redirect me to login page instead of that secure page.
Like Spring-security in spring, can any body tell me robust login mechanism or some logic that i can implement?
I'm making a login with redirection too.. this is working fine to me.
when i login, the token is stored on a cookie and a variable to render my menubar.
if my cookie is empty and my render too this redirect to the index.html (my login)
var miApp = angular.module('Natura', ['ngRoute', 'ngTable', 'ngCookies'])
.run(function ($rootScope, $location, $cookies, $window, loginService) {
$rootScope.render = $cookies.get('render');
$rootScope.$on('$routeChangeStart', function () {
if ($window.location.pathname !== "/NaturaWEB/index.html") {
if ($rootScope.render !== "true") {
$window.location.href = 'index.html#/';
}
}
});
});
with $window.location.pathname i can control what url is being trying to access.
sorry if this is a lot crappy.
I am using ui-router for my angular app. I have some views that contain images. I would like the angular-loading-bar to track also when these images are finished loading. Or more generally, until the view has finished rendering.
When I stumbled upon that post on how to do SEO with angularJS, it indicated that we must create static html snapshots of every "page" and serve them to search engine bots so that they can be crawled. To do that, we must ourselves crawl over all pages in our application with a headless browser and store the html into a file that will be served. But, with angular having to load views over ajax and all, we must wait for our page to be loaded before storing what the headless browser html. Otherwise we get empty html with empty views.
I have written a small script that can check for the ready status of our views. When the $rootScope.status property equals 'ready', I know I can store my headless browser's html as it has finished loading.
var app = angular.module("app", ["ui.router", 'ngAnimate','angular-loading-bar','angular-images-loaded','angular-google-analytics']);
app.run(['$rootScope', function($rootScope){
$rootScope.loadingCount = 0;
$rootScope.changeSuccess = false;
$rootScope.ready = function(){
$rootScope.loadingCount--;
if (($rootScope.loadingCount == 0) && ($rootScope.changeSuccess == true)){
$rootScope.status = 'ready';
}
};
$rootScope.loading = function(){
$rootScope.loadingCount++;
$rootScope.status = 'loading';
};
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.loadingCount = 0;
$rootScope.changeSuccess = false;
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
$rootScope.changeSuccess = true;
});
$rootScope.$on("$viewContentLoading", function(){
$rootScope.loadingCount++;
});
$rootScope.$on("$viewContentLoading", function(){
$rootScope.loadingCount--;
});
}]);
Then, in each of our controllers, we must call
$rootScope.loading();
and when the controller is ready
$rootScope.ready()
With this, we'll be able to check if all our controllers have rendered their views. It's not super elegant for now, but it does the job.
This script can be well integrated with angular-loading-bar as it tracks the readiness of the overall application. The progress bar could be an indicator of that progress. The drawback of this is that it has conflicts with the natural behaviour of angular-loading-bar tracking XHR requests.
For example, in my controllers I use this :
app.controller("WorksController", [
"$scope", "cfpLoadingBar",
function ($scope, cfpLoadingBar) {
cfpLoadingBar.start();
$scope.imgLoadedEvents = {
always: function () {
cfpLoadingBar.complete();
}
};
}
]);
This code should be migrated right in the $rootScope script that tracks the readiness of the views.
$rootScope.$watch('status', function(newValue, oldValue){
if (newValue == 'loading'){ cfpLoadingBar.start() }
else if (newValue == 'ready') { cfpLoadingBar.complete() }
})
Though, angular-progress-bar still works in the background. I let active the XHR interceptor. Though, if an XHR requests is completed before the image has loaded, the progress bar disappears even if the views have not finished. As well, if an image has loaded before the XHR request is completed, the progress bar disappears.
How can I integrate the XHR interception capabilities of the angular-loading-bar with this view readiness interception capabilities?
This isn't possible to do without modifying angular-loading-bar's code because the $http interceptor does not have any kind of API you can hook into.
You should fork the project on Github and modify the cfb.loadingBarInterceptor. Basically what you'll need to do is remove the code that hides and shows the loading bar. Leave the code in that broadcasts events on $rootScope. This is what you'll hook into.
return {
'request': function(config) {
// Check to make sure this request hasn't already been cached and that
// the requester didn't explicitly ask us to ignore this request:
if (!config.ignoreLoadingBar && !isCached(config)) {
$rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
}
return config;
},
'response': function(response) {
if (!response || !response.config) {
$log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
return response;
}
if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
}
return response;
},
'responseError': function(rejection) {
if (!rejection || !rejection.config) {
$log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
return $q.reject(rejection);
}
if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
}
return $q.reject(rejection);
}
};
Now, in your run block, simply add these lines at the bottom:
app.run(['$rootScope', function($rootScope){
// Previous code ...
$rootScope.$on("cfbLoadingBar:loaded", $rootScope.ready);
$rootScope.$on("cfbLoadingBar:loading", $rootScope.loading);
});
In case it's not clear what this does, this uses the events from your new $http interceptor which results in ready and loading being called in the same way that your controllers do.
The blogpost you are referring to is legacy.
Google will index your single page application without any html snapshots.
If you want to create Snapshots (e.g. for use of canonical tag which is not supported yet!) you should NOT do this manually but integrate a task in your build process. With Grunt/Gulp this is very simple and can be done in 10 minutes. There is no need to specify a "ready" Event - phantomjs will recognize when your script is done.
The proper way would be to move the function that loads your images in your states resolve function. Then you dont have to worry about sending ready events to phantom. ;-) To use this with a loading bar:
$rootScope.$on('$stateChangeStart', function () {
ngProgress.start();
// or your: cfpLoadingBar.start();
});
$rootScope.$on('$stateChangeSuccess', function () {
ngProgress.complete();
// or your: cfpLoadingBar.complete();
});
As #anid-monsur specified, impossible to do without modifying the loading-bar code. Though, I wanted to keep the initial functionality of angular-loading-bar so I followed a different path that suggested by him.
Instead of removing the hide-show code, I added custom event handlers like these in the angular-loading-bar interceptor :
$rootScope.$on("cfpLoadingBar:customRequest", function(event, args){
if (!args) {
args = {};
}
if (!args.url){
args.url = 'custom';
}
loading(args);
});
$rootScope.$on("cfpLoadingBar:customResponse", function(event, args){
if (!args) {
args = {};
}
if (!args.config){
args.config = {};
}
if (!args.config.url){
args.config.url = 'custom';
}
response(args);
});
Check the code here
So, when my ui-router changes state I can write :
app.run(['$rootScope', 'cfpLoadingBar', function($rootScope, cfpLoadingBar){
$rootScope.$broadcast("cfpLoadingBar:customRequest");
$rootScope.ready = function(){
$rootScope.$broadcast("cfpLoadingBar:customResponse");
};
$rootScope.loading = function(){
$rootScope.$broadcast("cfpLoadingBar:customRequest");
};
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.$broadcast("cfpLoadingBar:customRequest");
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
$rootScope.$broadcast("cfpLoadingBar:customResponse");
});
}]);
Notice the added $rootScope.ready() and $rootScope.loading()
They are used when a controller has a deferred loading process
For example, with angular-imagesloaded :
app.controller("WorksController", [
"$scope", '$rootScope',
function ($scope, $rootScope) {
$rootScope.loading();
$scope.imgLoadedEvents = {
done: function () {
$rootScope.ready();
}
};
}
]);
This way, I can still use the native interceptor provided by angular-loading-bar and register my own "custom" requests.
This issue is related to the Plugin as Anid Monsur said. You can solve the issue just upgrading angular-loading-bar to latest version or at least 7.0.1+.
I am having an app that every page except login requires authentication. I do this by checking the $stateChangeStart and then redirecting to /login when token is not set. on initial app load this works fine, but at login screen if i type another "restricted" url. It does the check, changes the url, but still loads the restricted page. What am I missing here?
//app.run:
app.lazy = $couchPotato;
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeStart', function(event, toState, toStateParams) {
console.log("state changed to "+toState.name);
console.log(toState);
Authentication.validateLogin(toState, toStateParams);
});
Authentication.validateLogin:
validateLogin:function(toState, toStateParams){
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
if ($localStorage.token == null) {
console.error("Not Authenticated!");
$location.url('/login');
}
}
I see that your using angular-ui, so I'm not exactly sure what advantages that has over using 'basic' angular, but I wrote this to handle validating a token when the route changes.
app.run(['$rootScope', 'authService',
function ($rootScope, authService) {
$rootScope.$on("$routeChangeSuccess", function (event, next, current) {
if (next && next.$$route && authService.isAuthenticated()) {
authService.validate();
$rootScope.appTitle = next.$$route.title;
}
});
}]);
The $routeChangeSuccess handles navigation to routes after the controller has loaded (very important for objects that load when the page loads when validation is confirmed), and then validates them.
This also performs 2 checks in that it checks if the token in local storage exists and is formed correctly, and if it is, send it back to the server to confirm that.
I also found that I had to use the $locationChangeStart to handle page refresh, to re-validate when someone tries to refresh the page.
The validateLogin solution:
validateLogin:function(toState, toStateParams){
if(toState.access){
if(toState.access.loginRequired){
if ($localStorage.token == null) {
$state.go('login');
}
}
}
Try this with the validateLogin:
validateLogin:function(toState, toStateParams){
if(toState.access){
if(toState.access.loginRequired){
if ($localStorage.token == null) {
$state.go('login');
}
}
}
}
Hey guys so i am using firebase and angular to build a sample application.
This is the registration function
$scope.register = function (){
Authentication.register($scope.user)
.then(function(user){
Authentication.login($scope.user);
})
.then(function(user){
$timeout(function(){
$location.path('/meetings');
}, 5);
})
.catch(function(error){
$scope.message = error.toString();
});
} //register
this function calls to methods in this factory
myApp.factory('Authentication', function($firebase,
$firebaseAuth,$location, FIREBASE_URL){
var ref = new Firebase (FIREBASE_URL);
var auth = $firebaseAuth(ref);
var myObject = {
login : function (user){
return auth.$authWithPassword({
email: user.email,
password: user.password
});
}, // login
logout: function (){
return auth.$unauth();
},
register : function (user){
return auth.$createUser(user.email,user.password);
} //register
} //myObject
return myObject;
});
here is the status.js controller which changes my index based on whether the user is logged in or not
auth.$onAuth(function(authData){
if (authData){
console.log("i entered authData");
$scope.userStatus = authData.password.email;
} else {
$scope.userStatus = false;
}
});
part of index.html file
<div class="userinfo"
ng-controller="StatusController" ng-show="userStatus">
<span class="user">Hi {{userStatus}}</span>
Log Out
</div>
my problem is that the ng-view needs a page refresh to show the new value. its not showing it automatically but my code works. if i refresh the page manually i can see that the user got registered and logged in.
Search for about 2 hours now and $scope.$apply here does nt seem to be the case.
Thank you in advance
I wanna thank you guys for the possible solutions but it seems that the solution lies in the $timeout variable.
Because the registration function below talks with an API and i am working from a local environment using GulpJS there is a delay in talking between my script and the Firebase API.
$scope.register = function (){
Authentication.register($scope.user)
.then(function(user){
Authentication.login($scope.user);
})
.then(function(user){
$timeout(function(){
$location.path('/meetings');
}, 5);
})
.catch(function(error){
$scope.message = error.toString();
});
} //register
Thats why i put a $timeout angular function in there in the first place but i forgot that the number 5 in input in the $timeout function is in milliseconds and it seems because of my setup(using a local environment while talking to the Firebase API online) i found that if i use a 2 second delay in the redirection now ng-show changes automatically without manually refreshing the page.
correct code would be the one below:
$scope.register = function (){
Authentication.register($scope.user)
.then(function(user){
Authentication.login($scope.user);
})
.then(function(user){
$timeout(function(){
$location.path('/meetings');
}, 2000);
})
.catch(function(error){
$scope.message = error.toString();
});
} //register
I suspect that if i use my angular app on the web i could possibly lower the delay or even remove the $timeout function completely.
I'm trying to cleanly implement a way to redirect the user to a login route if they're not logged in. I'm basing my solution off of another SO answer here that doesn't work out of the box. Here's my solution.
angular.module('myApp', ['ngResource', 'ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
var requireAuthentication = function () {
return {
load: function ($q) {
console.log('Can user access route?');
if (g_isloggedIn === true) { // fire $routeChangeSuccess
var deferred = $q.defer();
deferred.resolve();
console.log('Yes they can!');
return deferred.promise;
} else { // fire $routeChangeError
console.log('No they cant!');
return $q.reject("'/login'");
}
}
};
};
$routeProvider
.when('/some_page_that_requires_authentication', {
templateUrl: '/views/secret.html',
controller: 'secretCtrl',
resolve: requireAuthentication()
})
.when('/anybody_can_see_me', {
templateUrl: '/views/public.html',
controller: 'publicCtrl',
});
}]);
My question is, where can I listen on the $routeChangeError event so that I can redirect the route? I tried doing it in a directive, but could never get the event to fire. I can't put it into a controller, because it won't load if the promise is rejected. Any thoughts?
Is there a reason why you shouldn't redirect the user from inside the function?
This works fine for me, it doesn't load the controller / view if the promise is not resolved.
I modified the function like this:
var requireAuthentication = function () {
return {
load: function ($q, $location) {
console.log('Can user access route?');
var deferred = $q.defer();
deferred.resolve();
if (g_isloggedIn === true) { // fire $routeChangeSuccess
console.log('Yes they can!');
return deferred.promise;
} else { // fire $routeChangeError
console.log('No they cant!');
$location.path('/login');
// I don't think this is still necessary:
return $q.reject("'/login'");
}
}
};
};
You might find some help in this question: AngularJS: Understanding $rootScope.$on('$routeChangeSuccess
Generally I feel that using "resolve" for ensuring authentication is a bit odd, but there are not much better ways, I would suggest adding some services etc. Which is besides the point.
Finally, other routing solutions like https://github.com/dotJEM/angular-routing may provide you with a better degree of control.
I'm sure you're well past this issue, but for those looking for answers now, here's my two cents.
The only thing you are missing here is what happens when the promise is rejected. You'll want to do this with HTTP interception. You put a listener on routeChangeError, and redirect there. https://docs.angularjs.org/api/ng/service/$http (scroll down to interceptors section)