I want to add stripe elements to my nuxt js page However, I got an error
Stripe is not defined
I have inserted the <script src="https://js.stripe.com/v3/"></script> on my nuxt.config.js
Here's the code
head: {
title: process.env.npm_package_name || "",
script: [{ src: "https://js.stripe.com/v3/" }],
link: [
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
]
},
My payment page
<template>
<div>
<div ref="card"></div>
<button v-on:click="purchase">Purchase</button>
</div>
</template>
<script>
let stripe = Stripe("Key"),
elements = stripe.elements(),
card = undefined;
export default {
mounted: function() {
card = elements.create("card");
card.mount(this.$refs.card);
},
methods: {
async purchase() {
let result = await stripe.createToken(card);
}
}
};
</script>
I followed this tutorial and still can't fix it
https://alligator.io/vuejs/stripe-elements-vue-integration/
You are including stripe for into scripts, so it will be loaded in browser. But nuxt is SSR. And the code in your script section will be also executed on server. And on server there no stripe, so it wont work. You need to execute all your code that create Stripe in mounted hook, which is only executed on client
Related
I'm creating an app with MFE with Vuejs3 and webpack 5 module federation. One main app made with Vue will consume other apps (should be framework agnostic) and I need to share State from my Vue shell to other apps.
I tried making a store with the Composition api but the value get only updated in the app that call the event.
Here is the store that I expose from the vue shell:
import { reactive, toRefs } from 'vue'
const state = reactive({
data: {
quantity: 1,
},
})
export default function useStoreData() {
const updateQuantity = (quantity) => {
state.data.quantity += quantity
}
return {
...toRefs(state),
updateQuantity,
}
}
in vue shell :
<template>
<div>
<button #click="updateQuantity(1)">FOO +1</button>
<div>Quantity = {{ data.quantity }} </div>
</div>
</template>
<script setup>
import useStoreData from '../store/storeData'
const { updateQuantity, data } = useStoreData()
</script>
when I click on the button "FOO +1", value gets updated +1.
in my remote app:
<template>
<div>
<button #click="updateQuantity(5)">BAR +5</button>
<div>Quantity = {{ data.quantity }}</div>
</div>
</template>
<script setup>
import store from 'store/storeData'
const useStoreData = store
const { data, updateQuantity } = useStoreData()
</script>
When i click on button "BAR +5" the value get update +5
BUT everytime I click on one of those button, the value in the other app doesn't get updated.
What did I miss ?
Needed to add the shell app as a remote of itself then import the store in the shell app, same way as i'm doing in my remote app.
Here is the vue.config.js of the shell, where I need to expose AND remote the store.
const { ModuleFederationPlugin } = require('webpack').container
const deps = require('./package.json').dependencies
module.exports = {
publicPath: '/',
configureWebpack: {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
filename: 'remoteEntry.js',
remotes: {
test: 'test#http://localhost:8081/remoteEntry.js',
test2: 'test2#http://localhost:8082/remoteEntry.js',
store: 'shell#http://localhost:8080/remoteEntry.js', <= ADDED HERE the remote of itself
},
exposes: {
'./storeData': './src/store/storeData.js',
},
shared: [
{
...deps,
},
],
}),
],
},
devServer: {
port: 8080,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
},
},
}
In the Vue shell app:
<script setup>
// import useStoreData from '../store/storeData' <= wrong
import store from 'store/storeData' <= good
</script>
In projects that use express + require.js and vue cdn, I try to use require.js to define a template similar to vue components
In index.js, I have a data list, I want to use v-for in the index.html display list item, but I cannot connect the data list in html
This is my code, is there any mistake?
index.js
define([
'text!js/components/search/index.html',
'jquery',
], function (template) {
var $ = require('jquery');
var Vue = require('vue');
var ajax = require('js/ajax');
return {
name: 'search',
template: require('text!js/components/search/index.html'),
props: {
},
data: function () {
return {
// data list
Options: [],
};
},
mounted: function () {
this.loadOptions();
},
methods: {
//data list
loadOptions() {
ajax.get('/options/options').then(function (data) {
this.Options = data.Options;
console.log('this.Options Successful get data')
});
},
},
};
});
index.html
<div class="col-12 col-md-6 col-xl-6 ">
<a class="dropdown-item"
href="#"
v-for="opt in Options"
:key="opt.value">
<p :title="opt.label">
{{ opt.label }}
</p>
</a>
</div>
Maybe because of this?
I assume that console.log('this.Options Successful get data') works as expected. Try to change your loadOptions method as follows:
loadOptions() {
ajax.get('/options/options').then(data => {
this.Options = data.Options;
console.log('this.Options Successful get data')
});
},
Arrow function should help to point this to your Vue component instance.
From the code, your template file lives at components/search/index.html, is the file being referenced properly?
Better still, Please put your code in a sandbox/jsfiddle and share the link. So, it would be easy for anyone to look at it for you.
Anyone know why a sub page crashes when user reloads the page? It is fine when you navigate to the page using nuxt-link from another page (using route params), but when the user reloads/refreshes the page, it will crash.
Here is my sandbox where you can test it out: Codesandbox
Code from nuxt link:
<nuxt-link :to="{name: 'photos-id-title', params: { id: photo.id, title: photo.title }}">
Code from photo details page:
<template>
<section>
<!-- <template v-if="photo"> -->
<img
:id="photo.filename"
:src="photo.url"
class="img-fluid thumbnail rounded"
:alt="photo.title"
/>
<h1 class="h3">{{ photo.title }}</h1>
</section>
</template>
<script>
import { Annotorious } from '#recogito/annotorious'
export default {
data() {
return {
photo: {},
anno: {},
title: this.$route.params.title
}
},
async mounted() {
await this.getPhotoDoc()
this.anno = await new Annotorious({ image: this.photo.filename })
},
methods: {
async getPhotoDoc() {
const docRef = await this.$fireStore
.collection('photos')
.doc(this.$route.params.id)
.get()
try {
if (docRef.exists) {
// data() returns all data about the doc
console.log('got the doc: ', docRef.data())
this.photo = docRef.data()
} else {
console.log('no docs exist')
}
} catch (error) {
console.log('Error getting document:', error)
}
}
},
head() {
return {
title: this.photo.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.photo.description
}
]
}
}
}
</script>
User journey: Click PHOTOS from navbar, select any photo on the page. You shall see a "photo detail" page. Loads fine. Try to refresh the page -- it will crash.
Note: I also have this scaffolded in a regular/plain vue app using Vue-cli and have NO issues. Only seems to be a problem with Nuxt. Must be SSR related?
Thanks for any help...
If you think this is SSR related and the Annotorious plugin might cause the problem try this:
nuxt.config.js add the plugin with mode client
plugins: [
{ src: '~/plugins/client-only.js', mode: 'client' }, // only on client side
]
You can find more information in the nuxtjs docs here
I'm trying to watch page url. I don't use Vue Router.
My final goal is to set page url as input value:
<template>
<div>
<input v-model="pageUrl">
</div>
</template>
<script>
export default {
data() {
return {
pageUrl: window.location.href,
link: ''
}
},
watch: {
pageUrl: function() {
this.link = window.location.href
}
}
}
</script>
The example above doesn't work somewhy.
I've also tried
watch: {
'window.location.href': function() {
this.link = window.location.href
}
},
Input value is being set only once on component render.
What can be wrong?
well, that is exactly the reason you want to use vue-router!
vue can only detect changes in reactive properties: https://v2.vuejs.org/v2/guide/reactivity.html
if you want to react to changes in the url, you have 2 ways:
https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event or
https://developer.mozilla.org/en-US/docs/Web/API/Window/hashchange_event
i would rather use vue-router or a similar plugin.
I'm new to vue js.
I'm just creating a simple project where I just include vuejs through CDN. not using node/npm or cli.
I keep all my html markup in single html which looks messy as it grows. I tried to split html to views and want to include it by something analogous to ng-include of angularJs
I have worked in angular previously where there is ng-include to load external html files. I'm looking for something similar to that in vue. the whole point is to split my html files into more maintainable separate files.
have come across <template src="./myfile.html"/> but it doesn't work
Can somebody help me out
It's actually remarkably easy, but you need to keep something in mind. Behind the scenes, Vue converts your html template markup to code. That is, each element you see defined as HTML, gets converted to a javascript directive to create an element. The template is a convenience, so the single-file-component (vue file) is not something you'll be able to do without compiling with something like webpack. Instead, you'll need to use some other way of templating. Luckily there are other ways of defining templates that don't require pre-compiling and are useable in this scenario.
1 - string/template literals
example: template: '<div>{{myvar}}</div>'
2 - render function 🤢
example: render(create){create('div')}
Vue has several other ways of creating templates, but they just don't match the criteria.
here is the example for both:
AddItem.js - using render 😠 functions
'use strict';
Vue.component('add-item', {
methods: {
add() {
this.$emit('add', this.value);
this.value = ''
}
},
data () {
return {
value: ''
}
},
render(createElement) {
var self = this
return createElement('div', [
createElement('input', {
attrs: {
type: 'text',
placeholder: 'new Item'
},
// v-model functionality has to be implemented manually
domProps: {
value: self.value
},
on: {
input: function (event) {
self.value = event.target.value
// self.$emit('input', event.target.value)
}
}
}),
createElement('input', {
attrs: {
type: 'submit',
value: 'add'
},
on: {
click: this.add
}
}),
])
}
});
ListItem.js - using template literals (back-ticks)
'use strict';
Vue.component('list-item', {
template: `<div class="checkbox-wrapper" #click="check">
<h1>{{checked ? '☑' : '☐'}} {{ title }}</h1>
</div>`,
props: [
'title',
'checked'
],
methods: {
check() {
this.$emit('change', !this.checked);
}
}
});
and the html
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.0/vue.js"></script>
<script src="ListItem.js"></script>
<script src="AddItem.js"></script>
</head>
<body>
<div id="app">
<add-item #add='list.push({title:arguments[0], checked: false})'></add-item>
<list-item v-for="(l, i) in list" :key="i" :title="l.title" :checked="l.checked" #change="l.checked=arguments[0]"></list-item>
</div>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
newTitle: '',
list: [
{ title: 'A', checked: true },
{ title: 'B', checked: true },
{ title: 'C', checked: true }
]
}
});
</script>
</body>
</html>
TL; DR;
See it in action at : https://repl.it/OEMt/9
You cant. You must use async components - read guide here
Actually you can. This is kinda easy. Depends on your needs and situation. However, this code is NOT technically correct, however it will explain to you how it might work, gives you massive freedom and makes your original vue instance smaller.
To make this work, you will need vue router (cdn is ok) and in this case axios or fetch (if you dont care about supporting older browsers).
The only downfall in my opinion is that in content files you will need to add additional call parameter $parent . This will force vue to work.
index
<div id="app">
<router-link v-for="route in this.$router.options.routes" :to="route.path" :key="route.path">{{ route.name }}</router-link>
<section style="margin-top:50px;">
<component :is="magician && { template: magician }" />
</section>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
const viewer = axios.create({ baseURL: location.origin });
const routes = [
{"name":"Hello","slug":"hello","path":"/lol/index.html"},
{"name":"Page One","slug":"page_one","path":"/lol/page-one.html"},
{"name":"Page Two","slug":"page_two","path":"/lol/page-two.html"}
];
const app = new Vue({
router,
el: '#app',
data: {
magician: null,
},
watch: {
$route (to) {
this.loader(to.path);
}
},
mounted() {
this.loader(this.$router.currentRoute.path);
},
methods: {
viewer(opt) {
return viewer.get(opt);
},
loader(to) {
to == '/lol/index.html' ? to = '/lol/hello.html' : to = to;
this.viewer(to).then((response) => {
this.magician = response.data;
}).catch(error => {
alert(error.response.data.message);
})
},
huehue(i) {
alert(i);
}
}
});
</script>
hello.html content
<button v-on:click="$parent.huehue('this is great')">Button</button>
page-one.html content
<select>
<option v-for="num in 20">{{ num }}</option>
</select>
page-two.html content
// what ever you like
router explanation
To make this work perfectly, you will need to find a correct way to configure your htaccess to render everything if current page after first view is not index. Everything else should work fine.
As you can see, if it is index, it will load hello content file.
I faced the same issue and this is how I solved it , I also made a video about this question https://www.youtube.com/watch?v=J037aiMGGAw
create a js file ,for your component (logic) let's call it "aaaa.vue.js"
create an HTML file for your template that will be injected in your "aaaa.vue.js" and let's call it "aaaa.html"
Component file (Logic file javascript)
const aaaa = {
name:"aaaa",
template: ``,
data() {
return {
foo:"aaaa"
};
},
methods: {
async test() {
alert(this.foo)
},
},
};
Template file (HTML)
<!--template file-->
<div>
<button #click="test" > click me plz </button>
</div>
index.html
<html>
<head>
<title>my app</title>
</head>
<body>
<div id="app" class="main-content col-12">
<aaaa></aaaa>
</div>
</body>
</html>
<script src="axios.min.js"></script>
<script src="vue.js"></script>
<!-- load js file (logic) -->
<script src="aaaa.vue.js"></script>
<script>
document.addEventListener("DOMContentLoaded", async function () {
//register components
let html = await axios.get("aaaa.html"); // <---- Load HTML file
aaaa.template = html.data;
Vue.component("aaaa", aaaa);
new Vue({
el: "#app",
name: "main",
//... etc
});
});
</script>
Update :
I also created an example on github to see it in action
https://github.com/nsssim/Vue-CDN-load-component
Sure you can, this is the way we are doing it in all our components of our app.
<template src="../templates/the_template.html"></template>
<script>
export default {
name: 'ComponentName',
props: {},
computed: {},
methods: {},
};
</script>
<style lang="scss">
#import '../styles/myscss_file';
</style>
Will need to add
runtimeCompiler: true
to your vue.config.js file. That's it.