F
Filament9mo ago
SirFat

Rendering Custom Output in a Repeater Item

I'm using a Repeater with a custom action which opens a modal dialog. This process collections a large amount of information, some to be displayed, some not. It's unlikely going to match the object it will be saved into, so I will be manipulating it. I intend to show a structured rendered view of some of the output. The data is available in an array in the action method: https://gist.github.com/martinedge/6331a0404c49ed7b2989151ca0e15bbe At the moment I'm using StateUpdated to move the 'processed_message' to info_message' which is a 'PlaceHolder'.
->afterStateUpdated(function (Get $get, Set $set) {
if (!empty($get("processed_message"))) {
$set("info_message", $get("processed_message"));
}
self::updateTotals($get, $set);
})
->afterStateUpdated(function (Get $get, Set $set) {
if (!empty($get("processed_message"))) {
$set("info_message", $get("processed_message"));
}
self::updateTotals($get, $set);
})
The attached picture gives you an idea of what I'm looking to achieve. Not sure if a blade can be injected or what the best approach is. Cheers!
Gist
Action Block
Action Block. GitHub Gist: instantly share code, notes, and snippets.
No description
61 Replies
SirFat
SirFat9mo ago
But yes, best preference is to be able to inject a fresh blade or refresh the rendering of a blade that represents the repeater item during an OnChangeState javascript call. Do we know if this is possible? I also tried using new HtmlString() to see if it would bring it in without converting the HTML to text, but alas no luck (Bump) I will also post this on laravel daily to see if I have any luck, happy to chase the answer myself if someone even has an inkling on how to address this I'm going to try and create a custom component or field and see if that will give me a bit more control
awcodes
awcodes9mo ago
Maybe you could do it with a View or ViewField.
SirFat
SirFat9mo ago
Yeah, that's what I'm looking at at the moment. I'll have to follow those instructions more carefully, because getContent() is expecting a string, which is created by using evaluate on a Closure, which implicitly calls htmlspecialchars() somewhere View field would be better because it has ViewData components, which means I could properly layout the blade outcome as it needs to be it's just a matter of making sure that the State changed attempt to update the ViewData (rather than just hitting the field with a single value) updates each of those individual items It will be nice to nail this one this morning, as my list of "i have no idea how"'s is getting smaller, and this is a biggun!
awcodes
awcodes9mo ago
Just so I understand correctly are you trying to render the custom view inside each repeater item or does the repeater itself determine the view of a single element.
SirFat
SirFat9mo ago
the first one I'm trying to render the contents of an item all variables presently being populated are in there as hidden fields, but I could also populate the payload of the custom field just not sure how you go about that with Set because $set("myField", "blah"); obviously wouldn't address the individual ViewData components so for example, if I did ViewField::make('myField')->view('theview')->viewData(['phone' => 'myphone', 'access_fee' => 90.00]); how would the $set be constructed to set each portion or is it the same way
awcodes
awcodes9mo ago
Yea. I’m seeing similar issues where repeater actions don’t have a context to what repeater item they are in.
SirFat
SirFat9mo ago
$set("myField", ["phone" => 'myphone']); oh, I figured that bit out that's fine For that I've just created 'myCustomRepeater' which extends 'Repeater' and created an 'editAction' which essentially follows the same behaviour as the deleteAction so if you look at https://internal-development.asmorphic.com/ - you can add as many repeater items as you want, hit the 'edit' and it will populate the correct one each time It does mean it's not a reusable 'Repeater' as such (generically) - but that's fine if I'm say building a 'reusable Contact card' which invokes a modal with custom forms geared towards that
awcodes
awcodes9mo ago
God I hate how Debugbar affects the layout. 😂
SirFat
SirFat9mo ago
haha totally =]
awcodes
awcodes9mo ago
Especially on my phone.
SirFat
SirFat9mo ago
I think i've deployed to https://extranet.asmorphic.com/ as well where the debug mode is off
awcodes
awcodes9mo ago
Ok, so you need the data from the slideOver passed back to the repeater item.
SirFat
SirFat9mo ago
yeah which it does eg;
$items = $component->getState();
$items[$arguments["item"]]["imei_field"] = $data["imei_field"] ?? null;
$items[$arguments["item"]]["imei_type"] = $data["imei_type"] ?? null;
$items[$arguments["item"]]["device_make"] = $data["device_make"] ?? null;
$items[$arguments["item"]]["device_model"] = $data["device_model"] ?? null;
$items[$arguments["item"]]["subscription_plan_id"] = $data["subscription_plan_id"];
$items = $component->getState();
$items[$arguments["item"]]["imei_field"] = $data["imei_field"] ?? null;
$items[$arguments["item"]]["imei_type"] = $data["imei_type"] ?? null;
$items[$arguments["item"]]["device_make"] = $data["device_make"] ?? null;
$items[$arguments["item"]]["device_model"] = $data["device_model"] ?? null;
$items[$arguments["item"]]["subscription_plan_id"] = $data["subscription_plan_id"];
$arguments["item"] is the uuid so its then a matter of what the ViewField requires to address its internal components
awcodes
awcodes9mo ago
Sorry, trying to think through it all. Having trouble making all the connections in my head with the shared code.
SirFat
SirFat9mo ago
OK, I'll explain it a bit more - and at worse I can get the code somewhere visible
awcodes
awcodes9mo ago
It’s should a simple enough. I’m just getting lost in all the arrays. Lol.
SirFat
SirFat9mo ago
hahah so $component is the repeater in itself, which comprises of all items
awcodes
awcodes9mo ago
And discord mobile doesn’t syntax highlight
SirFat
SirFat9mo ago
the item, is passed into the repeaters action method via $arguments ->action(function (array $arguments, array $data, AgilitySignupRepeater $component, Set $set, Get $get): void { $items = $component->getState(); $items[$arguments["item"]]["imei_field"] = $data["imei_field"] ?? null; }) that then automatically populates the imei_field here: AgilitySignupRepeater::make('services') ->relationship() // what is this for? ->schema([ Hidden::make("imei_field"), ... (ignore the what is this here for, we've moved past that haha)
awcodes
awcodes9mo ago
Nice comment
SirFat
SirFat9mo ago
That's before I knew make('services') <-- that services takes the place of the relationship name oh and I should have mentioned, I also populate the item when filling the modal with:
->fillForm(function (Get $get, Repeater $component, array $data, array $arguments): array {
$items = $component->getState();
$fields = [
'imei_field' => $items[$arguments["item"]]["imei_field"],
->fillForm(function (Get $get, Repeater $component, array $data, array $arguments): array {
$items = $component->getState();
$fields = [
'imei_field' => $items[$arguments["item"]]["imei_field"],
So we have the full circle, it's just a matter of now defining this 'ViewField' with a custom view blade, and being able to receive the contents of ViewData items individually, and being able to use them in the view and wallah, we have the ability to do 'whatever we want' the docco on how to set ViewData items is a little vague tho as in set via Set $set
awcodes
awcodes9mo ago
I think you might be able to use $get() in the blade view.
SirFat
SirFat9mo ago
Yep, but it will get it from the core form, the core form payload is what needs updating via the modal action()
awcodes
awcodes9mo ago
Might need the statePath too though since it’s in a repeater.
SirFat
SirFat9mo ago
yah, so really the main thing to figure out in the modal action, is if this will work: $set('myViewField', ['variable1' => $data['variable1'], 'variable2' => $data['variable2']]); Forms\Components\ViewField::make('testVariable')->view('forms.components.custom-output')->viewData(["testVariable" => "myInitialValue"]) <-- to set 'testVariable' via Set $set
awcodes
awcodes9mo ago
I think the problem here is that you have two form contexts.
SirFat
SirFat9mo ago
sorta, but they aren't really connected as such, because I'm dehydrating the contents out to an array and that's being passed back to the other form
awcodes
awcodes9mo ago
Try injecting $livewire in you action. Then you can use the raw data_get and data_set on the main form from the action.
SirFat
SirFat9mo ago
OK, so like this: ->action(function (array $arguments, array $data, Livewire $livewire, AgilitySignupRepeater $component, Set $set, Get $get): void {
awcodes
awcodes9mo ago
I think it’s Component $livewire
SirFat
SirFat9mo ago
ah yes I did see that somewhere hm, that doesn't have any methods about raw or data thats' from Livewire\Livewire\Component tho
awcodes
awcodes9mo ago
What I’m getting at is that your action has a form, but you also have the main form where your repeater is. So I’m not totally sure $get and $set are going to work that way.
SirFat
SirFat9mo ago
No description
SirFat
SirFat9mo ago
yeah, I do have another alternative to that, so I believe in this case the scope of $get and $set in the action is the repeater item (the modal) but
awcodes
awcodes9mo ago
It’s no on the livewire instance It’d be like data_set($livewire, statePath, value)
SirFat
SirFat9mo ago
if I have the values populated in the array - I can use:
->afterStateUpdated(function (Get $get, Set $set) {
if (!empty($get("processed_message"))) {
$set("info_message", "FROM INFO:".$get("processed_message"));
$set("testVariable", $get("processed_message"));
}
);
->afterStateUpdated(function (Get $get, Set $set) {
if (!empty($get("processed_message"))) {
$set("info_message", "FROM INFO:".$get("processed_message"));
$set("testVariable", $get("processed_message"));
}
);
where the scope is the item itself OK, hopefully dd will show me what it sees (as I still to-date haven't used dd())
awcodes
awcodes9mo ago
I usually use ray() so it doesn’t halt the execution unless I need it to.
SirFat
SirFat9mo ago
I'm sure he totally appreciates being used like that =] OK, well, let me go and try all of those variants and I'll see what I come up with, happy to share the code with you (at least) when I'm (hopefully) done so you can bag me out on my hackery
awcodes
awcodes9mo ago
No worries. I’m off all week so if you’d like to share anything I’m happy to help. Just let me know. I think this is an interesting problem. And could even have some insight / value to core. Of course I feel like I’m relearning livewire since v3 came out.
SirFat
SirFat9mo ago
Absolutely! will do. I would be more than chuffed if I can make something clean of the solution so it's useful to the core as well Oh, see, I have the luxury of essentially being a laravel/filament/livewire virgin
awcodes
awcodes9mo ago
Free of charge too. I’m not after a consultancy fee or anything like that.
SirFat
SirFat9mo ago
My last MVC/ORM I was working with for 15 years we built ourselves, but that was an "attempt" at framework that's awesome! thankyou 🙂 I'll keep you posted But the beers will be on me if I solve all of this (c:
awcodes
awcodes9mo ago
Helped out someone else today with their plugin. It was fun. Also challenging to figure it out since it wasn’t typical use case. Sounds good.
SirFat
SirFat9mo ago
I bet it's interesting to see just how others can twist things to get a square peg in a round hole
awcodes
awcodes9mo ago
Feel free to add me to the repo if you need to. I have no problem being removed when it’s figured out.
SirFat
SirFat9mo ago
Cool, will do, is your github user the same as your user here?
awcodes
awcodes9mo ago
That’s every one of my plugins and probably why I’m even on the team. Lol. Yes. awcodes
SirFat
SirFat9mo ago
haha "Its easier just to have him as one of us"
awcodes
awcodes9mo ago
Just helps me to see how everything is working together. I’m good at debugging and working through it but definitely helps to have a more holistic view of the situation.
SirFat
SirFat9mo ago
easy - just have to import it - it's in bitbucket atm
awcodes
awcodes9mo ago
Sounds good though. Have a good one. Talk soon.
SirFat
SirFat9mo ago
Cheers - you too! OK - getting somewhere now I put my old XDebug tools back in place. To Modal:
->fillForm(function (Get $get, AgilitySignupRepeater $component, Component $livewire, array $data, array $arguments): array {
$items = $component->getState();
$childComponents = $component->getChildComponents();
$myTestComponent = null;
foreach ($childComponents as $childComponent) {
if ($childComponent->getName() == "myTest") {
$myTestComponent = $childComponent;
break;
}
}
->fillForm(function (Get $get, AgilitySignupRepeater $component, Component $livewire, array $data, array $arguments): array {
$items = $component->getState();
$childComponents = $component->getChildComponents();
$myTestComponent = null;
foreach ($childComponents as $childComponent) {
if ($childComponent->getName() == "myTest") {
$myTestComponent = $childComponent;
break;
}
}
From Modal:
$items = $component->getState();
$childComponents = $component->getChildComponents();
$myTestComponent = null;
foreach ($childComponents as $childComponent) {
if ($childComponent->getName() == "myTest") {
$myTestComponent = $childComponent;
break;
}
}
$myTestComponent->viewData["testVariable"] = "my new value";
$myTestComponent->viewData["testVariable2"] = "my second new value";
$items = $component->getState();
$childComponents = $component->getChildComponents();
$myTestComponent = null;
foreach ($childComponents as $childComponent) {
if ($childComponent->getName() == "myTest") {
$myTestComponent = $childComponent;
break;
}
}
$myTestComponent->viewData["testVariable"] = "my new value";
$myTestComponent->viewData["testVariable2"] = "my second new value";
SirFat
SirFat9mo ago
It's still running htmlspecialchars over it, so I'll have to figure out why that's the case, but I'm at least able to access the values and modify them. I have also noticed it appears to revert itself. Up initial submit (see attached)
No description
SirFat
SirFat9mo ago
a short time later:
No description
SirFat
SirFat9mo ago
So there is likely still a point of reference with the older stale data I will need to hunt down. I don't see why it would be re-addressing the form schema and resetting everything.
SirFat
SirFat9mo ago
The mostly final outcome (some tidying to do, but it works.) - thanks to @awcodes !
No description
Lara Zeus
Lara Zeus8mo ago
hey, sorry to bump up this, are you using the default repeater in this image? or custom? cant find a way to add action to the default one
awcodes
awcodes8mo ago
It’s a custom repeater.
SirFat
SirFat8mo ago
Only thing that would really benefit here is if the entire viewData payload was a Closure not just each fields value. Code would be a lot cleaner and scalable.