how to pass callback function in component? (laravel alpine.js) - laravel

I make a draft implementation for my reusable input component.
The code below obviously throws an error.
Question is how to pass the $event back to register blade to get or log the value of the input?
register.blade.php
<div>
<x-input onChange="(value) => {console.log('value', value)}"></x-input>
<div/>
input.blade.php
#props(['onChange' => 'null'])
<input x-on:change="{{ $onChange($event) }}">

A few things here.
First off, your markup is wrong. You have a the closing slash at the wrong end of the closing div. Should be </div> not <div/>.
Then you're using x-on without x-data. Alpine only picks up components with the x-data attribute.
Finally, events propagate automatically, so you could just listen on the parent instead:
{{-- register.blade.php --}}
<div x-data>
<x-input x-on:change="console.log('value', $event.target.value)" />
</div>
{{-- input.blade.php --}}
<input {{ $attributes }}>

I learned we could just achieve this through Alpine.Js dispatch. I don't need to pass onClick props via Laravel component. I just simply use dispatch to listen the event (x-on).
What I like in this implementation is that,
aside of event information, passing of extra data is easy
you don't have to use Laravel props and assigned unnecessary props in the tag.
register.blade.php
<div>
<x-input x-on:custom-input="console.log('your values =', $event.target.newValue)"
></x-input>
<div/>
input.blade.php
<input x-on:change="$dispatch('custom-input', { newValue: $event.target.value })">
you can pass "key" prop to distinguish each component.

Related

this' attributes don't get detected in x-on:click

This works :
<input
id="chk-products"
name="chk-products"
type="checkbox"
x-on:click="showProducts = document.getElementById('chk-products').checked">
But this doesn't :
<input
id="chk-products"
name="chk-products"
type="checkbox"
x-on:click="showProducts = this.checked">
I was wondering why this isn't available in alpinejs's directives ?
With Alpine.js you don't have to inspect/mutate the DOM manually. It uses the data model: first you define some data, then you bind it to some input elements and let Alpine.js handle the DOM mutations, etc.
<script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
<div x-data="{showProducts: false}">
<input type="checkbox" x-model="showProducts" /> Show products
<div x-show="showProducts">Products are shown.</div>
<div x-show="!showProducts">Products are hidden.</div>
</div>
The this keyword is available inside a component created with Alpine.data() global function.
The x-on:click directive in Alpine.js is designed to execute a JavaScript expression when an element is clicked. In this case, the expression is trying to access the checked state of the checkbox element, which can be done more directly by using the this keyword to access the element that the directive is applied to. Unfortunately, Alpine.js does not support the use of the this keyword in its directives.

Validation not working correctly in Laravel Livewire

I dont know what I am doing wrong. I have some fields with images, I want to do an update to the database only if there is an image. I have tried setting to nullable but I keep on getting error the photo must be of type image.
This is my code in Livewire class:
$this->validate([
'photo1'=>'sometimes|image',
'photo2'=>'nullable|image',
'photo3'=>'nullable|image'
]);
In the blade
<div class="col-md-6">
<label>Front Right</label>
<input type="file" wire:model="photo1" accept="image/*">
<span class="text-danger">#error('photo1'){{ $message }}#enderror</span>
</div>
<div class="col-md-6">
<label>Flont Left</label>
<input type="file" wire:model="photo2" accept="image/*">
<span class="text-danger">#error('photo2'){{ $message }}#enderror</span>
</div>
</div>
Probably it's bit late for it, but I had the same issue and this is the solution:
If you are using updated instead of updating it will work. The problem when using updating is that the property does not have the file assigned, so it's still null during validation.
Let's say your are using updating() and your property name is $logo:
You would need to assign the $value to the property ($logo) before validation, e.g.:
function updating($key,$value) {
$this-logo = $value; // This line you need to add to make it work
$this-validateOnly($key);
}
I do simply use updated() instead.
Have you added enctype="multipart/form-data" in form element?
Add mimes in validation: 'photo2'=>'nullable|image|mimes:jpg,jpeg,png,svg,gif' and then check if it works.

Multiple slots with the same name -Laravel Blade

Assuming I want o make a new Blade Component called multi-step-form. I want the component to be able to have as many as possible forms slots. How do I achieve this.
I tried this: But it didn't work:
<!--resources/views/multi-step-form.blade.php -->
<div>
#foreach($forms as $form)
<div class="form"> {{ $form }} </div>
#endforeach
<x-next-button> <x-prev-button>
</div>
And in the child:
<x-multi-step-form>
<x-slot name="forms" {{ -- or "forms[], none of them work --}}>
<!-- Some form --->
<x-slot name="forms">
<x-slot name="forms" >
<!-- Some form --->
<x-slot name="forms">
<x-slot name="forms">
<!-- Some form --->
<x-slot name="forms">
</x-multi-step-form>
Am I missing something here?
this is not possible using blade, you can do something like this in vuejs but unfortunately for now it is not possible in laravel blade.
my suggestion would be to make a child component for this component instead of a slot so it would be reusable as much as you want.

2 way binding with Vue3 Inline template (using slots) in laravel blade template files

In Vue3, inline-templates were depreciated and now slots are used. Is it possible to have 2-way binding of variables for Vuejs components written in blade templates?
I want to have 2-way binding for Vue components that's written inline with blade templates. Although I know I can pass data like <example-component name="Hello World"> It is a ton of work to add props everywhere.
Vue recommends using slots as a inline-template replacement since it got removed in v3, however, that documentation makes no sense. I've got the components displayed using the code below. It's a dead simple text field + paragraph to display the name.
home.blade.php (Removed unnecessary HTML for brevity)
<div>
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
example-component.vue
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
name: 'hi',
}
}
}
</script>
Unfortunately, this does not work, the name doesn't start as 'hi' and doesn't update when changing the textfield. When I try something like <slot :name=name></slot>. Which I believe would pass the name into the slots section, the component gets rendered for a second before disappearing.
Is having 2-way binding with vue variables in blade templates even possible? Any help is appreciated.
Vue: 3.0.5
Laravel: 8.29.0
Is there a reason you're storing the data in the child component? The reactivity design works by passing props down and emitting events up, even though (unfortunately) the reactivity is not maintained when passing a variable up to the parent component. Seems a little counter intuitive, but I might be missing something in what you're trying to create.
It will, however, work if you put the data into the app instead of the component.
// app
const app = Vue.createApp({
data() {
return {
name: 'hi',
}
}
})
// component
app.component('example-component', {
template: `
<div>
<slot></slot>
</div>`,
})
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.5/dist/vue.global.prod.js"></script>
<div id="app">
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
<!--

Append data to form using through DOM?

Form which uses POST method:
<div>
<form action="{{ route('post.store') }}" method="POST">
<input type="text" name="name">
<input type="text" name="text">
<button type="submit">Submit</button>
</form>
#foreach ($comments as $comment)
{{ $comment->text }}
Reply
#endforeach
</div>
Array of $comments is being returned when the view itself is returned.
Basically, I am intrested if there is a way to append $comment->id to the data that will be sent to server on Submit button click using Laravel Blade or AlpineJS, but without creating any additional functions between <script> tags? I mean, is it possible to do something like this: Reply?
EDIT:
I expressed myself wrongly. I don't want to append new id every time Reply is clicked, but to overwrite corresponding property in data which is going to be sent to server when Submit button is clicked with the $comment->id for which Reply was clicked for.
I assume you are using Laravel Resource Controller
Not sure I totally understand why you want to send all comment ids when submitting the form but when calling the route post.store you should better send the post id
<form action="{{ route('post.store', ['post' => $post]) }}" method="POST">
If you want to get all comment id and have proper model relationships set up in your models you can get all comments for a post in your controller.
To send a specific id in your form create a new <input name="comment_id" value=""> and let that field be populated.

Resources