Livewire to update live status message during function execution - laravel-livewire

I have Livewire Country component which has input text for user to input 2-letter country code. After input the code, user will click on Import button and it calls import function to process. It will find the Country by given code and import the cities. Is there anyway to display a live message to show process status, ex: "Importing for country name".
Here is my code:
Livewire component
class CityImportComponent extends Component
{
public string $iso2 = '';
public string $process_message = '';
public function resetFilters()
{
$this->reset(['iso2', 'process_message']);
}
public function getCountryProperty()
{
return Country::findByCode($this->iso2)->first();
}
public function render()
{
return view('livewire.import.state-and-city-not-in-france');
}
public function import()
{
$this->process_message = 'Importing cities for ' . $this->country->name;
}
}
Livewire view
<div class="position-relative">
#if (session()->has('error'))
<div class="alert alert-danger mb-3">
<i class="bi bi-info-circle-fill"></i> {{ session('error') }}
</div>
#endif
#if (session()->has('success'))
<div class="alert alert-success mb-3">
<i class="bi bi-check-circle-fill"></i> {{ session('success') }}
</div>
#endif
<div class="position-absolute w-100 h-100" wire:loading wire:target="import">
<div
class="d-flex align-items-center justify-content-start text-white w-100 h-100 bg-secondary ps-3 rounded gap-2">
<i class="bi bi-arrow-repeat spin"></i> <span wire:model="process_message">{{ $process_message }}</span>
</div>
</div>
<form wire:submit.prevent="import">
<input type="text" class="form-control text-uppercase" name="iso2" wire:model="iso2" placeholder="2-letter country code">
<button type="submit" class="btn btn-outline-secondary">
Import
</button>
</form>
</div>

You could add a div and call wire:loading:
<div class="alert alert-primary" role="alert" wire:loading wire:target.longest="import">
{{ $process_message }}
</div>
More info can be found here: Livewire Loading States

try this.
<div wire:loading.delay class="float-right">
<span class="bg-info col-md-12 rounded"><b>Loading...</b>{{ $process_message }}</span>
</div>

Related

Distinguish which link-button clicked in django

I have that html code:
<form id="my-form" method="POST" action="{% url 'my_view' %}">
{% csrf_token %}
<div class="row">
<div class="col-md-6">
<div class="md-form mb-1">
<textarea id="message" name="message" rows="2" class="form-control md-textarea"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="md-form mb-1">
<textarea id="message_then" name="message_then" rows="2" class="form-control md-textarea"></textarea>
</div>
</div>
</div>
<div class="text-center text-md-left">
<a class="btn btn-primary" onclick="document.getElementById('my-form').submit();" style="width: 78px;" name="name1">Click1</a>
</div>
<div class="text-center text-md-left">
<a class="btn btn-primary" onclick="document.getElementById('my-form').submit();" style="width: 78px;" name="name2">Click2</a>
</div>
</form>
Now I would like to get to know which "button" was clicked. Unfortunately request.POST doesn't have that information.
You should add another field to specify which button has been clicked.
Exp:
Add this to your form
then update your "a" elements onclick event code like this:
<a class="btn btn-primary" onclick="update_form(this)" style="width: 78px;">Click1</a>
And finally some js code to update the form
<script>
function update_form(button){
document.getElementById("id_button").value = document.getElementById(button).innerText;
document.getElementById('my-form').submit();
}
</script>
In your django view use this to get the selected button:
request.POST['id_button']

How is it possible to get data from one form that has multiple textareas in Django

I'd like to get data from one form that has multiple textareas inside , In my views: 'compare_ingredients' I've tried requesting data from my form id 'compareform' also the textarea id 'ingredients', but when I enter something into one texture and click on submit it comes back as 'None' when I print. Here is my html and views:
html :
<div class="container-fluid">
<h4>Please enter in your ingredients:</h4>
<h6>(Seperate each item by comma)</h6>
<br>
<button class="add_item">Add Item</button>
<br>
<div class="row row-cols-1 row-cols-md-2 mx-auto">
<form action="{% url 'compare_ingredients' %}" method="POST" name="compareform" id="compare">
{% csrf_token %}
<br>
<button type="submit" class="btn btn-info sub_btn">Submit</button>
<button type="submit" class="btn btn-secondary back_button">
Back
</button>
<br>
<br>
<div class="row form_row">
<div class="col mb-4">
<h5>Item 1</h5>
<div class="card form_card">
<div class="card-body compare_cardbody">
<textarea name="ingredients1" id="ingredients" cols="30" rows="10" form="compareform"></textarea>
</div>
</div>
</div>
<div class="col mb-4">
<h5>Item 2</h5>
<div class="card form_card">
<div class="card-body compare_cardbody">
<textarea name="ingredients2" id="ingredients" cols="30" rows="10" form="compareform"></textarea>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
views.py:
def compare_ingredients(request):
if request.method == "POST":
ingredients = request.POST.get('compareform')
print(ingredients)
return render(request, 'result/compare.html')
ok, I figured it out , the problem was in the html textarea I had the name of the form as 'compareform' when it should have been 'compare' :
<textarea name="ingredients2" id="ingredients" cols="30" rows="10" form="compare"></textarea>
then in my views I did:
ingredients1 = request.POST.get('ingredients1')

Bootstrap select2 in livewire keeps disappearing when I submit the form

I have a problem with my code the select2 is displaying only once when I refresh the page but it disappears when I submit the form or if there was a validation error it will disappear too. I tried wire:ignore The select2 doesn't disappear but it stops working properly and the form considered it like if it doesn't exist.
I use laravel 8, livewire 2 and turbolinks. Any help would be appreciated I've been stuck for almost 2 weeks.
Here is my code :
Blade:
<form wire:submit.prevent="submit">
{{ csrf_field() }}
<div class="form-group row">
<label for="to" class="col-form-label col-md-1">à :</label>
<div class="col-md-11" >
<select class="form-control mul-select" wire:model.lazy="id_to" name="id_to[]" multiple="multiple">
<option value="test#gmail.com">test#gmail.com</option>
<option value="test5#gmail.com">test5#gmail.com</option>
</select>
<div class="text-danger">
#error('id_to')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
</div>
<div class="form-group row">
<label for="cc" class="col-form-label col-md-1">Cc :</label>
<div class="col-md-11">
<input type="text" class="form-control" wire:model.lazy="cc" name="cc" placeholder="Cc">
<div class="text-danger">
#error('cc')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
</div>
<div class="form-group row">
<label for="sujet" class="col-form-label col-md-1">Sujet :</label>
<div class="col-md-11">
<input type="text" class="form-control" wire:model.lazy="sujet" name="sujet" placeholder="Sujet">
<div class="text-danger">
#error('sujet')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
</div>
<div class="form-group row">
<label for="message" class="col-form-label col-md-1">Message :</label>
<div class="col-md-11">
<textarea class="form-control" name="message" wire:model.lazy="message" rows="8"></textarea>
<div class="text-danger">
#error('message')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
</div>
{{-- <div class="email-editor">
<textarea class="form-control" id="summary-ckeditor" name="summary-ckeditor" wire:model.lazy="message"></textarea>
<div class="text-danger">
#error('message')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div> --}}
<div class="email-action mt-3">
<button class="btn btn-primary" type="submit">Envoyer</button>
<button class="btn btn-warning" wire:click="resetForm">Reset</button>
</div>
</form>
<script type="text/javascript">
document.addEventListener("livewire:load", function (event) {
$(document).ready(function() {
$('.mul-select').select2();
});
});
</script>
Component:
public $id_to, $id_from, $sujet, $cc, $message;
public $rules=[
'id_to' => 'required',
'id_from' => '',
'cc' => '',
'sujet' => 'required|max:30',
'message' => 'required|max:155',
];
public function render()
{
return view('livewire.admin.messages.messages');
}
public function submit()
{
$validateData=$this->validate();
$to_email=User::where('email', $validateData['id_to'])->first();
Mail::send(array(), array(), function ($message) {
$validateData=$this->validate();
$emails=$validateData['id_to'];
foreach($emails as $email)
{
$message->to($email)
->subject($validateData['sujet'])
->from(Auth::user()->email, 'ADMIN')
->setBody($validateData['message']);
}
});
$validateData['id_from']=Auth::user()->id;
$validateData['id_to']= implode(",", $to_email->id);
SendMessage::create($validateData);
session()->flash('success', 'data has been sent successfully');
$this->resetForm();
}
public function resetForm()
{
$this->id_to = '';
$this->cc = '';
$this->sujet = '';
$this->message = '';
}
Here is a simple way to achieve that
<div class="col-md-11">
<div wire:ignore> // this will make sure to ignore DOM changes
<select
class="form-control"
id="mul-select"
name="id_to[]"
multiple="multiple"
>
<option value="test#gmail.com">test#gmail.com</option>
<option value="test5#gmail.com">test5#gmail.com</option>
</select>
</div>
<div class="text-danger">
#error('id_to')
<span>
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
And then outside of your component root tag make sure you push this script to your layout
#push('scripts')
<script>
$(document).ready(function () {
$('#mul-select').select2();
$(document).on('change', '#mul-select', function (e) {
//when ever the value of changes this will update your PHP variable
#this.set('id_to', e.target.value);
});
});
</script>
#endpush
Finally, on your layout file append the pushed script like this just before closing your body tag
#stack('scripts')
</body
Normally for select2 in Livewire I use hydration for element's rerender.
<div class="form-group row" wire:ignore.self> // I'm not sure about this it's necessary
<label for="to" class="col-form-label col-md-1">à :</label>
<div class="col-md-11" >
<select class="form-control mul-select" wire:model.lazy="id_to" name="id_to[]" multiple="multiple">
<option value="test#gmail.com">test#gmail.com</option>
<option value="test5#gmail.com">test5#gmail.com</option>
</select>
<div class="text-danger">
#error('id_to') <span><strong>{{ $message }}</strong></span> #enderror
</div>
</div>
</div>
//......
<script>
$(document).ready(function() {
window.initSelectDrop=()=>{
$('.mul-select').select2({
placeholder: '{{ __('locale.Select') }}',
allowClear: true});
}
initSelectDrop();
$('.mul-select').on('change', function (e) {
livewire.emit('selectedCompanyItem', e.target.value)
});
window.livewire.on('select2',()=>{
initSelectDrop();
});
});
</script>
in component
public function hydrate()
{
$this->emit('select2');
}
protected $listeners = [
'selectedCompanyItem'
];
public function selectedCompanyItem($value)
{
dd($value);
}

'stripeToken' error when attempting to submit payment form

I'm following an outdate django e-commerce course using django and stripe. The course is about 4 years old so a lot has changed with django and stripe, which has allowed me to do a ton of research on both. I'm at the end of the tutorial and have run into an issue while creating the checkout page and more importantly using stripe to 'Create Charge'. I am receiving the following error message:
django.utils.datastructures.MultiValueDictKeyError
django.utils.datastructures.MultiValueDictKeyError: 'stripeToken'
I read in the documentation for Creating Charge which states
token = request.POST['stripeToken'] # Using Flask
is for Flask (I think), however when i remove ['stripeToken'], I receive this error message:
stripe.error.InvalidRequestError stripe.error.InvalidRequestError:
Request req_kikB88HKbEkq9W: Could not find payment information
I need a way to pass Stripe the payment information when the form is submitted. Also, I came across this post but the publisher was receiving error message do to multiple forms
view.py
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
# Create your views here.
#login_required
def checkout(request):
publish_key = settings.STRIPE_PUBLISHABLE_KEY
if request.method == 'POST':
token = request.POST['stripeToken']
stripe.Charge.create(
amount=999,
currency='usd',
description='Example charge',
source=token,
statement_descriptor='Custom descriptor',
)
context = {'publish_key': publish_key}
template = 'checkout.html'
return render(request, template, context)
checkout.html
{% extends 'base.html' %}
{% block content %}
<div class="col-sm-6 offset-md-3">
<h4 class="mb-3">Billing address</h4>
<form id="payment-form" action="" method="post">{% csrf_token %}
<div class="row">
<div class="col-md-6 mb-3">
<label for="firstName">First name</label>
<input type="text" class="form-control" id="firstName" required style="background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHklEQVQ4EaVTO26DQBD1ohQWaS2lg9JybZ+AK7hNwx2oIoVf4UPQ0Lj1FdKktevIpel8AKNUkDcWMxpgSaIEaTVv3sx7uztiTdu2s/98DywOw3Dued4Who/M2aIx5lZV1aEsy0+qiwHELyi+Ytl0PQ69SxAxkWIA4RMRTdNsKE59juMcuZd6xIAFeZ6fGCdJ8kY4y7KAuTRNGd7jyEBXsdOPE3a0QGPsniOnnYMO67LgSQN9T41F2QGrQRRFCwyzoIF2qyBuKKbcOgPXdVeY9rMWgNsjf9ccYesJhk3f5dYT1HX9gR0LLQR30TnjkUEcx2uIuS4RnI+aj6sJR0AM8AaumPaM/rRehyWhXqbFAA9kh3/8/NvHxAYGAsZ/il8IalkCLBfNVAAAAABJRU5ErkJggg=="); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%;" required/>
<div class="invalid-feedback">
Valid first name is required.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="lastName">Last name</label>
<input type="text" class="form-control" id="lastName" required />
<div class="invalid-feedback">
Valid last name is required.
</div>
</div>
</div>
<div class="mb-3">
<label for="email">Email <span class="text-muted"></span></label>
<input type="email" class="form-control" id="email" placeholder="you#example.com" required />
<div class="invalid-feedback">
Please enter a valid email address for shipping updates.
</div>
</div>
<div class="mb-3">
<label for="address">Address</label>
<input type="text" class="form-control" id="address" placeholder="1234 Main St" required />
<div class="invalid-feedback">
Please enter your shipping address.
</div>
</div>
<div class="mb-3">
<label for="address2">Address 2 <span class="text-muted"></span></label>
<input type="text" class="form-control" id="address2" placeholder="Apartment or suite" />
</div>
<div class="row">
<div class="col-md-5 mb-3">
<label for="city">City</label>
<input type="text" class="form-control" id="city" required />
<div class="invalid-feedback">
Zip code required.
</div>
</div>
<div class="col-md-4 mb-3">
<label for="state">State</label>
<select class="custom-select d-block w-100" id="state" required>
<option value="">Choose...</option>
<option>California</option>
</select>
<div class="invalid-feedback">
Please provide a valid state.
</div>
</div>
<div class="col-md-3 mb-3">
<label for="zip">Zip</label>
<input type="text" class="form-control" id="zip" required />
<div class="invalid-feedback">
Zip code required.
</div>
</div>
</div>
<hr class="mb-4">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="same-address">
<label class="custom-control-label" for="same-address">Shipping address is the same as my billing address</label>
</div>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="save-info">
<label class="custom-control-label" for="save-info">Save this information for next time</label>
</div>
<hr class="mb-4">
<h4 class="mb-3">Payment</h4>
<div class="d-block my-3">
<label for="card-element">Credit or debit card</label>
<div id="card-element" class="form-control" style='height: 2.4em; padding-top: .7em;'>
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<hr class="mb-4">
<button class="btn btn-outline-success btn-lg btn-block" type="submit">Continue to checkout</button>
</form>
</div>
{% endblock %}
app.js
// Create a Stripe client.
const stripe = Stripe('{{ publish_key }}');
// Create an instance of Elements.
const elements = stripe.elements();
// Create an instance of the card Element.
const card = elements.create('card', {});
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', ({error}) => {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = '';
}
});
// Create a token or display an error when the form is submitted.
const form = document.getElementById('payment-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const {token, error} = await stripe.createToken(card);
if (error) {
// Inform the customer that there was an error.
const errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
// Send the token to your server.
stripeTokenHandler(token);
}
});
// Submit the form with the token ID.
const stripeTokenHandler = (token) => {
// Insert the token ID into the form so it gets submitted to the server
const form = document.getElementById('payment-form'),
hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}

Django eternicode bootstrap-datepicker doesn't work on phones

In my Django app i'm using bootstrap-datepicker. User chooses a date, then date is passed in Ajax post request to /check/ view, then sub_template is reloaded with retrieved data.
Everything works like a charm on desktop, but it seems like datepicker doesn't work on phones at all. I've tested it on Iphone 6s, android phone and windows phone(all default browsers). When i press date it just doesn't respond in any way.
Here is my main page html:
<div class="container">
<div class="row text-center">
<h2>Availability</h2>
</div>
<hr>
<div class="row text-center hidden-xs ">
</div>
<div class="row row-pad-30 text-center">
<div class="col-lg-1 md-hidden"></div>
<div class="col-lg-4 col-md-12 col-sm-12 col-xs-12">
<h3>1. Choose date</h3>
<form style="color: white;" class="form-date" role="form" action="" method="get">
{% csrf_token %}
<div class="form-group" id="datepicker">
<div></div>
<input data-date-format="YYYY-MM-DD" type="hidden" name="dt_due" id="dt_due" val="">
</div>
</form>
</div>
<div id="refresh-div" class="timedisplay">{% include 'booking/time.html' %}</div>
<div class="col-lg-1 md-hidden"></div>
</div>
</div>
sub page html:
<div class = "col-lg-6">
<h3 class = "h3-hour">2. Choose hour</h3>
<div class="row row-pad-20">
{% for time_list in availability_table %}
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-12">
{% for i in time_list %}
<button type="submit" class="fade-in-quick btn btn-lg btn-block btn-info btn-core" value="{{ forloop.counter0 }}" name="time" id="time">{{ i.0|date:'H:i' }} do {{ i.1|date:'H:i' }}</button>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
main page script:
$('#datepicker div').datepicker({
language:"pl",
startDate: "+1d"
}).on('changeDate', function(event) {
var date = event.format()
console.log(date);
$.ajax({
type:'POST',
url: '/check/',
data: {'date': date, 'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val()},
success: function(data) {
$('#refresh-div').html(data);
}
});
});
Any idea why it doesn't work?
So apparently this div:
<div class="col-lg-4 col-md-12 col-sm-12 col-xs-12"></div>
Was causing problems. When i just deleted all classes after col-lg-4(they are not necessary anyway) everything is working correctly.