Git Product home page Git Product logo

Comments (9)

ksassnowski avatar ksassnowski commented on June 14, 2024 1

Just tagged version 0.4.0 which loosens the return type declarations of the ResponseRenderer interface. Feel free to reopen this issue if there are still problems left.

from arcanist.

MillerAdulu avatar MillerAdulu commented on June 14, 2024

@mklein994 ,

There's a renderer available for Inertia.js. Have a look at this URL: https://github.com/laravel-arcanist/inertia-response-renderer

As for the blade templates, that comes out of the box. Please have a look at this demo video done before the package was released to grasp the core concepts and see how to implement a basic wizard. https://www.youtube.com/watch?v=R_9jNFYPqrE&t=5404s

If I have a JavaScript frontend project (Vue, React, Svelte, etc.)

I am only familiar with Vue.js. Vue.js allows you access to the Vuex store and since there are no full page reloads, I am guessing you could have your Vuex store handle that bit of saving the state between steps for you. Removing the need for Arcanist. Ideally, the wizard only enables you to collect data then it commits it at the end which is where you would make the API request.

from arcanist.

ksassnowski avatar ksassnowski commented on June 14, 2024

I think writing a RestApiResponseRenderer would be pretty straight forward, since all you’d have to do is to return the wizard’s data as a JsonResponse instead of a Blade or Inertia view.

So something like this

public function renderStep(WizardStep $step, AbstractWizard $wizard, $data)
{
    return new JsonResponse($data);
}

I think the bigger problem for your use-case is the fact that you have a separate SPA for your frontend. Since you're now doing client-side routing, this might conflict with Arcanist since it will try and redirect you between the different steps of a wizard. I haven’t tried it out yet, but it’s possible that a library like axios would automatically take care of the redirection if the backend returns a redirect response.

from arcanist.

thoresuenert avatar thoresuenert commented on June 14, 2024

@ksassnowski your idea with the JsonResponse is not working because the renderStep have conflicting return types.
JsonResponse and Response have different trees.

We have a working MVP but we had to change return types in AbstractWizard and Contracts\Renderer.

Are you open for PR to fix the return types?
After the small changes we can provide a package with a renderer to use with a vue spa.

Our change can be viewed in suenert@52ef0e0
We are open to discuss the right return types because forcing RedirectResponse is an architectural thing.

from arcanist.

ksassnowski avatar ksassnowski commented on June 14, 2024

Can you elaborate a little bit on what exactly the issue with the JsonResponse is? Because just from looking at your change, I don't quite understand what you were trying to do that conflicted with this return type.

I'm definitely open to make a change to the return types if it helps. I just want to understand the problem properly first.

from arcanist.

thoresuenert avatar thoresuenert commented on June 14, 2024

As you mentioned above to write a JsonRenderer is straightforward and enough to build a Protocol on top of archaist to drive an SPA.

The idea is in a vue context:
Use the Vue Router for client side rounting an reimplement the two get routes of arcanist.
Build an JsonRenderer to receive the data as json in the frontend.
To handle redirects build a Protocol on top of arcanist.

Example for a JsonRenderer

class JsonRenderer implements ResponseRenderer
{

    public function renderStep(WizardStep $step, AbstractWizard $wizard, array $data = []): Response | Responsable | Renderable
    {
        $viewData = [
            'arcanist' => array_filter([
                'wizard' => $wizard->summary(),
                'step' => $data,
            ])
        ];

        return new JsonResponse($viewData);
    }

    public function redirect(WizardStep $step, AbstractWizard $wizard): RedirectResponse
    {
        return new JsonResponse([
            'redirect' => [
                'name' => 'step',
                'params' => [
                    'wizardSlug' => $wizard::$slug,
                    'wizardId' => $wizard->getId(),
                    'step' => $step->slug,
                ]
            ]
        ]);
    }

    public function redirectWithError(WizardStep $step, AbstractWizard $wizard, ?string $error = null): RedirectResponse
    {
        return new JsonResponse([
            'url' => route(
                'wizard.' . $wizard::$slug . '.show',
                [$wizard->getId(), $step->slug]
            ),
            'error' => $error
        ]);
    }
}

The Problem
This Renderer isn't working because it throws type mismatches for each function.
For renderStep the JsonResponse isn't a Response.
For redirect and redirectWithError the JsonResponse isn't a RedirectResponse and can't be in anyway.

Solution
Add JsonResponse or dive deeper into the inheritance tree an find a Class which both Responses rely on Symfony\Component\HttpFoundation\Response.
To solve the problem with the RedirectResponse isn't so easy because that is a strong architectural decision to force a redirect. (That is forced in the AbstractWizard as well).
The easy way is to add JsonReponse to the types but that feels a bit against your vision.
Maybe we have to rethink how the Renderer works with the Wizard to reduce the mismatches into one place and find good solution.
Quick idea come up while I am writing: Return Renderable Objects from the Renderer, so we have to overwrite 3 Renderable Objects instead of the Renderer. But don't know if that works with Redirects.

Extended Problem
We want to build a form builder with vue on top of arcanist, that the ui is driven by the step fields definition. (Hello nova my old friend).
That works without any modification, our solution here is to use the Step::viewData function, e.g:

/**
    * Returns the view data for the template.
    */
   public function viewData(Request $request): array
   {
       return [
           'data' => $this->withFormData(),
           'fields' => $this->fields(),
       ];
   }

But we need additional infos for the Field like $component = 'TextField.
For that a change is needed:
You have build a make function which uses new self that is a problem when we want to extend the class.

arcanist/src/Field.php

Lines 17 to 20 in 14bd127

public static function make(string $name): Field
{
return new self($name);
}

Here we need to use new static because we want an instance of the class we call the make function on.

Hopefully that describes the problem in a better and more in depth way.
I am open to chat/voice etc. if you want.
I am planning to build two big application on this package because it is well thought out.

from arcanist.

ksassnowski avatar ksassnowski commented on June 14, 2024

Thanks for the thorough explanation. I think I got confused because your linked commit shows a diff with a previous commit from your fork where you had already added the JsonResponse. I'm fine with changing the return types to allow for kinds of responses. I want to have a look how this can be simplified so it doesn't end up with a monstrous union type.

Regarding the Field class, I'd be happy for a PR that changes self to static, both inside the function as well as the return type. This is a fairly straight-forward change that I could merge quickly.

The other changes require a bit more care since this also affects the existing renderers. I will have to tag new releases for all of them.

from arcanist.

thoresuenert avatar thoresuenert commented on June 14, 2024

The other changes require a bit more care since this also affects the existing renderers. I will have to tag new releases for all of them.

Let me know if we can assist you to make the change happen and/or discuss other solutions.

from arcanist.

thoresuenert avatar thoresuenert commented on June 14, 2024

We have one thing left:
To render fields in the frontend SPA we need to access the field list from the Render like this:

public function renderStep(WizardStep $step, AbstractWizard $wizard, array $data = [] ): Response | Responsable | Renderable
    {
        return new JsonResponse([
            'wizard' => $wizard->summary(),
            'step' => $step,
            'fields' => $step->getFields(),
            'formData' => $data,
        ]);
    }

As you see we call a custom function getFields(). We only need this because the fields() function is protected.

Is there any chance to change that to public ?
https://github.com/laravel-arcanist/arcanist/blob/ecb3f51c5b63222f1c40d700218f09699f5b6f14/src/WizardStep.php#L91-94

from arcanist.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.