Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue-Plugins #23

Closed
renky opened this issue Aug 9, 2024 · 12 comments
Closed

Vue-Plugins #23

renky opened this issue Aug 9, 2024 · 12 comments

Comments

@renky
Copy link

renky commented Aug 9, 2024

Hi there and Thanks for this great work

I have evaluated it and mingle exactly solves an issue that I have currently - Livewire app but for several components we need component based frontend-engineering, that we are not able to solve with alpine. Running it with mingle and vue just works... Fine so far.

Unfortunatelly mingle decides itself how to create the Vue app and to run it without additional plugins and I just wonder, if it would be possible to add a solution (maybe a callback right before the component is mounted) to add additional stuff to vue...

I already tried it without autoMounting like that:

import mingle from '@mingle/mingleVue'
import Ticket from './Ticket.vue'

const {app, node} = await mingle('resources/js/mingles/Ticket.js', Ticket, {autoMount: false})
app.do-other-stuff-and-add-plugins
app.mount(node)

But this only works for the first mingle of this type. because this file is only loaded once... I'd have to hook into the boot method - there I'd have to add the additional plugins - right?

I also thought about overwriting the mingle blade view and do it in the x-init-attribute - but the boot-call doesn't return the app and node- only the register-function returns it (what is really strange, because there are different nodes and different apps...
so right now I see no solution to use the same mingle multiple times and extending the vue-app...

edit:
also using automount and adding the plugins AFTER mounting, only works for the first component, since it is only called once. So it is really adding my plugin, but only for the first mingle-component of this type on the page...

const {app} = await mingle('resources/js/mingles/Ticket.js', Ticket)
app.use(...);
@ijpatricio
Copy link
Owner

Hello @renky , thanks for the kind words!

I'm trying to understand it better. If there was a hook where the consumer app could provide a callback, wouldn't that script just run equally? Just it would be the library to invoke the callback, instead of the consumer doing it on its end.

I'm all-in to solve this and change things accordingly.
I'm currently with a huge project which I'm not using Vue with, so I don't get many opportunities to battle test it.
So this is very important to get right, and I'm missing to understand this 100%.

I have a couple of tasks to work on Mingle today/tomorrow, so I'll try to reproduce.

Thanks

@ijpatricio
Copy link
Owner

Hey @renky

Would something like this be good? 😄 I'm thinking so. You can do anything like so

import mingle from '@mingle/mingleVue'
import Counter from './Counter.vue'

mingle('resources/js/vue-counter/index.js', Counter, {
    createComponent: ({createApp, props, el, wire, mingleId, wireId, mingleData}) => {
        const app = createApp(Counter, props)

        // Do whatever you want with the app :)

        app.mount(el)
    }
})

Let me know your thoughts

@renky
Copy link
Author

renky commented Aug 10, 2024

Thanks for the fast reply

For recreation:

Just create a simple mingle - simple Vue-Component that only shows a colored div or something like that.
In my case the mingle is called "Ticket" - so I have a Livewire Mingle Component named Ticket and I have two js files, Ticket.js and Ticket.vue.

Then in my page I include it like this:

    @foreach($tickets as $ticket)
            <div wire:key="$ticket->id">
                <livewire:ticket :ticket="$ticket" :key="$ticket->id" />
            </div>
    @endforeach

This works fine out of the box - I can see all tickets rendered, all vue-components working - so everything is goot until here...

but if you change the Ticket.js like I did it above:

import mingle from '@mingle/mingleVue'
import Ticket from './Ticket.vue'

const {app, node} = await mingle('resources/js/mingles/Ticket.js', Ticket, {autoMount: false})
app.mount(node)

It doesn't work any more - only the first one is rendered now...
and indeed this makes really sense, because the initialisation of the mingles just creates ONE single entry in window.Mingle.Elements for this mingle.js file. so the Ticket.js file is only included once, and this means the code I added (app.mount) is only executed once.

But indeed the boot method inside, this one is called for each Vue-Component, and we would need to do the app.mount inside the boot method.

It also might be another issues -- if you want to keey ALL mingle instances inside the window.Mingle.Elements, then the name would have to care about loops and additional calls... currently the name only contains the Ticket.js-file-path... maybe this is the issue...

@ijpatricio
Copy link
Owner

ijpatricio commented Aug 10, 2024

Thanks. I was able to reproduce quickly.

image

On this demo repository.. customizing on the index.js file, would result in only first renders.

So I changed it already

You can code like this, after I push the changes. Are you able to test now if I push?

mingle('resources/js/vue-counter/index.js', Counter, {
    createComponent: ({createApp, props, el, wire, mingleId, wireId, mingleData}) => {
        const app = createApp(Counter, props)

        console.log(mingleData)
        // Do whatever you want with the app :)
        app.mount(el)
    }
})

@ijpatricio
Copy link
Owner

For clarity: That code is not working on the current version. on my machine only

@renky
Copy link
Author

renky commented Aug 10, 2024

Thanks - I can check it today but not immediately. So you will add a new option as a create-app-hook where I can create the app on my own? Thats looking good to me...

@ijpatricio
Copy link
Owner

Yes, exactly.

Instead of a "before-create", this way you can have all the control on how everything is done.

And have all wireId, $wire object, mingleData, etc available

@ijpatricio
Copy link
Owner

@renky I pushed a new version

Notice how you have to use the named export createMingle.

The createApp function is the original Vue with runtime compiler.
The props object is created for you for conveninence, but ofc, you can modify it if you want.
You can even make some logic depending on initial mingleData that comes from the server.

Now you have full control the app is created.

import mingle, { createMingle } from '@mingle/mingleVue'
import Counter from './Counter.vue'

const helloPlugin = {
    install: (app, options) => {
        app.config.globalProperties.$hello = (name) => {
            alert(`Hello ${name}!`)
        }
    }
}

// mingle('resources/js/vue-counter/index.js', Counter)

createMingle('resources/js/vue-counter/index.js', ({createApp, props, el, wire, mingleId, wireId, mingleData}) => {
    const app = createApp(Counter, props)
    app.use(helloPlugin)
    app.mount(el)
    return true
})

@ijpatricio
Copy link
Owner

@renky , I'm closing the issue, as I believe it's taken car of.

Docs here: https://minglejs.unitedbycode.com/anatomy-vue-specifics#advanced-vuejs-components

Let me know how it went for you. Reopen if necessary! Thanks!

@renky
Copy link
Author

renky commented Aug 10, 2024

Thanks a lot !! I'll have a try later

@renky
Copy link
Author

renky commented Aug 10, 2024

Just to keep you up to date: it works like a charm...
I just wonder if there is potential to refactor and reuse code in both functions - you now doubled up a lot of it...
And one small thing: in the docs you can change the first line

import mingle, { createMingle } from '@mingle/mingleVue'

I think this is enough:

import{ createMingle } from '@mingle/mingleVue'

@ijpatricio
Copy link
Owner

Woohoo, thanks!!

Yeah, I duplicated a little bit, but it's ok. Sometimes it's better to duplicate than making the wrong abstraction 😅

I'm already having end-to-end tests, which ultimately will be the "safety net"!

Yep, using the named export alone is enough!

Thanks, @renky , and enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants