How to access a form value in Redux dispatch? - react-redux

I have the following form component:
export class LoginPage extends React.PureComponent {
render() {
return (
<div>
<Form onSubmit={this.props.onSubmitForm}>
<Input id="username" type="text" value={this.props.username} />
<button type="submit">Submit</button>
</Form>
</div>
);
}
}
Just after the component I have a mapDispatchToProps function:
export function mapDispatchToProps(dispatch) {
return {
onSubmitForm: (evt) => {
// I need to access the `username` property here to pass it as argument.
dispatch(postUsername(username));
},
};
}
I need to access the username inside the mapDispatchToProps function.
What is the simplest way to do it? I don't need to store the username in this step.
Thank you.

You can use the event.target.username.value property, so your code should look like this:
export function mapDispatchToProps(dispatch) {
return {
onSubmitForm: (evt) => {
// I need to access the `username` property here to pass it as argument.
dispatch(postUsername(evt.target.username.value));
},
};
}
By doing this you should have the username value.

Related

stripe payment method is returning null when I submit form

when I try to create a new subscription I get this error (This customer has no attached payment source or default payment method. ) so I checked the PaymentController with dd($paymentMethod) which returned null
so I don't know why the variable $paymentMethod in store method is returning NULL from the $request but the request, for the price is returning the price_id. Please any help is appreciated
but when console.log() setupIntent.payment_method it returned the payment_method in the console
Here is my PaymentController
public function index()
{
$availablePlans = [
'price_1HnIiLLzAo4pwMcyh2aGaznB' => 'Monthly',
'price_1HnJ2vLzAo4pwMcygQT66juk' => 'Yearly',
'price_1HnIhILzAo4pwMcy9iH3j30L' => 'Free Membership'
];
$user = auth()->user();
$data = [
'intent' => $user->createSetupIntent(),
'plans' => $availablePlans
];
return view('payments.checkout')->with($data);
}
public function store(Request $request)
{
$user = auth()->user();
$paymentMethod = $request->payment_method;
// dd($paymentMethod);
$planId = $request->plan;
$user->newSubscription('premium', $planId)->create($paymentMethod);
return response(['status' => 'success']);
}
This is the Javascript
window.addEventListener('load', function (){
// Create a Stripe client.
const stripe = Stripe('pk_test_51H2OqqLzAo4pwMcyT4h405wpFRAn3FWhvByfvmVnW6tabrIsDoU1dBXJ0UaWexUJeacCJ9uKpb5OBmmA2KaCg4sd00ZZ5tj2q8');
// Create an instance of Elements.
const elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
// const cardElement = elements.create('card', {style: style});
// Create an instance of the card Element.
const cardElement = elements.create('card');
// Add an instance of the card Element into the `card-element` <div>.
cardElement.mount('#card-element');
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
const plan = document.getElementById('subscription-plan').value;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.handleCardSetup(
clientSecret, cardElement, {
payment_method_data: {
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
// console.log('handling success', setupIntent.payment_method);
axios.post('/subscribe', {
payment_method: setupIntent.payment_method,
plan: plan
})
}
});
});
Here is the form
<form action="{{ route('subscribe')}}" method="POST" id="">
#csrf
<div class="form-content">
<div class="field">
<select class="form-control" name="plan" id="subscription-plan">
#foreach ($plans as $key=>$plan )
<option value="{{$key}}">{{$plan}}</option>
#endforeach
</select>
</div>
<div class="field">
<input type="text" autocorrect="off" spellcheck="false" id="card-holder-name" maxlength="25" />
<span class="focus-bar"></span>
<label for="cardholder">Card holder (Name on card)</label>
</div>
<div class="field mb-5" id="card-element">
<!-- Stripe Elements Placeholder -->
</div>
<button id="card-button" data-secret="{{ $intent->client_secret }}"><span>Pay</span></button>
</div>
</form>
The Route
Route::resource('payments', 'PaymentsController', [
'names'=> [
'index' => 'checkout',
'store' => 'subscribe',
]
]);
Looks like there is something wrong with how you're using axios. Have you tried taking a look at laravel simple axios with argument
Adding a hidden input field in the form and setting the value to setupIntent.payment_method passed the payment_method id to the controller which is used to create the subscription so the problem is solved.
A few modifications and adding a hidden input field to the JS
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
//cardButton.addEventListener('click', async (e) => {
//e.preventDefault()
const { setupIntent, error } = await stripe.handleCardSetup(
clientSecret, cardElement, {
payment_method_data: {
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
//console.log('handling success', setupIntent.payment_method);
axios.post('/subscribe',{
plan : plan
})
var paymentMethod = setupIntent.payment_method;
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'payment_method');
hiddenInput.setAttribute('value', paymentMethod);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}

Why this validation gives me always required?

probably I'm doing something wrong, I have been coding by instinct haha. Laravel validation seems super easy to implement but for some reason between my vuejs component to my php function I'm always getting "required".
I'm new with both Laravel and Vuejs, it seems to me that my php function is fine (for what I can see on the web) but probably I'm missing something on the comunication between laravel and vue. Can you tell me whats wrong?
public function createTag(Request $request)
{
try {
$data = request()->validate([
'title' => 'required'
]);
$tag = new Tag;
$tag->title = $request->title;
if($tag->save())
{
$tag->usercontracts()->attach($request->usercontractId);
}
return response()->success(__('success.showing', ['resource' => 'des Vertrags', 'resourceE' => 'tag']), $tag->id, 200);
} catch (Exception $e) {
return response()->error(__('error.showing', ['resource' => 'des Vertrags', 'resourceE' => 'tag']), 400, $e);
}
}
<template>
<div id="relative">
<button #click.prevent="show = 1" v-if="show == 0">+tag</button>
<input type="text" v-model="title" name="title" v-if="show == 1">
<button #click="createTag" v-if="show == 1">add</button>
</div>
</template>
<script>
import TagService from "#/services/TagService";
export default {
name: "add-tag-component",
props: ['usercontractId'],
data(){
return {
title:null,
show:0
};
},
methods:
{
async createTag()
{
const {body: {data}} = await TagService.createTag(this.title, this.usercontractId);
this.$emit('addedTag', this.title, data);
this.title = '';
this.show = 0;
}
}
};
</script>
And this is TagService
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag/${title}/${usercontractId}`, tagId);
}
}
I'm also getting this error. May be here is the answer?
Vue warn]: Error in v-on handler (Promise/async): "[object Object]"
found in
---> at resources/assets/js/components/contract/listing/AddTagComponent.vue
at resources/assets/js/components/contract/listing/ContractListingItemComponent.vue
at resources/assets/js/components/contract/listing/ContractListingComponent.vue
In your TagService
You need to pass the ${title} as payload not as uri.
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag/${title}/${usercontractId}`, tagId);
}
}
to
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag`, {
tagId: tagId,
title: title,
usercontractId: usercontractId
});
}
}
Laravel validates the payload you pass.

Vue.js: Conditional rendering based on computed property

I am trying to conditionally render element(s) based on a computed property but not having much luck even though my computed property is returning true or false correctly.
I am passing the user object in as a property (from Laravel) and all is working fine. I am able to see the user -- and their related role(s).
I've tested via Laravel to make sure I am sending the correct user and the relationship and everything looks good there as well.
Controller
$user = User::with('roles')->find(Auth::id());
blade
<my-component :user="{{ $user }}"></my-component>
VueComponent.vue
<template>
<div>
<div v-if="admin">
<p>You are an admin!</p>
</div>
<div v-else>
<p>You are not an admin.</p>
</div>
</div>
</template>
<script>
export default {
...
computed: {
admin: function () {
this.user.roles.forEach((role) => {
console.log('role: ', role); // role: admin (string)
if (role.name === 'admin') {
console.log('user is an admin!'); // I am getting here.
return true;
} else {
console.log('user is NOT an admin.');
}
});
return false;
},
},
methods: {
//
},
props: {
user: {
type: Object,
required: true,
},
},
}
</script>
I'm sure I am not implementing the computed property correctly; any help is greatly appreciated!
You problem is using foreach in wrong way! please use this instead:
computed: {
admin: function () {
for (var i = 0; i < this.user.roles.length; i++){
if ( this.user.roles[i].name === 'admin') {
return true;
}
}
return false;
}
}
you can read this article about forEach in js https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Your mistake was return true from forEach callback function and leave this true value useless and then return false value on admin function.

Laravel nova, custom resource tool not appears in edit mode

I have a custom resource-tool working fine in the view panel of a resource, but it dont appears when i go o the edit mode. Is there something i should add to the component or to the Nova configuration to enable the component in the edit mode?
Code in User.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('First name', 'firstName')
->sortable()
->rules('required', 'max:255'),
Text::make('Last name', 'lastName')
->sortable()
->rules('required', 'max:255'),
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
Password::make('Password')
->onlyOnForms()
->creationRules('required', 'string', 'min:6')
->updateRules('nullable', 'string', 'min:6'),
YesNovaUserPermissions::make(),
];
}
User view:
User edit:
Nova does not seem to allow you to obtain this functionality with a custom resource but you can with a custom field. You basically create a "dummy" field which does not really exist on the model and use a mutator on the model to overwrite the default model saving functionality.
Following the documentation above, you can build a Vue component which will appear within the resource edit form itself, similarly to how I have done with the tags picker pictured below.
Code for that:
<template>
<default-field :field="field" :errors="errors" :show-help-text="showHelpText">
<label for="tag" class="inline-block text-80 pt-2 leading-tight">Tag</label>
<template slot="field">
<div id="multitag-flex-holder">
<div id="multitag-search-holder" class="w-1/2">
<div class="search-holder">
<label>Search Categories</label>
<input type="text" v-model="searchString" #focus="isSearching = true" #blur="isSearching = false" style="border:2px solid #000"/>
<div class="found-tags" v-if="isSearching">
<div v-for="(tag, i) in foundTags" #mousedown="addToSelected(tag)" :key="i">{{tag.name}}</div>
</div>
</div>
</div>
<div class="select-tags-holder w-1/2">
<div class="selected-tags">
<div v-for="(tag, i) in selectedTags" :key="'A'+i" #click="removeTag(tag)">{{tag.name}} X</div>
</div>
</div>
</div>
</template>
</default-field>
</template>
<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data: function () {
return {
selectedTags:[],
isSearching:false,
searchString:''
}
},
mounted(){
console.log(this.field)
this.field.value.forEach((tag)=>{
this.addToSelected(tag)
})
formData.append('whereType', 'Tag');
},
computed: {
// a computed getter
foundTags() {
// `this` points to the vm instance
return this.field.tags.filter((tag) => {
if(tag.name.search(new RegExp(this.searchString, "i")) >= 0){
if(this.selectedTagNames.indexOf(tag.name) == -1){
return tag;
}
};
})
},
selectedTagNames(){
var selNames = this.selectedTags.map((tag) => {
return tag.name;
})
return selNames;
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
removeTag(tag){
var index = this.selectedTags.indexOf(tag);
if (index > -1) {
this.selectedTags.splice(index, 1);
}
},
addToSelected(tag){
this.selectedTags.push(tag)
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
var tagIdArray = []
this.selectedTags.forEach((tag)=>{
tagIdArray.push(tag.id)
})
formData.append(this.field.attribute, tagIdArray)
},
},
}
</script>
Then, you can overwrite how the save functionality works in your model to accommodate for the "dummy" field. Note below instead of syncing the tags directly on the mutator, which will work most of the time depending on your data structure, I had to pass the tags to the "Saved" event on the model to accommodate for when creating a new record and the associated record id is not yet available, thus cannot be synced for a many to many relationship.
public function setTagsAttribute($value)
{
$tags = explode(",", $value);
$this->tempTags = $tags;
unset($this->tags);
}
protected static function booted()
{
static::saved(function ($article) {
$article->tags()->sync($article->tempTags);
});
}

sending an email address with axios within Vue.Js component to a laravel backend is failing

I am trying to make a vue component that checks if a given input is available or not in the database. The calls to the server i am doing with axios inside the vue component. for some reason, when i type the # symbol in the email, it breaks.here is my code so far:
this is the backend.
class TestController extends Controller
{
public function verifyName($name){
if (request()->wantsJson()){
$names=User::where('name', $name)->count();
if ($names>0)
return 1;
if ($names==0)
return 0;
}
}
public function verifyEmail($email){
if (request()->wantsJson()){
$emails=User::where('email', $email)->count();
if ($emails>0)
return 1;
if ($emails==0)
return 0;
}
}
}
here is the vue script:
<script>
export default {
data(){
return{
name: '',
email: '',
nameTaken: false,
emailTaken: false,
nameAvailable:false,
emailAvailable:false,
nameIsTooShort:false
}
},
methods:{
verifyName(){
axios.get('/verify/'+this.name)
.then((response) => {
if (response.data){
this.nameTaken=true;
this.nameAvailable=false
}
if(!response.data){
this.nameTaken = false;
this.nameAvailable=true
}
});
},
verifyEmail(){
axios.get('/verify/'+this.email)
.then((response) => {
if (response.data){
this.emailTaken=true;
this.emailAvailable=false
}
if(!response.data){
this.emailTaken = false;
this.emailAvailable=true
}
});
}
}
}
</script>
and here are my routes:
Route::get('/verify/{name}','TestController#verifyName');
Route::get('/verify/{email}','TestController#verifyEmail');
and here is a small gif to show it.
as requested, here is the html part where i show the message.
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" id="email" type="email" name="email" value="{{ old('email') }}" v-model="email" #change="verifyEmail">
<div class="help is-danger" v-show="emailTaken" v-cloak>This e mail is taken</div>
<div class="help is-success" v-show="emailAvailable" v-cloak>Great. this email is available</div>
</p>
</div>
</div>
Thanks.
Try using laravel's validator instead.
One way to go about it, in your "App\Http\Controllers\Auth\RegisterController"
use Illuminate\Support\Facades\Validator;
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255|unique:users',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
Make note of the unique:xxxxx rule where xxxxx is your table. https://laravel.com/docs/5.5/validation
If the email or name is not unique the validator will return the error response. So you would use it at the beginning of your register function.
public function register(Request $request)
{
$this->validator($request->all())->validate();
// rest of registration routine here....
}
I think its happens because of you are using "#" symbol in url string. This is reserved character. (Which characters make a URL invalid?)
Solution: send email to verification on server in json body of post request.

Resources