Dependency not found - Vue Js - vue.js

I have recently added axios to a file called services.js so it's better organised. This file is on my root folder.
#/services.js
import axios from "axios";
const axiosInstance = axios.create({
baseURL: " server url here",
});
export const api = {
get(endpoint) {
return axiosInstance.get(endpoint);
},
post(endpoint, body) {
return axiosInstance.post(endpoint, body);
},
};
Then I have a component called Post.vue in my view folder:
<template>
<section>
<div>
<ul></ul>
</div>
</section>
</template>
<script>
import { api } from "#/services.js";
export default {
name: "Post",
props: ["id"],
data() {
return {
post: null,
};
},
methods: {
getPost() {
api.get(`/post/${this.id}`).then(response => {
this.post = response.data;
console.log(this.post);
});
},
},
created() {
this.getPost();
},
};
</script>
<style></style>
I also have a router.ts file with all my routes:
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Home from "../views/Home.vue";
import Podcasts from "../views/Podcasts.vue";
import Post from "../views/Post.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/podcasts",
name: "podcasts",
component: Podcasts,
},
{
path: "/post/:id",
name: "post",
component: Post,
props: true,
},
],
});
export default router;
It's giving me a dependency error like #/services.js did not exist.
Unsure what's wrong at this stage.
Thanks a lot in advance for helping out

In a standard Vue CLI project, the # symbol resolves to /src
If your file is in the root of your project try
import { api } from '#/../services'
But personally, I'd move it into src
You can check the Webpack configuration using
vue inspect
Look for the resolve.alias rules.

Check your webpack configuration, depends on the version of webpack you have, there should be an alias # like this:
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
"#": path.resolve(__dirname) // check the path here
}
}
};
Or if you are using vue.config.js
configureWebpack: {
name: name,
resolve: {
alias: {
'#': path.resolve(__dirname)// check the path here
}
}
},
Make sure the path is correctly set up. You mentioned you have another project working fine, which makes it a good reference.

Related

How to use Environment Variables inside Vue3+Vite component library?

I have created a component as part of my component library that I am building with Vue3 and Vite. Everything works well, except when I try to use environment variables. I want the app that consumes this component library to be able to provide the component with environment specific data.
I have played around and found that if I have a .env file as part of the component library project, I am able to access those variables, but I want to be able to provide that during runtime and not during build time.
Here is my vite.config.ts
import { defineConfig } from "vite";
import { resolve } from "path";
import vue from "#vitejs/plugin-vue";
import dts from "vite-plugin-dts";
export default ({ mode }) => {
return defineConfig({
optimizeDeps: {
exclude: ["vue-demi"],
},
plugins: [
vue(),
dts({
insertTypesEntry: true,
}),
],
server: {
open: true,
},
build: {
lib: {
entry: resolve(__dirname, "src/lib.ts"),
name: "complib",
fileName: "complib",
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
exports: "named",
},
},
},
});
};
The entry looks like:
import { App, install } from "vue-demi";
import TestComp from "./components/TestComp.vue";
import "./tailwind.css";
install();
export default {
install: (app: App) => {
app.component("TestComp", TestComp);
},
};
export { Header };
And here is a minimal component TestComp.vue:
<script setup lang="ts">
import { onMounted } from "vue";
onMounted(() => {
console.log(import.meta.env.VITE_TEST_VAR);
});
</script>
<template>
<span>Test Comp</span>
</template>

Vue 3 router: props and query not working on beforeEach navigation guard

Using Vue 3 / Vue Router 4: I'm trying to implement a login screen that redirects to the requested deep link after login. But any prop or query I add to the navigation guard (so I can pass the requested URL to the login component) isn't visible to the login component. Here's the relevant code:
// Router
import { createRouter, createWebHistory } from "vue-router";
import Login from "#/views/Login.vue";
import Header from "#/components/Header.vue";
const routes = [
{
path: "/",
name: "Login",
components: {
default: Login,
Header: Header,
},
props: {
Header: { showMenu: false },
},
meta: { requiresAuth: false },
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeEach((to) => {
if (to.meta.requiresAuth && !router.app.user.isAuthenticated()) {
return { name: "Login", props: { default: { target: to.name } } };
}
});
// Login.vue
<script>
export default {
name: "Login",
props: {
target: {
type: String,
default: "Home",
},
},
</script>
The target property remains at the default value no matter which named route I try to request. Nor does passing the value through the query string appear to work. I'm able to pass properties to components in the route definitions themselves without incident, it's just the navigation guard function that causes problems. What am I missing?
I might be missing something but the code you posted throws errors for me and the way you handle the navigation guard seems a bit strange (you should always have at least one next() in the guard).
Anyway, if I understand correctly and if you insist on using the same route for Header and Login pages, you could do this in your SFC and remove the guard from router file:
// App.vue
<template>
<router-view :name="page" />
</template>
<script>
export default {
data() {
return {
user: null
}
},
computed: {
page() {
if (this.$route.meta.requiresAuth && !this.user?.isAuthenticated()) {
return 'Login'
}
return undefined
}
}
created() {
this.user = <your_method_to_get_user>
}
}
</script>
// Router
import { createRouter, createWebHistory } from "vue-router";
import Login from "#/views/Login.vue";
import Header from "#/components/Header.vue";
const routes = [
{
path: "/",
name: "Login",
components: {
default: Login,
Header: Header,
},
props: {
Header: { showMenu: false }, // showMenu prop will beaccessible in Header
},
meta: { requiresAuth: false },
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
But I'd suggest using 2 different routes for login and header and redirecting from header to login if user not logged in and vice versa via the next() as described here.

Vue 3 Components Not Loading

I've spent the last 5 hours trying to figure out what's not working with my setup.
What I basically want to do is have a "wrapper" app that loads my common codebase (which is a Vue component).
App.js
import { createApp } from "vue";
import AppWrapper from "#src/js/AppWrapper.vue";
import Router from "#common/libraries/Router.js";
const vm = createApp(AppWrapper);
vm.use(Router);
vm.mount("#app");
AppWrapper.vue
<template>
<div>
<app></app>
</div>
</template>
<script>
import App from "#common/App.vue";
export default {
created() {},
components: {
App,
},
};
</script>
<style lang="scss" scoped></style>
App.vue
<template>
<div>
<h1>Ok</h1>
</div>
</template>
<script>
export default {
created() {},
};
</script>
Somehow, the app tag isn't replaced and my component is not loading in the page.
Here's the webpack file:
const Webpack = require("webpack");
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const LiveReloadPlugin = require("webpack-livereload-plugin");
module.exports = (env) => {
return {
entry: {
app: ["./app/src/common/App.js"],
},
output: {
path: path.resolve(__dirname, "public/"),
filename: "js/[name].js",
sourceMapFilename: "js/[name].js.map",
},
resolve: {
alias: {
"#src": path.resolve(__dirname, "app/src/"),
"#common": path.resolve(__dirname, "app/src/common/"),
},
},
module: {
rules: [
{
test: /\.vue$/,
use: ["vue-loader"],
},
],
},
watchOptions: {
aggregateTimeout: 300,
poll: 500,
ignored: /node_modules/,
},
plugins: [new VueLoaderPlugin(), new LiveReloadPlugin()],
};
};
Am I missing something?
Thanks!
Alright, I figured it out. Vue was loaded twice, once in my app package.json and once in the common sources. Removing it from one location worked!

Vue-router dynamic load menu tree

I'm trying to create a menu tree with vue-router by ajax request,but the $mount function was called before the ajax request responsed, so the router in the Vue instance always null.
Is there any good solution here?
Here is my code (index.js):
import Vue from 'vue';
import Element from 'element-ui';
import entry from './App.vue';
import VueRouter from 'vue-router';
import VueResource from 'vue-resource';
import Vuex from 'vuex'
import configRouter from './route.config';
import SideNav from './components/side-nav';
import Css from './assets/styles/common.css';
import bus from './event-bus';
import dynamicRouterConfig from './dynamic.router';
Vue.use(VueRouter);
Vue.use(Element);
Vue.use(VueResource);
Vue.use(Vuex);
Vue.http.interceptors.push((request, next) => {
bus.$emit('toggleLoading');
next(() => {
bus.$emit('toggleLoading');
})
})
Vue.component('side-nav', SideNav);
app = new Vue({
afterMounted(){
console.info(123);
},
render: h => h(entry),
router: configRouter
});
app.$mount('#app');
route.config.js:
import navConfig from './nav.config';
import dynamicRouterConfig from './dynamic.router';
let route = [{
path: '/',
redirect: '/quickstart',
component: require('./pages/component.vue'),
children: []
}];
const registerRoute = (config) => {
//require(`./docs/zh-cn${page.path}.md`)
//require(`./docs/home.md`)
function addRoute(page) {
if (page.show == false) {
return false;
}
let component = page.path === '/changelog' ? require('./pages/changelog.vue') : require(`./views/alert.vue`);
if (page.path === '/edit') {
component = require('./views/edit.vue');
}
let com = component.default || component;
let child = {
path: page.path.slice(1),
meta: {
title: page.title || page.name,
description: page.description
},
component: com
};
route[0].children.push(child);
}
//if (config && config.length>0) {
config.map(nav => {
if (nav.groups) {
nav.groups.map(group => {
group.list.map(page => {
addRoute(page);
});
});
} else if (nav.children) {
nav.children.map(page => {
addRoute(page);
});
} else {
addRoute(nav);
}
});
//}
return { route, navs: config };
};
const myroute = registerRoute(navConfig);
let guideRoute = {
path: '/guide',
name: 'Guide',
redirect: '/guide/design',
component: require('./pages/guide.vue'),
children: [{
path: 'design',
name: 'Design',
component: require('./pages/design.vue')
}, {
path: 'nav',
name: 'Navigation',
component: require('./pages/nav.vue')
}]
};
let resourceRoute = {
path: '/resource',
name: 'Resource',
component: require('./pages/resource.vue')
};
let indexRoute = {
path: '/',
name: 'Home',
component: require('./pages/index.vue')
};
let dynaRoute = registerRoute(dynamicRouterConfig).route;
myroute.route = myroute.route.concat([indexRoute, guideRoute, resourceRoute]);
myroute.route.push({
path: '*',
component: require('./docs/home.md')
});
export const navs = myroute.navs;
export default myroute.route;
And dynamic.router.js:
module.exports = [
{
"name": "Edit",
"path": "/edit"
}
]
Now the static route config is woking fine ,but how can I load data from server side by ajax request in the route.config.js instead of static data.
Waiting for some async request at page render is fine, just set empty initial values in the data section of component like:
data() {
someStr: '',
someList: []
}
and make sure you handle the empty values well without undefined errors trying to read things like someList[0].foo.
Then when the request comes back, set the initially empty values to those real data you get from the request.
Giving the user some visual indicate that you're fetching the data would be a good practice. I've found v-loading in element-ui useful for that.

Using CKEditor 5 with nuxtjs

I'm trying to import a custom build of CKEditor 5 in my Nuxtjs project and I've been tried every possible way to import it correctly but none of them worked for me and this is one of them:
let ClassicEditor
let CKEditor
if (process.client) {
ClassicEditor = require('./../../static/js/ckeditor')
CKEditor = require('#ckeditor/ckeditor5-vue')
}else{
CKEditor = { component : {template:'<div></div>'}}
}
data() {
return {
editor: ClassicEditor,
}
}
components:{
ckeditor: CKEditor.component
},
<client-only><ckeditor :editor="editor" /></client-only>
Every time I change the way a different error appears, for example, Window is not Defined and when i use different way shows a different error so I want to know what is the most correct way to use CKEditor with Nuxtjs in universal mode, consider that i haven't done anything and help me with the correct way starting from the installation
try this
npm install --save #blowstack/ckeditor-nuxt
You can use by importing the package on client side.
Create Editor.vue components
<template>
<ckeditor
:editor="editor"
:config="editorConfig"
/>
</template>
<script>
let ClassicEditor
let CKEditor
if (process.client) {
ClassicEditor = require('#ckeditor/ckeditor5-build-classic')
CKEditor = require('#ckeditor/ckeditor5-vue2')
} else {
CKEditor = { component: { template: '<div></div>' } }
}
export default {
components: {
ckeditor: CKEditor.component
},
data () {
return {
editor: ClassicEditor,
editorConfig: {}
}
}
</script>
Usage:
<client-only placeholder="Loading Text Editor...">
<editor v-model="textInput"/>
</client-only>
window is not defined
If you set ssr: true in the nuxt.config.js and put your custom VCKEditor.vue into the components folder, the Nuxt will scan the components folder by Server Side and it doesn't know the window keyword which is the JavaScript object in #ckeditor/ckeditor5-vue2.
You can see more about components doc.
I don't like use the process.client to check ssr mode.
I have two solutions, just choose one
set components: false in the nuxt.config.js.
Move the your custom VCKEditor.vue to components folder outside.
Finally, register custom VCKEditor.vue as plugins and set plugins ssr: false in the nuxt.config.js.
Here is the sample project.
The code snippet
nuxt.config.js
const path = require('path')
const CKEditorWebpackPlugin = require("#ckeditor/ckeditor5-dev-webpack-plugin")
const CKEditorStyles = require("#ckeditor/ckeditor5-dev-utils").styles
export default {
// ignore other settings...
ssr: true,
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
{
src: '~/plugins/ckeditor.js', ssr: false
},
],
// set false to disable scan the components folder
components: false,
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
transpile: [/ckeditor5-[^/\\]+[/\\]src[/\\].+\.js$/],
plugins: [
// If you set ssr: true that will cause the following error. This error does not affect the operation.
// ERROR [CKEditorWebpackPlugin] Error: No translation has been found for the zh language.
new CKEditorWebpackPlugin({
// See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
language: "zh",
additionalLanguages: 'all',
addMainLanguageTranslationsToAllAssets: true,
})
],
// If you don't add postcss, the CKEditor css will not work.
postcss: CKEditorStyles.getPostCssConfig({
themeImporter: {
themePath: require.resolve("#ckeditor/ckeditor5-theme-lark")
},
minify: true
}),
extend(config, ctx) {
// If you do not exclude and use raw-loader to load svg, the following errors will be caused.
// Cannot read property 'getAttribute' of null
const svgRule = config.module.rules.find(item => {
return /svg/.test(item.test);
})
svgRule.exclude = [path.join(__dirname, 'node_modules', '#ckeditor')];
// add svg to load raw-loader
config.module.rules.push({
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
use: ["raw-loader"]
})
}
}
}
components/editor/VCKEditor.vue
<template>
<ckeditor
v-model="editorData"
:editor="editor"
:config="editorConfig"
></ckeditor>
</template>
<script>
import CKEditor from '#ckeditor/ckeditor5-vue2'
import ClassicEditor from '#ckeditor/ckeditor5-editor-classic/src/classiceditor'
import Bold from '#ckeditor/ckeditor5-basic-styles/src/bold'
import Italic from '#ckeditor/ckeditor5-basic-styles/src/italic.js'
import Underline from '#ckeditor/ckeditor5-basic-styles/src/underline'
import Strikethrough from '#ckeditor/ckeditor5-basic-styles/src/strikethrough'
// less Heading + Essentials plugin can't input the text
import Heading from '#ckeditor/ckeditor5-heading/src/heading'
import Essentials from '#ckeditor/ckeditor5-essentials/src/essentials'
import ImageUpload from '#ckeditor/ckeditor5-image/src/imageupload'
import ImageInsert from '#ckeditor/ckeditor5-image/src/imageinsert'
import AutoImage from '#ckeditor/ckeditor5-image/src/autoimage'
import Image from '#ckeditor/ckeditor5-image/src/image'
import ImageResizeEditing from '#ckeditor/ckeditor5-image/src/imageresize/imageresizeediting'
import ImageResizeHandles from '#ckeditor/ckeditor5-image/src/imageresize/imageresizehandles'
import Base64UploadAdapter from '#ckeditor/ckeditor5-upload/src/adapters/base64uploadadapter'
export default {
name: 'VCKEditor',
components: { ckeditor: CKEditor.component },
props: {
value: {
type: String,
},
},
computed: {
editorData: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
},
},
},
data() {
return {
editor: ClassicEditor,
editorConfig: {
plugins: [
Bold,
Italic,
Underline,
Strikethrough,
Heading,
Essentials,
ImageUpload,
ImageInsert,
AutoImage,
Image,
ImageResizeEditing,
ImageResizeHandles,
Base64UploadAdapter,
],
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'underline',
'strikethrough',
'|',
'insertImage',
],
},
language: 'zh',
},
}
},
}
</script>
plugins/ckeditor.js
import Vue from 'vue';
import VCKEditor from "../components/editor/VCKEditor.vue";
Vue.component('v-ckeditor', VCKEditor);
pages/index.vue
<template>
<client-only>
<v-ckeditor v-model="text" />
</client-only>
</template>
<script>
export default {
data() {
return {
text: 'Hello World!!',
}
},
}
</script>
Use #blowstack/ckeditor-nuxt package.
Here is editor config for uploader.
Use #blowstack/ckeditor-nuxt package.
Here is editor config for uploader.
editorConfig: {
simpleUpload: {
uploadUrl: `${process.env.apiUrl}/api/console/uploads/single_file`,
headers: {
authorization: `Bearer ${_.get(
this.$store,
"state.agency.global.token"
)}`,
},
},
removePlugins: ["Title"],
}
Response data from upload API like this:
{
url: ".../image.png"
}
Refs: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/upload-adapter.html#passing-additional-data-to-the-response
editorConfig: {
simpleUpload: {
uploadUrl: `${process.env.apiUrl}/api/console/uploads/single_file`,
headers: {
authorization: `Bearer ${_.get(
this.$store,
"state.agency.global.token"
)}`,
},
},
removePlugins: ["Title"],
}