ntohq / buefy-next

Lightweight UI components for Vue.js (Vue3) based on Bulma
https://v3.buefy.org
MIT License
112 stars 11 forks source link

Issue with Programmatic open function #248

Closed William-Kara closed 2 months ago

William-Kara commented 3 months ago

Overview of the problem

Buefy version: 0.1.3 Vuejs version: 3.4.27

Description

When using Buefy's SnackbarProgrammatic from the programmatic UI components, the open function is not recognized as a function. This results in the error message "Uncaught TypeError: (intermediate value).open is not a function" when attempting to use SnackbarProgrammatic.open()

Steps to reproduce

Import SnackbarProgrammatic from Buefy in a Vue 3 component. Attempt to call SnackbarProgrammatic.open() with appropriate configuration options. Observe the error in the browser console indicating that open is not a function.

typescript

import { SnackbarProgrammatic as Snackbar } from 'buefy'

Snackbar.open({
  message: 'Hello, Snackbar!',
  type: 'is-info'
})

Expected behavior

I expect to be able to use SnackbarProgrammatic.open() to display snackbar notifications instead of encountering a "not a function" error.

Actual behavior

Encounter the above error.

wesdevpro commented 3 months ago

@William-Kara Could you try using JavaScript to make sure this error is reproducible in both languages? (there shouldn't be a difference) And could you please send a screen shot of the error in the console?

William-Kara commented 3 months ago

I managed to make it work by doing

new Snackbar().open({
    message: 'Hello, Snackbar!',
    type: 'is-info'
})

Here is the screenshot of the Error

Capture d’écran 2024-06-21 à 09 41 06

Sorry I can't take the time to reproduce in JS right now but like you said it shouldn't be different.

wesdevpro commented 3 months ago

I managed to make it work by doing


new Snackbar().open({

    message: 'Hello, Snackbar!',

    type: 'is-info'

})

Here is the screenshot of the Error

Capture d’écran 2024-06-21 à 09 41 06

Sorry I can't take the time to reproduce in JS right now but like you said it shouldn't be different.

It's strange that you have to initialize a new object👀 Which is completely different compared to our example in the docs 🪦I'll take a look into this on my own when I get free time. For now I'm focusing on v0.1.4 of Buefy-next Let me know if you find any thing new. I'll keep you posted if I find anything

wesdevpro commented 2 months ago

I will be testing this tonight

jaybeecave commented 2 months ago
import {
  DialogProgrammatic,
  LoadingProgrammatic,
  ModalProgrammatic,
  SnackbarProgrammatic,
  NotificationProgrammatic,
  ToastProgrammatic
} from '@ntohq/buefy-next'

import {
  BDialogConfig,
  BComponent,
  BPromptDialogConfig,
  BLoadingConfig,
  BLoadingComponent,
  BModalConfig,
  BModalComponent,
  BNoticeComponent,
  BNoticeConfig,
  BNotificationConfig,
  BSnackbarConfig
} from '@ntohq/buefy-next/types/components'

const dialog: {
  alert: (params: BDialogConfig | string) => BComponent
  confirm: (params: BDialogConfig) => BComponent
  prompt: (params: BPromptDialogConfig) => BComponent
  // @ts-expect-error
} = new DialogProgrammatic()

const loading: {
  open: (params: BLoadingConfig) => BLoadingComponent
  // @ts-expect-error
} = new LoadingProgrammatic()

const modal: {
  open: (params: BModalConfig | string) => BModalComponent
  // @ts-expect-error
} = new ModalProgrammatic()

const snackbar: {
  open: (params: BSnackbarConfig | string) => BNoticeComponent
  // @ts-expect-error
} = new SnackbarProgrammatic()

const notification: {
  open: (params: BNotificationConfig | string) => BNoticeComponent
  // @ts-expect-error
} = new NotificationProgrammatic()

const toast: {
  open: (params: BNoticeConfig | string) => BNoticeComponent
  // @ts-expect-error
} = new ToastProgrammatic()

export { dialog, loading, modal, snackbar, notification, toast }
DarkPo013 commented 2 months ago

any luck with this?

wesdevpro commented 2 months ago

I managed to make it work by doing


new Snackbar().open({

    message: 'Hello, Snackbar!',

    type: 'is-info'

})

Here is the screenshot of the Error

Capture d’écran 2024-06-21 à 09 41 06

Sorry I can't take the time to reproduce in JS right now but like you said it shouldn't be different.

How are you importing the types? All of those functions are defined as functions. There must be some logic I'm missing when it comes to TS classes. Maybe the initialization of an object is required because we defined a constructor

Notice how the Buefy Vue2 implementation does not define a class with a constructor but rather an constant object with a method https://github.com/buefy/buefy/blob/dev/src/components/notification/index.js#L10C1-L17C10

I believe the reason for the change is the initialization and use of the app property. Which is ultimately required because of the Vue3 changes. The ultimate issue is the docs needs to be updated for vue3

@kikuomax is this correct?

kikuomax commented 2 months ago

In the PR #102, I changed SnackbarProgrammatic and other XyzProgrammatic associated with components that can be programmatically opened, e.g., ModalProgrammatic, from constant objects to constructors which take a Vue's App instance as a parameter. These changes were to address a plugin issue (#101) in programmatically opened components.

@wesdevpro Yes, we have to update the docs.

@William-Kara I recommend you using this.$buefy.snackbar.open(...) in the Options API context. We have a poor support for the Composition API for now (please see the discussion #152).

William-Kara commented 2 months ago

I don't like the idea of having views with options API in my project that uses composition API as a standard but I will do it this way for the moment.

nejcskofic commented 2 months ago

@William-Kara I solved this by using inject/provide mechanism. Below is an example for toast (other programmatic component would work the same way). app.ts

...
app
  .use(Buefy as unknown as Plugin<[config: BuefyConfig]>, {
    defaultIconPack: 'fas'
  })
  .provide('buefyToast', app.config.globalProperties.$buefy.toast)

ProgrammaticComponents.ts

import { ToastProgrammatic } from 'buefy'
import { inject } from 'vue'

type ToastProgrammaticType = typeof ToastProgrammatic

export function useToast() {
  const toast = inject<ToastProgrammaticType>('buefyToast')
  if (!toast) {
    throw new Error(
      'useToast must be used inside a component with Buefy installed'
    )
  }
  return toast
}

Component.ts

...
<script setup land="ts">
import { useToast } from '@/composables/ProgrammaticComponents'

const toast = useToast()
...

function openToast() {
    toast.open('Some message')
}
</script>
em3link commented 1 month ago

@William-Kara @nejcskofic I used a similar approach using provide / inject but providing directly the app.config.globalProperties.$buefy namespace.

main.ts

...
app.use(Buefy)
app.provide('buefy-instance', app.config.globalProperties.$buefy)

Component.vue

<script setup lang="ts">
import { type BuefyNamespace } from 'buefy'
const buefy = inject('buefy-instance') as BuefyNamespace

function openToast() {
    buefy.toast.open({
      message: 'Some toast message.',
      type: 'is-danger'
    })
}
</script>
tatwww commented 1 month ago

@William-Kara @nejcskofic我使用了類似的方法,使用provide/inject但直接提供app.config.globalProperties.$buefy名稱空間。

main.ts

...
app.use(Buefy)
app.provide('buefy-instance', app.config.globalProperties.$buefy)

Component.vue

<script setup lang="ts">
import { type BuefyNamespace } from 'buefy'
const buefy = inject('buefy-instance') as BuefyNamespace

function openToast() {
    buefy.toast.open({
      message: 'Some toast message.',
      type: 'is-danger'
    })
}
</script>

Sorry, I'm new to front-end. If I use javascript, can I use toast normally according to your settings?

By the way, will this problem be considered to be solved in subsequent versions? @wesdevpro

tatwww commented 1 month ago

This seems like an unsolvable problem to me. Are my settings below correct?

mylogin.vue image image

main.js image

toast.vue image