I have the following code:
order =
setupForm: ->
$('.new_order').submit ->
$form = $(this)
$form.find('input[type=submit]').attr('disabled', true)
order.processCard($form)
false
processCard: ($form)->
card =
number: $form.find('.card_number').val()
cvc: $form.find('.card_code').val()
expMonth: $form.find('.card_month').val()
expYear: $form.find('.card_year').val()
Stripe.createToken card, (status, response) ->
order.handleStripeResponse(status, response, $form)
handleStripeResponse: (status, response, $form) ->
if status == 200
$form.find('.order_stripe_card_token').val(response.id)
$form.submit()
else
$form.find('.stripe_error').text(response.error.message)
$form.find('input[type=submit]').attr('disabled', false)
It renders errors perfectly ok (if the card number is wrong/missing etc.), but when I put in the correct card details provided by Stripe, it doesn't submit anything.
I'm sure it's something very obvious I'm missing, but need some fresh eyes to point out where there error lies.
Your problem is that your submit handler calls your submit handler which calls your submit handler which ...
The sequence of events goes like this:
The <form> is submitted.
The submit handler calls processCard.
processCard does an AJAX call to stripe via Stripe.createToken.
The createToken callback calls handleStripeResponse.
handleStripeResponse calls $form.submit().
The <form> is submitted.
...
If we look at a simplified and instrumented example, the problem might be clearer. Given a simple <form id="f"> with a single submit button and this code:
hand_break = 0
ajax_simulator = ($f) ->
fn = ->
console.log("AJAX callback called: #{hand_break}")
$f.submit()
setTimeout(fn, 500)
$('#f').submit ->
return false if(++hand_break > 3)
console.log("submit handler called: #{hand_break}")
ajax_simulator($(#))
false
You'll see that it loops three times when you hit the submit button, the hand_break stuff is just there to manually stop the infinite loop.
Demo: http://jsfiddle.net/ambiguous/JHF3f/
So how do you break the cycle? Your $('.new_order').submit handler needs to know when to return false and when to return true, if you return true then the form will submit to the server and everything will be okay. You're storing the Stripe token already:
$form.find('.order_stripe_card_token').val(response.id)
so you could just check if that's there:
$('.new_order').submit ->
return true if($form.find('.order_stripe_card_token').val())
#...
You'd want to make sure that .order_stripe_card_token was properly initialized, cleared on errors, etc.
Related
I want to get an data attribute from my axios post and write it to an local variable to reuse it.
If i console.log it inside the axios .then, tha data is set, if i write it to my variable and want to use it after, it is empty.
export default {
data(){
return {
post:{},
projectId: '',
existingProjects: []
}
},
methods: {
addPost(){
//check if project exists else create
let uriProj = 'http://localhost:4000/projects/add';
this.axios.post(uriProj, {
projectName: this.post.project,
}).then(response => this.projectId = response.data.data);
console.log("project_id: "+this.projectId)
}
}
What am i doing wrong?
Another Question:
Is this the right way if i want to reuse the id in another method?
My Goal is to first create a project if it is not already in my db, then i want to reuse the id of the created or returned project model to create a new customer in my db, if the customer already has the project with the id of this project, it shouldnt be added, if it is a new one it should be added.
Has this to be done in multiple requests or is there a simple method for doing this?
I believe the issue you are seeing has to do with the asynchronous nature of network calls. When axios submits the post request it returns a Promise then the addPost function continues executing. So the projectId gets logged after it the initial value gets set, but before the network request completes. Everything inside the then() function executes once the network request has been completed so you can test by moving the console.log to be executed once the request is done. You could also output the value in the template so you can see it update {{ projectId }}
this.axios.post(uriProj, {
projectName: this.post.project,
}).then(response => {
this.projectId = response.data.data
console.log("project_id: "+this.projectId)
});
I would ideally recommend using the VueJS dev tools browser extension because it allows you to inspect the state of your Vue components without having to use console.log or add random echos to your template markup.
#jfadich is correct. I recommend using async/await instead of then, it's more intuitive to read.
async addPost(){
//check if project exists else create
let uriProj = 'http://localhost:4000/projects/add';
let resp = await this.axios.post(uriProj, {
projectName: this.post.project,
})
this.projectId = resp.data.data
console.log("project_id: "+this.projectId)
}
I am using the email verification feature that Parse offers and would like my users to be able to resend the email verification if it fails to send or they cannot see it. Last I saw, Parse does not offer an intrinsic way to do this (stupid) and people have been half-hazzerdly writing code to change the email and then change it back to trigger a re-send. Has there been any updates to this or is changing the email from the original and back still the only way? Thanks
You should only need to update the email to its existing value. This should trigger another email verification to be sent. I haven't been able to test the code, but this should be how you do it for the various platforms.
// Swift
PFUser.currentUser().email = PFUser.currentUser().email
PFUser.currentUser().saveInBackground()
// Java
ParseUser.getCurrentUser().setEmail(ParseUser.getCurrentUser().getEmail());
ParseUser.getCurrentUser().saveInBackground();
// JavaScript
Parse.User.current().set("email", Parse.User.current().get("email"));
Parse.User.current().save();
You have to set the email address to a fake one save and then set it back to the original and then parse will trigger the verification process. Just setting it to what it was will not trigger the process.
iOS
if let email = PFUser.currentUser()?.email {
PFUser.currentUser()?.email = email+".verify"
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
PFUser.currentUser()?.email = email
PFUser.currentUser()?.saveEventually()
}
})
}
Poking around the source code for Parse server, there doesn't seem to be any public api to manually resend verification emails. However I was able to find 2 undocumented ways to access the functionality.
The first would be to use the internal UserController on the server (for instance from a Cloud function) like this:
import { AppCache } from 'parse-server/lib/cache'
Cloud.define('resendVerificationEmail', async request => {
const userController = AppCache.get(process.env.APP_ID).userController
await userController.resendVerificationEmail(
request.user.get('username')
)
return true
})
The other is to take advantage of an endpoint that is used for the verification webpage:
curl -X "POST" "http://localhost:5000/api/apps/press-play-development/resend_verification_email" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "username": "7757429624" }'
Both are prone to break if you update Parse and internals get changed, but should be more reliable than changing the users email and then changing it back.
We were setting emails to an empty string, but found that there was a race condition where 2 users would hit it at the same time and 1 would fail because Parse considered it to be a duplicate of the other blank email. In other cases, the user's network connection would fail between the 2 requests and they would be stuck without an email.
Now, with Parse 3.4.1 that I'm testing, you can do (for Javascript):
Parse.User.requestEmailVerification(Parse.User.current().get("email"));
BUT NOTE that it will throw error if user is already verified.
Reference:
http://parseplatform.org/Parse-SDK-JS/api/3.4.1/Parse.User.html#.requestEmailVerification
To resend the verification email, as stated above, you have to modify then reset the user email address. To perform this operation in secure and efficient way, you can use the following cloud code function:
Parse.Cloud.define("resendVerificationEmail", async function(request, response) {
var originalEmail = request.params.email;
const User = Parse.Object.extend("User");
const query = new Parse.Query(User);
query.equalTo("email", originalEmail);
var userObject = await query.first({useMasterKey: true});
if(userObject !=null)
{
userObject.set("email", "tmp_email_prefix_"+originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
userObject.set("email", originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
response.success("Verification email is well resent to the user email");
}
});
After that, you just need to call the cloud code function from your client code. From Android client, you can use the following code (Kotlin):
fun resendVerificationEmail(email:String){
val progress = ProgressDialog(this)
progress.setMessage("Loading ...")
progress.show()
val params: HashMap<String, String> = HashMap<String,String>()
params.put("email", email)
ParseCloud.callFunctionInBackground("resendVerificationEmail", params,
FunctionCallback<Any> { response, exc ->
progress.dismiss()
if (exc == null) {
// The function executed, but still has to check the response
Toast.makeText(baseContext, "Verification email is well sent", Toast.LENGTH_SHORT)
.show()
} else {
// Something went wrong
Log.d(TAG, "$TAG: ---- exeception: "+exc.message)
Toast.makeText(
baseContext,
"Error encountered when resending verification email:"+exc.message,
Toast.LENGTH_LONG
).show()
}
})
}
I have a meteor app where I want to let the user click buttons to switch between 2 different filtered views of the data, say "chart" records with a status of 10 or 11. Some users of the app might never want to see status 10, while others might never want to see status 11.
I'm trying to find a way through publish/ subscribe to work this out most efficiently... not pulling up records someone doesn't want to see, while also reducing network traffic.
First idea was the following... a publish on the server with a parameter of the status:
Meteor.publish("charts1", function (status) {
console.log('someone subscribed to my publish of charts.. returning all charts now of status ' + status + " max 500 though");
return Chart.find({"chartStatus": status}, {limit: 500, sort: {"demographics.lastName": 1}});
});
then, on client, I have a template helper:
chartsOnClient: function () {
return Chart.find({"chartStatus":Session.get('currentStatusFilter')}, {sort: {"demographics.lastName": 1}});
}
and 2 buttons which, when clicked will set the session filter and subscribe at the same time:
Template.addChartForm.events = {
'click #mybutton10': function () {
console.log("subscribing to to status 10...");
Session.set('currentStatusFilter', 10);
Meteor.subscribe('charts1', 10);
},
'click #mybutton11': function () {
console.log("subscribing to status 11...");
Session.set('currentStatusFilter', 11);
Meteor.subscribe('charts1', 11);
},
}
and, of course, a template that iterated over "chartsOnClient" to display the records.
This worked... when I clicked button10, I got status 10 in my template, and clicking button11 gave me status 11 in my template...And, the added benefit was that if I never clicked button11, my local client only held the records of status 10, and vice versa, and clicking both buttons would fill up my local collection with the union of both sets.
The only issue I see with this method is that every click of the buttons results in the server message "someone subscribed to my publish of charts..."... indicating that the client is talking to the server and running the find method.
I suspect this is not ideal... for, if I wanted, I could move the 2 "subscribe" calls outside of the click events, like this:
Meteor.subscribe('charts1', 10);
Meteor.subscribe('charts1', 11);
Template.addChartForm.events = {
'click #mybutton10': function () {
console.log("subscribing to to status 10...");
Session.set('currentStatusFilter', 10);
},
'click #mybutton11': function () {
console.log("subscribing to status 11...");
Session.set('currentStatusFilter', 11);
},
}
and when I do this, I get the same end user experience, however, the server console only shows the "someone subscribed to my publish"... message once upon startup, instead of every time the buttons are clicked.
The downside is that both sets of records are pulled up to the client for each user, even users that might never click on both buttons. But the upside is that the subscribe method is not called each time they click to switch between views of the data...
Is there something I'm not understanding here? Which method is best? is there a better way to do this altogether? I'm new to meteor and mongo.
Thank you.
Edit, based on #mattk , I am going to do the subscribes in the button clicks, but use an array in a session variable to prevent a second subscription if I've already subscribed with that particular filter:
'click #mybutton10': function () {
Session.set('currentStatusFilter', 10);
var filtersAppliedSoFar = Session.get('filtersAppliedSoFar');
if (filtersAppliedSoFar.indexOf(10) == -1) {
console.log("subscribing to to status 10...");
filtersAppliedSoFar.push(10);
Session.set('filtersAppliedSoFar', filtersAppliedSoFar);
Meteor.subscribe('charts1', 10);
}
},
'click #mybutton11': function () {
Session.set('currentStatusFilter', 11);
var filtersAppliedSoFar = Session.get('filtersAppliedSoFar');
if (filtersAppliedSoFar.indexOf(11) == -1) {
console.log("subscribing to status 11...");
filtersAppliedSoFar.push(11);
Session.set('filtersAppliedSoFar', filtersAppliedSoFar);
Meteor.subscribe('charts1', 11);
}
},
This way, I don't pull up the data until the user clicks on the particular filter, but I also don't resubscribe if they click back and forth between the 2 filters, which is expected.
EDIT AGAIN: after asking this question How do you securely log out and clear all subscriptions? and getting directed toward Subscription Manager https://github.com/meteorhacks/subs-manager I have discovered that Subscription manager achieves what I was looking for here: I didn't want it to hit the server a second time if my client called .subscribe again. Instead of using a session variable (filtersAppliedSoFar) to know if the client has already subscribed, the subs manager keeps track of this automatically...I just call .subscribe on the subscription manager object and it won't hit the server the second time. .. then the added benefit is I can call .clear() when logging out and all subscriptions are stopped.
There's nothing wrong with your first pattern. Every time you talk to the server, you want to ask it or tell it something new, and that's exactly what you're doing: you're asking it for ONLY the data you need. If you want to reduce bandwidth, limit the fields returned; chances are you aren't going to need every field that is stored in the doc.
I follow something like this:
//Client code (event click)
Meteor.subscribe('patients',
{"chartStatus":Session.get('currentStatusFilter')},
{
fields: {'demographics': 1, 'chartStatus': 1},
sort: {"demographics.lastName": 1}
});
//Server code
Meteor.publish('patients', function(query, options) {
if (options) {
check(options, {
sort: Match.Optional(Object),
limit: Match.Optional(Number),
fields: Match.Optional(Object)
});
}
return Patients.find(query,options);
});
Note that the client can now ask for whatever fields she wants. If in the future there are certain fields in the doc that shouldn't be sent over the wire, you need to do a permission check, too. Right now, this doesn't seem like a problem for you since you've been sending over the entire doc.
I am using FuelPHP's rest controller.
I am trying to break the flow and display my response after encountering an error.
Here is my basic flow needed:
When any of the methods are called I run a "validate" function, which validates parameters and other business logic.
If the "validate" function determines something is off, I want to stop the entire script and display the errors I have complied so far.
I have tried the following in my "validate" function but it simply exits the validate function... then continues to the initial method being requested. How do I stop the script immediately and display the contents of this response?
return $this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
) );
That is very bad practice. If you exit you not only abort the current controller, but also the rest of the framework flow.
Just validate in the action:
// do your validation, set a response and return if it failed
if ( ! $valid)
{
$this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
), 400); //400 is an HTTP status code
return;
}
Or if you want to do central validation (as opposed to in the controller action), use the router() method:
public function router($resource, $arguments)
{
if ($this->valid_request($resource))
{
return parent::router($resource, $arguments);
}
}
protected function valid_request($resource)
{
// do your validation here, $resource tells you what was called
// set $this->response like above if validation failed, and return false
// if valid, return true
}
I am new to FuelPHP so if this method is bad practice, let me know.
If you want your REST controller to break the flow at some other point than when the requested method returns something, use this code. You can change the $this->response array to return whatever you want. The main part of the script is the $this->response->send() method and exit method.
$this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
), 400); //400 is an HTTP status code
//The send method sends the response body to the output buffer (i.e. it is echo'd out).
//pass it TRUE to send any defined HTTP headers before sending the response body.
$this->response->send(true);
//kill the entire script so nothing is processed past this point.
exit;
For more information on the send method, check out the FuelPHP documentation for the response class.
I use a custom AJAX vtype, which works and all.
However I notice the extjs docs note that
"Note: this method does not cause the Field's validate or isValid methods to return false if the value does pass validation. So simply marking a Field as invalid will not prevent submission of forms submitted with the Ext.form.action.Submit.clientValidation option set."
How does one mark a field as invalid so that form.isValid() returns false?
My vtype:
Ext.apply(Ext.form.VTypes, {
username: function(value, field){
var conn = new Ext.data.Connection();
conn.request({
url: 'test.php',
params:{email:value},
method: 'POST',
success: function(responseObject){
var resp = responseObject.responseText;
if (resp == 0) {
field.markInvalid('This email is invalid or already registered!');
} else if (resp == 1) {
field.clearInvalid();
}
},
failure: function(){
Ext.Msg.show({title: 'Error', msg: 'Connection error!',});
}
});
return true;
}
});
Well, if you look at the definition of clientValidation:
Determines whether a Form's fields are validated in a final call to
isValid prior to submission. Pass false in the Form's submit options
to prevent this. If not defined, pre-submission field validation is
performed.
This is basically saying, if its set to false then the form will be automatically validated prior to submission... at which point it will fail if you have marked any fields as invalid. If you set it to true then you need to manually check if the form is valid before submitting.
It's worth pointing out that your AJAX vtype is never going to work. When the form calls the vtype to check whether the field is valid, all it gets in response is true... so the form submits no matter what. Even if you tell it to return after the ajax request has completed, the form would submit before the vtype ajax request completes.
A better solution would be to include the call to the vtype manually in your submit button handler...then in your success of the ajax request just do
if(form.isValid()) {
form.getForm().submit();
}
This will force the submit button's click event to first validate the username field and then, and only then, will it submit the form.