Delay in retrieving data from Ionic 2 storage - ionic2

Before I launches the app I will check with local storage if any user data available, If yes I will navigation to Home page else Login page.
Here I'm unable to retrieve stored data, Any inputs please...
Currently using Ionic 2 SQlite plugin.
Note: In browser it's working fine but on Android device it's not working.
app.component.ts : checking user data
loadUser() {
this.userSettings.getUser().then(user => {
this.userObj = JSON.stringify(user);
if (user) {
console.log('App : ', this.userObj);
this.nav.setRoot(HomePage,this.userObj);
} else {
console.log('App : No user data');
this.rootPage = LoginPage;
}
});
}
login.ts : Saving user data
this.userSettings.addUser(
userData.employeeCode,
userData.password,
userData.role
);
user-settings.ts : Storage file in providers
getUser() {
if (this.sql) {
return this.sql.get('user').then(value => value);
} else {
return new Promise(resolve => resolve(this.storage.get('user').then(value => value)));
}
}
addUser(employeeCode, password, role) {
let item = { employeeCode: employeeCode, password: password, role: role };
if (this.sql) {
this.sql.set('user', JSON.stringify(item)).then(data => {
this.events.publish('userObj:changed');
});
} else {
return new Promise(resolve => {
this.storage.set('user', JSON.stringify(item)).then(() => {
this.events.publish('userObj:changed');
resolve();
});
});
}
}
app.module.ts:
providers: [
{ provide: ErrorHandler, useClass: IonicErrorHandler },
AuthService,
SqlStorage,
UserSettings,
Storage
]
Thanks in advance.

Problem solved
After calling the sqlite operation in ngAfterViewInit it's working fine.
ngAfterViewInit() {
this.storage.get('user').then((user: any) => {
if (user) {
this.userCredentials = JSON.parse(user);
this.nav.setRoot(HomePage, this.userCredentials);
}
else {
this.rootPage = LoginPage;
}
});
}
[Source] (https://github.com/driftyco/ionic-conference-app/blob/master/src/pages/account/account.ts)
Cheers :)

As you point out that your code is working in Chrome, but not on your device, you might be calling sqlite before cordova's device.ready() has fired.
In app.component.ts ensure you call this.loadUser() in the following manner: (platform.ready() should already be in the constructor)
platform.ready().then(() => {
this.loadUser();
});

Related

Svelte with Apollo GraphQl - Mutation is not getting triggered

I'm working with my own api and I can see it work if I use #urql/svelte but since we're using Apollo with React on most of our projects, I would like to see the differences between frameworks using the same dependency.
My lib/client.js looks like this:
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client/core';
function createApolloClient() {
const httpLink = new HttpLink({
uri: 'MY_API'
});
const cache = new InMemoryCache();
const client = new ApolloClient({
httpLink,
cache
});
return client;
}
const client = new createApolloClient();
export default client;
My index.svelte is looking like this
<script>
import { setClient, mutation } from 'svelte-apollo';
import { gql } from '#apollo/client/core';
import { browser } from '$app/env';
import { onMount } from 'svelte';
import client from '../lib/client';
const email = 'AN_EMAIL';
const password = 'A_PASSWORD';
let userName;
let isLoggedIn = false;
setClient(client);
const SIGN_IN = gql`
mutation ($email: String!, $password: String!) {
userSignIn(email: $email, password: $password) {
email
id
isEnabled
name
surname
userType
}
}
`;
const signInMutation = mutation(SIGN_IN);
async function signInAction() {
await try {
signInMutation({ variables: { email, password } }).then((result) => console.log(result));
} catch (error) {
console.log(error);
}
}
const isUserLoggedIn = () => {
if (browser && localStorage.getItem('isLoggedIn') && localStorage.getItem('userName')) {
isLoggedIn = true;
userName = localStorage.getItem('userName');
}
};
onMount(() => {
isUserLoggedIn();
});
</script>
<button on:click={signInAction}>Trigger</button>
{#if isLoggedIn}
<h1>Welcome {userName}</h1>
{/if}
I honestly can't figure out what I'm missing with the Apollo setup.
I have no errors on my console and my network doesn't show anything when I click the button. The UI seems to work fine with the urql setup.
Could someone point me in the right direction? Thank you!
You have an issue in your client setup:
// ...
// const client = new createApolloClient(); // wrong use of 'new' keyword, createApolloClient() is a regular function, not a class constructor!
const client = createApolloClient();
// ...
As stated in my comment, you also have an issue in your signInAction function definition. You need to settle for one syntax:
// async/await
async function signInAction() {
try {
const result = await signInMutation({ variables: { email, password } });
console.log(result);
} catch (error) {
console.log(error);
}
}
// then/catch
function signInAction() {
signInMutation({ variables: { email, password } })
.then((result) => console.log(result))
.catch((error) => console.log(error));
}
Off-topic and opinionated: svelte-apollo radically differs from the react apollo client, is not an 'official' apollo client, and has not been updated for the past year+. You will be much better off going back to #urql/svelte.

Why does didenterregion triggers multiple times even I am already in that beacon region?

I have added one beacon region for monitoring.I am using the method didenterregion to identify when ever he enters into beacon region but the thing is even I am already in beacon region then also I am getting multiple times didenterregion signal.Can anyone help me why is this happening?
`
#Injectable()
export class BeaconProvider {
delegate: any;
region: any;
constructor(public platform: Platform, public events: Events,public IBeacon:IBeacon,public appService:AppService) {
}
initialise(): any {
let promise = new Promise((resolve, reject) => {
if (this.platform.is('cordova')) {
// Request permission to use location on iOS
this.IBeacon.requestAlwaysAuthorization();
this.IBeacon.getMonitoredRegions().then(data=>{
console.log(data);
});
this.IBeacon.getRangedRegions().then(data=>{
console.log(data);
});
this.delegate = this.IBeacon.Delegate();
// Subscribe to some of the delegate's event handlers
this.delegate.didEnterRegion()
.subscribe(
data => {
this.events.publish('this.delegate.didEnterRegion', data);
},
error => console.error()
);
this.delegate.didExitRegion()
.subscribe(
data => {
this.events.publish('this.delegate.didExitRegion', data);
},
error => console.error()
);
this.appService.getBeacons()
.subscribe(beacons => {
console.log(beacons);
var i=0;
for(i=0;i<beacons.length;i++){
// setup a beacon region – CHANGE THIS TO YOUR OWN UUID
this.region = this.IBeacon.BeaconRegion(beacons[i].beaconDetailes.beaconId,beacons[i].beaconDetailes.uuid,beacons[i].beaconDetailes.major,beacons[i].beaconDetailes.minor);
this.region.NotifyEntryStateOnDisplay = true;
this.region.NotifyOnEntry = true;
this.region.NotifyOnExit = true;
// start ranging
this.IBeacon.startMonitoringForRegion(this.region)
.then(
() => {
resolve(true);
console.log("monitor for region");
},
error => {
resolve(false);
}
);
}
}
);
} else {
resolve(false);
}
});
return promise;
}
}
`
This is the provider for beacon monitor.In some other pages I am triggering the method initialize to register beacon and start monitoring the beacons.Using events publish I am getting the beacon details when ever I entered into region in other pages.

how to unit test Angularfire2(version 5) auth service with google provider login

I'm trying to set up unit tests for a sample Angular5 app using AngularFire2 (version5) google provider login, My auth service is fairly simple and it looks like this:
let authState = null;
let mockAngularFireAuth: any = {authState: Observable.of(authState)};
#Injectable()
export class AuthService {
loggedIn: boolean;
private user: Observable<firebase.User>;
constructor(
public afAuth: AngularFireAuth
) {
this.user = afAuth.authState;
this.user.subscribe(
(user) => {
if (user) {
this.loggedIn = true;
} else {
this.loggedIn = false;
}
});
}
// --------------------------------- Google Login -----------------------------------
loginWithGoogle() {
// Sign in/up with google provider
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
alert('This email address is already registered');
}
});
});
}
// ------------------------- Checks User Authentication -----------------------
isAuthenticated() {
// returns true if the user is logged in
return this.loggedIn;
}
// --------------------------------- User LogOut -----------------------------------
logOut() {
this.afAuth.auth.signOut()
.then(() => {
this.loggedIn = false;
});
}
}
I want to test my loginWithGoogle() method but I am not sure where to start. So far my auth service spec file looks like this:
describe('AuthService', () => {
let authService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireDatabaseModule,
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule
],
providers: [
{provide: AngularFireAuth, useValue: mockAngularFireAuth},
AuthService,
]
});
inject([AuthService], (service: AuthService) => {
authService = service;
})();
});
it('should be defined', () => {
expect(authService).toBeDefined();
});
it('should return true if loggedIn is true', () => {
expect(authService.isAuthenticated()).toBeFalsy();
authService.loggedIn = true;
expect(authService.isAuthenticated()).toBeTruthy();
});
});
Any help would be appreciated.
Well, this is what I did. I mocked the AngularFireAuth and returned the promise with reject or resolve promise to be caught. I am new to jasmine and testing, so feel free to correct me if I am doing something wrong.
it('should return a rejected promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.reject({
code: 'auth/account-exists-with-different-credential'
}),
}),
authState: Observable.of(authState)
};
mockAngularFireAuth.auth.signInWithPopup()
.catch((error: { code: string }) => {
expect(error.code).toBe('auth/account-exists-with-different-credential');
});
});
it('should return a resolved promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
uid: 'nuDdbfbhTwgkF5C6HN5DWDflpA83'
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.resolve({
user: authState
}),
})
};
mockAngularFireAuth.auth.signInWithPopup()
.then(data => {
expect(data['user']).toBe(authState);
});
});

Angular 2: BehaviorSubject/Subject does not work outside constructor

Hi I need pass the data from one component to another, for this I am using the class BehavorSubject(I tried too with Subject class, but doesnt works for me). This is my code:
Home page has a filter, and when is selected a filter, it called the service and it service should change a variable of homePage
HomePage.ts
#Component({
providers: [PatientService],
})
export class HomePage {
subscription: Subscription;
constructor( public auth: AuthService,
public patientService: PatientService) {
this.subscription = this.patientService.nameGiven.subscribe(
nameGiven => {
this.patientsByElement = nameGiven.toString();
});
------ more code---
}
Filtro.ts
export class FiltroPage {
showFilter(filter : FiltroT): void{
... code ...
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.PatientService.getPatientsByTags(this.token,this.filterSelected);
} , 1000);
}
}
patient-service.ts
import { Subject } from 'rxjs/Subject';
import { Observable ,BehaviorSubject } from 'rxjs/Rx';
#Injectable()
export class PatientService {
nameSource = new BehaviorSubject("asd");
nameGiven = this.nameSource.asObservable();
this.nameSource.next('hi!!!'); //**it works but only in the constructor**
this.nameGiven.subscribe(()=>{
console.log("it changed");
});
getPatientsByTags(token: String, tags: Array<string>){
return new Promise(resolve => {
this.http.get(ConnectionParams.DevEnv + ProceduresNames.TagsByPatient + ProceduresNames.TagIdEtiqueta + tags, options)
.map(res => res.json())
.subscribe(data => {
if(data.data.poAnicuRegistros){
console.log("here")
this.nameSource.next('hi TON :/'); // <-- ***here is the problem. It doesnt work***
}
else
console.log("XX");
resolve( this.data);
});
});
}
}
Finally i didn't use the BehaviorSubject/Subject, i pass the data from the filter to Homepage of this way :
HomePage.ts
public popoverCtrl: PopoverController
//code ...
showFilter(myEvent) {
let popover = this.popoverCtrl.create(FiltroPage, {
showConfirm: (x) => {
//do something with the data received from the filter
}
});
popover.present({
ev: myEvent
});
}
Filter.ts
//code...
params: NavParams;
showConfirm() {// function that return the data to homePage
this.params.get('showConfirm')(this.patientsBytag);
}

Testing component logic with Angular2 TestComponentBuilder

There are a lot of different approaches to unit test your angular application you can find at the moment. A lot are already outdated and basically there's no real documentation at this point. So im really not sure which approach to use.
It seems a good approach at the moment is to use TestComponentBuilder, but i have some trouble to test parts of my code especially if a function on my component uses an injected service which returns an observable.
For example a basic Login Component with a Authentication Service (which uses a BackendService for the requests).
I leave out the templates here, because i don't want to test them with UnitTests (as far as i understood, TestComponentBuilder is pretty useful for this, but i just want to use a common approach for all my unit tests, and the it seems that TestComponentBuilder is supposed to handle every testable aspect, please correct me if i'm wrong here)
So i got my LoginComponent:
export class LoginComponent {
user:User;
isLoggingIn:boolean;
errorMessage:string;
username:string;
password:string;
constructor(private _authService:AuthService, private _router:Router) {
this._authService.isLoggedIn().subscribe(isLoggedIn => {
if(isLoggedIn) {
this._router.navigateByUrl('/anotherView');
}
});
}
login():any {
this.errorMessage = null;
this.isLoggingIn = true;
this._authService.login(this.username, this.password)
.subscribe(
user => {
this.user = user;
setTimeout(() => {
this._router.navigateByUrl('/anotherView');
}, 2000);
},
errorMessage => {
this.password = '';
this.errorMessage = errorMessage;
this.isLoggingIn = false;
}
);
}
}
My AuthService:
#Injectable()
export class AuthService {
private _user:User;
private _urls:any = {
...
};
constructor( private _backendService:BackendService,
#Inject(APP_CONFIG) private _config:Config,
private _localStorage:LocalstorageService,
private _router:Router) {
this._user = _localStorage.get(LOCALSTORAGE_KEYS.CURRENT_USER);
}
get user():User {
return this._user || this._localStorage.get(LOCALSTORAGE_KEYS.CURRENT_USER);
}
set user(user:User) {
this._user = user;
if (user) {
this._localStorage.set(LOCALSTORAGE_KEYS.CURRENT_USER, user);
} else {
this._localStorage.remove(LOCALSTORAGE_KEYS.CURRENT_USER);
}
}
isLoggedIn (): Observable<boolean> {
return this._backendService.get(this._config.apiUrl + this._urls.isLoggedIn)
.map(response => {
return !(!response || !response.IsUserAuthenticated);
});
}
login (username:string, password:string): Observable<User> {
let body = JSON.stringify({username, password});
return this._backendService.post(this._config.apiUrl + this._urls.login, body)
.map(() => {
this.user = new User(username);
return this.user;
});
}
logout ():Observable<any> {
return this._backendService.get(this._config.apiUrl + this._urls.logout)
.map(() => {
this.user = null;
this._router.navigateByUrl('/login');
return true;
});
}
}
and finally my BackendService:
#Injectable()
export class BackendService {
_lastErrorCode:number;
private _errorCodes = {
...
};
constructor( private _http:Http, private _router:Router) {
}
post(url:string, body:any):Observable<any> {
let options = new RequestOptions();
this._lastErrorCode = 0;
return this._http.post(url, body, options)
.map((response:any) => {
...
return body.Data;
})
.catch(this._handleError);
}
...
private _handleError(error:any) {
...
let errMsg = error.message || 'Server error';
return Observable.throw(errMsg);
}
}
Now i want to test the basic logic of logging in, one time it should fail and i expect an error message (which is thrown by my BackendService in its handleError function) and in another test it should login and set my User-object
This is my current approach for my Login.component.spec:
Updated: added fakeAsync like suggested in Günters answer.
export function main() {
describe('Login', () => {
beforeEachProviders(() => [
ROUTER_FAKE_PROVIDERS
]);
it('should try and fail logging in',
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
tcb.createAsync(TestComponent)
.then((fixture: any) => {
tick();
fixture.detectChanges();
let loginInstance = fixture.debugElement.children[0].componentInstance;
expect(loginInstance.errorMessage).toBeUndefined();
loginInstance.login();
tick();
fixture.detectChanges();
expect(loginInstance.isLoggingIn).toBe(true);
fixture.detectChanges();
expect(loginInstance.isLoggingIn).toBe(false);
expect(loginInstance.errorMessage.length).toBeGreaterThan(0);
});
})));
it('should log in',
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
tcb.createAsync(TestComponent)
.then((fixture: any) => {
tick();
fixture.detectChanges();
let loginInstance = fixture.debugElement.children[0].componentInstance;
loginInstance.username = 'abc';
loginInstance.password = '123';
loginInstance.login();
tick();
fixture.detectChanges();
expect(loginInstance.isLoggingIn).toBe(true);
expect(loginInstance.user).toEqual(jasmine.any(User));
});
})));
});
}
#Component({
selector: 'test-cmp',
template: `<my-login></my-login>`,
directives: [LoginComponent],
providers: [
HTTP_PROVIDERS,
provide(APP_CONFIG, {useValue: CONFIG}),
LocalstorageService,
BackendService,
AuthService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: function(backend:ConnectionBackend, defaultOptions:BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
})
]
})
class TestComponent {
}
There are several issues with this test.
ERROR: 'Unhandled Promise rejection:', 'Cannot read property 'length' of null' I get this for the test of `loginInstance.errorMessage.length
Expected true to be false. in the first test after i called login
Expected undefined to equal <jasmine.any(User)>. in the second test after it should have logged in.
Any hints how to solve this? Am i using a wrong approach here?
Any help would be really appreciated (and im sorry for the wall of text / code ;) )
As you can't know when this._authService.login(this.username, this.password).subscribe( ... ) is actually called you can't just continue the test synchronically and assume the subscribe callback has happened. In fact it can't yet have happened because sync code (your test) is executed to the end first.
You can add artificial delays (ugly and flaky)
You can provide observables or promises in your component that emit/resolve when something you want to test is actually done (ugly because test code added to production code)
I guess the best option is using fakeAsync which provides more control about async execution during tests (I haven't used it myself)
As far as I know there will come support in Angular tests using zone, to wait for the async queue to become empty before the test continues (I don't know details about this neither).