Accessing Repeater field from JS code in view

Hi fellows, I have a basic form like this:
ViewField::make('qr-codes')
->view('filament.forms.components.qr-code-reader'),
Repeater::make('payload')
->defaultItems(0)
->label('Notificaciones')
->columns(6)
->schema([
TextInput::make('sent_at')->label('Enviado el'),
TextInput::make('sent_by')->label('Enviado por'),
TextInput::make('received_by')->label('Recibe'),
TextInput::make('received_at')->label('Recibido el'),
TextInput::make('receiver_phone')->label('Telefono receptor'),
]),
ViewField::make('qr-codes')
->view('filament.forms.components.qr-code-reader'),
Repeater::make('payload')
->defaultItems(0)
->label('Notificaciones')
->columns(6)
->schema([
TextInput::make('sent_at')->label('Enviado el'),
TextInput::make('sent_by')->label('Enviado por'),
TextInput::make('received_by')->label('Recibe'),
TextInput::make('received_at')->label('Recibido el'),
TextInput::make('receiver_phone')->label('Telefono receptor'),
]),
This render QRCode scanner preview window. Here I use https://unpkg.com/[email protected]/html5-qrcode.min.js libary and it works liek expected. Problem I having is could not find a way from this view to access the repeater and add scanned code.
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
const dec = decodedText.split(';');
document.getElementById("{{ $getId() }}").value = dec[9];
//alert('Scanned ' + decodedText);
}
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
const dec = decodedText.split(';');
document.getElementById("{{ $getId() }}").value = dec[9];
//alert('Scanned ' + decodedText);
}
using this call to getElementById I have access to temporary input I'm using for debug, but how can I access repeater and add elements from here? Or mayne I could try other approach? Tanks in advance for your answers
No description
Solution:
Hi, I think finally get it working. Added wire:ignore and now qr-scanner is not dissapearing after $wire.dispatchFormEvent('addItem', $data); ``` <div class="mt-6" name="{{ $getName() }}" id="{{ $getId() }}" wire:ignore...
Jump to solution
10 Replies
LeandroFerreira
LeandroFerreira2mo ago
I think you can work with alpine in your custom view field:
<div x-data="{
onScanSuccess() {

//your code..

const payload = {
sent_at: '2025-01-05 00:00:00',
};
$wire.dispatchFormEvent('addPayloadItem', payload);
},
}">
<button type="button" @click="onScanSuccess">Scan Success</button>
</div>
<div x-data="{
onScanSuccess() {

//your code..

const payload = {
sent_at: '2025-01-05 00:00:00',
};
$wire.dispatchFormEvent('addPayloadItem', payload);
},
}">
<button type="button" @click="onScanSuccess">Scan Success</button>
</div>
Register a listener in your repeater to add a new item to the repeater state:
ViewField::make('qrcodes')
->view('filament.forms.components.qr-code-reader'),
Repeater::make('payload')
->registerListeners([
'addPayloadItem' => [
function (Repeater $component, array $payload) {
$state = $component->getState();
$state[(string) Str::uuid()] = $payload;
$component->state($state);
},
],
])
->schema([
TextInput::make('sent_at'),
])
ViewField::make('qrcodes')
->view('filament.forms.components.qr-code-reader'),
Repeater::make('payload')
->registerListeners([
'addPayloadItem' => [
function (Repeater $component, array $payload) {
$state = $component->getState();
$state[(string) Str::uuid()] = $payload;
$component->state($state);
},
],
])
->schema([
TextInput::make('sent_at'),
])
sistemasjg
sistemasjgOP2mo ago
Hi @LeandroFerreira thanks a lot for the tip. Have it working partially. Had to adapt like this:
div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="grid gap-y-2">
<div
id = "rpt"
x-data="{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}">
<x-filament::input.wrapper class="relative">
<div id="qr-reader" style="width:350px;height:350px;"></div>
<x-filament::input
type="text"
name="{{ $getName() }}"
id="{{ $getId() }}"
value="{{ $getState() }}"

class="w-full pr-10"
/>

</x-filament::input.wrapper>
</div>
</div>
</div>`

....

docReady(function () {
function onScanSuccess(decodedText, decodedResult) {
var rpt = Alpine.$data(document.getElementById('rpt'));
rpt.onScanSuccess(decodedText);
}

function createQRReader() {
var html5QrcodeScanner = new Html5QrcodeScanner("qr-reader", { fps: 30, qrbox: 220,});
html5QrcodeScanner.render(onScanSuccess);
}

createQRReader();
});
div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="grid gap-y-2">
<div
id = "rpt"
x-data="{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}">
<x-filament::input.wrapper class="relative">
<div id="qr-reader" style="width:350px;height:350px;"></div>
<x-filament::input
type="text"
name="{{ $getName() }}"
id="{{ $getId() }}"
value="{{ $getState() }}"

class="w-full pr-10"
/>

</x-filament::input.wrapper>
</div>
</div>
</div>`

....

docReady(function () {
function onScanSuccess(decodedText, decodedResult) {
var rpt = Alpine.$data(document.getElementById('rpt'));
rpt.onScanSuccess(decodedText);
}

function createQRReader() {
var html5QrcodeScanner = new Html5QrcodeScanner("qr-reader", { fps: 30, qrbox: 220,});
html5QrcodeScanner.render(onScanSuccess);
}

createQRReader();
});
This way I can handle Repeater json/array handling on php side. But having another don't know if related problem. After succefull scan, $wire.dispatchFormEvent('addItem', $data); is called and I can aff item, no problem. But QR code reader just disappears, js code added by the lib is just cleared. If I comment the call to $wire.dispatchFormEvent('addItem', $data); scanner continues showinf without any problem. But this only happends with this custom qr field, If I add some text inputs or any other input it still present after calling $wire.dispatchFormEvent('addItem', $data);. Thanks a lot for your help
No description
No description
sistemasjg
sistemasjgOP2mo ago
Hi, update for this issue. I've trying differents approachs, but everything fails. I just changed repeater control for a simple texarea using registerListeners mechanism to add scaned line to texarea. Behaviour is the same, after first scan, line is added to textarea but qr-code field just dissapears. Is like it is reloading form/section and js component just dissapears. Any workaround could try? Thanks in advance
awcodes
awcodes2mo ago
I’m not 💯 sure on this but it sounds like a livewire dom diffing issuing. Ie, the state is either handled in the js or the php, but it feels like the state is getting out of sync by not ignoring and setting it via one or the other. If that makes sense.
sistemasjg
sistemasjgOP2mo ago
Yep, I think something like that as well. DOM is modified on JS side when creating the qr-view and maybe this modification is not tracked by livewire internals, so when updating it just set DOM like it was before plus modifications made. is ther any workaround? Maybe soe kind of notification to Livewire fro the JS side about the DOM was changed?
awcodes
awcodes2mo ago
I would need to see all of the code to offer more help. Sorry.
sistemasjg
sistemasjgOP2mo ago
Cool, thanks a lot for you help. Code is simple. On field view I just have this:
<script src="https://unpkg.com/[email protected]/html5-qrcode.min.js" type="text/javascript"></script>

<div class="mt-6"
name="{{ $getName() }}"
id="{{ $getId() }}"
x-init="
html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 220,});
html5QrcodeScanner.render(onScanSuccess);
"
x-data="{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}"
wire:key="item-{{ $getId() }}">

<div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="grid gap-y-2">
<div wire:key="item-{{ $getId() }}-xxxxx" id="qr-reader" style="width:95%;height:95%;"></div>

</div>
</div>
</div>
<script src="https://unpkg.com/[email protected]/html5-qrcode.min.js" type="text/javascript"></script>

<div class="mt-6"
name="{{ $getName() }}"
id="{{ $getId() }}"
x-init="
html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 220,});
html5QrcodeScanner.render(onScanSuccess);
"
x-data="{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}"
wire:key="item-{{ $getId() }}">

<div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="grid gap-y-2">
<div wire:key="item-{{ $getId() }}-xxxxx" id="qr-reader" style="width:95%;height:95%;"></div>

</div>
</div>
</div>
I was just testing if that wire:key attr could do the trick, but no. Form definition like:
Section::make('Scanner Códigos QR')
->schema([
ViewField::make('qr-codes')
->dehydrated(false)
->view('filament.forms.components.qr-code-reader'),
]),

Grid::make(columns: 1)
->schema([
Select::make('template_id')
->label('Plantilla')
->relationship('template', 'name')
->required()
->columnSpan(1),
Textarea::make('items')
->label('Items')
->registerListeners([
'addItem' => [
function (Textarea $component, string $code) {
self::addItem($component, $code);
return;
},
]]),
Section::make('Scanner Códigos QR')
->schema([
ViewField::make('qr-codes')
->dehydrated(false)
->view('filament.forms.components.qr-code-reader'),
]),

Grid::make(columns: 1)
->schema([
Select::make('template_id')
->label('Plantilla')
->relationship('template', 'name')
->required()
->columnSpan(1),
Textarea::make('items')
->label('Items')
->registerListeners([
'addItem' => [
function (Textarea $component, string $code) {
self::addItem($component, $code);
return;
},
]]),
And addItem function is just:
private static function addItem(Textarea $component, string $code)
{

$arr = explode(';', $code);

if (count($arr) == 0) {
Notification::make()
->title('Valor invalido ')
->body($code)
->warning()
->send();

return;
}

$state = $component->getState();

$data = [];
$data['id'] = $arr[0];
$data['ref'] = $arr[1];
$data['sent_at'] = $arr[2];
$data['sent_by'] = $arr[3];
$data['received_by'] = $arr[8];
$data['receiver_phone'] = $arr[9];

$data['item'] = $data;

// $state[(string) Str::uuid()] = $data;

$state = $state . "|" . $data['id'];

$component->state($state);
}
private static function addItem(Textarea $component, string $code)
{

$arr = explode(';', $code);

if (count($arr) == 0) {
Notification::make()
->title('Valor invalido ')
->body($code)
->warning()
->send();

return;
}

$state = $component->getState();

$data = [];
$data['id'] = $arr[0];
$data['ref'] = $arr[1];
$data['sent_at'] = $arr[2];
$data['sent_by'] = $arr[3];
$data['received_by'] = $arr[8];
$data['receiver_phone'] = $arr[9];

$data['item'] = $data;

// $state[(string) Str::uuid()] = $data;

$state = $state . "|" . $data['id'];

$component->state($state);
}
` Hope you can have a general overview now, will be here in case you need something else from my side. Really appreciate it
awcodes
awcodes2mo ago
I think the problem maybe your script tag include. That will only initialize on page load. But Lw and alpine work with Ajax requests so the include will never re-init on subsequent requests. What is the code of your view?
sistemasjg
sistemasjgOP2mo ago
Just this, nothing more
Solution
sistemasjg
sistemasjg2mo ago
Hi, I think finally get it working. Added wire:ignore and now qr-scanner is not dissapearing after $wire.dispatchFormEvent('addItem', $data);
<div class="mt-6" name="{{ $getName() }}" id="{{ $getId() }}"
wire:ignore
x-init= "
html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 220, });
html5QrcodeScanner.render(onScanSuccess);
"
x-data="
{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}"
>

<div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="md:container md:mx-auto">
<div class="columns-3">
<div id="qr-reader"></div>
</div>
</div>
</div>
</div>

<script src="https://unpkg.com/[email protected]/html5-qrcode.min.js" type="text/javascript"></script>
<div class="mt-6" name="{{ $getName() }}" id="{{ $getId() }}"
wire:ignore
x-init= "
html5QrcodeScanner = new Html5QrcodeScanner('qr-reader', { fps: 10, qrbox: 220, });
html5QrcodeScanner.render(onScanSuccess);
"
x-data="
{
onScanSuccess($data) {
$wire.dispatchFormEvent('addItem', $data);
},
}"
>

<div xmlns:x-filament="http://www.w3.org/1999/html">
<div class="md:container md:mx-auto">
<div class="columns-3">
<div id="qr-reader"></div>
</div>
</div>
</div>
</div>

<script src="https://unpkg.com/[email protected]/html5-qrcode.min.js" type="text/javascript"></script>

Did you find this page helpful?