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
Related
I am trying to switch a project of mine from Nuxt.js to Nuxt 3 and I have come across a problem.
I used to have the following option in my nuxt config config file:
export default {
router: {
base: '/example-default-route/'
},
}
Does anyone know how can I implement the same functionality of the above option, but in nuxt 3?
export default {
app: {
baseURL: '/example-default-route/'
}
}
https://v3.nuxtjs.org/api/configuration/nuxt.config#baseurl
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("-")
}
}
}
}
...
]
}
...
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))
}
}
})
I'm trying to use a service worker in my Vue.js project. I have it functioning with a very basic worker that I just dropped into my public/ directory (example below), but this doesn't allow me to load dependencies using require(), which is what I'd like to do next.
self.addEventListener('install', event => {
self.skipWaiting();
console.log(self)
});
self.addEventListener('fetch', function(event) {
const url = new URL(event.request.url);
console.log(url)
})
I found service-worker-loader, but it presents me with this during compilation: Syntax Error: TypeError: Cannot read property 'unlink' of null. The error persists even when I provide a serviceWorker.js file that is completely blank. My vue.config.js has the loader configured:
module.exports = {
configureWebpack: {
module: {
rules: [
{ test: /serviceWorker\.js$/, use: { loader: "service-worker-loader" }}
]
}
}
}
And I'm referencing my worker using import registerServiceWorker from 'service-worker-loader!./serviceWorker';.
Am I missing something basic? What's the "standard" way to include custom service worker code into a Vue.js project?
I'm using the plugin vue-i18n for translations in a Nuxt.js-powered SPA. This allows easy access to messages within components, like this:
$t('footer.press')
But how do I get translations outside components? In my specific case, I need them in a store action:
export const actions = {
async myAction({ commit, state, rootState, rootGetters }, options) {
(...)
const message = $t("example.message.key") // doesn't work, undefined
const message1 = this.$i18n.t("example.message.key") // doesn't work, undefined
(...)
})
}
This is how I include the vue-i18n plugin in the project:
package.json
…
"dependencies": {
…
"vue-i18n": "^8.18.2",
…
},
…
nuxt.config.js
…
plugins: [
…
'~/plugins/i18n',
…
],
…
After some research, I found a working solution on the Vue Forum here:
const message = this.app.i18n.t("example.message.key")
Works for me like a charm!
this.$t('logInWongCredentials')
(nuxt)