Why can't I use dragula in Vue3 setup but mounted? - vue.js

When I use dragula in vue3 setup. It isn't working. Like this:
setup() {
const dragFrom = ref(null);
const dragTo = ref(null);
onMounted(() => {
dragula([dragFrom, dragTo], {
copy: (el) => {
console.log(el);
return true;
},
accepts: () => {
return true;
},
});
});
return { dragFrom, dragTo };
}
But this way can be successful:
mounted() {
const dragFrom = this.$refs.dragFrom;
const dragTo = this.$refs.dragTo;
dragula([dragFrom, dragTo], {
copy: function (el, source) {
console.log(el);
return true;
},
accepts: function (el, target) {
return true;
},
});
}
Both methods are based on vue3.What's wrong?

Your issue comes from the fact that you are not accessing the value of the ref, i.e. dragFrom.value and dragTo.value when passing them into the dragula() function. Remember that when you create a reactive and mutable ref object, you will need to access its inner value using the .value property.
This should therefore work:
setup() {
const dragFrom = ref(null);
const dragTo = ref(null);
onMounted(() => {
// Ensure you access the VALUE of the ref!
dragula([dragFrom.value, dragTo.value], {
copy: (el) => {
console.log(el);
return true;
},
accepts: () => {
return true;
},
});
});
return { dragFrom, dragTo };
}
See proof-of-concept on this demo CodeSandbox I've created: https://uwgri.csb.app/

Related

Unable to update state in redux when using #reduxjs/toolkit

I currently started learning redux. My code was working perfectly with core redux, then I tried out #reduxjs/toolkit and now I'm unable to access the function to change the state in the store. Here is my code of reducer.
const seasonEdits = createSlice({
name: "seasons",
initialState: [],
reducers: {
addSeason(state, action) {
state.push(action.payload);
console.log("this here");
},
removeSeason(state, action) {
state.filter((season) => season.id !== action.payload);
},
markComplete(state, action) {
state.map((season) => {
if (season.id == action.payload) season.isWatched = !season.isWatched;
});
},
},
});
export const { addSeason, removeSeason, markComplete } = seasonEdits.actions;
export default seasonEdits.reducer;
and my store.js file
import { configureStore } from "#reduxjs/toolkit";
import seasonReducer from "./reducer";
export default store = configureStore({
reducer: {
seasons: seasonReducer,
},
});
and the add.js file which has add functionality. Calling a handleSubmit function which is creating an object and adding it to an array which is the state in store.
const handleSubmit = async () => {
try {
if (!name || !totalNoSeason) {
return alert("Please add both fields");
}
const seasonToAdd = {
id: shortid.generate(),
name,
totalNoSeason,
isWatched: false,
};
addSeason(seasonToAdd);
navigation.navigate("Home");
} catch (error) {
console.log(error);
}
};
const mapDispatchToProps = (dispatch) => {
return {
addSeason: (data) => dispatch(addSeason(data)),
};
};
Add.propTypes = {
addSeason: propTypes.func.isRequired,
};
export default connect(null, mapDispatchToProps)(Add);
The issue is that array.map() and array.filter() return new arrays! Right now your reducers are calling those functions, and then just throwing away the new arrays:
removeSeason(state, action) {
// The return value is thrown away and ignored!
state.filter((season) => season.id !== action.payload);
},
You need to return the new value:
removeSeason(state, action) {
// Now RTK will see the new return value
return state.filter((season) => season.id !== action.payload);
},
See https://redux-toolkit.js.org/usage/immer-reducers#resetting-and-replacing-state for more details.

How to integrate paypal Payment Button Vuejs3 Composition API (setup function)

I'm trying to integrate PayPal buttons with my Vuejs3 project using Composition API (setup ) but all what i get is errors i try to integrate it without using setup and its working fine i leave the working script down
the esseu is i couldent pass data from data to methodes
<script>
import { inject, onMounted, ref } from "vue";
export default {
data() {
return {
loaded: false,
paidFor: false,
product: {
price: 15.22,
description: "leg lamp from that one movie",
img: "./assets/lamp.jpg",
},
};
},
setup() {
const store = inject("store");
console.log(store.state.prodects_in_cart);
return { store };
},methods:{
setLoaded: function() {
this.loaded = true;
paypal_sdk
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: this.product.description,
amount: {
currency_code: "USD",
value: this.product.price
}
}
]
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.data;
this.paidFor = true;
console.log(order);
},
onError: err => {
console.log(err);
}
})
.render(this.$refs.paypal);
}
},
mounted: function() {
const script = document.createElement("script");
script.setAttribute('data-namespace',"paypal_sdk");
script.src ="https://www.paypal.com/sdk/js?client-id=Here i pute my Client Id";
script.addEventListener("load", this.setLoaded);
document.body.appendChild(script);
},
};
</script>
the error i get when i use setup() is
The error image
my script using setup()
setup() {
const store = inject("store");
const paypal = ref(null);
let loaded = ref(false);
let paidFor = ref(false);
const product = {
price: 15.22,
description: "leg lamp from that one movie",
img: "./assets/lamp.jpg",
};
onMounted: {
const script = document.createElement("script");
script.setAttribute("data-namespace", "paypal_sdk");
script.src =
"https://www.paypal.com/sdk/js?client-id=AXDJPmFjXpXm9HMXK4uZcW3l9XrCL36AxEeWBa4rhV2-xFcVYJrGKvNowY-xf2PitTSkStVNjabZaihe";
script.addEventListener("load", ()=>{
loaded = true;
console.log('hello adil');
paypal_sdk
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: 'this is product description',
amount: {
currency_code: "USD",
value: 120.00,
},
},
],
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.data;
this.paidFor = true;
console.log(order);
},
onError: (err) => {
console.log(err);
},
})
.render(paypal);
});
document.body.appendChild(script);
}
return { store ,paypal};
}
paypal is a ref. You're currently passing to paypal_sdk the ref itself and not the inner value, which would be the template ref's element. To fix this, pass the ref's .value.
Your onMounted code is not properly invoked, as it must be passed a callback.
import { onMounted, ref } from 'vue'
export default {
setup() {
const paypal = ref(null)
onMounted(/* 2 */ () => {
const script = document.createElement('script')
//...
script.addEventListener('load', () => {
paypal_sdk
.Buttons(/*...*/)
.render(paypal.value) /* 1 */
})
})
return {
paypal
}
}
}
The reason why you are getting that error is because you are using option Api onMounted life cycle hook, instead of doing that use the vue 3 life cycle hooks for onMounted.
First you will have to import it from vue like this.
<script>
import {onMounted} from 'vue'
then you are going to use it like this.
return it as a call back function
onMounted(() => {
//all your code should placed inside here and it will work
})
</script>
Here is my answer using the paypal-js npm package
<template>
<div ref="paypalBtn"></div>
</template>
<script>
import { onMounted, ref } from 'vue';
import { loadScript } from '#paypal/paypal-js';
const paypalBtn = ref(null);
onMounted(async () => {
let paypal;
try {
paypal = await loadScript({
'client-id': 'you_client_id_goes_here',
});
} catch (error) {
console.error('failed to load the PayPal JS SDK script', error);
}
if (paypal) {
try {
await paypal.Buttons().render(paypalBtn.value);
} catch (error) {
console.error('failed to render the PayPal Buttons', error);
}
}
});
</script>

How to dispatch store actions in namespaced modules ( NuxtJS)?

I have made lots of research and since keywords are always similar I cannot find a correct way of usage store modules in nuxtjs. I will shorten the codes to make it readable. In my nuxtjs application, I am trying to reach my vuex modules in my home.page but instead I get
pages/index.vue
TypeError
Cannot read property 'then' of undefined
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
.then(() => {
While creating modules first in the store folder i have created an index.js file:
import Vuex from "vuex";
import articles from "./modules/articles";
// ...
import videolessons from "./modules/videolessons";
const debug = process.env_NODE_ENV !== 'production';
export const store = new Vuex.Store({
modules: {
articles,
books,
members,
pages,
status,
user,
videolessons,
},
strict: debug,
plugins: [],
})
and basically my modules are similar to my articles module:
const getDefaultState = () => {
return {
indexArticles: [],
}
}
const state = getDefaultState()
const getters = {
indexArticles (state) {
return state.indexArticles
},
}
const mutations = {
fetchStart (state) {
state.loading = true
},
fetchEnd (state) {
state.loading = false
},
setIndexArticles (state, pArticles) {
state.indexArticles = pArticles
state.errors = {}
},
setError (state, errors) {
state.errors = errors
},
resetState (state) {
Object.assign(state, getDefaultState())
}
}
const actions = {
// ...
async fetchIndexArticles ({ commit }) {
try {
const response = await articlesService.fetchIndexArticles()
commit('fetchStart')
commit('setIndexArticles', response.data)
commit('fetchEnd')
return response
} catch (error) {
commit('setError', error)
this._vm.$q.loading.hide()
}
},
...
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
and in my index page:
<script>
import store from '../store/'
export default {
computed: {
indexarticles() {
return this.$store.getters['articles/indexArticles'];
}
},
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
...
.then(() => {
this.isLoading = false;
});
}
};
</script>
<template>...</template>
can you help to correct my store modules?
Thanks
ps:
videolessons.js
const getDefaultState = () => {
return {
indexvideolessons: [],
}
}
const state = getDefaultState()
const getters = {
indexVideolessons (state) {
return state.indexvideolessons
},
}
const mutations = {
setIndexVideolessons (state, pVideolessons) {
state.indexvideolessons = pVideolessons
state.errors = {}
},
}
const actions = {
async fetchIndexVideolessons ({ commit, dispatch }) {
try {
const response = await videolessonsService.fetchIndexVideolessons()
commit('setIndexVideolessons', response.data)
return response
} catch (error) {
commit('setError', error)
}
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

how can i use async and await in action object in vuex?

I'm gonna use an API and take it off some information, I use async/ await in mutations but as you know it's not standard that we used async data in mutation, and we have to use it in actions but how we can do it?
here my vuex codes:
import axios from "axios";
const state = {
token: "hjOa0PgKqC7zm86P10F3BQkTuLsEV4wh",
posts: [],
pending: true,
error: false,
}
const mutations = {
async getDataFromApi(state) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
state.posts = res.data;
state.error = false;
}
} catch (e) {
state.posts = null;
state.error = e;
}
state.pending = false;
},
};
const actions = {
showData({
commit
}) {
commit("getDataFromApi");
},
}
and here vuejs codes that I used in the component :
<script>
import { mapState } from "vuex";
export default {
name: "Home",
mounted() {
this.getDataFromApi();
},
computed: {
...mapState(["pending", "error", "posts"]),
},
methods: {
getDataFromApi() {
this.$store.dispatch("showData");
},
},
};
</script>
It works perfectly in mutation but for standards, how can use this in action instead of mutation?
Well, actually it is pretty similar to what you have done so far :
const mutations = {
getDataFromApi(state, data) {
state.posts = data;
state.error = false;
state.pending = false;
},
setError(state, error) {
state.error = error;
state.posts = null;
state.pending = false;
},
};
const actions = {
async showData({ commit }) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
commit("getDataFromApi", res.data);
} else {
commit("setError", new Error("Something went wrong."));
}
} catch (e) {
commit("setError", e);
}
},
};

Reset Vue for every jest test?

I am using Vue JS with #vue/test-utils and jest. For my tests I am calling:
let localVue = createLocalVue();
vueMount(MyComponent, { localVue: localVue, options });
The problem is, I am referencing libraries which does stuff like this:
import Vue from 'vue'
import Msal from 'vue-msal'
//...
Vue.use(Msal, {...});
The Vue.use() registers some global stuff on the prototype, etc. For testing purposes, I need this to start fresh each test. The only thing I could think of is to use mockImplementation() with jest on the Vue object. But I am not quite sure how I could accomplish that, if at all possible.
Is there any way to do this? Thanks!
It took me a while, but here is how I solved this...
let setupComplete = false;
let setupFailure = false;
let testContext = {};
function resetTestContext() {
Object.keys(testContext).forEach(function(key) { delete testContext[key]; });
}
function createTestContext(configureTestContext) {
beforeEach(() => {
jest.isolateModules(() => {
setupFailure = true;
jest.unmock('vue');
resetTestContext();
testContext.vueTestUtils = require('#vue/test-utils');
testContext.vue = testContext.vueTestUtils.createLocalVue();
jest.doMock('vue', () => testContext.vue);
testContext.vuetify = require('vuetify');
testContext.vue.use(testContext.vuetify);
testContext.vuetifyInstance = new testContext.vuetify();
if (configureTestContext) {
configureTestContext(testContext);
}
setupComplete = true;
setupFailure = false;
});
});
afterEach(() => {
setupComplete = false;
resetTestContext();
jest.resetModules();
setupFailure = false;
});
return testContext;
},
What made this possible was the jest.isolateModules() method. With this approach, Vue and it's prototype, and also Vuetify, are completely recreated and brand new with each test case.
For it to work, the test spec and the library containing the utility above may not 'import' Vue or any module which depends on Vue. Instead, it needs to be required in the configureTestContext() function or in the test case itself.
My test specs look like this:
import createTestContext from '#/scripts/createTestContext'
describe('sample', () => {
const testContext = createTestContext(function configureTestContext(testContext)
{
testContext.vueDependency = require('#/scripts/vueDependency').default;
});
test('demo', () => {
// I added a helper to more easily do this in the test context...
const sample = testContext.vueTestUtils.mount(require('#/components/Sample').default, {
localVue: testContext.vue,
vuetify: testContext.vuetifyInstance
});
expect(testContext.vueDependency.doSomething(sample)).toBe(true);
expect(sample.isVueInstance()).toBeTruthy();
});
});
import { shallowMount, createLocalVue } from '#vue/test-utils';
import Vuex from 'vuex';
const localVue = createLocalVue();
const { user } = require('./customer.mock');
const originUser = { ...user };
const resetUserData = wrapper => {
wrapper.setData( { userData: originUser } );
};
const TestComponent = localVue.component( 'TestComponent', {
name : 'TestComponent',
data : () => {
return { userData: user };
},
render( createElement ) {
return createElement( 'h3', 'hoy hoy' );
},
} );
describe( 'computed fields', () => {
afterEach( () => {
resetUserData( wrapper );
} );
it( 'isPrivatePerson should return false', () => {
wrapper.setData( { userData: { Contacts: [{ grpid: 'bad field' }] } } );
expect( !wrapper.vm.isPrivatePerson ).toBeTruthy();
} );
it( 'isPrivatePerson should return true', () => {
expect( wrapper.vm.isPrivatePerson ).toBeTruthy();
} );
});