After choosing the file, this error will be displayed in the console :
Blade file :
<form wire:submit.prevent="Insert" enctype="multipart/form-data">
<input type="file" wire:model.lazy="img" id="img" name="img"/>
#if ($img)
<img src="{{ $img->temporaryUrl() }}">
#endif
<div wire:loading wire:target="img">در حال آپلود ... </div>
<button type="submit"> Submit </button>
</form>
class livewire $img is model :
class Article extends Component
{
use WithFileUploads;
public $img;
public function Insert()
{
dd($this->img);
}
public function render()
{
return view('livewire.article');
}
}
My problem was solved by changing the value of upload_tmp_dir in php.ini
Related
I'm trying to create a way to dynamically load Livewire nested components by having a main conntainer component like this:
ComponentContainer.php
class ComponentContainer extends Component
{
public $component = 'active';
protected $listeners = [
'switch'
];
public function switch(string $component)
{
$this->component = $component;
}
public function render()
{
return view('livewire.component-container', [
'component' => $this->component,
'key' => random_int(PHP_INT_MIN, PHP_INT_MAX),
]);
}
}
component-container.blade.php
<div>
<h1>Component Container</h1>
#livewire($component, key($key))
<button wire:click="$emit('switch', 'active')">
{{ __('Active') }}
</button>
<button wire:click="$emit('switch', 'expired')">
{{ __('Expired') }}
</button>
</div>
It's working insomuch as when I click "active" or "expired" it's loading one of these components:
Active.php
class Active extends Component
{
public function render()
{
return view('livewire.active');
}
}
active.blade.php
<div>
active
</div>
Expired.php
class Expired extends Component
{
public function render()
{
return view('livewire.expired');
}
}
expired.blade.php
<div>
expired
</div>
But it's giving the following error in the console log, and I don't understand why:
Livewire: Multiple root elements detected. This is not supported. See docs for more information https://laravel-livewire.com/docs/2.x/troubleshooting#root-element-issues <div wire:id="jdHgW7KzB3wWUCl7vMI4">expired</div>
Doesn't seem to make sense as it's clearly just one root element. Interestingly it doesn't do it on the first load, regardless of whether I set $component to active or expired
The error is being caused by the final HTML comment (wire:end) being missing. For some reason, this only happens if the buttons are placed after the dynamic component call....
This displays the "multiple root elements detected" error
<div>
<h1>Component Container</h1>
#livewire($component, key($key))
<button wire:click="$emit('switch', 'active')">
{{ __('Active') }}
</button>
<button wire:click="$emit('switch', 'expired')">
{{ __('Expired') }}
</button>
</div>
But this works fine...
<div>
<h1>Component Container</h1>
<button wire:click="$emit('switch', 'active')">
{{ __('Active') }}
</button>
<button wire:click="$emit('switch', 'expired')">
{{ __('Expired') }}
</button>
#livewire($component, key($key))
</div>
I'm not sure if this is expected behaviour or if it's a bug, but this fixes the issue described.
The laravel custom exception doesn't work in the livewire component, but I don't know why.
This is the livewire blade:
<div>
<form method="POST" wire:submit.prevent="submit">
#csrf
<div x-data="{ id: #entangle('id') }">
<input id="id" type="text" wire:model="id" x-mask="*">
</input>
</div>
#isset($email)
your email is {{$email}}
#endisset
<button type="submit">submit</button>
</form>
</div>
This is the livewire component
class Form extends Component
{
public $id = '';
public $email;
public function submit()
{
$user = User::find($this->id);
if(!empty($user)){
return $this->email=$user->email;
}
throw new UserNotFoundException;
}
}
This is the UserNotFoundException
class UserNotFoundException extends Exception
{
public function render($request)
{
return response()->view('user-not-found');
}
}
After I submitted a wrong id, the browser didn't change to the 'user-not-found' view.
What do I miss? Any suggestion? Thank you!
with livewire 1.3 / alpinejs 2.x.x I make listing of data with filter text
and selection inputs and clicking on “Search” button I need to run search when
“Search” button is clicked.
But search is run when text/selection input lose focus without clicking on “Search” button.
I see it by form's wire:loading block and 1 line in log file, which I trigger in render method
That is not what I need : I need to run render method only clicking on “Search” button.
I tried to use alpinejs, but failed...
In component app/Http/Livewire/Admin/AppNews.php I have:
<?php
namespace App\Http\Livewire\Admin;
use Carbon\Carbon;
use Livewire\Component;
use App\News;
use Livewire\WithPagination;
use Livewire\WithFileUploads;
class AppNews extends Component
{
use WithPagination;
use WithFileUploads;
public $filters = [
'title' => '',
'published' => '',
];
];
public $uploaded_file_name;
public $uploadedImageFilenameData;
public $uploadedNewsImage;
protected $listeners = ['fileUpload' => 'handleFileUpload'];
public $current_news_id;
public $updateMode = 'browse';
public function render()
{
\Log::info('-1 NewsRENDER $this->filters ::' . print_r($this->filters, true));
$news_rows_count = News
::getByTitle($this->filters['title'], true)
->getByPublished($this->filters['published'], true)
->count();
$backend_per_page = Settings::getValue('backend_per_page', CheckValueType::cvtInteger, 20);
$this->emit('app_news_opened', ['mode' => 'browse', 'id' => null]);
$newsPublishedValueArray = SetArrayHeader([['key' => '', 'label' => ' -Select published- ']], News::getNewsPublishedValueArray(true));
$newsDataRows = News
::getByTitle($this->filters['title'], true)
->getByPublished($this->filters['published'], true)
->with('creator')
->orderBy('news.created_at', 'desc')
->paginate($backend_per_page);
$newsDataIds = [];
foreach ($newsDataRows as $nextNews) {
$newsDataIds[] = $nextNews->id;
}
return view('livewire.admin.app-news.container', [
'newsDataRows' => $newsDataRows,
'newsDataIds' => $newsDataIds,
'form' => $this->form,
'news_rows_count' => $news_rows_count,
'newsPublishedValueArray' => $newsPublishedValueArray,
]);
}
and in the template resources/views/livewire/admin/app-news/container.blade.php :
<article class="admin_page_container">
<div class="card form-admin-news">
<div class="card-body card-block">
<div class="spinner-border" role="status" wire:loading>
<span class="sr-only">Loading...</span>
</div>
...
<fieldset class="bordered text-muted p-2 m-2" x-data="{ title: '{{$filters['title']}}', published: '{{$filters['published']}}' ,
publishedItems: <?php print str_replace('"',"'",json_encode( $newsPublishedValueArray) ) ?> } ">
<div> $filters::{{ var_dump($filters) }}</div>
title::<div x-html="title"></div>
published::<div x-html="published"></div>
<legend class="bordered">Filters</legend>
<dl class="block_2columns_md m-0 p-2">
<dt class="key_values_rows_label_13">
<label class="col-form-label" for="temp_filter_title">
By title
</label>
</dt>
<dd class="key_values_rows_value_13" >
<div class="content_with_right_button">
<div
class="content_with_right_button_left_content"
wire:model.lazy="filters.title"
>
<input
class="form-control admin_control_input"
type="text"
x-model="title"
x-on:blur="$dispatch('input', title)"
id="title"
>
</div>
<div class="content_with_right_button_right_button pl-2">
<button class="btn btn-outline-secondary nowrap_block" wire:click="makeSearch( )">
{!! $viewFuncs->showAppIcon('filter') !!}Search
</button>
</div>
</div>
</dd>
</dl>
<dl class="block_2columns_md m-0 p-2">
<dt class="key_values_rows_label_13">
<label class="col-form-label" for="temp_filter_published">
By published
</label>
</dt>
<dd
class="key_values_rows_value_13"
wire:model.lazy="filters.published"
>
<select
x-model="published"
x-on:blur="$dispatch('select', published)"
id="published"
class="form-control editable_field admin_control_input"
>
<template x-for="nextPublishedItem in publishedItems">
<option :value="nextPublishedItem.key" x-text="nextPublishedItem.label"></option>
</template>
</select>
#error('form.published')
<div class="validation_error">{{ clearValidationError($message,['form.'=>'']) }}</div> #enderror
</dd>
</dl>
</fieldset> <!-- Filters -->
...
#endif {{-- #if($updateMode=='browse')--}}
#endif {{-- #if($updateMode=='browse')--}}
#if(count($newsDataRows) > 0)
<div class="table-responsive table-wrapper-for-data-listing" x-data="selectedNewsIdsBoxXData()">
<table>
...
</table>
</div> <!-- <div class="table-responsive table-wrapper-for-data-listing"> -->
#endif {{-- #if(count($newsDataRows) > 0) --}}
#endif {{-- #if($updateMode=='browse') --}}
</div> <!-- <div class="card-body card-block"> -->
</div> <!-- <div class="card"> -->
</article> <!-- page_content_container -->
Which is the valid way?
Modified BLOCK 2:
I try another way with using of alpinejs2 : I try to use it in this case, as when public var of component is changed:
with dispatch methid when button “Search” is clicked
<div class="card form-admin-facilities" x-data="adminFacilitiesComponent()">
...
filter_name: {{$filter_name}}<br>
...
temp_filter_name: <span x-html="temp_filter_name"></span><br>
...
<fieldset class="bordered text-muted p-2 m-2">
<legend class="bordered">Filters</legend>
<div class="content_with_right_button" wire:model.lazy="filter_name">
<div class="content_with_right_button_left_content" >
<input
class="form-control admin_filter_input"
x-model="temp_filter_name"
type="text"
>
</div>
<div class="content_with_right_button_right_button pl-2" >
<button class="btn btn-outline-secondary" #click="$dispatch('input', temp_filter_name)" type="button">Search
</button>
<!-- In more complicated form can be several filter fields : text and select inputs -->
</div>
</div>
</fieldset> <!-- Filters -->
...
<script>
function adminFacilitiesComponent() {
return {
temp_filter_name:'',
and in the component I defined public $filter_name var, which is used in render method :
class Facilities extends Component
{
public $form= [
'name'=>'',
'descr'=> '',
'created_at'=> '',
'is_reopen' => false,
];
public $current_facility_id;
public $filter_name= '';
public $updateMode = 'browse';
public function render()
{
\Log::info( '-1 render Facilities $this->filter_name ::' . print_r( $this->filter_name, true ) );
$this->facility_rows_count = Facility
::getByName($this->filter_name, true)
->count();
$backend_per_page = Settings::getValue('backend_per_page', CheckValueType::cvtInteger, 20);
return view('livewire.admin.facilities.container', [
'facilityDataRows' => Facility
::orderBy('created_at', 'desc')
->getByName($this->filter_name, true)
->paginate($backend_per_page),
'facility_rows_count'=> $this->facility_rows_count
]);
}
But it does not work as I expect : entering value in text input when this input lose focus
form is rendered again. I expected form to be rendered only when I click on “Search” button
and form will be rendered with new entered value. I do not use blur event for text input and
do not understand why the form is rendered when this input lose focus?
Modified BLOCK 3:
Using x-ref= I do :
<div class="content_with_right_button" wire:model.lazy="filter_name">
<div class="content_with_right_button_left_content" >
<input
class="form-control admin_filter_input"
x-model="temp_filter_name"
x-ref="searched"
type="text"
> 1111111
</div>
<div class="content_with_right_button_right_button pl-2" >
<button class="btn btn-outline-secondary nowrap_block" wire:click="makeSearch(this.$refs.searched)" type="button">
Search
</button>
</div>
</div>
But I got error clicking on search button:
VM1983:6 Uncaught TypeError: Cannot read property 'searched' of undefined
at eval (eval at parseOutMethodAndParams (directive.js:55), <anonymous>:6:27)
at _default.parseOutMethodAndParams (directive.js:55)
Looks like it is impossible to use this.$refs. value in
wire:click="makeSearch .
I need to trigger component method
public function makeSearch($search_value='')
{
and send entered value into it.
Looks like the way I tried was invalid.
If there is a valid way?
Thanks!
In your modified block 2, you should use wire:ignore in base div of your AlpineJS component. This will make livewire ignore the component.
<div class="card form-admin-facilities" wire:ignore x-data="adminFacilitiesComponent()">
Your $dispatch() should handle setting the value when you click the button.
In order to make livewire ignore the component just add wire:ignore to your component's div in modified block 2 and then in your dispatch method you can write the logic that happens after clicking the button.
Ok so I need some help. I am trying to have the address field from my CreateView form auto populate with the Places API result. So I see 2 options:
Override the address field in the CreateView. Use the address input for Places API as the input into the address field for my model.
Autocomplete the address field in the form, from the output of the address input (Places API lookup).
Any suggestions would be greatly appreciated. I have tried multiple different options and cant seem to get it to actually work correctly.
Thanks.
autocomple.html
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search predictions to
// geographical location types.
autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autocomplete'), {types: ['geocode']});
// Avoid paying for data that you don't need by restricting the set of
// place fields that are returned to just the address components.
autocomplete.setFields(['address_component']);
// When the user selects an address from the drop-down, populate the
// address fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
console.log(place)
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details,
// and then fill-in the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle(
{center: geolocation, radius: position.coords.accuracy});
autocomplete.setBounds(circle.getBounds());
});
}
}
views.py
class PropertyCreateView(CreateView):
model = Property
fields = ['address', 'postal_code', 'price',
'details', 'sales_status', 'property_type']
def form_valid(self, form):
return super().form_valid(form)
detail.html
{% load crispy_forms_tags %}
{% block content%}
<div class="container">
<div class="card o-hidden border-0 shadow-lg my-2">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-12">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">New Property</h1>
</div>
<form method="POST">
{% csrf_token %}
<div class="form-group">
<label>Address: </label>
<div id="locationField">
<input id="autocomplete"
placeholder="Enter your address"
onFocus="geolocate()"
type="text"
name="full_address"
class="form-control"
/>
</div>
</div>
<fieldset class="form-group">
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-primary btn-user btn-block"" type="submit">Add Property</button>
</div>
</form>
<hr>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'properties_app/autocomplete.html' %}
I have this form which contains a button with some dynamic value, when I click on it it should add the product to the checkout page.
Here is the html form :
{%for p in product %}
<div class="single-product">
<div class="product-f-image">
<img src="data:image/png;base64,{{p.image_medium}}" alt="">
<div class="">
{% if not user.is_authenticated %}
<form action="/login/">
<button class="add-to-cart-link" type="submit"> Add to cart</button>
</form>
{%else%}
<form id="form-id" action="" method="POST">
{% csrf_token %}
<button class="add-to-cart-link" type="submit" name="product" value="{{p.id}}" >
<input type="hidden" name="product_name" value="{{p.name}}">
<input type="hidden" name="product_price" value="{{p.lst_price}}">
Add to cart</button>
</form>
{%endif%}
<i class="fa fa-link"></i> See details
</div>
</div>
<h2>{{p.id}}</h2>
<div class="product-carousel-price">
<ins>{{p.lst_price}} €</ins>
</div>
</div>
{%endfor%}
And here is my what I am doing with selenium:
bon_commande = self.selenium.find_element_by_xpath("//button[#name='product' and #value='37']/parent::form")
bon_commande.submit()
And thanks for the help !
You don't need to locate Submit button to submit a form - use any element inside form or form element itself:
self.selenium.find_element_by_id("form-id").submit()
self.selenium.find_element_by_class_name("add-to-cart-link").submit()
Update
Try to wait until django variable "{{p.id}}" is replaced with generated value:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(self.selenium, 10).until(EC.presence_of_element_located((By.XPATH, "//button[#name='product' and #value='37']"))).submit()
Change to click the submit button:
// add some sleep to wait the JS files of page
// load completely to register click event to the submit button
// otherwise nothing to response to the click
// (because the `action` of the form is empty.)
self.selenium.sleep(15); // sleep 15 seconds
self.selenium.find_element_by_xpath("//button[#name='product' and #value='37']").click()
To click on the button with text as Add to cart you can use the following line of code :
self.selenium.find_element_by_xpath("//form[#id='form-id']/button[#class='add-to-cart-link' and #name='product']").submit()
#or
self.selenium.find_element_by_xpath("//form[#id='form-id']/button[#class='add-to-cart-link' and #name='product']").click()