Dynamic img src were handled by Webpack's require:
<img :src="require(`#/assets/${posts.img}`)" alt="">
How to do this on a vite-app that uses Rollup?
you can refer to docs of webpack-to-vite:
use Vite's API import.meta.glob to convert dynamic require(e.g. require('#assets/images/' + options.src)), you can refer to the following steps
create a Model to save the imported modules, use async methods to dynamically import the modules and update them to the Model
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
const assets = import.meta.glob('../assets/**')
Vue.use(Vuex)
export default new Vuex.Store({
state: {
assets: {}
},
mutations: {
setAssets(state, data) {
state.assets = Object.assign({}, state.assets, data)
}
},
actions: {
async getAssets({ commit }, url) {
const getAsset = assets[url]
if (!getAsset) {
commit('setAssets', { [url]: ''})
} else {
const asset = await getAsset()
commit('setAssets', { [url]: asset.default })
}
}
}
})
use in .vue SFC
// img1.vue
<template>
<img :src="$store.state.assets['../assets/images/' + options.src]" />
</template>
<script>
export default {
name: "img1",
props: {
options: Object
},
watch: {
'options.src': {
handler (val) {
this.$store.dispatch('getAssets', `../assets/images/${val}`)
},
immediate: true,
deep: true
}
}
}
</script>
Related
I've got a NUXT/VUE component which does an API call and assigns the result to the data, therefore template maps the result to the page. Simple API call.
I am just not sure how I can do that in Storybook component?
Do I have to mock the API fetch or pass the static data to the component in the Storybook?
Examples on the the official website is all about props, nothing about data
https://storybook.js.org/docs/react/writing-stories/args
Here is my simple component
<template>
<div>{{blogPosts.title}}</div>
</template>
<script>
export default {
data() {
return {
blogPosts: [],
};
},
async fetch() {
this.blogPosts = await this.$http.$get("https://api.nuxtjs.dev/posts");
},
};
</script>
Here is my Storybook Component:
import { Meta, Story } from "#storybook/vue";
import BlogCarousel from "./BlogCarousel.vue";
import { BlogPost } from "~/lib/types/BlogPost";
export default {
title: "BlogCarousel",
components: BlogCarousel,
} as Meta;
const Template: Story<BlogPost> = (args) => {
return {
components: { BlogCarousel },
template: `<BlogCarousel v-bind=${args.blogPosts} />`,
};
};
export const Default = Template.bind({});
Default.args = {
blogPosts: [
{
title: "test",
created: "today",
},
],
};
I am recieving "Critical dependency: require function is used in a way in which dependencies cannot be statically extracted friendly-errors 16:21:14" error when using the package scrollMonitor in my nuxt project
plugins/scroll-monitor.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import ScrollMonitor from 'scrollmonitor';
Vue.use(ScrollMonitor);
nuxt.config.js
plugins: [
'~/plugins/wordpress-api',
{ src: '~/plugins/scroll-monitor.js', ssr: false }
],
build: {
/*
** You can extend webpack config here
*/
vendor: ['scrollmonitor'],
extend(config, ctx) {
}
}
At my index.vue file
let scrollmonitor
if (process.client) {
scrollmonitor = require('scrollmonitor')
}
More context
Still not working.
I am using new computer, at my old one everything is working fine.
index.vue
<template>
<div class="index page-padding-top">
<TheHero
:scaledUpDot="scaledUpDot"
:isFirstImageVisible="isFirstImageVisible"
/>
<ProjectsList :projects="projects" />
</div>
</template>
<script>
import { mapGetters } from "vuex";
import TheHero from "~/components/TheHero";
import ProjectsList from "~/components/ProjectsList";
export default {
async mounted () {
if (process.browser) {
const scrollMonitor = await import('scrollmonitor')
Vue.use(scrollMonitor)
console.log('HELLO FROM MOUNTED')
}
},
name: "Index",
components: { TheHero, ProjectsList},
data() {
return {
scaledUpDot: false,
isFirstImageVisible: false,
};
},
computed: {
...mapGetters({
projects: "getProjects",
}),
},
mounted() {
this.handleScaling();
this.hideScrollSpan();
},
destroyed() {
this.handleScaling();
this.hideScrollSpan();
},
methods: {
handleScaling() {
if (process.client) {
const heroSection = document.querySelectorAll(".hero");
const heroSectionWtcher = scrollMonitor.create(heroSection, 0);
heroSectionWtcher.enterViewport(() => {
this.scaledUpDot = true;
});
}
},
hideScrollSpan() {
if (process.client) {
const images = document.querySelectorAll(".projects-home img");
const firstImage = images[0];
const imageWatcher = scrollMonitor.create(firstImage, -30);
imageWatcher.enterViewport(() => {
this.isFirstImageVisible = true;
});
}
},
},
};
</script>
In my old computer I have it imported like this :
import { mapGetters } from 'vuex'
import scrollMonitor from 'scrollmonitor'
But when I want to run this in a new one I get an error that window is not defined
So I have started to add this plugin in other way and still not working
Still not working.
I am using new computer, at my old one everything is working fine.
index.vue
<template>
<div class="index page-padding-top">
<TheHero
:scaledUpDot="scaledUpDot"
:isFirstImageVisible="isFirstImageVisible"
/>
<ProjectsList :projects="projects" />
</div>
</template>
<script>
import { mapGetters } from "vuex";
import TheHero from "~/components/TheHero";
import ProjectsList from "~/components/ProjectsList";
export default {
async mounted () {
if (process.browser) {
const scrollMonitor = await import('scrollmonitor')
Vue.use(scrollMonitor)
console.log('HELLO FROM MOUNTED')
}
},
name: "Index",
components: { TheHero, ProjectsList},
data() {
return {
scaledUpDot: false,
isFirstImageVisible: false,
};
},
computed: {
...mapGetters({
projects: "getProjects",
}),
},
mounted() {
this.handleScaling();
this.hideScrollSpan();
},
destroyed() {
this.handleScaling();
this.hideScrollSpan();
},
methods: {
handleScaling() {
if (process.client) {
const heroSection = document.querySelectorAll(".hero");
const heroSectionWtcher = scrollMonitor.create(heroSection, 0);
heroSectionWtcher.enterViewport(() => {
this.scaledUpDot = true;
});
}
},
hideScrollSpan() {
if (process.client) {
const images = document.querySelectorAll(".projects-home img");
const firstImage = images[0];
const imageWatcher = scrollMonitor.create(firstImage, -30);
imageWatcher.enterViewport(() => {
this.isFirstImageVisible = true;
});
}
},
},
};
</script>
In my old computer I have it imported like this :
import { mapGetters } from 'vuex'
import scrollMonitor from 'scrollmonitor'
But when I want to run this in a new one I get an error that window is not defined
So I have started to add this plugin in other way and still not working
In vuejs 2 it's possible to assign components to global variables on the main app instance like this...
const app = new Vue({});
Vue.use({
install(Vue) {
Vue.prototype.$counter = new Vue({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ },
}
});
}
})
app.$mount('#app');
But when I convert that to vue3 I can't access any of the properties or methods...
const app = Vue.createApp({});
app.use({
install(app) {
app.config.globalProperties.$counter = Vue.createApp({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ }
}
});
}
})
app.mount('#app');
Here is an example for vue2... https://jsfiddle.net/Lg49anzh/
And here is the vue3 version... https://jsfiddle.net/Lathvj29/
So I'm wondering if and how this is still possible in vue3 or do i need to refactor all my plugins?
I tried to keep the example as simple as possible to illustrate the problem but if you need more information just let me know.
Vue.createApp() creates an application instance, which is separate from the root component of the application.
A quick fix is to mount the application instance to get the root component:
import { createApp } from 'vue';
app.config.globalProperties.$counter = createApp({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ }
}
}).mount(document.createElement('div')); 👈
demo 1
However, a more idiomatic and simpler solution is to use a ref:
import { ref } from 'vue';
const counter = ref(1);
app.config.globalProperties.$counter = {
value: counter,
increment() { counter.value++ }
};
demo 2
Not an exact answer to the question but related. Here is a simple way of sharing global vars between components.
In my main app file I added the variable $navigationProps to global scrope:
let app=createApp(App)
app.config.globalProperties.$navigationProps = {mobileMenuClosed: false, closeIconHidden:false };
app.use(router)
app.mount('#app')
Then in any component where I needed that $navigationProps to work with 2 way binding:
<script>
import { defineComponent, getCurrentInstance } from "vue";
export default defineComponent({
data: () => ({
navigationProps:
getCurrentInstance().appContext.config.globalProperties.$navigationProps,
}),
methods: {
toggleMobileMenu(event) {
this.navigationProps.mobileMenuClosed =
!this.navigationProps.mobileMenuClosed;
},
hideMobileMenu(event) {
this.navigationProps.mobileMenuClosed = true;
},
},
Worked like a charm for me.
The above technique worked for me to make global components (with only one instance in the root component). For example, components like Loaders or Alerts are good examples.
Loader.vue
...
mounted() {
const currentInstance = getCurrentInstance();
if (currentInstance) {
currentInstance.appContext.config.globalProperties.$loader = this;
}
},
...
AlertMessage.vue
...
mounted() {
const currentInstance = getCurrentInstance();
if (currentInstance) {
currentInstance.appContext.config.globalProperties.$alert = this;
}
},
...
So, in the root component of your app, you have to instance your global components, as shown:
App.vue
<template>
<v-app id="allPageView">
<router-view name="allPageView" v-slot="{Component}">
<transition :name="$router.currentRoute.name">
<component :is="Component"/>
</transition>
</router-view>
<alert-message/> //here
<loader/> //here
</v-app>
</template>
<script lang="ts">
import AlertMessage from './components/Utilities/Alerts/AlertMessage.vue';
import Loader from './components/Utilities/Loaders/Loader.vue';
export default {
name: 'App',
components: { AlertMessage, Loader }
};
</script>
Finally, in this way you can your component in whatever other components, for example:
Login.vue
...
async login() {
if (await this.isFormValid(this.$refs.loginObserver as FormContext)) {
this.$loader.activate('Logging in. . .');
Meteor.loginWithPassword(this.user.userOrEmail, this.user.password, (err: Meteor.Error | any) => {
this.$loader.deactivate();
if (err) {
console.error('Error in login: ', err);
if (err.error === '403') {
this.$alert.showAlertFull('mdi-close-circle', 'warning', err.reason,
'', 5000, 'center', 'bottom');
} else {
this.$alert.showAlertFull('mdi-close-circle', 'error', 'Incorrect credentials');
}
this.authError(err.error);
this.error = true;
} else {
this.successLogin();
}
});
...
In this way, you can avoid importing those components in every component.
When we use vue2 to create API, we just follow options API like below:
data are in data
methods are in methods
<script>
export default {
name: 'demo',
components: {},
filter:{},
mixins:{},
props: {},
data(){
return{
}
},
computed:{},
watch:{},
methods: {},
}
</script>
But the vue3 changed, how should I build a component with vue3 composition API?
Some example say that I should import reactive etc. From vue first and put all codes in setup(){}?
Some example show that I can add setup to <script>?
Please give me an example.
ok bro , Composition Api works like that:
<script>
import { fetchTodoRepo } from '#/api/repos'
import {ref,onMounted} from 'vue'
export default {
setup(props){
const arr = ref([]) // Reactive Reference `arr`
const getTodoRepo = async () => {
arr.value = await fetchTodoRepo(props.todo)
}
onMounted(getUserRepo) // on `mounted` call `getUserRepo`
return{
arr,
getTodoRepo
}
}
}
</script>
There are two ways to create a component in vue3.
One:<script> + setup(){},such as this:
<script>
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup (props, { emit }) {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
onMounted(() => {
console.log('title: ' + props.title)
})
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
return {
login,
state
}
}
}
</script>
Two:use <script setup="props">
loading....
I'm new to Vue.js and looking for the equivalent of a service in AngularJS, specifically for storing data once and getting it throughout the app.
I'll be mainly storing the results of network requests and other promised data so I don't need to fetch again on very state.
I'm using Vue.JS 2.0 with Webpack.
Thanks!
I think what u are seeking for is vuex, which can share data from each component.
Here is a basic demo which from my code.
store/lottery.module.js
import lotteryType from './lottery.type'
const lotteryModule = {
state: {participantList: []},
getters: {},
mutations: {
[lotteryType.PARTICIPANT_CREATE] (state, payload) {
state.participantList = payload;
}
},
actions: {
[lotteryType.PARTICIPANT_CREATE] ({commit}, payload) {
commit(lotteryType.PARTICIPANT_CREATE, payload);
}
}
};
export default lotteryModule;
store/lottery.type.js
const PARTICIPANT_CREATE = 'PARTICIPANT_CREATE';
export default {PARTICIPANT_CREATE};
store/index.js
Vue.use(Vuex);
const store = new Vuex.Store();
store.registerModule('lottery', lotteryModule);
export default store;
component/lottery.vue
<template>
<div id="preparation-container">
Total Participants: {{participantList.length}}
</div>
</template>
<script>
import router from '../router';
import lotteryType from '../store/lottery.type';
export default {
data () {
return {
}
},
methods: {
},
computed: {
participantList() {
return this.$store.state.lottery.participantList;
}
},
created() {
this.$store.dispatch(lotteryType.PARTICIPANT_CREATE, [{name:'Jack'}, {name:'Hugh'}]);
},
mounted() {
},
destroyed() {
}
}
</script>
You don't need Vue-specific services in Vue2 as it is based on a modern version of JavaScript that uses Modules instead.
So if you want to reuse some services in different locations in your code, you could define and export it as follows:
export default {
someFunction() {
// ...
},
someOtherFunction() {
// ...
}
};
And then import from your Vue code:
import service from 'filenameofyourresources';
export default {
name: 'something',
component: [],
data: () => ({}),
created() {
service.someFunction();
},
};
Note that this is ES6 code that needs to be transpiled to ES5 before you can actually use it todays browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export