Open bastien70 opened 10 months ago
Could you create a little small reproducer, it's easier to understand what you are doing / expecting.
But right now i'd say the component has not been re-rendered, so this.component still contain original items ?
This is a reproducer : https://github.com/bastien70/live-component-onUpdated-reproducer
In this simple and unnecessary example, we will display a date in either French or English format, depending on the checkbox.
The update of the component's "date" model will be carried out at postMount but also when the "displayAsFrench" model is updated.
composer install yarn install --force yarn run build symfony serve
Component :
#[AsLiveComponent('date', template: 'components/date.html.twig')]
final class DateComponent
{
use DefaultActionTrait;
use ComponentToolsTrait;
#[LiveProp(writable: true, onUpdated: 'onDisplayAsFrenchUpdated')]
public bool $displayAsFrench = false;
#[LiveProp]
public ?string $date = null;
#[PostMount]
public function postMount(): array
{
$this->reloadDate();
return [];
}
public function onDisplayAsFrenchUpdated($previousValue): void
{
// $this->query already contains a new value
// and its previous value is passed as an argument
$this->reloadDate();
dump($this->date);
$this->dispatchBrowserEvent('refresh:date');
}
public function reloadDate(): void
{
$date = new \DateTime();
if($this->displayAsFrench)
{
$this->date = $date->format('d/m/Y H:i:s');
} else {
$this->date = $date->format('Y-m-d H:i:s');
}
}
}
Stimulus controller :
// assets/controllers/some-custom-controller.js
// ...
import { Controller } from '@hotwired/stimulus';
import { getComponent } from '@symfony/ux-live-component';
export default class extends Controller {
async initialize() {
this.component = await getComponent(this.element);
// Get live component
updateValue(this.component);
this.component.on('render:started', (component) => {
// do something after the component re-renders
});
this.component.on('render:finished', (component) => {
// loadChart(component);
});
window.addEventListener('refresh:date', (event) => {
updateValue(this.component);
});
function updateValue(component) {
let date = component.getData('date')
console.log(date);
document.querySelector('#dateValue').innerHTML = date;
}
}
}
template :
<div{{ attributes.add(stimulus_controller('display-date')) }}>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="displayAsFrench" data-model="displayAsFrench">
<label class="form-check-label" for="displayAsFrench">Display as French</label>
</div>
<b>Value setted from controller :</b>
<div id="dateValue"></div>
</div>
Just go to https://127.0.0.1:8000/ The date is displayed in English format. Click on the “Display as French” checkbox. The onUpdated is triggered, and the code of the onDisplayAsFrenchUpdated method is launched. The date is then updated. The value is dumped, you can find it in the profiler. A dispatchBrowserEvent is fired to update the contents of the HTML, based on the contents of "date", but it retains the old value. (check console)
This is what I have on the browser, and in the console initially. Good values
Now, if I click on the checkbox, the onDisplayAsFrenchUpdated method is triggered. It renews the date, according the "displayAsFrench" new value, dump the value, and and dispatches the browser event which will refresh the HTML with the value.
In the profiler, I can see the (correct) new "date" value :
But, in the browser, I've still the previous value :
If you're looking on the "console" tab, the "console.log(date)" displays the same old value :
In this function :
function updateValue(component) {
let date = component.getData('date')
console.log(date);
document.querySelector('#dateValue').innerHTML = date;
}
However, we have recovered the content of the "date" model from the component, but which does not seem to have been updated here whereas when we dump it from the component, we have the new value.
Hope this all helps you
Yes i understand.
You should start by making it work without the custom controller JS first (with an input text for example)
As you do not have any model in the HTML for the display date, you should follow those instructions if you want to update your model... or it'll keep the first data i think:
Hmm but you're right, as you want to do it .. it probably won't work.
Maybe instead of this event cascade you can use a simple action ?
It was just a (completely stupid) example that makes no sense, to show the "problem". Yes I can do otherwise here, sure, but the goal is to show that there is a problem with the onUpdated.
So for cases where we really need onUpdated (me or someone else), the problem will be present :)
Well you are right. For now what you want to do will not work..
i'd still advise you to use LiveActions when you want to update your model (without persisting it) ... or to persist your data as you seem to require state storage
Thank you for this issue. There has not been a lot of activity here for a while. Has this been resolved?
Friendly reminder that this issue exists. If I don't hear anything I'll close this.
Hey,
I didn't hear anything so I'm going to close it. Feel free to comment if this is still relevant, I can always reopen!
Hello, in my LiveComponent, I've this :
The goal is to simply trigger an event on my stimulus controller as soon as the "query" model is updated.
The "loadItems" method will do all the processing and assign to the "items" model.
The problem is that just before the dispatchBrowserEvent, if I dump the content of the "items" model, I have the new content, but not from my stimulus controller, which keeps the old value
Is this normal behavior? Is there a method to run from the component to tell it to "flush" the new model values so that from the stimulus controller, I can retrieve it?