Using Stencil components with Ionic Vue - vue.js

In the Stencil docs section on framework integration with Vue it states the following:
In order to use the custom element library within the Vue app, the
application must be modified to define the custom elements and to
inform the Vue compiler which elements to ignore during compilation.
According to the same page this can be achieved by modifying the config of your Vue instance like this:
Vue.config.ignoredElements = [/test-\w*/];
This relates to Vue 2 however. With Vue 3 (which Ionic Vue uses) you have to use isCustomElement as stated here.
Regretably, I can’t for the life of me get Vue and Stencil to play nice. I have tried setting the config like this:
app.config.compilerOptions.isCustomElement = tag => /gc-\w*/.test(tag)
This causes Vue throw the following warning in the console:
[Vue warn]: The `compilerOptions` config option is only respected when using a build of Vue.js that includes the runtime compiler (aka "full build"). Since you are using the runtime-only build, `compilerOptions` must be passed to `#vue/compiler-dom` in the build setup instead.
- For vue-loader: pass it via vue-loader's `compilerOptions` loader option.
- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader
- For vite: pass it via #vitejs/plugin-vue options. See https://github.com/vitejs/vite/tree/main/p
However, I have no idea how to implement any of the above suggestions using Ionic Vue. I have been messing around with chainWebpack in config.vue.js but without success so far.
Any help would be greatly appreciated.

I'm not an expert in Vue but here's how I did it:
Add the following to your ./vue.config.js (or create it if it doesn't exist):
/**
* #type {import('#vue/cli-service').ProjectOptions}
*/
module.exports = {
// ignore Stencil web components
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions = {
...options.compilerOptions,
isCustomElement: tag => tag.startsWith('test-')
}
return options
})
},
}
This will instruct Vue to ignore the test-* components. Source: https://v3.vuejs.org/guide/web-components.html#skipping-component-resolution
Next, load the components in ./src/main.ts.
Import the Stencil project:
import { applyPolyfills, defineCustomElements } from 'test-components/loader';
Then replace createApp(App).use(router).mount('#app') with:
const app = createApp(App).use(router);
// Bind the custom elements to the window object
applyPolyfills().then(() => {
defineCustomElements();
});
app.mount('#app')
Source: https://stenciljs.com/docs/vue

Also, if anyone is using vite2+, just edit the vite.config.js accordingly:
import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.startsWith('test-') // ✅ Here
}
}
}) ],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

Related

How do you resolve the 500 error "Navigator is not defined" when installing third-party plugins in a Nuxt 3 app?

I am currently unable to use third-party plugins in my Nuxt 3 application. Here's what my code/setup looks like:
In my package.json I have the following dependency: "#braze/web-sdk": "^4.6.1"
In my plugins/ directory, I have a braze.client.js file
Within the braze.client.js file, I have the following:
import * as braze from '#braze/web-sdk'
export default defineNuxtPlugin(() => {
braze.initialize(apiKey, {
baseUrl: apiEndpoint
})
return {
provide: {
openSession
}
}
})
Within my nuxt.config.ts, I added the following (since my project has Vite, I followed Braze's documentation here: https://www.braze.com/docs/developer_guide/platform_integration_guides/web/initial_sdk_setup/#vite):
optimizeDeps: {
exclude: ['#braze/web-sdk']
}
Then within my <script> tags within the page/component, I have the following:
const { $openSession } = useNuxtApp()
onMounted(() => {
$openSession()
})
All the steps above continue to create a "500 navigator is not defined" error and I'm stumped with how I can resolve it.
I tried using beforeOnMount, not using as lifecycle hook, as well as placing $openSession() within an if statement:
if (process.client) {
}
Yet no matter what, it still renders the 500 error.

Unable to load stencil components lib with Vue3 using Vite

I created a sample project to reproduce this issue: https://github.com/splanard/vue3-vite-web-components
I initialized a vue3 project using npm init vue#latest, as recommanded in the official documentation.
Then I installed Scale, a stencil-built web components library. (I have the exact same issue with the internal design system of my company, so I searched for public stencil-built libraries to reproduce the issue.)
I configured the following in main.ts:
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('scale-')
applyPolyfills().then(() => {
defineCustomElements(window);
});
And the same isCustomElement function in vite.config.js:
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I inserted a simple button in my view (TestView.vue), then run npm run dev.
When opening my test page (/test) containing the web component, I have an error in my web browser's console:
failed to load module "http://localhost:3000/node_modules/.vite/deps/scale-button_14.entry.js?import" because of disallowed MIME type " "
As it's the case with both Scale and my company's design system, I'm pretty sure it's reproducible with any stencil-based components library.
Edit
It appears that node_modules/.vite is the directory where Vite's dependency pre-bundling feature caches things. And the script scale-button_14.entry.js the browser fails to load doesn't exist at all in node_modules/.vite/deps. So the issue might be linked to this "dependency pre-bundling" feature: somehow, could it not detect the components from the library loader?
Edit 2
I just found out there is an issue in Stencil repository mentioning that dynamic imports do not work with modern built tools like Vite. This issue has been closed 7 days ago (lucky me!), and version 2.16.0 of Stencil is supposed to fix this. We shall see.
For the time being, dropping the lazy loading and loading all the components at once through a plain old script tag in the HTML template seems to be an acceptable workaround.
<link rel="stylesheet" href="node_modules/#telekom/scale-components/dist/scale-components/scale-components.css">
<script type="module" src="node_modules/#telekom/scale-components/dist/scale-components/scale-components.esm.js"></script>
However, I can't get vite pre-bundling feature to ignore these imports. I configured optimizeDeps.exclude in vite.config.js but I still get massive warnings from vite when I run npm run dev:
export default defineConfig({
optimizeDeps: {
exclude: [
// I tried pretty much everything here: no way to force vite pre-bundling to ignore it...
'scale-components-neutral'
'#telekom/scale-components-neutral'
'#telekom/scale-components-neutral/**/*'
'#telekom/scale-components-neutral/**/*.js'
'node_modules/#telekom/scale-components-neutral/**/*.js'
],
},
// ...
});
This issue has been fixed by Stencil in version 2.16.
Upgrading Stencil to 2.16.1 in the components library dependency and rebuilding it with the experimentalImportInjection flag solved the problem.
Then, I can import it following the official documentation:
main.ts
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
applyPolyfills().then(() => {
defineCustomElements(window);
});
And configure the custom elements in vite config:
vite.config.js
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I did not configure main.ts
stencil.js version is 2.12.1,tsconfig.json add new config option in stencil:
{
"compilerOptions": {
...
"skipLibCheck": true,
...
}
}
add new config option in webpack.config.js :
vue 3 document
...
module: {
rules:[
...
{
test: /\.vue$/,
use: {
loader: "vue-loader",
options: {
compilerOptions: {
isCustomElement: tag => tag.includes("-")
}
}
}
}
...
]
}
...

CASL Vue - How to access $can in route guard

I am building a Nuxt app and trying to create a route guard. How can I access $can in my Nuxt middleware?
export default (context) => {
const { route } = context
route.matched.some((routeRecord) => {
const options = routeRecord.components.default.options
console.log(options)
// should use $can here
return true
})
}
I import the following in my nuxt.config.js:
plugins: [
{ src: '#plugins/vue-can.js' },
]
I add casl as plugins on the Nuxt $auth module:
auth: {
plugins: [
'#plugins/vue-casl.js',
]
}
This is because I need to access $auth.user in my plugins.
All of this works good, except I can't find $abilities or $can in my Nuxt middleware.
PS: I am talking about this package: #casl/vue
$abilities and $can should both be available as properties of the context object in your middleware, so you should be able to access them using context.$foo.
If you don't see it, it is probably because it was not injected properly. The plugin you registered in your nuxt.config.js should use inject to add $can to the global vue instance and make it available as a property of this and context. See the documentation for further detail: https://nuxtjs.org/docs/2.x/directory-structure/plugins/#inject-in-root--context

plugin is not defined in instance.vue

I struggle to add a plugin in Nuxt.js. I have been looking to the doc and all kind of similar problems, but I got the same error: simpleParallax is not defined.
I tried different approach on all files
nuxt.config.js:
plugins: [
{src: '~/plugins/simple-parallax.js', mode:'client', ssr: false}
],
plugins/simple-parallax.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.use(new simpleParallax);
index.vue:
Export default {
plugins: ['#/plugins/simple-parallax.js'],
mounted() {
var image = document.getElementsByClassName('hero');
new simpleParallax(image, {
scale: 1.8
});
}
}
Error message:
ReferenceError: simpleParallax is not defined.
The best solution I found out so far is to register simpleParallax on the Vue prototype like so in a plugin nuxt file with the name simple-parallax.client.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.prototype.$simpleParallax = simpleParallax;
Also my nuxt.config.js file if anyone would like to verify that as well:
plugins: [
{src: '~/plugins/simple-parallax.client.js', mode: 'client', ssr: false}
],
I then have access to the plugin before instantiation in my case in the mounted life cycle of the primary or root component to grab the desired HTML elements and instantiate their individual parallax with the newly added global method this.$simpleParallax
For example I can then intiate a certain HTML element to have its parallax like so:
const someHTMLElement = document.querySelectorAll('.my-html-element');
const options = {...} // your desired parallax options
new this.$simpleParallax(someHTMLElement, options);
Actually you don't need to use plugin here.
Just import simpleParallax from 'simple-parallax-js' in your component and init it with your image in mounted hook.
index.vue:
import simpleParallax from 'simple-parallax-js'
export default {
...
mounted() {
// make sure this runs on client-side only
if (process.client) {
var image = document.getElementsByClassName('thumbnail')
new simpleParallax(image)
}
},
...
}
And don't forget to remove previously created plugin, it's redundant here.

How to use BugSnag inside of a nuxt.js app?

BugSnag provides a very useful and initially free product for tracking errors in your vue app. The problem is that there is no documentation for using this in a nuxt app. A plugin would be the best place to utilize it in the app.
Trying to resolve this was killing me for a while but I was able to find help from Patryk Padus from the comments on this post.
For anyone trying to make this happen, do the following:
1.Place the following code inside of a plugin located in the /plugins folder of your application root:
#/plugins/bugsnag.js
import Vue from 'vue'
import bugsnag from '#bugsnag/js'
import bugsnagVue from '#bugsnag/plugin-vue'
const bugsnagClient = bugsnag({
apiKey: 'YOUR-KEY',
notifyReleaseStages: [ 'production', 'staging' ]
})
bugsnagClient.use(bugsnagVue, Vue);
export default (ctx, inject) => {
inject('bugsnag', bugsnagClient)
}
2.Inside of the nuxt.config add the following to your plugins section:
plugins: [
'#/plugins/bugsnag.js',
],
3.Inside of your vue layout reference the bugsnag object using the $bugsnag object:
this.$bugsnag.notify(new Error('Nuxt Test error'))
If you're reading this in January 2021 and using Nuxt v2.x.x and above, the above answer might not work for you.
Here's what I did instead:
import Vue from 'vue'
import bugsnag from '#bugsnag/js'
import BugsnagVue from '#bugsnag/plugin-vue'
const bugsnagClient = bugsnag.start({
apiKey: process.env.BUGSNAG_KEY,
plugins: [new BugsnagVue()], // this is important
})
Vue.use(bugsnagClient) // // this is also important
export default (ctx, inject) => {
inject('bugsnag', bugsnagClient)
}
Tip: Install the #nuxt/dotenv module to be able to use process.env in your plugin.
References:
Bugsnag Vue installation reference