Angular2 return true/false with subscription - asynchronous

I need to check if user exists in the database and return true if so. I have a function (i am using firebase by the way):
checkIfUserExists(login: string): boolean {
this.af.database.list('/users', {
query: {
orderByChild: 'login',
equalTo: login
}
}).subscribe(response => {
if(response.length === 0 ) return true
})
return false
}
The problem is that the function always returns false. It does not wait for checking subscription. Is there some way to fix it?

You could return the observable itself:
checkIfUserExists(login: string): Observable<boolean> {
return this.af.database.list('/users', {
query: {
orderByChild: 'login',
equalTo: login
}
}).map(response => {
if(response.length === 0 ) {
return true;
} else {
return false;
}
});
}
In this case, you can use it like this:
checkIfUserExists('some username')
.subscribe(userExists => {
// Do something
});
This is because your processing is asynchronous so you need to handle processing with callbacks. This way you'll be sure to execute processing when asynchronous processing are executed.
You can use either observables (rxjs - like in my sample) or promises for such use cases.

You could return a promise instead and then call find():
checkIfUserExists(login: string): boolean {
return this.af.database.list('/users', {
query: {
orderByChild: 'login',
equalTo: login
}
}).subscribe(response => {
if(response.length === 0 )
return true;
return false;
}).toPromise().find();
}

You can return a promise using Native Promise API:
checkIfUserExists(login: string): boolean {
return new Promise(function(resolve, reject) {
this.af.database.list('/users', {
query: {
orderByChild: 'login',
equalTo: login
}
}).subscribe(response => {
if(response.length === 0 ) {
reject();
} else {
resolve()
}
})
});
}
Then use it as,
checkIfUserExists('login').then(function(response) {
// found
}, function() {
// not found
});

Related

should I wrap my try catch block inside of the for loop RTK QUERY?

I use rtk query and make optimistic UI. So I select by endpoints and change the data but there is one thing. I have a constant varibale "patchResult" in my for loop and outer for loop I await my query fullfilled. So I can not patchResult.undo() because patchResult variable is inside the for loop and my try catch block is outer the for loop so I dont have access to the variable. Should I put the try catch block in the for loop or is it bad ?
async onQueryStarted({ user_id }, { dispatch, queryFulfilled, getState }) {
for (const { endpointName, originalArgs } of Api.util.selectInvalidatedBy(getState(), [{ type: 'USER'}])) {
const patchResult = dispatch(
UserApi.util.updateQueryData('users', originalArgs, (draft) => {
return {
...draft,
user: {
...draft.user,
is_follow: !draft.user.is_follow
}
}
})
);
}
try {
await queryFulfilled
} catch {
patchResult.undo();
}
You will probably need an array of patches:
async function onQueryStarted(
{ user_id },
{ dispatch, queryFulfilled, getState }
) {
let allPatches = [];
for (const { endpointName, originalArgs } of Api.util.selectInvalidatedBy(
getState(),
[{ type: "USER" }]
)) {
const patchResult = dispatch(
UserApi.util.updateQueryData("users", originalArgs, (draft) => {
return {
...draft,
user: {
...draft.user,
is_follow: !draft.user.is_follow,
},
};
})
);
allPatches.push(patchResult);
}
try {
await queryFulfilled;
} catch {
for (const patchResult of allPatches) {
patchResult.undo();
}
}
}

wait until first hook is complete before fetching data

I have this custom hook which fetches the query.me data from graphql. The console.log statement shows that this hook is running a number of times on page load, but only 1 of those console.logs() contains actual data.
import { useCustomQuery } from '../api-client';
export const useMe = () => {
const { data, isLoading, error } = useCustomQuery({
query: async (query) => {
return getFields(query.me, 'account_id', 'role', 'profile_id');
},
});
console.log(data ? data.account_id : 'empty');
return { isLoading, error, me: data };
};
I then have this other hook which is supposed to use the id's from the above hook to fetch more data from the server.
export const useActivityList = () => {
const { me, error } = useMe();
const criteria = { assignment: { uuid: { _eq: me.profile_id } } } as appointment_bool_exp;
const query = useQuery({
prepare({ prepass, query }) {
prepass(
query.appointment({ where: criteria }),
'scheduled_at',
'first_name',
'last_name',
);
},
suspense: true,
});
const activityList = query.appointment({ where: criteria });
return {
activityList,
isLoading: query.$state.isLoading,
};
};
The problem I am facing is that the second hook seems to call the first hook when me is still undefined, thus erroring out. How do I configure this, so that I only access the me when the values are populated?
I am bad with async stuff...
In the second hook do an early return if the required data is not available.
export const useActivityList = () => {
const { me, error } = useMe();
if (!me) {
return null;
// or another pattern that you may find useful is to set a flag to indicate that this query is idle e.g.
// idle = true;
}
const criteria = { assignment: { uuid: { _eq: me.profile_id } } } as appointment_bool_exp;
...

Window object in nativescript

I need to create a window object so that the external file, which is loaded inside iframe, can call nativescript function.
I, specifically need window object because the file I am loading can be any file that follows conformant (SCORM) related to LMS.
Edit :
The file I have loaded is a SCORM conformant file. They search for window.API object/window.parent.API and so on, for starting communication with the container in which they have been loaded. I can't alter that file.
Tell me if more details needed.
We are successfully handling SCORM content in our NativeScript application, but it does require a little bit of hackery to accomplish.
I authored a utility class that will inject into the main entry file (index) of the SCORM content an override for the window API.
Background:
In our app we decompress the zip courses to the devices: documents/courses/UUID (that's what identity is referencing)
You can change the path as needed for your implementation
Example usage of the utility class:
const documents = fs.knownFolders.documents();
const destination = fs.path.join(documents.path, 'courses', this.media.identity);
this._readAccessUrl = destination;
const src = `file://${destination}`;
if (fs.File.exists(destination)) {
SCORMUtils.readSCORMManifest(this.media.identity).then(fileName => {
this._src = `${src}/${fileName}`;
SCORMUtils.makeOfflineCompatible(fs.path.join(destination, fileName))
.then(() => {
this._loading = false;
});
this._loading = false;
});
}
Utility Class:
import { File, knownFolders } from 'tns-core-modules/file-system';
const SCORM_API = `
<script type="text/javascript">
(function () {
window.parent.API = (function() {
return {
LMSInitialize: function () {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSInitialize");
}
return "true";
},
LMSCommit: function () {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSCommit");
}
return "true";
},
LMSFinish: function () {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSFinish");
}
return "true";
},
LMSGetValue: function (key) {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSGetValue");
}
return "";
},
LMSSetValue: function (key, value) {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSSetValue");
}
return "true";
},
LMSGetLastError: function () {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSGetLastError");
}
return "0";
},
LMSGetErrorString: function (errorCode) {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSGetErrorString");
}
return "No error";
},
LMSGetDiagnostic: function (errorCode) {
if (window && window.webkit) {
window.webkit.messageHandlers.print.postMessage("LMSGetDiagnostic");
}
return "No error";
}
}
})();
})();
</script>
</head>
`;
export class SCORMUtils {
/**
* Converts a SCORM course to be opened offline
* #param entryFile The main entry point determined by imsmanifest.xml
*/
static makeOfflineCompatible(entryFile: string) {
return new Promise((resolve, reject) => {
// Rewrite the entry file first
this._rewriteFile(entryFile)
.then(() => {
this._discoverHTMLEntry(entryFile)
.then(() => {
resolve();
}, () => {
console.error('Unable to rewrite alternative HTML entry');
reject();
});
}, () => {
console.error(`Unable to rewrite primary entry point: ${entryFile}`);
reject();
});
});
}
/**
* Digests a SCORM Manifest file to determine the main point of entry
* for the course viewer. Normally this is a index.html file.
*/
static readSCORMManifest(identity: string): Promise<string> {
return new Promise((resolve, reject) => {
const manifestFile = knownFolders.documents()
.getFolder('courses')
.getFolder(identity)
.getFile('imsmanifest.xml');
if (!File.exists(manifestFile.path)) {
alert({
title: 'Error',
message: 'Course is missing imsmanifest.xml file',
okButtonText: 'Ok'
});
return reject();
}
const data = manifestFile.readTextSync(() => {
alert({
title: 'Error',
message: 'Cannot open course.',
okButtonText: 'Ok'
});
return reject();
});
const matches = data.match(/type="webcontent"+.+?href="(.*?)"/);
if (matches === null || matches.length < 1) {
alert({
title: 'Error',
message: 'Invalid imsmanifest.xml file',
okButtonText: 'Ok'
});
}
else {
resolve(matches[1]);
}
});
}
/**
* Rewrites a file to be SCORM offline-compliant
* #param path The path of the file to re-write
*/
private static _rewriteFile(path: string) {
return new Promise((resolve, reject) => {
const entryFile = File.fromPath(path);
entryFile.readText()
.then(htmlText => {
this._injectOfflineAPI(htmlText)
.then(updatedHtml => {
entryFile.writeText(updatedHtml).then(() => {
resolve();
}, () => {
console.error(`Error writing to file: ${path}`);
reject();
});
});
}, () => {
console.error(`There was an entry reading the entry file at: ${path}`);
reject();
});
});
}
/**
* Attempts to find another SCORM entry point for re-write
* #param mainEntry The main entry point to branch from
*/
private static _discoverHTMLEntry(mainEntry: string): Promise<any> {
return new Promise((resolve, reject) => {
const entryFile = File.fromPath(mainEntry);
entryFile.readText()
.then(htmlText => {
let htmlEntry = htmlText.match(/{"type":"html5","url":"(.*?)"}/);
if (htmlEntry === null || htmlEntry.length < 1) {
// Check for Articulate
htmlEntry = htmlText.match(/location\.href\.replace\("index_lms", "(.*?)"/);
}
if (htmlEntry !== null && htmlEntry.length > 0) {
let fileName = htmlEntry[1];
if (fileName.indexOf('.html') === -1) {
fileName = `${fileName}.html`;
}
const directory = mainEntry.substr(0, mainEntry.lastIndexOf('/'));
const entryPoint = `${directory}/${fileName}`;
if (File.exists(entryPoint)) {
this._rewriteFile(entryPoint)
.then(() => {
resolve();
}, () => {
console.error('Error discovering main entry point.');
reject();
});
}
else {
console.error(`Cannot find alternative entry point: ${entryPoint}`);
reject();
}
}
else {
// This course does not have an alternative entry point
console.error('Course does not have an alternative entry, skipping...');
resolve();
}
}, () => {
reject();
});
});
}
/**
* Injects the extended SCORM API for offline-compatible viewing
* #param text The unmodified HTML source text
*/
private static _injectOfflineAPI(text: string): Promise<string> {
return new Promise((resolve, reject) => {
// Prevent multiple rewrites of the same file
if (this._isConverted(text)) {
return resolve(text);
}
// Finds the end of the head tag for script injection
const head = text.match(/<\/head>/gi);
if (head !== null && head.length > 0) {
resolve(text.replace(head.toString(), SCORM_API));
}
else {
console.error('Unable to parse incoming HTML for head tag.');
reject({
message: 'Unable to parse HTML'
});
}
});
}
/**
* Checks if the HTML has already been converted for offline-viewing
* #param text The incoming HTML source text
*/
private static _isConverted(text: string) {
const match = text.match(/window.parent.API/);
return match !== null && match.length > 0;
}
}

How can I use Ionic 2 Pulling Refresher in my app?

Hi all I'M working on angularjs 2/ionic2 mobile app , i need to do pulling refresher in my app, we have tried this link:- https://ionicframework.com/docs/v2/api/components/refresher/Refresher/ process i got refreshing the page but it's not get dismissLoader, we have given the images of my app refreshing:-
we don't know where we did the mistake and where we need to add the correct functionality in my project...
while we pulling the page it's refreshing but it's not get dismiss, Refreshing text and icon showing it's not get dismissed...
what we expecting once we pulled the page it's need to refresh after that refreshing text and icon need to be dismiss...
**we added coding only in html:-****
<ion-refresher (ionRefresh)="setFilteredItems($event)">
<ion-refresher-content refreshingSpinner="circles" refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
we have not added anything in type script part...so please check and update the solution please....
we have created example Plunker
please update the plunker as well to know the solution, thanks.....
My Type Script constructor code:-
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { GlobalStateService } from '../../services/global-state.service';
import { AccountSigninPage } from '../account-signin/account-signin';
import { AccountSignupPage } from '../account-signup/account-signup';
import { ActivityAddPage } from '../activity-add/activity-add';
import { Activity } from "../../services/actopi-sdk/model/Activity";
import { UserLoginService } from "../../services/account-management.service";
import { ResourceListPage } from '../resource-list/resource-list';
import { IamAuthorizerClient } from "../../services/actopi-api.service";
import { CustomAuthorizerClient, NoAuthorizationClient, UserPoolsAuthorizerClient } from "../../services/actopi-api.service";
import { Config } from '../../config/config'
import { Logger } from '../../services/logger.service';
import 'rxjs/add/operator/debounceTime';
import { FormControl } from '#angular/forms';
declare const AWS: any;
#Component({
templateUrl: 'activity-list.html',
})
export class ActivityListPage {
initialized = false;
accountSigninPage = AccountSigninPage;
accountSignupPage = AccountSignupPage;
activityAddPage = ActivityAddPage;
activitys: Activity[] = [];
resourceListPage = ResourceListPage;
searchTerm: string = '';
searchControl: FormControl;
displayDeleteActivityConfirmation(activityId, activityName) {
console.log("Deleting activityID " + activityId);
let confirm = this.globals.getAlertController().create({
title: 'Delete activity?',
message: `Are you sure you want to delete [<b>${activityName}</b>]? All resources and bookings associated with [<b>${activityName}</b>] will also be deleted!`,
buttons: [
{
text: 'Cancel',
handler: () => { /* do nothing */ }
},
{
text: 'OK',
handler: () => {
this.deleteActivity(activityId)
.then(() => {
this.globals.dismissLoader();
this.globals.displayToast(`Activity [${activityName}] has been successfully deleted`);
})
.catch((err) => {
this.globals.dismissLoader();
this.globals.displayAlert('Error encountered',
'Delete failed. Please check the console logs for more information.');
console.log(err);
});
}
}
]
});
confirm.present();
}
deleteActivity(activityId): Promise<void> {
return new Promise<void>((resolve, reject) => {
// delete from the database
this.globals.displayLoader("Deleting...");
this.customAuthClient.getClient().activitysDelete(activityId).subscribe(
() => {
// remove the item from the activitys array
let index = this.activitys.findIndex(activity => { return activity.activityId == activityId });
if (index > -1) {
this.activitys.splice(index, 1);
}
resolve();
},
(err) => {
reject(err);
}
);
});
}
gotoResourceListPage(activity) {
this.navCtrl.push(ResourceListPage, activity);
}
filterItems(searchTerm): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
this.activitys = data.items.filter((activity) => {
return activity.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
});
}
loadActivitysWithAuth(): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
let searchTerm: string = '';
// this.activitys = data.items.sort((a, b) => {
// return a.name.localeCompare(b.name);
// });
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
loadActivitysWithoutAuth(): void {
this.activitys = [];
this.noAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
this.activitys = data.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
constructor(private navCtrl: NavController, public globals: GlobalStateService, private noAuthClient: NoAuthorizationClient, private customAuthClient: CustomAuthorizerClient, private userPoolsAuthClient: UserPoolsAuthorizerClient, private authClient: IamAuthorizerClient) {
this.searchControl = new FormControl();
}
ionViewDidEnter() {
Logger.banner("Activitys");
this.activitys = [];
if (!this.initialized) {
this.initialized = false;
if (UserLoginService.getAwsAccessKey() != null) {
// if (CognitoUtil.getUserState() === UserState.SignedIn) {
// console.log(AWS.config.credentials);
UserLoginService.getAwsCredentials()
.then((data) => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.searchControl.valueChanges.debounceTime(700).subscribe(search => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.globals.dismissLoader();
});
})
.catch((err) => {
console.log("ERROR: Unable to load activitys!");
console.log(err)
})
}
}
}
setFilteredItems(refresher) {
return this.filterItems(this.searchTerm);
refresher.complete();
}
}
You need to call refresher.complete() to dismiss your refresher once loading of new data is done..
setFilteredItems(refresher?:any){
//async call to load.
// in the then function
if(refresher)
refresher.complete();
}
}
The refresher is sent from the html onRefresh. With ? you can call without passing object in your code.
this.setFilteredItems();
Also consider refactoring your code. You should ideally call complete after async task ia done and no point in returning another function to the html side and calling complete after return will just end up as dead code.

Redux-saga dispatch more than one action with reusable generator

I am using a reusable generator function to make the call to REQUEST/SUCCESS/FAILURE actions. I then have another generator to call that function but I would like to get some kind of feedback and raise another action. Not sure how to explain it, here is an example of what I want to do:
/* in actions/index.js */
export const login = {
request: () => action(constants.LOGIN.REQUEST),
success: (response) => {
try {
jwtDecode(response.auth_token);
} catch (e) {
return action(constants.LOGIN.FAILURE,
{ payload: { error: {
status: 403,
statusText: 'Invalid token',
} } });
}
return action(constants.LOGIN.SUCCESS, { payload: { response } });
},
failure: error => action(constants.LOGIN.FAILURE, { payload: { error } }),
};
/* sagas/index.js */
function* postEntity(entity, apiFn, body) {
yield put(entity.request());
const { response, error } = yield apply(null, apiFn, body);
if (response) {
yield put(entity.success(response));
} else {
yield put(entity.failure(error));
}
}
function* postLogin(action) {
yield postEntity(login, api.login, [action.payload.email, action.payload.password]);
// How can I get some kind of feedback (succeed or not) from postEntity here and do a put(something_else) if succeeded?
}
export default function* rootSaga() {
yield takeLatest(constants.LOGIN_USER, postLogin);
}
Any feedback is really appreciated.
Thanks!
Have postEntity return a value like response or true/false. Then in postLogin check for that value.
const result = yield postEntity(login, api.login, [action.payload.email, action.payload.password]);
then check the value of result and fire success/failure events accordingly like you did in postEntity.
if(result) {yield put(successCreator())} else { ...}

Resources