I have a view made with livewire and it contains two dynamically add sections for spoken languages and the other one for courses and certificates. The language section was working fine until I added the add certificates section. now when the user clicks on the add section for the certificate section to add a new one, one of the added language sections disappears and then will appear again once you add another one. I guess it will appear again once the view re-renders. I have been going back and force moving functions around fromrender
method toupdate/updated/hydrate/dehydrate
but got no luck. at first, I thought it had something to do with the$loop->index
that I use in my view but after changing that I realized it was not it. I'm hitting a dead-end here and can't figure out what's going on here.
I also made a screen record of what is happening so it might help: https://drive.google.com/file/d/1Hq2wTKcPvhs05SKFMICaRoOAzpSyj9Iz/view?usp=sharing
View section:
<!-- language section -->
<div class="card card-profile shadow-sm mt-4">
<div class="px-4 mt-4 mb-4">
<div class="h5 font-weight-bold mb-4">Spoken languages</div>
<div class="heading text-muted mb-4">you only can add 3 languages to your profile.</div>
@foreach ($languages as $lindex => $language)
<div class="card card-body mb-4" wire:key="{{ $lindex }}">
<div class="text-left"><span class="fa fa-trash text-gray language-delete" wire:click="removeLanguage({{ $lindex }}, {{ !empty($language['id']) ? $language['id'] : 0 }})"></span></div>
<div class="row">
<div class="form-group col-md-6">
<label class="" for="languageName">language</label>
<select class="form-control form-control-alternative" name="language-name" {{-- id="languageName" --}} wire:model="languages.{{ $lindex }}.language_name">
<option value="" class="form-control" selected disabled>select language</option>
@foreach ($language_names as $name)
<option value="{{ $name->abbreviation }}" class="form-control">{{ $name->language }}</option>
@endforeach
</select>
</div>
<div class="form-group col-md-6">
<label class="" for="languageProficiency">proficiency level</label>
<select class="form-control form-control-alternative" name="language-proficiency" {{-- id="languageProficiency" --}} wire:model="languages.{{ $lindex }}.language_level">
<option value="" class="form-control" selected disabled>proficiency level</option>
@foreach ($language_levels as $level)
<option value="{{ $level->level }}" class="form-control">{{ $level->name }}</option>
@endforeach
</select>
</div>
</div>
</div>
@endforeach
@error('languages.*.language_level')
<small class="text-warning">{{ $message }}</small>
@enderror
@error('languages.*.language_language')
<small class="text-warning">{{ $message }}</small>
@enderror
@if (count($languages) < 3)
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-outline-secondary btn-round btn-block" wire:click="addLanguage"><span class="btn-inner--icon"><i class="fa fa-plus fa-2x"></i></span></button>
</div>
</div>
@endif
</div>
</div>
<!-- end language section -->
<!-- other certificates section -->
<div class="card card-profile shadow-sm mt-4">
<div class="px-4 mt-4 mb-4">
<div class="h5 font-weight-bold mb-4">Other related certificates</div>
<div class="heading text-muted mb-4">if you have other related certificates in your files you can add then here.</div>
@foreach ($certificates as $cindex => $certificate)
<div class="card card-body mb-4" wire:key="{{ $cindex }}">
<div class="row">
<div class="form-group col-md-6">
<label class="" for="other-certificates-name">certificate name</label>
<input type="text" class="form-control form-control-alternative" placeholder="" name="ptherCertificatesName">
</div>
<div class="form-group col-md-6">
<label class="" for="other-certificates-school-name">School name</label>
<input type="text" class="form-control form-control-alternative" placeholder="" name="otherCertificatesSchoolName">
</div>
<div class="form-group col-md-6">
<label class="" for="other-certificates-verification-link">URL<small>(optional)</small></label>
<input type="text" class="form-control form-control-alternative text-left" placeholder="" name="otherCertificatesVerificationLink">
</div>
<div class="form-group col" dir="ltr">
<label class="" for="other-certificates-grad-date" dir="rtl">finished date</label>
<div class="input-group input-group-alternative">
<div class="input-group-prepend">
<span class="input-group-text"><i class="ni ni-calendar-grid-58"></i></span>
</div>
<input type="text" class="form-control form-control-alternative datePicker" placeholder="" name="otherCertificatesGradDate" value="">
</div>
</div>
</div>
</div>
@endforeach
@if (count($certificates) < 5)
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-outline-secondary btn-round btn-block" wire:click="addCertificate"><span class="btn-inner--icon"><i class="fa fa-plus fa-2x"></i></span></button>
</div>
</div>
@endif
</div>
</div>
<!-- end other certificates section -->
Livewire component controller:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\CityName;
use App\Models\Language;
use App\Models\LanguageLevel;
use App\Models\User;
use App\Models\UserLanguage;
use App\Models\UserProfile;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Auth;
class UserInfo extends Component
{
public $name;
public $email;
public $phone;
public $states = [];
public $selectedstate;
public $cities = [];
public $selectedcity;
public $jobTitle;
public $aboutMe;
public $employer;
public $position;
public $edu_course;
public $edu_school;
public $edu_level;
public $edu_start_date;
public $edu_end_date;
public $employment_looking;
public $employment_hired;
public $twitter;
public $linkedin;
public $github;
public $instagram;
public $website;
public $languages = [];
public $language_names;
public $language_levels;
public $languageToDelete = [];
public $certificates = [];
public $certificate_name;
public $certificate_school;
public $certificate_link;
public $certificate_date;
public $certificateToDelete = [];
public $skillsQuery;
public $skillResults;
public $skills;
public function render()
{
$this->retriveStates();
$this->retriveCities();
$this->retriveLanguages();
return view('livewire.user-info');
}
public function retriveStates()
{
$this->states = CityName::distinct()->get(['state']);
}
public function retriveCities()
{
$this->cities = CityName::where('state', $this->selectedstate)->get(['city']);
}
public function updatedSkillsQuery()
{
$this->skillResults = $this->skills->where('name', 'like', $this->skillsQuery) ?? collect();
}
public function retriveLanguages()
{
$this->language_names = Language::all();
$this->language_levels = LanguageLevel::all();
}
public function addLanguage()
{
if (count($this->languages) <= 3)
{
array_push($this->languages, ['language_name'=>'', 'language_level'=>'', 'id'=>'']);
}
else
{
$this->sweetAlert('error', 'you only can add 3 languages to your profile.');
}
}
public function getUserLanguages()
{
$this->languages = auth()->user()->userLanguages->toArray();
}
public function removeLanguage($languagePosition, $languageId)
{
if (isset($languageId))
{
if ($languageId == 0)
{
array_splice($this->languages, $languagePosition, 1);
}
else
{
array_push($this->languageToDelete, $languageId);
array_splice($this->languages, $languagePosition, 1);
}
}
}
public function addCertificate()
{
if (count($this->certificates) <= 5)
{
array_push($this->certificates, ['name'=>'', 'school'=>'', 'link'=>'', 'date'=>'', 'id'=>'']);
/* dd($this->languages); */
}
else
{
$this->sweetAlert('error', 'you only can add 5 certificates to your profile.');
}
}
public function mount()
{
$this->skills = collect([
['name' => 'vue'],
['name' => 'vue'],
['name' => 'vue'],
['name' => 'laravel'],
['name' => 'laravel'],
['name' => 'laravel'],
]);
$this->skillResults= [];
$this->skillsQuery = '';
/* $this->retriveLanguages();
$this->retriveStates();
$this->retriveCities(); */
$this->getUserLanguages();
$this->name = auth()->user()->name;
$this->email = auth()->user()->email;
$this->phone = auth()->user()->phone;
$this->selectedstate = auth()->user()->userprofile->state;
$this->selectedcity = auth()->user()->userprofile->city;
$this->jobTitle = auth()->user()->userprofile->job_title;
$this->aboutMe = auth()->user()->userprofile->about_me;
$this->employer = auth()->user()->userprofile->employer;
$this->position = auth()->user()->userprofile->position;
$this->edu_course = auth()->user()->userprofile->edu_course;
$this->edu_school = auth()->user()->userprofile->edu_school;
$this->edu_level = auth()->user()->userprofile->edu_level;
$this->edu_start_date = auth()->user()->userprofile->edu_start_date;
$this->edu_end_date = auth()->user()->userprofile->edu_end_date;
$this->employment_looking = auth()->user()->userprofile->employment_looking;
$this->employment_hired = auth()->user()->userprofile->employment_hired;
$this->twitter = auth()->user()->userprofile->twitter;
$this->linkedin = auth()->user()->userprofile->linkedin;
$this->github = auth()->user()->userprofile->github;
$this->instagram = auth()->user()->userprofile->instagram;
$this->website = auth()->user()->userprofile->website;
}
public function rules()
{
return [
'name' => ['required', 'string', 'max:250'],
'email' => [
'required',
'email',
'max:250',
Rule::unique('users')->ignore(auth()->id()),
],
'phone' => ['required', 'digits:11'],
'selectedstate' => 'required',
'selectedcity' => 'required',
'jobTitle' => ['required', 'string', 'max:250'],
'aboutMe' => ['required', 'string', 'max:250'],
'employer' => ['string', 'max:250'],
'position' => ['string', 'max:250'],
'edu_course' => ['nullable', 'string', 'max:250'],
'edu_school' => ['nullable', 'string', 'max:250'],
'edu_level' => ['nullable', 'string', 'max:250'],
'edu_start_date' => ['nullable', 'string'],
'edu_end_date' => ['nullable', 'string'],
'employment_looking' => ['nullable', 'boolean'],
'employment_hired' => ['nullable', 'boolean'],
'twitter' => ['nullable', 'string', 'max:250'],
'linkedin' => ['nullable', 'string', 'max:250'],
'github' => ['nullable', 'string', 'max:250'],
'instagram' => ['nullable', 'string', 'max:250'],
'website' => ['nullable', 'string', 'max:250'],
'languages.*.language_name' => ['required', 'exists:App\Models\Language,abbreviation'],
'languages.*.language_level' => ['required', 'exists:App\Models\LanguageLevel,level'],
];
}
public function submit()
{
$user = Auth::user();
$this->validate();
User::where('id', auth()->id())->update([
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
]);
UserProfile::where('user_id', auth()->id())->update([
'state' => $this->selectedstate,
'city' => $this->selectedcity,
'job_title' => $this->jobTitle,
'about_me' => $this->aboutMe,
'employer' => $this->employer,
'position' => $this->position,
'edu_course' => $this->edu_course,
'edu_school' => $this->edu_school,
'edu_level' => $this->edu_level,
'edu_start_date' => $this->edu_start_date,
'edu_end_date' => $this->edu_end_date,
'employment_looking' => $this->employment_looking,
'employment_hired' => $this->employment_hired,
'twitter' => $this->twitter,
'linkedin' => $this->linkedin,
'github' => $this->github,
'instagram' => $this->instagram,
'website' => $this->website,
]);
if (!empty($this->languageToDelete))
{
/* $user = Auth::user(); */
foreach ($this->languageToDelete as $delete)
{
$user->userLanguages()->where('id', $delete)->delete();
}
}
foreach ($this->languages as $language)
{
/* $user = Auth::user(); */
$user->userLanguages()->updateOrCreate([
'language_name' => $language['language_name'],
],
[
'language_name' => $language['language_name'],
'language_level' => $language['language_level']
]
);
}
$this->getUserLanguages();
$this->sweetAlert('success', 'changes saved!');
}
public function sweetAlert($type, $message)
{
$this->alert($type, $message, [
'position' => 'bottom-end',
'timer' => 5000,
'toast' => true,
'text' => null,
'showCancelButton' => false,
'showConfirmButton' => false
]);
}
}
The issue has been solved. the problem indeed was a DOM issue caused by multiple loop index. as what I thought at first the$loop->index
was causing it and I was on the right track to change it to something else like$languages as $lindex => $language
but apparently even by doing that those two sections will still have the samewire:key
because of the loop iterations which returns0,1,2
and so on in order. so by appending a string to the key likelang{{ $loop->index }}
they will be unique and therefore problem solved.
Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.
Find the answer in similar questions on our website.
Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.
PHP (from the English Hypertext Preprocessor - hypertext preprocessor) is a scripting programming language for developing web applications. Supported by most hosting providers, it is one of the most popular tools for creating dynamic websites.
The PHP scripting language has gained wide popularity due to its processing speed, simplicity, cross-platform, functionality and distribution of source codes under its own license.
https://www.php.net/
Laravel is a free open source PHP framework that came out in 2011. Since then, it has been able to become the framework of choice for web developers. One of the main reasons for this is that Laravel makes it easier, faster, and safer to develop complex web applications than any other framework.
https://laravel.com/
Welcome to the Q&A site for web developers. Here you can ask a question about the problem you are facing and get answers from other experts. We have created a user-friendly interface so that you can quickly and free of charge ask a question about a web programming problem. We also invite other experts to join our community and help other members who ask questions. In addition, you can use our search for questions with a solution.
Ask about the real problem you are facing. Describe in detail what you are doing and what you want to achieve.
Our goal is to create a strong community in which everyone will support each other. If you find a question and know the answer to it, help others with your knowledge.