Angular service returning undefined to component - javascript

I want to pass the return value to my component. I wrote this service using firebase. When calling the geLoggedInUser method in my component it returns undefined. Ideally, I want to pass the doc.id to my component from my service please help;
SERVICE
docId: any;
getLoggedInUser() {
this.fireAuth.onAuthStateChanged((user) => {
if (user) {
console.log(user.email, 'user is valid');
const db = collection(this.firestore, 'users');
getDocs(db).then((resp) => {
this.data = [
...resp.docs.map((item) => {
return { ...item.data(), id: item.id };
}),
];
//filter users array to get current logged in users data
this.authUser = this.data.filter((item: any) => {
this.docId = user.uid;
return item.email === user.email;
});
});
} else {
this.router.navigate(['/login']);
return;
}
});
return this.docId;
}
COMPONENT
ngOnInit(): void {
console.log(this.auth.getLoggedInUser());
}

I recommend get involved in the Promise and Observable concepts.
https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth
Your problem is when you call getLoggedInUser() is doings this:
return this.docId;
Calls this.fireAuth.onAuthStateChanged which returns a Promise, wait and then execute the rest of the response.
What you can do is use an observable variable in the SERVICE and connect to it from the component.
Please read the following Firebase: Should Promise or Observable be used in an Angular project?
I hope this helps!

In angular, when you use services it’s not common to use variable to pass data to components. To that you should use a observables or promises/await/async.
In this case, I would recommend you use a observable. Here you have an example how you can implement it:
docIdSubject = new BehaviorSubject<any>(null);
Public get docId(){
return docIdSubject.asObservable();
}
getLoggedInUser() {
this.fireAuth.onAuthStateChanged((user) => {
if (user) {
console.log(user.email, 'user is valid');
const db = collection(this.firestore, 'users');
getDocs(db).then((resp) => {
this.data = [
...resp.docs.map((item) => {
return { ...item.data(), id: item.id };
}),
];
//filter users array to get current logged in users data
this.authUser = this.data.filter((item: any) => {
//this.docId = user.uid
return item.email === user.email;
};
this.docIdSubject.next(user.uid);
});
} else {
this.router.navigate(['/login']);
return;
}
})
}
ngOnInit(): void {
this.auth.docId.subscribe(res =>
console.log(res);
);
this.auth.getLoggedInUser();
}

Related

How put Firebase object to state in react?

I'm learning react and I'm having difficulty putting data from Firebase into the application state. I use Rebase but I am open to any solution!
still have an error similar to this one :
Thank you for your help !
Here is the code :
class App extends Component {
state = {
user:'',
vampires: {}
}
componentDidMount() {
if(this.state.user === ''){
firebase.auth().onAuthStateChanged(user => {
if(user){
this.handleUserLogin({ user })
this.setVampires({ user })
} else {
console.log('error')
}
})
}
}
setVampires = async (authData) => {
console.log(this.state.user)
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
console.log(data)
let vampires = this.state.vampires;
vampires = {..._.cloneDeep(data)};
this.setState({vampires: {vampires}})
})
}
handleUserLogin = async authData => {
this.setState({user: authData.user.uid})
}
Your Firebase data is returned as an Object, with properties VampM5-..... React expects that you pass any repeated data as an array, not as an object.
So you'll need to convert the data from Firebase to an array, for example with:
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
vampires = [];
data.forEach((child) => {
vampires.push({ ...child.val(), ".key": child.key });
})
this.setState({ vampires: vampires })
})

React Apollo: Uncaught (in promise) TypeError: Cannot read property 'refetch' of undefined

I have a function that has a bit of a promise chain going on, but that's besides the point.
When I run a certain mutation's refetch, it gives me Uncaught (in promise) TypeError: Cannot read property 'refetch' of undefined.
The strange part is that if I remove a mutation before it, it will work. So here's the code:
Promise.all(this.props.questionnaireData.map(({ kind, id }): Promise<any> => {
const responses = this.props.formData[kind];
return this.props.updateQuestionnaire(id, responses);
})).then(() => {
this.props.finishAssessment(this.props.assessmentId)
.then(() => {
track('Assessment -- Finished', {
'Assessment Kind' : this.props.assessmentKind,
'Assessment Id' : this.props.assessmentId,
});
if (this.props.assessmentKind === 'INITIAL_ASSESSMENT') {
this.props.getCompletedInitialAssessment.refetch().then(() => {
Router.replace(routes.LoadingAssessmentResults.to, routes.LoadingAssessmentResults.as);
});
this.submitEmailNotifications();
} else if(this.props.assessmentKind === 'GOAL_CHECK_IN') {
Router.replace(routes.MemberProgressDashboard.to, routes.MemberProgressDashboard.as);
} else {
Router.replace(routes.MemberDashboard.to, routes.MemberDashboard.as);
}
});
});
The error happens at this.props.getCompletedInitialAssessment.refetch(), to which I don't know why. However, when I remove this.props.finishAssessment(this.props.assessmentId), only then the refetch will work.
Basically:
Promise.all(this.props.questionnaireData.map(({ kind, id }): Promise<any> => {
const responses = this.props.formData[kind];
return this.props.updateQuestionnaire(id, responses);
})).then(() => {
track('Assessment -- Finished', {
'Assessment Kind' : this.props.assessmentKind,
'Assessment Id' : this.props.assessmentId,
});
if (this.props.assessmentKind === 'INITIAL_ASSESSMENT') {
this.props.getCompletedInitialAssessment.refetch().then(() => {
Router.replace(routes.LoadingAssessmentResults.to, routes.LoadingAssessmentResults.as);
});
this.submitEmailNotifications();
} else if(this.props.assessmentKind === 'GOAL_CHECK_IN') {
Router.replace(routes.MemberProgressDashboard.to, routes.MemberProgressDashboard.as);
} else {
Router.replace(routes.MemberDashboard.to, routes.MemberDashboard.as);
}
});
will make refetch work. Otherwise it complains that it doesn't know what refetch is.
For Apollo, I'm using the graphql HOC, and it looks like this:
graphql(getCompletedInitialAssessment, {
name : 'getCompletedInitialAssessment',
options : { variables: { status: ['Finished'], limit: 1 } },
}),
graphql(updateQuestionnaire, {
props: ({ mutate }) => ({
updateQuestionnaire: (id, responses) => {
let normalized = {};
for (let res in responses) {
let num = +responses[res];
// If the value is a stringified numuber, turn it into a num
// otherwise, keep it a string.
normalized[res] = Number.isNaN(num) ? responses[res] : num;
}
const input = {
id,
patch: { responses: JSON.stringify(normalized) },
};
return mutate({
variables: { input },
});
},
}),
}),
graphql(finishAssessment, {
props: ({ mutate }) => ({
finishAssessment: (id) => {
const input = { id };
return mutate({
variables : { input },
refetchQueries : ['getMemberInfo'],
});
},
}),
}),
What I've tried is even rewriting this to use async/await, but the problem still happens:
try {
await Promise.all(this.props.questionnaireData.map(({ kind, id }): Promise<any> => {
const responses = this.props.formData[kind];
return this.props.updateQuestionnaire(id, responses);
}));
const finishAssessmentRes = await this.props.finishAssessment(this.props.assessmentId);
console.log(finishAssessmentRes)
if (this.props.assessmentKind === 'INITIAL_ASSESSMENT') {
const res = await this.props.getCompletedInitialAssessment.refetch();
console.log(res);
this.submitEmailNotifications();
Router.replace(routes.LoadingAssessmentResults.to, routes.LoadingAssessmentResults.as);
} else if(this.props.assessmentKind === 'GOAL_CHECK_IN') {
Router.replace(routes.MemberProgressDashboard.to, routes.MemberProgressDashboard.as);
} else {
Router.replace(routes.MemberDashboard.to, routes.MemberDashboard.as);
}
} catch (error) {
console.error(error);
}
I honestly don't know what's happening or why refetch wouldn't work. Would refactoring into hooks help? Does anyone have any idea?
From the docs
The config.props property allows you to define a map function that takes the props... added by the graphql() function (props.data for queries and props.mutate for mutations) and allows you to compute a new props... object that will be provided to the component that graphql() is wrapping.
To access props that are not added by the graphql() function, use the ownProps keyword.
By using the props function, you're telling the HOC which props to pass down either to the next HOC or to the component itself. If you don't include the props that were already passed down to it in what you return inside props, it won't be passed to the component. You need do something like this for every props function:
props: ({ mutate, ownProps }) => ({
finishAssessment: (id) => {
//
},
...ownProps,
}),
Composing HOCs is a pain and the graphql HOC is being deprecated anyway in favor of hooks. I would strongly advise migrating to the hooks API.

How can I call provider method from global service method in ionic 2, angular

I am trying to insert data from an API call to the database. The API call is done from the global services. When I tried to do it within the constructor method it is working. But if I try it from an other outside method from service I am getting an error “ERROR TypeError: Cannot read property 'setMetadataKey' of undefined(…)”.
import { Database } from './../providers/database/database';
import {Injectable} from '#angular/core';
#Injectable()
export class mAppService{
showValue: string;
public database;
userLoggedIn: boolean = false;
constructor(database: Database) {
//If i call the database method from here its working**
}
getConnect(ipaddress,portnumber){
try {
var t = ipaddress;
var p = portnumber;
client = new linear.client({transports: [
{type: 'websocket',
host: t, port: p}
]});
client.connect();
client.transport.onopen=this.onopen;
client.transport.onclose=this.onclose;
client.onconnect = client.ondisconnect = this.getStatus;
client.onnotify = this.recvNotify;
let stream$ = new Observable(observer => {
let interval = setInterval(() =>{
observer.next(client.transport.state);
/*observer.error(client.transport.onclose);
observer.complete();*/
},1000);
return() =>{
clearInterval(interval);
}
}).share();
return stream$;
} catch (error) {
console.log("catch.."+error);
}
}
recvNotify(m) {
var method = m.name;
var value = m.data;
var buttonType = "EFT_PAN";
this.database.setMetadataKey(value,buttonType).then((result) => {
console.log("Successfully saved the setMetadataKey.")
}, (error) => {
alert("ERROR: "+ error);
});
}
}
Can anyone help me to solve this please

Check for unique fields before creating object using Mongoose

I'm building a GraphQL Server where I need to do some sort of validation before committing data to database (MongoDB and Mongoose).
One of these checks is related to unique fields. So, a model may have one or more unique fields and I need to be able to check for that before saving into database.
So, I have build some helper functions to do it and the code is below:
Helper code:
import mongoose from "mongoose";
const isFieldUnique = (modelName, fieldName, fieldValue) => {
let model = mongoose.model(modelName);
let query = {};
query[fieldName] = fieldValue;
return model.findOne(query).exec();
};
const executeUniquePromises = (uniques, modelName, data) => {
let promises = [];
uniques.map(name => {
let value = data[name];
if (!value)
throw new Error("Cannot test uniqueness for a null field.");
promises.push(
isFieldUnique(modelName, name, value)
.then(value => {
if (value) {
let error = name + ' is not unique';
console.log(error);
return error;
}
console.log(name + ' is unique');
return null;
})
.catch(error => {
throw new Error(error);
})
)
});
return Promise.all(promises);
};
export const checkUniqueness = (uniques, modelName, data) => {
return new Promise((resolve, reject) => {
executeUniquePromises(uniques, modelName, data).then(result => {
let errors = [];
// Check for errors
result.map((error) => {
if (error)
errors.push(error);
});
if (errors.length > 0)
return reject(errors);
else
resolve();
});
});
}
Mongoose static create function:
import * as helper from './helper';
schema.statics.create = function (data) {
let uniques = ['name', 'email'];
helper.checkUniqueness(uniques,'Company', data)
.then(result => {
let user = new this(data);
return company.save();
})
.catch(error => {
throw new Error(error);
});
}
GraphQL code:
const createUser = {
type: UserType,
description: "Create a user",
args: {
data: {
name: "user",
type: new GraphQLNonNull(UserInputType)
}
},
resolve(root, args) {
return UserModel.create(args.data);
}
};
The helper code seens to be confused and I´m not using my usage of promises with other promises are the correct way of doing it.
Remember that I may need to check several fields for uniqueness, so that is why I´ve created the promise array.
One problem is that when I´m inserting data where there are not uniques matching I get no return in my GraphQL Server.
I want to find out a better way of doing it and discover why I´m not getting back the saved object.
MongoDB already handles unique out of the box. Set the field to unique: true in the Mongoose schema. You can use mongoose-beautiful-unique to make the error messages similar to the validation error messages. And finally, read this when you can't get unique: true to work.

How would one simplify a state dependent getter, preferrably in a SOLID manner?

I have an Angular component that uses a type, what would be an easily readable solution to get one data on one case and the other in another?
The component could also be separated in two components, the phone component and the email component, but most of the logic albeit small would be duplicated.
var getContactInfo, hasContactInfo;
if(type === 'email') {
getContactInfo = function (profile) { return profile.getEmail()};
hasContactInfo = function (profile) { return profile.hasEmail()};
} else if(scope.type === 'phone') {
getContactInfo = function (profile) { return profile.getPhone()};
hasContactInfo = function (profile) { return profile.hasPhone()};
}
I would probably have used an object mapping the methods depending on the type:
const buildContactInfo = (getContactInfo, hasContactInfo) => ({ getContactInfo, hasContactInfo });
const contactInfoByType = {
email: buildContactInfo((profile) => profile.getEmail(), (profile) => profile.hasEmail()),
phone: buildContactInfo((profile) => profile.getPhone(), (profile) => profile.hasPhone())
};
Then, when calling:
const contactInfo = contactInfoByType[type];
if (!contactInfo) {
throw new Error(`No contact info matched type '${type}'`);
} else {
contactInfo.hasContactInfo(profile);
contactInfo.getContactInfo(profile);
}

Categories