Angular & Firebase get data in observable in value changes - javascript

Problem with got data correctly execute function many one times, these function is execute in ngOnInit one time with abstraction but i dont know ocurrs these problem in a server, i thing in snapshotChanges but i don't know.
thx for help
https://i.stack.imgur.com/EinQg.png
return <Observable<Products[]>> t.db.collection(PATHS_FIRESTORE.products).snapshotChanges()
.pipe(
map(actions => {
let arr = actions.map((res) => {
let doc: any = <any>res.payload.doc.data()
let obj: any = {}
if (!isNullOrUndefined(cart)) {
for (const prod in cart) {
if (cart.hasOwnProperty(prod)) {
const element = cart[prod];
if (doc.uid === prod) {
obj[doc.uid] = {
name_product: doc.name_product,
path_img: doc.path_img,
price: doc.price,
quantity: doc.quantity + element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
} else {
t.db.collection(PATHS_FIRESTORE.products).doc(prod).ref.get().then( res => {
const data = res.data()
return obj[res.id] = {
name_product: data.name_product,
path_img: data.path_img,
price: data.price,
quantity: element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
})
}
}
console.log(obj)
}
return obj
}else {
obj = {
...doc
}
return obj
}
})
.filter((b: any) => {
return b.uid_local === uid_local
})
.filter((b: any) => {
return b.quantity > 0
})
.filter((b: any) => {
return !b.status
})
console.log(arr)
return arr
})
)

Related

how to only return specific property form an array of objects in JavaScript

I only want to return the socket id, but forEach returns an undefined value, and find returns the entire object.
I only want the output to be 12345.
const users= [
{ host: true, socketId: "12345" },
{ host: false, socketId: "987654" },
{ host: false, socketId: "5678345" },
];
let socketId = users.forEach((user) => {
if (user.host === true) {
return user.socketId;
}
});//this return undefined
let socketId = users.find((user) => {
if (user.host === true) {
return user.socketId;
}
});//this return the whole object {host:true,socketId:"12345"}
const getHostSocketId = () => {
for (var i = 0; i < users.length; i++) {
if (users[i].host === true) {
return users[i].socketId;
}
}
};
let socketId = getHostSocketId(); //This works, but I'd rather use a method like the ones mentioned above.
console.log(socketId); //i want this to be 12345
let socketId = users.find(u => u.host)?.socketId
let answer = (inputArray) => {
const outputArray = []
inputArray.forEach((element) => {
if (element.host === true) {
outputArray.push(element.socketId)
}
})
return JSON.stringify(outputArray, null, ' ')
}

Cancelling my axios call on ReactJS but not working

I'm working on a project with graphs and I need to be able to cancel my requests if the user selects a different tab.
Here's my API call
export const getDifferentialData = (
sourceId: string,
sourceLine: string,
source: any
) => {
const graph1Request = getData(
sourceId,
sourceLine,
source
)
const graph2Request = getData(
sourceId,
sourceLine,
source
)
return Promise.all([graph1Request, graph2Request]).then(results => {
const [graphA, graphB] = results
return {
graphA: parsedData(graphA),
graphB: parsedData(graphB),
}
})
}
export const getData = (
sourceId: string,
sourceLine: string,
source?: any
) => {
if (sourceId && sourceLine) {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
} else {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
}
}
And the component where I'm doing the call. userDidChangeTab is called when pressing on a tab and it calls fetchGraph
const Graph: FC<Props> = () => {
const source = axios.CancelToken.source();
// we ensure that the query filters are up to date with the tab selected
const userDidChangeTab = (tabIndex: number) => {
const isDifferentialTabSelected = isDifferentialTab(tabIndex)
let newFilters = queryFilters
if (isDifferentialTabSelected) {
newFilters = {
// props go here
}
} else {
newFilters = {
// props go here
}
}
source.cancel()
fetchGraph(isDifferentialTabSelected)
setActiveTab(tabIndex)
}
// Function to fetch two differential graphs.
const fetchGraph = (isDifferential: boolean) => {
setFetching(true)
if (isDifferential) {
getDifferentialData(
sourceId,
sourceLine,
source
)
.then(({ graphA, graphB }: any) => {
setGraphData(graphA)
setMatchData(new diffMatch(graphA, graphB, 1.0))
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
} else {
getGraph(
sourceId,
sourceLine,
source
)
.then((graphData: any) => {
setGraphData(graphData)
setMatchData(null)
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
}
}
}

javascript recursive function memory leak

I am not good at English. Successfully make recursive call function. However, there is a memory leak for some reason. The question is that there is no return. The purpose of this feature is to view and explore objects, arrays, and the rest of their properties.
How do I change the code if I have a problem with my return?
Thank you in advance.
I was able to know the cause of the memory leak through Google dev tools profiles.
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
recursionProperty(null, {foo:'bar', baz: ['x','y']}, (prop, obj) => console.log(prop, obj));
my original code
import _ from 'lodash';
import fs from 'fs';
import path from 'path';
import errors from '#feathersjs/errors';
import connections from '../../../connections';
import config from '../../../config';
/**
* #param req
* #param serviceItem
* #param query
* #returns {Promise<any>}
*/
const getServicePromise = async (req, serviceItem, query) => {
let serviceName = serviceItem;
if (typeof serviceItem !== 'string') {
serviceName = `datasets/${serviceItem.name}`;
}
return new Promise(async (resolve, reject) => {
let result;
let objResult;
try {
result = await req.app.service(serviceName).find(query);
} catch (e) {
result = null;
console.log(e);
}
// console.log(result);
if (result) {
if (typeof serviceItem !== 'string') {
objResult = { [serviceItem.name]: result.data };
} else {
objResult = { [serviceName]: result.data };
}
resolve(objResult);
} if (result === null) {
objResult = { [serviceName]: [] };
resolve(objResult);
} else {
reject({
error: 'Not found data.'
});
}
});
};
/**
* 파일 경로 프로퍼티를 찾는 재귀함수
* 객체, 배열, 원시타입 등 여러 타입이 섞여있어도 사용 가능
* #param prop
* #param obj
* #param fn
*/
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
/**
* #param req
* #returns {Promise<{any}>}
*/
const getService = async req => {
const result = {};
const serverPath = [];
const { sheet, dataset, icon } = req.data;
const iconState = Object.prototype.hasOwnProperty.call(req.data, 'icon');
const sheetState = Object.prototype.hasOwnProperty.call(req.data, 'sheet');
const datasetState = Object.prototype.hasOwnProperty.call(req.data, 'dataset');
try {
// sheets
if (sheetState) {
const itemList = ['sheets'];
if (sheet.length === 0) {
const query = {
query: {
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else if (sheet.length > 0) {
const query = {
query: {
_id: {
$in: sheet,
},
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else {
result.sheets = [];
}
} else {
result.sheets = [];
}
// 파일 경로 구하기
if (sheet) {
const { sheets } = result;
// const filePath = [];
recursionProperty('files', sheets, (prop, value) => {
// 여기서 원하는 필드인지 검색후 처리함
if (prop === 'fullPath' && fs.existsSync(path.join(__dirname, '../../../../files', value))) {
// filePath.push(path.join(__dirname, '../../../../files', value));
serverPath.push(value);
}
});
// const deduplication = Array.from(new Set(serverPath));
// const deduplicationPath = await deduplicationFilePath(deduplication);
//
// Object.assign(result, { filePath: deduplicationPath });
} else {
// result.filePath = [];
}
// files
if (sheet) {
const deduplicationFiles = Array.from(new Set(serverPath));
if (deduplicationFiles.length > 0) {
const query = {
query: {
$sort: {
createdAt: -1,
},
fullPath: {
$in: deduplicationFiles,
},
}
};
const files = await req.app.service('files').find(query);
Object.assign(result, { files: files.data });
} else {
result.files = [];
}
} else {
result.files = [];
}
// dataset
if (datasetState) {
const query = {
query: {
// $limit: 100000
}
};
if (dataset.length === 0) {
const meta = await req.app.service('datasets/_meta_').find();
Object.assign(result, { _meta_: meta });
const db = await connections.getConnection(connections.DATASETS_DB);
const collectionNames = _.filter(await db.client.db(config.database_datasets.dbname).listCollections().toArray(), o => o.name !== '_meta_');
// collectionNames.forEach(str => {
// const detectA = iconvDetect.detect(Buffer.from(str.name));
// console.log('collection type', str.name, detectA);
// });
await Promise.all(meta.map(serviceItem => {
// const detectA = iconvDetect.detect(Buffer.from(serviceItem.key));
// console.log('meta type', serviceItem.key, detectA);
return getServicePromise(req, `datasets/${serviceItem.key}`, query);
})).then(data => {
Object.assign(result, { datasets: data });
});
} else if (dataset.length > 0) {
const metaQuery = {
query: {
$sort: {
createdAt: -1,
},
key: {
$in: dataset
}
}
};
const meta = await req.app.service('datasets/_meta_').find(metaQuery);
// console.log(meta);
Object.assign(result, { _meta_: meta });
await Promise.all(dataset.map(serviceItem => getServicePromise(req, `datasets/${serviceItem}`, query))).then(data => {
const d = Array.from(new Set(data));
const s = d.filter(item => item !== null);
if (d.length > 0) {
Object.assign(result, { datasets: s });
} else {
result.datasets = [];
result._meta_ = [];
}
});
} else {
result.datasets = [];
result._meta_ = [];
}
} else {
result.datasets = [];
result._meta_ = [];
}
if (iconState) {
const itemList = ['iconCategories', 'iconItems'];
const query = {};
if (icon.length === 0) {
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
}
} else {
result.iconCategories = [];
result.iconItems = [];
}
} catch (e) {
throw new errors.BadRequest('The data is invalid.', e);
}
return result;
};
export default getService;
There is most likely no memory leak in your code. Yes, recursive functions can be more memory aggressive than normal functions, because the call stack can grow very quickly, but remember that all functions will implicitly return even if there is no return statement. (Imagine that there is always a return undefined; line at the end of all your functions)
When doing a recursion, call stack will grow until you reach the bottom of a recursion branch (no function returns until then). Once the recursion branch end is reached, in your case this happens anytime you reach your else block, call stack will 'collapse' and all functions preceding will 'return'.
Memory will ultimately be freed by garbage collection as required.

Collections within documents in firebase and angular 7

I have the following code to obtain two observables in one and be able to manipulate them.
public products: any;
ngOnInit() {
this.products = this.productService.products().snapshotChanges().map(productSnaps => {
return productSnaps.map(product => {
const productData = product.payload.doc.data();
const productId = product.payload.doc.id;
return this.productService.getProductImages(productId).snapshotChanges().map(uploadSnap => {
let number = 0;
return uploadSnap.map(upload => {
if(number == 0) {
number++;
return upload.payload.doc.data();
}
})
})
.map(uploads => {
return {productId, ...productData, uploads: uploads};
})
})
})
.flatMap(products => Observable.combineLatest(products));
}
The services of products() and getProductImages() are the following:
type productsCollection = AngularFirestoreCollection<Product[]>;
products(): productsCollection {
return this.afs.collection<Product[]>('products');
}
getProductImages(productId: string) {
return this.afs.doc<Product>(`products/${productId}`).collection('uploads');
}
I have a base in firebase with the following structure:
my estructur of firebase
But it does not compile me with rxjs 6, I tried to do it like this:
ngOnInit() {
this.products = this.productService.products().snapshotChanges().pipe(map(productSnaps => {
return productSnaps.map(product => {
const productData = product.payload.doc.data();
const productId = product.payload.doc.id;
return this.productService.getProductImages(productId).snapshotChanges().pipe(map(uploadSnap => {
let number = 0;
return uploadSnap.map(upload => {
if (number === 0) {
number++;
return upload.payload.doc.data();
}
});
}),
map(uploads => {
return {productId, ...productData, uploads: uploads};
})
);
});
})
).flatMap(products => Observable.combineLatest(products));
}
But mark the flatMap and the combineLatest error
What at the end of accounts I need is that in the variable products both the documents of the collection "products" are stored as well as the collection that is inside each document and that contains the images of these.
and be able to use them like this:
<img height="250" *ngIf="product.uploads[0]" mat-card-image [src]="product.uploads[0].url" />
<mat-card-title>{{ product.name }}</mat-card-title>
tanks for you help!.

How can I refactor this RXJS / Angular code?

How can I refactor these nested arrays so that I can call something once all of the subscriptions have finished? I am sure it has to do with a combination of pipes, mergeMaps, concatMaps, etc.
this.teams = [
{
Assignments: [{Id: 0, Name: 'assignment', Notes: 'notes'}]
},
{
Assignments: [{Id: 0, Name: 'assignment', Notes: 'notes'}]
}]
this.teams.map((team:any) => {
team.Assignments.map((a: Assignment) => {
return this.videoService.getById(a.VideoId).subscribe(
res => {
let e = new Event();
e.Id = a.Id;
e.title = a.Name;
e.location = '';
e.message = a.Notes;
e.startDate = a.StartDate;
e.endDate = a.EndDate;
e.video = res;
e.team = team.Name;
this.eventList.push(e);
},
err => {
});
})
})
With lodash:
Observable.from(
lodash.flatten(
this.teams.map(team => team.Assignments)
)
)
.flatMap(a => this.videoService.getById(a.VideoId))
. subscribe(
res => {
//handle individual responses
},
err => {},
() => {
//handle after all complete
}
)
You can't listen on subscriptions, however, you could return an observable for each assignment an do a forkJoin of them, something like:
this.teams.map((team:any) => {
forkJoin(...team.Assignments.map((a: Assignment) => {
return this.videoService.getById(a.VideoId).map(
res => {
const e = new Event();
e.Id = a.Id;
e.title = a.Name;
e.location = '';
e.message = a.Notes;
e.startDate = a.StartDate;
e.endDate = a.EndDate;
e.video = res;
e.team = team.Name;
this.eventList.push(e);
});
})).subscribe(data => {
// Do something;
})
})
Now, I would refactor a little that code in order to make it more readable, something like:
function mapToEvent(team, assignment, response) {
const e = new Event();
e.Id = assignment.Id;
e.title = assignment.Name;
e.location = '';
e.message = assignment.Notes;
e.startDate = assignment.StartDate;
e.endDate = assignment.EndDate;
e.video = response;
e.team = team.Name;
return e;
}
this.teams.map(team => {
forkJoin(
...team.Assignments.map(a =>
this.videoService
.getById(a.VideoId)
.map(res => mapToEvent(team, a, res))
.do(event => this.events.push(event))
)
).subscribe(data => {
// Do something;
});
});
p.s. Some alternative syntax I was thinking on is:
function mapToEvent(team, assignment, response) {
const obj = {
Id: assignment.Id,
title: assignment.Name,
location: '',
message: assignment.Notes,
startDate: assignment.StartDate,
endDate: assignment.EndDate,
video: response,
team: team.Name
};
return Object.assign(new Event(), obj);
}
However, I'm not sure how it looks, although this may cause some underlying issues with V8, due to hidden classes.
Based on the other answer
I'm not much a fan of lodash, so I just wanted to present a vanilla js alternative:
Observable.from(
this.teams
.map(team => team.Assignments)
.reduce((acc, a) => [...acc, ...a], [])
)
.flatMap(a => this.videoService.getById(a.VideoId))
.catch(err => {
// Do Something
})
.finally(() => {
// Do something
})
.subscribe(res => {
// Handle Single
});

Categories