Share external script between VueJS components - vue.js

I am trying to load the external Stripe script in one component here https://gitlab.com/gadelkareem/skeleton/-/blob/feature/stripe/src/frontend/src/components/payment/PaymentMethods.vue#L167
this.includeStripe('js.stripe.com/v3/', function () {
this.configureStripe()
}.bind(this))
Then I load a dialog inside the same page which also needs the same Stripe() script so I have to load it again here https://gitlab.com/gadelkareem/skeleton/-/blob/feature/stripe/src/frontend/src/components/payment/AddPaymentMethod.vue#L147 with similar duplicated code.
Is there a way to share that script between both components? preferably without making it global.

You can create the init() and configureStripe() method in Vue's global context.
main.js
new Vue({
...
methods: {
init() {
this.includeStripe('js.stripe.com/v3/', function () {
this.configureStripe()
}.bind(this))
},
configureStripe () {
// eslint-disable-next-line no-undef
this.stripe = Stripe(this.pk)
this.elements = this.stripe.elements()
this.card = this.elements.create('card', {
hidePostalCode: false,
style: {
base: {
iconColor: '#666EE8',
color: '#31325F',
lineHeight: '40px',
fontWeight: 300,
fontFamily: 'Helvetica Neue',
fontSize: '15px',
'::placeholder': {
color: '#CFD7E0'
}
}
}
})
this.card.mount('#card-element')
},
}
...
});

Related

Default values of a prop of type object in vue are not being used

I'm not sure why the default values of a prop, in object format, in vue are not being accepted but when I pass in the props dynamically the values appear fine.
I have a component, component1, it has:
<label :style="setTextStyles"></label>
props: {
textStyles: {
type: Object,
default: () => ({
'textColor': 'white',
'uppercase': 'uppercase',
'fontWeight': 'bold',
})
}
},
computed:{
setTextStyles() {
return {
'font-weight': this.textStyles.fontWeight,
'text-transform': this.textStyles.uppercase,
'--color': this.textStyles.textColor
}
}
I have a second component that uses component 1:
<component1 :textStyles="{'textColor':'red', 'uppercase':'None','fontWeight':'bold'}"/>
Which ever prop I specify, ie textColor, or upprecase or fontWeight, I see the expected result. However the default values don't seem to show up if I don't specify it. For example, the fontWeight is not bold by default even though it's set as the default font weight. This is the same with textColor.
Below does not give me a bold font:
<component1 :textStyles="{'textColor':'red', 'uppercase':'None'}"/>
Below does not give me a white text color:
<component1 :textStyles="{'uppercase':'None','fontWeight':'bold'}"/>
I'm not sure why the default values are not appearing
Do not expect vue props to act like function arguments in javascript. Instead you can have the default values in a separate variable inside the component and use Object.assign() to build the final desired object.
For example:
const defaultStyles = {
'textColor': 'white',
'uppercase': 'uppercase',
'fontWeight': 'bold',
}
export default {
props : {
textStyles : {
default : {},
type : Object
}
},
data(){
return {
finalStyles : Object.assign({}, defaultStyles, textStyles)
}
}
}
You could also use a composable like this.
This way you can also use vue-router props without loosing your defaults.
<script>
/**
* This composable is merging default Properties against a given property
* usage:
* setup(props) {
const defaultedProperty = useMergePropertyDefaults(
{
teleportBottom: true,
showErrors: true,
showLoadingAnimation: true,
showPublishedWarning: true,
},
toRef(props, "renderControl")
);
return { defaultedProperty };
},
props: {
renderControl: {
type: Object,
required: false,
},
},
**/
import { computed } from "vue";
export default function useMergePropertyDefaults(
defaultProperties,
propertyRef
) {
const processed = computed(() => {
const values = {
...defaultProperties,
...propertyRef.value,
};
return values;
});
return processed;
}
</script>

Nuxt - Cannot access data inside a method

I'm using CKEditor 5 + CKFinder (Modal Mode) to select an image using the #click event. The problem is that I don't have access to data inside the onInit function.
Here is the method:
data() {
return {
post: {
thumbnail: null,
},
};
},
methods: {
openModal() {
console.log(this.post.thumbnail) // NO PROBLEM! this.post.thumbnail IS ACCESSIBLE
CKFinder.modal( {
chooseFiles: true,
width: 800,
height: 600,
onInit: function( finder ) {
finder.on( 'files:choose', function( evt ) {
var file = evt.data.files.first();
this.post.thumbnail = file.getUrl(); // PROBLEM !! $this.post is undefined
} );
}
} );
},
},
And this is my Template:
<div class="btn btn-danger" #click="openModal">Choose Image</div>
<img class="mx-auto d-block" :src="post.thumbnail" />
As mentioned in the comment, you need to use an arrow function to resolve this in the vue object.
Whenever you use function () {}, this refers to the properties of the function, not the Vue object that you intend to reference
// example
methods () {
openModal() {
onInit: function () {
this.post // 'this' is the onInit function, not the Vue object
}
}
}
// solution
methods () {
openModal() {
onInit: () => {
this.post // 'this' is the Vue object
}
}
}
Answer
data() {
return {
post: {
thumbnail: null,
},
};
},
methods: {
openModal() {
console.log(this.post.thumbnail) // NO PROBLEM! this.post.thumbnail IS ACCESSIBLE
CKFinder.modal( {
chooseFiles: true,
width: 800,
height: 600,
onInit: finder => {
finder.on( 'files:choose', evt => {
var file = evt.data.files.first();
this.post.thumbnail = file.getUrl(); // PROBLEM !! $this.post is undefined
});
}
});
},
},

How can I use Vuex in Sweetalert2 modal?

I'm using sweetalert2 with a vue component injected in the html template of modal. How can I access the vuex inside my component?
Vue.prototype.$myComponentDialog = function (title, propsData) {
let swalOptions = {
title: title,
showCloseButton: true,
showConfirmButton: false,
html:
'<div id="component-container"></div>',
onBeforeOpen: () => {
let ComponentClass = Vue.extend(MyComponent);
let instance = new ComponentClass({
propsData: propsData
});
instance.$mount();
document.getElementById('component-container').appendChild(instance.$el);
}
};
return Vue.swal.mixin({
customClass: {
container: cssClass
},
position: 'center'
}).fire(swalOptions);
};

GeoJSON - Method within onEachFeature

Vue2Leaflet is a library to implement Leaflet into the Vue2 Framework; with the ability to show GeoJSON objects on a map.
With multiple GeoJSON lines, I want to have a mouse click event that effects the style of other lines (e.g. it toggles a selectedLineId variable). I managed to change the style of geojson lines on mouse over itself; see this JSFiddle.
Core is the onEachFeature which adds the mouse over event to every feature. But I have no idea how to run a Vue method from here;
function onEachFeature (feature, layer) {
layer.on('mouseover', function (e) {
e.target.setStyle({
color: "#FF0000"
});
});
layer.on('mouseout', function (e) {
e.target.setStyle({
color: "#000000"
});
});
}
You could bind your onEachFeature function to your Vue object:
data() {
return {
// ...
lines: {
geojson: geojsondata,
options: {
onEachFeature: onEachFeature.bind(this),
style: {
color: "#000000"
}
}
}
}
}
this in onEachFeature will then reference your Vue object:
function onEachFeature (feature, layer) {
var v = this;
layer.on('mouseover', function (e) {
// assuming you have a getColor method defined
e.target.setStyle({
color: v.getColor()
});
});
layer.on('mouseout', function (e) {
e.target.setStyle({
color: "#000000"
});
});
}
Here's an updated Fiddle : https://jsfiddle.net/qywaz1h8/96/

Vue.js can not set options afer setting data

I try to set an option for my Vue component after getting my required data through an API. The data is set correctly when the Vue instance is created but it seems that does not affect my condition.
This is the snippet:
import axios from 'axios';
Vue.component("order-now", {
delimiters: ["${", "}"],
props: {
dynamic: {
type: Boolean,
default: false
},
template: null
},
data() {
return {
order: '',
startInterval: false,
}
},
/**
* created
*/
created() {
this.getOrderNow();
this.$options.template = this.template;
},
mounted() {
if(this.startInterval)
this.$options.interval = setInterval(this.getOrderNow(), 10000);
},
/**
* beforeDestroy
*/
beforeDestroy() {
clearInterval(this.$options.interval);
},
methods: {
/**
* getOrderNow
*
* Receive data from api route
* and store it to components data
*/
getOrderNow() {
axios.get('/rest/order-now').then(({data}) => {
this.order = data.orderNow.order;
this.startInterval = data.orderNow.startInterval;
}).catch(e => {
console.error('Could not fetch data for order string.')
});
}
}
});
I call my getOrderNow() method when the created hook is called. This works fine and my data is set.
As you can see, in the mounted() hook, I try to look if setInterval is set true or false and condionally set an option but setInterval is always false.
I thought that might has been changed after calling my method in the created hook but it does not.
this.startInterval is false because it probably never gets set to true at the time mounted() is applied. The thing is that you set startInterval after the promise returned by axios is resolved, which most likely happens after mounted().
To solve this you can just set interval inside axios.then().
Update after reading a comment (working demo):
const API = {
counter: 0,
getItems() {
return new Promise((fulfill) => {
setTimeout(() => {
fulfill(API.counter++);
})
});
},
};
new Vue({
el: "#app",
data: {
interval: false,
data: '',
},
methods: {
fetchThings() {
API.getItems().then((data) => {
this.data = data;
});
},
},
created() {
this.fetchThings();
this.interval = setInterval(this.fetchThings, 1000);
},
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<pre>
{{data}}
</pre>
</div>
And jsfiddle