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

Using Plugins With Options as a Second Arg #41

Open
Aston13 opened this issue Nov 13, 2023 · 6 comments
Open

Using Plugins With Options as a Second Arg #41

Aston13 opened this issue Nov 13, 2023 · 6 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@Aston13
Copy link

Aston13 commented Nov 13, 2023

Hi,

Firstly, I would just like to say that I am very appreciative of this library, it has enabled me to upgrade very smoothly from Vue 2 to Vue 3.

I have found a issue when trying to register a plugin that has a second options argument.

Vue's app.use function (interface App<HostElement = any>) has some overloads for passing options.

use<Options extends unknown[]>(plugin: Plugin<Options>, ...options: Options): this;
use<Options>(plugin: Plugin<Options>, options: Options): this;

However, I can't see how to do this in ngVue3 as there is only 1 argument for the use function. For example with PrimeVue:

$ngVueProvider.use(PrimeVue, { unstyled: true }); // Doesn't work
@jaredmcateer
Copy link
Owner

I'll look into adding this as it's definitely a common enough use case. However, you should be able to work around this in the meantime by creating your own custom ngVue plugins (oof looking at the docs I clearly need to update them).

import angular from "angular";
import { useNgVue, useNgVuePlugins, NgVueProvider } from "@jaredmcateer/ngvue3";
import { App } from "vue";
import PrimeVue from 'primevue/config';

angular.module("myModule", [useNgVue(), useNgVuePlugin()])
  // ...
  .config(($ngVueProvider: NgVueProvider) => {
    $ngVueProvider.installNgVuePlugin(() => ({
      $name: "ngVuePrimeVue",
      $plugin: (_$injector: ng.auto.IInjectorService, app: App<any>) => {
        app.use(PrimeVue, { unstyled: true });
      }
    }));
  });

@jaredmcateer jaredmcateer added enhancement New feature or request good first issue Good for newcomers labels Nov 14, 2023
@jaredmcateer
Copy link
Owner

Just for additional reference since the docs are inaccurate right now.

export type PluginHook = ($injector: ng.auto.IInjectorService, app: App<Element>) => void;

export interface NgVuePlugin {
  /** Plugin name */
  $name: string;
  /** Plugin install function */
  $plugin: PluginHook;
  /** 
   * Methods/properties to expose on the angular side as part
   * of the ngVueProvider.plugins.<$name>.<$config.key> 
   */
  $config: Record<string, unknown>;
}

/**
 * Installs an ngVue plugin, this gives access to configuration via the
 * ngVueProvider and gives the Vue plugin install method access to the angular
 * injector.
 *
 * @param plugin a function that returns a ngVue plugin config
 * @member NgVueProvider
 */
installNgVuePlugin(plugin: () => NgVuePlugin) {
  const { $name, $config, $plugin } = plugin();
  this.pluginHooks.push($plugin);
  this.pluginConfig[$name] = $config;
}

@Aston13
Copy link
Author

Aston13 commented Nov 28, 2023

Just wanted to say thanks for the fast response! I went for this in the end with your help.

...
import NgVueModules, { type NgVuePlugin } from './NgVueModules';
import PrimeVue, { PrimeVueConfiguration } from 'primevue/config';

const primeVueConfig: PrimeVueConfiguration = { unstyled: false, pt: Bootstrap_PT, ripple: true };

const ngVueInjectables: NgVuePlugin[] = [
    { injectable: i18n },
    { injectable: HighchartsVue },
    { injectable: PrimeVue, context: { name: "ngVuePrimeVue", config: primeVueConfig }}
];
NgVueModules.create(angular, ngVueInjectables).registerComponents()
...

...
export type NgVuePlugin = {
    injectable: any;
    context?: {
        name: string;
        config: any;
    }
}

export default class NgVueModules {
    angularInstance: IAngularStatic;
    vuePlugins?: NgVuePlugin[];

    private constructor(angularInstance: IAngularStatic, vuePlugins?: NgVuePlugin[]) {
        this.angularInstance = angularInstance;
        this.vuePlugins = vuePlugins;
    }

    static create(angularInstance: IAngularStatic, vuePlugins?: NgVuePlugin[]): NgVueModules {
        return new NgVueModules(angularInstance, vuePlugins);
    }

    registerComponents() {
        this.angularInstance // Angular module that uses the ngVue3 lib to enable the usage of Vue 3 components in Angular 1.x.
        .module('vue.components', [
            useNgVue(),
            useNgVuePlugins()
        ])
        .config(['$ngVueProvider', ($ngVueProvider: NgVueProvider) => { // Syntax supports minimisation of $ngVueProvider
            if(this.vuePlugins){

                // Pass through for native Vue Plugins to the app instance.
                this.vuePlugins.forEach(vuePlugin => {
                    if(vuePlugin.context){
                        $ngVueProvider.installNgVuePlugin(() => ({
                            $name: vuePlugin.context.name,
                            $plugin: (_$injector/*: ng.auto.IInjectorService */, app: App<any>) => {
                              app.use(vuePlugin.injectable, vuePlugin.context.config);
                            },
                            $config: {}
                        }))
                    } else {
                        $ngVueProvider.use(vuePlugin.injectable);
                    }
                })
                $ngVueProvider.component('valueType', <unknown>ValueType as Component)
            }
        }])
...

@rbanks54
Copy link

Leaving this here to potentially help others:

I had some "fun" getting Pinia working as a plugin.

Setting it up with the ngvue3 example apps was a breeze. Call app.use(createPinia()) as the $plugin function, and use I can use a store as per usual. i.e. const.store = useXyzStore(); in the vue component.

However, in my large angularjs app, this didn't work. The vue component couldn't find the root store (no active pinia) so it threw an exception (see this code here)

I haven't been able to track down the root cause, but I suspect either the injection context is being rewritten or the Symbol used with the provide/inject approach is no longer aligned.

My work around (for now) is as follows.

//in the angularjs app
.config(($ngVueProvider) => {
    $ngVueProvider.installNgVuePlugin(() => ({
        $name: "pinia",
        $plugin: (_$injector, app) => {
            const pinia = createPinia();
            app.use(pinia);
            //add the rootStore to the injection context.
            app.provide('piniaInstance', pinia);
        }
    }))
})

and when a vue component needs to use a store, inject the rootStore instance.

import { inject } from 'vue';
const store = useXyzStore(inject('piniaInstance'));

@jaredmcateer
Copy link
Owner

This seem eerily similar to a problem people are also having on the other version of ngVue, ngVue/ngVue#158, and $store being missing. It would be super helpful if we could get a minimal reproducible example.

@sirSayed98
Copy link

you can check this solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

4 participants