make search in Observable sync - javascript

I want to check, if given clientName is present in Observable Collection, but in cause of async run, i don't get "false" return at all. How to transform my function to sync - i would like not to use callbacks - just return true/false
checkIfClientNameIsUnique(clientName: string): boolean {
var isUnique = true;
this.getAll()
.subscribe(clients => {
clients.forEach(client => {
if (clientName == client.name) {
isUnique = false
}
})
});
return isUnique
}

I see three options:
make checkIfClientNameIsUnique to return Promise then you
can use it like checkIfClientNameIsUnique(name).then(isUnique =>
{...})
Load all clients to array on initial state. I suppose you have ClientsService there you can put clients array, then your
checkIfClientNameIsUnique method can be sync and use already loaded
clients array.
3.If you emit to ES6 you can use async await keywords and it will look like this.
checkIfClientNameIsUnique(clientName: string): Promise<boolean> {
return new Promise((resolve, reject) => {
this.getAll()
.subscribe(clients => {
for (let client of clients) {
if (clientName == client.name) {
resolve(false);
break;
}
}
resolve(true);
});
});
}
// ...
async main() {
let isUnique = await checkIfClientNameIsUnique(name);
}

Related

I'm trying to use async/await to get a service, but the second service returns don't fill my variables

I have a service to get a list from server. But in this list I need to call another service to return the logo img, the service return ok, but my list remains empty. What i'm did wrong ?
I tried to use async/await in both services
I tried to use a separate function to get the logos later, but my html don't change.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
this.listaOportunidades = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise()
.then(result => {
this.totalSize = result['totalElements'];
return result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return { opportunities };
});
});
this.getTasks(this.totalSize);
}
No errors, just my html don't change.
in my
console.log(opportunities.logoDesktopUrl);
return undefined
but in the end return filled.
info:
Angular 7
server amazon aws.
await is used to wait for promise.
You should return promise from getBrand if you want to wait for it in getOpportunitiesByPage.
Change the getBrand function as following.
getBrand(brandsUuid): Observable<string> {
this.brandService.getById(brandsUuid).pipe(map(res => {
console.log(res.logoDesktopUrl); return res.logoDesktopUrl;
}))
}
Change opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']); to opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']).toPromise();
Please make sure you imported map from rxjs/operators.
At first,when you await, you should not use then.
At second, async/await runs only with Promises.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
const result = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise();
this.totalSize = result['totalElements'];
this.listaOportunidades = result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return opportunities;
});
this.getTasks(this.totalSize);
}
getBrand(brandsUuid) {
return new Promise((resolve, reject) => {
this.brandService.getById(brandsUuid).subscribe(res => {
console.log(res.logoDesktopUrl);
return resolve(res.logoDesktopUrl);
}, err => {
return reject(err);
});
});
}
But, because rxjs is a used in Angular, you should use it instead of async/await :
getOpportunitiesByPage: void(_searchQueryAdanced: any = 'active:true') {
this._opportunities.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced).pipe(
tap(result => {
// we do that here because the original result will be "lost" after the next 'flatMap' operation
this.totalSize = result['totalElements'];
}),
// first, we create an array of observables then flatten it with flatMap
flatMap(result => result['content'].map(opportunities => this.getBrand(opportunities['brandsUuid']).pipe(
// merge logoDesktopUrl into opportunities object
map(logoDesktopUrl => ({...opportunities, ...{logoDesktopUrl}}))
)
),
// then we make each observable of flattened array complete
mergeAll(),
// then we wait for each observable to complete and push each result in an array
toArray()
).subscribe(
opportunitiesWithLogoUrl => {
this.listaOportunidades = opportunitiesWithLogoUrl;
this.getTasks(this.totalSize);
}, err => console.log(err)
);
}
getBrand(brandsUuid): Observable<string> {
return this.brandService.getById(brandsUuid).pipe(
map(res => res.logoDesktopUrl)
);
}
Here is a working example on stackblittz
There might be a simpler way to do it but it runs :-)

Angular Firestore: Check if data exist and update a global variable based on that

Requested behaviour:
I would like to create an AngularService which checks if a certain document exists and updates a global variable based on the result.
Current State
The function checks the existence of the document successfully. It also updates the global variable in the if/else statement.
issue
Even, the first part works well, it always returns "undefined".
How can I fix that? Is it related to function scope?
My Service:
export class ProfileFollowService {
//global variable which should be updated
followState: boolean;
constructor(private angularFirestore: AngularFirestore) { }
checksFollow(followingID: string, followerID: string): boolean {
const followDoc =
this.angularFirestore.collection(`users/${followingID}/following`).doc(followerID).ref;
followDoc.get().then((doc) => {
if (doc.exists) {
this.followState = true;
} else {
this.followState = false;
}
});
return this.followState;
}
}
followDoc.get() is async function that returns promise. In order to return updated this.followState you have to wait for then
one way to do this is to use async / await
async checksFollow(followingID: string, followerID: string): boolean {
const followDoc =
this.angularFirestore.collection(`users/${followingID}/following`).doc(followerID).ref;
return followDoc.get().then((doc) => {
if (doc.exists) {
this.followState = true;
} else {
this.followState = false;
}
return this.followState;
});
}
In other part of your code where you call checksFollow you can put keyword await and wait for the response.
async someMethodToCallChecksFollow() {
const result = await this.checksFollow();
console.log(result);
}
If you want to use the response in your html, I would suggest changing followState from primitive boolean to BehaviorSubject<boolean> and then call this.followState.next(true)
For example:
export class YourService {
public followState = new BehaviorSubject<boolean>(false);
async checksFollow(followingID: string, followerID: string): boolean {
const followDoc =
this.angularFirestore.collection(`users/${followingID}/following`).doc(followerID).ref;
return followDoc.get().then((doc) => {
if (doc.exists) {
this.followState.next(true);
} else {
this.followState.next(false);
}
return this.followState.getValue();
});
}
}
And then in your html you can use async pipe.
<div *ngIf="yourServiceInstance.followState | async">It is true</div>

missing timing from promised value

So I am using Forge with View API to analyze all parts from a model which contain concrete and hide everything that is not concrete. The problem is that the properties for checking concrete are called from a DB which requires me to make it a promise. Checking for concrete is working as expected and then the problem starts. I return the Ids containing concrete, but my function which is supposed to hide it uses the Ids before the promise is resolved, so the array is empty.
console.log logs it as expected but everything else misses the timing.
My code:
getProperties(dbId) {
return new Promise((resolve, reject) => {
this.gui.getProperties(
dbId,
args => {
resolve(args.properties)
},
reject
)
})
}
async getConcreteIds() {
let wallfloorids = this.getWallIds().concat(this.getFloorIds());
let concreteIds = [];
for (let id of wallfloorids) {
let p1 = this.view.getProperties(id);
p1.then(props => {
for (let prop of props) {
if (prop.displayCategory === "Materialien und Oberflächen" && prop.displayValue.contains("Concrete")) {
concreteIds.push(id);
}
}
}).catch(() => {
});
}
return new Promise((resolve, reject) => {
try {
resolve(concreteIds)
} catch (e) {
console.log("Err", reject)
}
})
}
async onlyConcrete() {
this.getConcreteIds().then(concrete => {
debugger;
this.viewer.isolateById(concrete)
});
}
Map an array of promises for your loop and use Promise.all() to resolve after all the promises in loop resolve
Something like:
async getConcreteIds() {
let wallfloorids = this.getWallIds().concat(this.getFloorIds());
let concreteIds = [];
let promises = wallfloorids.map(id => {
let p1 = this.view.getProperties(id);
return p1.then(props => {
for (let prop of props) {
if (prop.displayCategory === "Materialien und Oberflächen" && prop.displayValue.contains("Concrete")) {
concreteIds.push(id);
}
}
});
});
return Promise.all(promises)
.then(_ => concreteIds)
.catch(err => console.log("Err", err))
}

ReactJS how to wait for all API calls to be ended in componentDidMount of simple component

I'm using latest react and very basic app which calls 3rd party service API which actually is not well designed in meaning of following.
I have to execute one call which return list and then have to iterate and call other end point to get data for item from list and then again in data have new list for which I have to call 3rd API end point.
After I receive all data I combined it to one items array and place it in state in componentDidMount function but this final step works only if I surround it with setTimeout.
Is there some elegant way to do that?
I'm using fetch and really pure react components, have my own simple service from where I call API, here is some code parts...
items[tag].sensors = [];
API.getObjects(sessionData, userDetails, tag).then(links => {
Object.keys(links.link).forEach(link => {
API.getObjects(sessionData, userDetails, link).then(objLink => {
Object.keys(objLink.link).forEach(function (key) {
let obj = objLink.link[key];
if (obj && obj.type === 'sensor') {
API.getSensorNames(sessionData, key).then(response => {
const sensor = response.sensor;
// some sensor calculations....
items[tag].sensors.push(sensor);
});
}
});
});
});
});
// this part only works if it's surrounded with timeout
setTimeout(function() {
let processedItems = [];
for (var key in items) {
if (items.hasOwnProperty(key)) {
processedItems.push(items[key]);
}
}
self.setState({
items: processedItems
});
}, 1000);
Thanks in advance.
Simply, You can use Promise to wait until you get values from the API call, therefore you will put your code in function like this
function prepareItems() {
items[tag].sensors = [];
return new Promise((resolve, reject) => {
API.getObjects(sessionData, userDetails, tag).then(links => {
Object.keys(links.link).forEach(link => {
API.getObjects(sessionData, userDetails, link).then(objLink => {
Object.keys(objLink.link).forEach(function(key) {
let obj = objLink.link[key];
if (obj && obj.type === "sensor") {
API.getSensorNames(sessionData, key).then(response => {
const sensor = response.sensor;
// some sensor calculations....
items[tag].sensors.push(sensor);
// whenever you set resolve it will end the promise
//and pass the result it to the then function
resolve(items)
});
}
});
});
});
});
});
}
and use then to get the result from the prepareItems function after its resolved
prepareItems().then(items => {
//Do what ever you want with prepared item
})
What about using async/await operators.
These operators allows you to wait until the response is ready.
You can use this kind of helper function.
getItems = async (...) => {
...
items[tag].sensors = []
const links = await API.getObjects(sessionData, userDetails, tag)
Object.keys(links.link).forEach(async (link) => {
const objLink = await API.getObjects(sessionData, userDetails, link)
Object.keys(objLink.link).forEach(async (key) => {
let obj = objLink.link[key]
if (obj && obj.type === 'sensor') {
const response = await API.getSensorNames(sessionData, key)
const sensor = response.sensor
items[tag].sensors.push(sensor)
}
})
})
this.setState({ items })
}
Also you can see this great documentation.

Typescript returning boolean after promise resolved

I'm trying to return a boolean after a promise resolves but typescript gives an error saying
A 'get' accessor must return a value.
my code looks like.
get tokenValid(): boolean {
// Check if current time is past access token's expiration
this.storage.get('expires_at').then((expiresAt) => {
return Date.now() < expiresAt;
}).catch((err) => { return false });
}
This code is for Ionic 3 Application and the storage is Ionic Storage instance.
You can return a Promise that resolves to a boolean like this:
get tokenValid(): Promise<boolean> {
// |
// |----- Note this additional return statement.
// v
return this.storage.get('expires_at')
.then((expiresAt) => {
return Date.now() < expiresAt;
})
.catch((err) => {
return false;
});
}
The code in your question only has two return statements: one inside the Promise's then handler and one inside its catch handler. We added a third return statement inside the tokenValid() accessor, because the accessor needs to return something too.
Here is a working example in the TypeScript playground:
class StorageManager {
// stub out storage for the demo
private storage = {
get: (prop: string): Promise<any> => {
return Promise.resolve(Date.now() + 86400000);
}
};
get tokenValid(): Promise<boolean> {
return this.storage.get('expires_at')
.then((expiresAt) => {
return Date.now() < expiresAt;
})
.catch((err) => {
return false;
});
}
}
const manager = new StorageManager();
manager.tokenValid.then((result) => {
window.alert(result); // true
});
Your function should be:
get tokenValid(): Promise<Boolean> {
return new Promise((resolve, reject) => {
this.storage.get('expires_at')
.then((expiresAt) => {
resolve(Date.now() < expiresAt);
})
.catch((err) => {
reject(false);
});
});
}

Categories