Im working on my first Ionic + Firebase project, and im not understanding this:
Im searching and getting an object from firebase, I can access its details on html and show it to the user.
But now I need to save the createdBy field on that object so I can use it to search for its creator on firebase.
But when I try to access that info its always undefined. Why is that? Any tips on how to fix this?
export class VisitDetailsPage implements OnInit {
public trips: Observable<HomeTripCardsModel>;
public trip: HomeTripCardsModel;
public buddyInfo;
public targetBuddyId: any;
constructor(private router: Router, private navCtrl: NavController,
public fireStorageService: FireStorageService,
private route: ActivatedRoute, public db: AngularFirestore) {
}
ngOnInit() {
const tripId: string = this.route.snapshot.paramMap.get('id');
this.db.collection('users').get()
.subscribe(querySnapshot => {
querySnapshot.forEach(doc => {
this.trips = this.fireStorageService.getTripDetail(tripId, doc.id);
this.trips.forEach((element: HomeTripCardsModel) => {
if (element?.id === tripId) {
this.trip = element;
this.targetBuddyId = element.createdBy;
}
});
});
});
// buddy
console.log(this.trip?.createdBy); // returns undefined
console.log('saved ', this.targetBuddyId) // returns undefined
}}
Data is loaded from Firebase asynchronously. If you set some breakpoints and run in the debugger, or add a log inside the subscribe method, you'll see that your console.log(this.trip?.createdBy) runs before this.trip = element has ever been run. So at that point, it indeed doesn't have a value yet.
For this reason, all code that needs data from the database, needs ot be inside the subscribe callback:
this.db.collection('users').get()
.subscribe(querySnapshot => {
querySnapshot.forEach(doc => {
this.trips = this.fireStorageService.getTripDetail(tripId, doc.id);
this.trips.forEach((element: HomeTripCardsModel) => {
if (element?.id === tripId) {
this.trip = element;
this.targetBuddyId = element.createdBy;
}
});
// buddy
console.log(this.trip?.createdBy); // returns undefined
console.log('saved ', this.targetBuddyId) // returns undefined
});
});
Related
This question already has answers here:
Why does my function that calls an API or launches a coroutine return an empty or null value?
(4 answers)
Closed 1 year ago.
What I am trying to do: Simply return data from Firebase Cloud Function.
The function is used to create a payment order in the payment gateway's server.
My required data about the order's details are present in the function(err,data) (see below), but I need this data sent back to my Android app.
Problem I faced: I could see the data printed in the Firebase console's log but it doesn't return to my Android app.
My Firebase Cloud Function:
const functions = require("firebase-functions");
exports.order = functions.https.onCall((amnt, response) => {
const Ippopay = require('node-ippopay');
const ippopay_instance = new Ippopay({
public_key: 'YOUR_PUBLIC_KEY',
secret_key: 'YOUR_SECRET_KEY',
});
ippopay_instance.createOrder({
amount: amnt,
currency: 'DOLLAR',
payment_modes: "cc,dc,nb,cheque",
customer: {
name: "Test",
email: "test#gmail.com",
phone: {
country_code: "42",
national_number: "4376543210"
}
}
}, function (err, data) {
return data.order.order_id;
});
});
My Android client-side code:
public class Payment extends AppCompatActivity implements IppoPayListener {
Button pay;
EditText amount;
private FirebaseFunctions mFunctions;
TextView order_data;
String data;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
}
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
pay=findViewById(R.id.pay_button);
amount=findViewById(R.id.user_amount);
order_data=findViewById(R.id.data_text);
pay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("PAY Button clicked", "yes");
mFunctions = FirebaseFunctions.getInstance("us-central1");
mFunctions.getHttpsCallable("order").call(5).continueWith(new Continuation<HttpsCallableResult, Object>() {
#Override
public Object then(#NonNull Task<HttpsCallableResult> task) throws Exception {
HttpsCallableResult result=task.getResult();
if(result !=null)
{
data=result.getData().toString();
return result.getData().toString();
}
return null;
}
});
order_data.setText(data);
onPaymentClick();
}
});
}
/* ... */
}
I'm a Beginner so there's a high possibility of some dead silly mistakes. :)
Based on what your code looks like at the moment, you have a mix of code from a Callable Cloud Function and the older HTTP Request Cloud Function.
To return data from a callable Cloud Function, you should return a Promise, a method of running asynchronous code that returns a value. Older JavaScript and many other languages use callbacks instead, which is what you have here.
In it's simplest form, this callback-based method:
someModule.doSomething(input, function (err, result) {
// check for errors and handle result
});
would be converted to use Promises using:
new Promise((resolve, reject) => {
someModule.doSomething(
input,
(err, result) => err ? reject(err) : resolve(result) // short form of "if error, reject with an error, otherwise resolve (succeed) with result"
)
});
For errors to be handled correctly by clients, you need to wrap any errors in a functions.https.HttpsError.
Combining this together gives:
const functions = require("firebase-functions");
exports.order = functions.https.onCall((amnt, context) => {
const Ippopay = require('node-ippopay');
return new Promise((resolve, reject) => {
const ippopay_instance = new Ippopay({
public_key: 'YOUR_PUBLIC_KEY',
secret_key: 'YOUR_SECRET_KEY',
});
ippopay_instance.createOrder({
amount: amnt,
currency: 'DOLLAR',
payment_modes: "cc,dc,nb,cheque",
customer: {
name: "Test",
email: "test#gmail.com",
phone: {
country_code: "42",
national_number: "4376543210"
}
}
}, function (err, data) {
if (err) {
// something went wrong, send error back to caller
reject(new functions.https.HttpsError('unknown', 'Ippopay threw an unexpected error', err));
return;
}
// successful, send data back to caller
resolve(data.order.order_id);
});
});
});
You should also make sure you make use of context.auth to restrict access to this function. You wouldn't want to bill the wrong customer.
I am trying to call a Google cloud function from an Android app that does not work (First call just after deployment works 90 % of the times but subsequent calls fails, nothing is displayed on firebase log console either).
public Task<String> myCloudFunction() {
return FirebaseFunctions.getInstance()
.getHttpsCallable("createUser")
.call(data)
.continueWith(task -> {
String result = (String) task.getResult().getData();
return result;
});
}
Endpoint in Functions Dashboard
https://us-central1-xyz:555.cloudfunctions.net/createUser
This is how I call it.
public void callCloudFunction() {
createFirebaseUserAccount.myCloudFunction()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
} else {
Timber.d(task.getResult());
}
}
}
});
}
Here is the cloud function:
$GOOGLE_APPLICATION_CREDENTIALS is pointing to service_key.json file which contains the private key.
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://XYZ.firebaseio.com"
});
exports.createUser = functions.https.onCall((data, context) => {
const callerEmail = data.email;
const callerPassword = data.password;
const callerDisplayName = data.displayName;
return admin.auth().createUser({
email: callerEmail,
emailVerified: false,
password: callerPassword,
displayName: callerDisplayName,
disabled: false
}).then(userRecord => {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully created new user:", userRecord.uid);
return userRecord.uid;
}).catch(error => {
console.log("Error creating new user ", error);
return error;
});
});
Thanks for reading! :)
You're not returning a promise from the the function that contains the data to send to the client. Actually, you're not passing anything at all. You should instead return the promise chain from your async work:
return admin.auth().createUser({
email: callerEmail,
emailVerified: false,
password: callerPassword,
displayName: callerDisplayName,
disabled: false
}).then(userRecord => {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully created new user:", userRecord.uid);
return userRecord.uid;
}).catch(error => {
console.log("Error creating new user ", error);
return error;
});
Note the new return before the whole thing. You should definitely take some time to learn about how JavaScript promises work in order to make effective use of Cloud Functions, and they will not work correctly without observing their rules and conventions.
I am getting this error -
"Objects are not valid as a React child (found: object with keys {$$typeof , type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead"
This is specifically running the Android Emulator.
Here's my code:
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
userEmail: null
}
//this.authCheck = this.authCheck.bind(this)
//this.toggleLogin = this.toggleLogin.bind(this)
this.signIn = this.signIn.bind(this)
}
componentDidMount() {
this.authListener()
}
authListener() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log(user.email)
this.setState({userEmail: user.email})
} else {
console.log('no user signed in')
this.setState({userEmail: null})
}
})
}
signIn(email, password) {
//console.log(email, password)
//console.log(this.state.userEmail)
firebase.auth().signInWithEmailAndPassword(email, password)
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage)
// ...
})
}
signUp(email, password) {
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage)
// ...
})
}
logOut() {
firebase.auth().signOut()
}
render() {
return (
<View style={styles.container}>
{this.state.userEmail ? <ItWorked logOut={this.logOut}/> : <SignUp signIn={this.signIn} signUp={this.signUp} toggleLogin={this.toggleLogin}/>}
</View>
);
}
}
If your code is exactly as you wrote it, then ItWorked and SignUp should be undefined, which would give you an error if you try to use them in the render method.
If you did import those two components, then you might not be importing and exporting them correctly. If this is the case, you might have done something like:
// App.js
// Note that this import expects SignUp to be the default export of SignUp.js
import SignUp from './components/SignUp.js'
export default class App extends Component {
...
// SignUp.js
// Note that this is a named export of SignUp
export function SignUp() {
return <div>Lorem Ipsum</div>
}
To solve this, either export SignUp as the default:
// SignUp.js
export default function SignUp() {
return <div>Lorem Ipsum</div>
}
OR import SignUp as a named import:
// App.js
import { SignUp } from './components/SignUp.js'
...
But not both.
Another possible issue is that the rendered components (
ItWorked and SignUp) are wrapped in higher order components (like react-redux's connect), but the exporting file does not actually call the HOC.
I suspect you might find that one (or both) of those files does something like this:
// SignUp.js
function SignUp(props) {
return <div>Lorum Ipsum</div>
}
// Possible current export
export default connect(mapStateToProps)
// "Correct" export
export default connect(mapStateToProps)(SignUp)
Either way, the problem probably lies in the SignUp and ItWorks components or how they are imported by App, and it would help to get a bit more context into those components.
I am using firebase cloud functions as serverside for Paypal payment. Documentations are not obvious to understand.
when I am trying to send an object from android app to firebase cloud functions, nothing has happened. I think I added it wrong. so how can I pass an object from android app to the function??
public void payout(String PayerID,String paymentId) {
// Create the arguments to the callable function.
JSONObject postData = new JSONObject();
try {
postData.put("PayerID", PayerID);
postData.put("paymentId",paymentId);
} catch (JSONException e) {
e.printStackTrace();
}
mFunctions
.getHttpsCallable("payout")
.call(postData)
.continueWith(new Continuation<HttpsCallableResult, Object>() {
#Override
public Object then(#NonNull Task<HttpsCallableResult> task)
throws Exception {
return null;
}
});
}
///////////////////////////////////////////
exports.payout=functions.https.onRequest((req,res)=>{
const sender_batch_id = Math.random().toString(36).substring(9);
const payReq=JSON.stringify({
sender_batch_header: {
sender_batch_id: sender_batch_id,
email_subject: "You have a nice payment"
},
items: [
{
recipient_type: "EMAIL",
amount: {
value: 0.90,
currency: "USD"
},
receiver: "amrmahmoudM#app.com",
note: "Thank you very much.",
sender_item_id: "item_3"
}
]
});
paypal.payout.create(payReq,(error, payout)=>{
if (error) {
console.warn(error.res);
res.status('500').end();
throw error;
}else{
console.info("payout created");
console.info(payout);
res.status('200').end();
}
});
});
exports.process = functions.https.onRequest((req, res) => {
const paymentId = req.body.paymentId;
var payerId = {
payer_id: req.body.PayerID
};
return paypal.payout.execute(paymentId, payerId, (error, payout) => {
if (error) {
console.error(error);
} else {
if (payout.state === 'approved') {
console.info('payment completed successfully, description: ',
payout.transactions[0].description);
const ref=admin.firestore().collection("Users").doc(payerId);
ref.set({'paid': true});
} else {
console.warn('payment.state: not approved ?');
}
}
}).then(r =>
console.info('promise: ', r));
});
The problem comes from the fact that in your Android app you call an HTTPS Callable Function (via mFunctions.getHttpsCallable("payout")) but your Cloud Function is not an HTTPS Callable Function but a "simple" HTTPS Function.
HTTPS Callable Functions are written like:
exports.payout = functions.https.onCall((data, context) => {
// ...
});
while HTTPS Functions are written like:
exports.payout = functions.https.onRequest((req,res)=> {
// ...
})
So you should adapt the code of your Cloud Function according to the documentation: https://firebase.google.com/docs/functions/callable
Note that another option could be to write to the database (Real Time database or Firestore) and trigger the Cloud Function with an onWrite or onCreate trigger. The advantage of this approach is that you directly save the information of the payment in the database.
This question already has an answer here:
Variable not updating with fetch response data in Angular 2 beta
(1 answer)
Closed 6 years ago.
I try to explain in English, but I don't speak it.
I'm working in a Ionic 2. I try to do a http request with post method and I am emulate in SDK Android emulator and I can see in the logcat:
Cannot call method 'post' of undefined at
file:///android_asset/www/build/js/app.bundle.js:2265
But I review and don't see anything, I rewrite my clientId and ClientSecret to can post here. I put a trace console.log(this.http) in the login function and this attribute is undefined, althought is inject in the class' constructor.
My code:
import {Page, Platform} from 'ionic-angular';
import {Http, Headers, HTTP_PROVIDERS} from 'angular2/http';
#Page({
templateUrl: 'build/pages/home/home.html',
providers: [ HTTP_PROVIDERS ]
})
export class HomePage {
static get parameters() {
return [[Platform],[Http]];
}
constructor(platform, http) {
this.platform = platform;
this.http = http;
this.clientId = "clientId";
this.clientSecret = "clientSecret";
}
login() {
this.platform.ready().then(() => {
this.googleLogin().then((success) => {
alert(success.access_token);
}, (error) => {
alert(error);
});
});
}
googleLogin() {
return new Promise(function(resolve, reject) {
var browserRef = window.cordova.InAppBrowser.open("https://accounts.google.com/o/oauth2/auth?client_id=" + "clientId" + "&redirect_uri=http://localhost/callback&scope=email%20profile&approval_prompt=force&response_type=code&access_type=offline", "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
browserRef.addEventListener("loadstart", (event) => {
if ((event.url).indexOf("http://localhost/callback") === 0) {
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
var parameters = "client_id=" + "clientId" + "&client_secret=" + "clientSecret" + "&redirect_uri=http://localhost/callback" + "&grant_type=authorization_code" + "&code=" + requestToken
var requestToken = (event.url).split("code=")[1];
this.http.post("https://accounts.google.com/o/oauth2/token", parameters, { header:headers })
.subscribe( data => { resolve(data); },
error => { reject("Problem authenticating with Google"); }
);
browserRef.removeEventListener("exit", (event) => {});
browserRef.close();
}
});
browserRef.addEventListener("exit", function(event) {
reject("The Google sign in flow was canceled");
});
});
}
}
The code tries to authenticate with Google OAuth2, althought the error seems to be in the attributes in the constructor(http, clientId, clientSecret) there are not defined when the login function is called. I don't know what's wrong!
It might have something to do with the scoping of 'this', depending on what calls the googleLogin function.
Try using an arrow function:
googleLogin = () => {
...
}
It's because you don't use an arrow function when defining your promise. So the this keyword doesn't correspond to the instance of the component itself. With arrow functions, you can use the lexical this that will correspond to the component instance.
googleLogin() {
return new Promise((resolve, reject) => {
(...)
});
}
instead of
googleLogin() {
return new Promise(function(resolve, reject) {
(...)
});
}
See this link for more hints about the lexical this of arrow functions:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions.