Vue PayPal implementation with vue-head, paypal not defined - vue.js

I'm trying to add the paypal sdk via vue-head(https://www.npmjs.com/package/vue-head) in my component but I keep getting this error:
Error in mounted hook: "ReferenceError: paypal is not defined"
What am I doing wrong here? Is the SDK simply not loading before mounted?
Is there a better way to accomplish this? Does anyone have an example of their paypal implementation in vue? Any help would be greatly appreciated.
edit: Also if I include the script tag server side (rails) then try to access paypal in vue I see this error:
Could not find driver for framework: [object Object]
<template>
<div id="paypal-button" />
</template>
<script>
import { mapState as mapConfigState } from '../scripts/store/appConfig';
export default {
props: {
totalPrice: {
type: String,
required: true,
},
currency: {
type: String,
required: true,
'default': 'USD',
},
buttonStyle: {
type: Object,
required: false,
},
},
computed: {
...mapConfigState({
customer: state => state.customer,
}),
paypalEnvironment() {
return (this.customer.paypalTestingMode) ? 'sandbox' : 'production';
},
client() {
return {
sandbox: this.customer.paypalClientIdSandbox,
production: this.customer.paypalClientIdLIVE,
};
},
},
head: {
script() {
return [
{
type: 'text/javascript',
src: `https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}`,
},
];
},
},
mounted() {
const total = this.totalPrice;
const currency = this.currency;
paypal.Buttons.driver(
{
env: this.paypalEnvironment,
client: this.client,
style: this.buttonStyle,
createOrder(data, actions) {
return actions.order.create({
purchase_units: [
{
amount: {
value: total,
currency,
},
},
],
});
},
onApprove(data, actions) {
return actions.order.capture();
},
}, '#paypal-button'
);
},
};
</script>
edit2: I tried adding the script in my mounted hook like this:
let el = document.querySelector(`script[src="https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}"]`);
if (!el) {
const src = `https://www.paypal.com/sdk/js?client-id=${this.client[this.paypalEnvironment]}`;
el = document.createElement('script');
el.type = 'text/javascript';
el.async = true;
el.src = src;
document.head.appendChild(el);
}
I can see the script in the head tag in the dev console but paypal still is not defined.

For anyone else who is trying to implement PayPal in a Vue component:
<template>
<div id="paypal-button" />
</template>
<script>
export default {
mounted() {
function loadScript(url, callback) {
const el = document.querySelector(`script[src="${url}"]`);
if (!el) {
const s = document.createElement('script');
s.setAttribute('src', url); s.onload = callback;
document.head.insertBefore(s, document.head.firstElementChild);
}
}
loadScript('https://www.paypal.com/sdk/js?client-id=sb&currency=USD', () => {
paypal.Buttons({
// Set up the transaction
createOrder(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01',
},
}],
});
},
// Finalize the transaction
onApprove(data, actions) {
return actions.order.capture().then(details => {
// Show a success message to the buyer
alert(`Transaction completed by ${details.payer.name.given_name}`);
});
},
}).render('#paypal-button');
});
},
};
</script>
Alternatively you can use this: https://github.com/paypal/paypal-js

Related

Vue.js access variable from method

I try to fetch stocks data from an API. This data should be used to create a chart.js graph.
how do I access in vue.js data to generate a chart.js line chart from the methods http(axios) call?
Is it possible to access the data directly in the mounted component or should I define a const in the section and create the variables there?
<template>
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
<div>Selected: {{ selected }}</div>
<div>
<canvas id="myChart" height="200" width="650"></canvas>
</div>
<script>
export default {
mounted() {
const ctx = document.getElementById("myChart");
const myChart = new Chart(ctx, {
type: "line",
data: {
labels: [prices[0].date],
datasets: [
{
label: 'Dataset msft',
data: prices[0].price
},
{
label: 'Dataset google',
data: prices[1].price
},
],
},
});
},
data() {
return {
selected: "",
prices: [],
options: [
{ text: "msft", value: "msft" },
{ text: "GOOGL", value: "GOOGL" },
],
};
},
watch: {
selected: function () {
this.getPrice();
},
},
methods: {
getPrice: function () {
var this_ = this;
axios
.get(
"https://site/...."
)
.then((response) => {
// JSON responses are automatically parsed.
this_.prices = response.data;
})
},
},
};
</script>
Yes, you can access variables in data() from mounted().
You need to prepend variables with this. when using the Options API
ex: this.prices[0].price
As you are putting watcher on selected but I did not see any changes in the selected variable in your code. As per my understanding you are making an API call to get the graph data based on the selected option.
If Yes, Instead of generating a chart in mounted you can generate it inside your getPrice() method itself based on the response. It should be :
methods: {
getPrice: function () {
var this_ = this;
axios
.get(
"https://site/...."
)
.then((response) => {
this.generateChart(response.data);
})
},
generateChart(prices) {
const ctx = document.getElementById("myChart");
const myChart = new Chart(ctx, {
type: "line",
data: {
labels: [prices[0].date],
datasets: [
{
label: 'Dataset msft',
data: prices[0].price
},
{
label: 'Dataset google',
data: prices[1].price
}
]
}
});
}
}
Here, a very basic example:
<script>
export default {
async mounted() {
await this.$nextTick();
const ctx = document.getElementById("myChart");
this.chart = new Chart(ctx, {
type: "line",
data: {
labels: [],
datasets: [],
},
});
},
data() {
return {
selected: "",
chart: null,
options: [
{ text: "msft", value: "msft" },
{ text: "GOOGL", value: "GOOGL" },
],
};
},
watch: {
selected: function () {
this.getPrice();
},
},
methods: {
async getPrice() {
let { data } = await axios.get("https://site/....");
this.chart.data.datasets = [{ label: "dummy data" , data: [2, 3, 4]}];
this.chart.data.label = [1, 2, 3];
this.chart.update(); //very important, always update it
},
},
};
</script>
You create a property called chart and save your chart to it.
Then, after you fetch your data, you can access your chart with this.chart and then you set your datasets and labels. Whenever you make an change to the chart, use this.chart.update() to update it on the browser.
If you execute this code, you should see some dummy data in the chart

Apexchats.js axios gives me undefined with Vue

I am trying to get data from server using vue and apexcharts, but even after I called data with axios, it gives me undefined..
What have I missed?
template
<apexchart
ref="chart1"
width="100%"
:options="chartOptions" :series="series">
</apexchart>
data from url
{
"pageviews": 1313,
"new_users": 1014
}
script
export default {
data: function () {
return {
series: [],
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
},
created: function () {
this.getByVisitor()
},
methods: {
getByVisitor() {
const url = 'url';
axios
.get(url)
.then(response => {
this.$refs.chart1.updateSeries([{
name: 'Sales',
data: response.data
}])
})
.catch(error => (this.byVisitor = error.data));
console.log(`---------------this.$refs.chart1`, this.$refs.chart1);
},
}
See Updating Vue Chart Data
There's no need to directly call the updateSeries() method on the chart component since it is able to react to changes in series. All you have to do is update your series data property
export default {
data: () => ({
series: [], // 👈 start with an empty array here
byVisitor: null, // 👈 you seem to have missed this one for your error data
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
}),
created: function() {
this.getByVisitor()
},
methods: {
async getByVisitor() {
const url = 'url';
try {
const { data } = await axios.get(url)
// now update "series"
this.series = [{
name: "Sales",
data
}]
} catch (error) {
this.byVisitor = error.data
}
},
}
}

Get tiptap-vuetify to work with "Mentions" in a Vue Nuxt app

so my problem is that I can't seem to make use of the "Mentions" functionality of tiptap inside a vuetify-nuxt project.
The original example can be found here
More useful info:
Example implementation from the tiptap github here
Similar question asked and not answered fully here
Similar question asked in the vuetify integration library here
To do that I'm trying to combine documentation examples.
I guess it goes wrong around the time I try to use "tippy", which is the css library used to display the popup that actually lists users to pick from (after the #), but I can't seem to understand the real issue.
So when I type # the keydown/up event listeners are functioning, but the tippy seems to not bind the popup successfully (it's not displayed), and the following error occurs:
Editor.vue?6cd8:204 Uncaught TypeError: Cannot read property '0' of undefined
at VueComponent.enterHandler (Editor.vue?6cd8:204)
at onKeyDown (Editor.vue?6cd8:175)
at Plugin.handleKeyDown (extensions.esm.js?f23d:788)
at eval (index.es.js?f904:3298)
at EditorView.someProp (index.es.js?f904:4766)
at editHandlers.keydown (index.es.js?f904:3298)
This is my tippy.js nuxt plugin:
import Vue from "vue";
import VueTippy, { TippyComponent } from "vue-tippy";
Vue.use(VueTippy, {
interactive: true,
theme: "light",
animateFill: false,
arrow: true,
arrowType: "round",
placement: "bottom",
trigger: "click",
// appendTo: () => document.getElementById("app")
});
Vue.component("tippy", TippyComponent);
This is the component in which I'm trying to show the editor and the suggestions/mentiosn functionality:
<template>
<div>
<div class="popup">
aaaa
</div>
<editor-menu-bar v-slot="{ commands }" :editor="editor">
<div class="menubar">
<v-btn class="menubar__button" #click="commands.mention({ id: 1, label: 'Fred Kühn' })">
<v-icon left>#</v-icon>
<span>Mention</span>
</v-btn>
</div>
</editor-menu-bar>
<tiptap-vuetify v-model="localValue" :extensions="extensions" :native-extensions="nativeExtensions" :toolbar-attributes="{ color: 'grey' }" #init="onInit" />
</div>
</template>
<script>
// import the component and the necessary extensions
import {
TiptapVuetify,
Heading,
Bold,
Italic,
Strike,
Underline,
Code,
CodeBlock,
Image,
Paragraph,
BulletList,
OrderedList,
ListItem,
Link,
Blockquote,
HardBreak,
HorizontalRule,
History,
} from "tiptap-vuetify";
// TESTING
import { EditorMenuBar, Editor } from "tiptap";
import { Mention } from "tiptap-extensions";
import tippy, { sticky } from "tippy.js";
export default {
components: { TiptapVuetify, EditorMenuBar },
props: {
value: {
type: String,
default: "",
},
},
data: () => ({
editor: null,
extensions: null,
nativeExtensions: null,
// TESTING
query: null,
suggestionRange: null,
filteredUsers: [],
navigatedUserIndex: 0,
insertMention: () => {},
popup: null,
}),
computed: {
localValue: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
},
},
// TESTING
hasResults() {
return this.filteredUsers.length;
},
showSuggestions() {
return this.query || this.hasResults;
},
},
created() {
this.extensions = [
History,
Blockquote,
Link,
Underline,
Strike,
Italic,
ListItem,
BulletList,
OrderedList,
[
Heading,
{
options: {
levels: [1, 2, 3],
},
},
],
Bold,
Link,
Code,
CodeBlock,
Image,
HorizontalRule,
Paragraph,
HardBreak,
];
this.nativeExtensions = [
// https://github.com/ueberdosis/tiptap/blob/main/examples/Components/Routes/Suggestions/index.vue
new Mention({
// a list of all suggested items
items: async () => {
await new Promise((resolve) => {
setTimeout(resolve, 500);
});
return [
{ id: 1, name: "Sven Adlung" },
{ id: 2, name: "Patrick Baber" },
{ id: 3, name: "Nick Hirche" },
{ id: 4, name: "Philip Isik" },
{ id: 5, name: "Timo Isik" },
{ id: 6, name: "Philipp Kühn" },
{ id: 7, name: "Hans Pagel" },
{ id: 8, name: "Sebastian Schrama" },
];
},
// When # is pressed, we enter here
onEnter: ({ items, query, range, command, virtualNode }) => {
this.query = query; // the field that the # queries? currently empty
this.filteredUsers = items;
this.suggestionRange = range;
this.renderPopup(virtualNode); // render popup - failing
this.insertMention = command; // this is saved to be able to call it from within the popup
},
// probably when value after # is changed
onChange: ({ items, query, range, virtualNode }) => {
this.query = query;
this.filteredUsers = items;
this.suggestionRange = range;
this.navigatedUserIndex = 0;
this.renderPopup(virtualNode);
},
// mention canceled
onExit: () => {
// reset all saved values
this.query = null;
this.filteredUsers = [];
this.suggestionRange = null;
this.navigatedUserIndex = 0;
this.destroyPopup();
},
// any key down during mention typing
onKeyDown: ({ event }) => {
if (event.key === "ArrowUp") {
this.upHandler();
return true;
}
if (event.key === "ArrowDown") {
this.downHandler();
return true;
}
if (event.key === "Enter") {
this.enterHandler();
return true;
}
return false;
},
// there may be built-in filtering, not sure
onFilter: async (items, query) => {
await console.log("on filter");
},
}),
];
},
methods: {
// TESTING
// navigate to the previous item
// if it's the first item, navigate to the last one
upHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + this.filteredUsers.length - 1) %
this.filteredUsers.length;
},
// navigate to the next item
// if it's the last item, navigate to the first one
downHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + 1) % this.filteredUsers.length;
},
enterHandler() {
const user = this.filteredUsers[this.navigatedUserIndex];
if (user) {
this.selectUser(user);
}
},
// we have to replace our suggestion text with a mention
// so it's important to pass also the position of your suggestion text
selectUser(user) {
this.insertMention({
range: this.suggestionRange,
attrs: {
id: user.id,
label: user.name,
},
});
this.editor.focus();
},
renderPopup(node) {
if (this.popup) {
return;
}
// ref: https://atomiks.github.io/tippyjs/v6/all-props/
this.popup = tippy(".page", {
getReferenceClientRect: node.getBoundingClientRect, // input location
appendTo: () => document.body, // must be issue
interactive: true,
sticky: true, // make sure position of tippy is updated when content changes
plugins: [sticky],
content: this.$refs.suggestions,
trigger: "mouseenter", // manual
showOnCreate: true,
theme: "dark",
placement: "top-start",
inertia: true,
duration: [400, 200],
});
},
destroyPopup() {
if (this.popup) {
this.popup[0].destroy();
this.popup = null;
}
},
beforeDestroy() {
this.destroyPopup();
},
/**
* NOTE: destructure the editor!
*/
onInit({ editor }) {
this.editor = editor;
},
},
};
</script>
How can I get the "suggestions" item display in the aforementioned setting?

Unable to display chart using API call in chartjs in the context of Vuejs

Im trying to display chart using chartjs by calling API, but unable to do so.
Here s my LineChart.vue:
<script>
import {Line, mixins} from 'vue-chartjs' // We specify what type of chart we want from vue-chartjs and the mixins module
const { reactiveProp } = mixins
export default { //We are extending the base chart class as mentioned above
extends: Line,
mixins: [reactiveProp],
data () {
return {
options: { //chart options
lineTension: 0,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
// eslint-disable-next-line no-unused-vars
callback (value, index, values) {
return `${value }%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
labelString: ''
}
}
]
}
}
}
},
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
}
</script>
And here is my Home.vue where i have imported the LineChart:
<template>
<div class="chart">
<line-chart :chart-data="datacollection"></line-chart>
</div>
</template>
<script>
import LineChart from './LineChart'
import axios from 'axios'
import DateTime from 'luxon'
export default {
data () {
return {
date: {},
challenge: {},
datacollection: {}
}
},
component: {LineChart},
created() {
this.fillData()
},
mounted () {
this.fillData()
},
methods: {
fillData () {
axios.get('https://my_api_goes_here')
.then(response => {
const results = response.data
const dateresult = results.map(a => a.date)
const challengeresult = results.map(a => a.challenge)
this.date = dateresult
this.challenge = challengeresult
this.datacollection = {
labels: [this.date].map(labels => DateTime.fromMillis(labels * 1000).toFormat('MMM yyyy')),
datasets: [
{
data: [this.challenge],
label: 'Africa',
borderColor: '#7367F0'
}
]
}
})
.catch(error => {
console.log(error)
})
}
}
}
</script>
Dont know why the chart did not appear even though my other resources have been loaded from the API call, when i checked out my console, this is what error im getting:
TypeError: results.map is not a function
Please check out my logic and let me where the error is.

How can I fix my Vue component to properly show my Vue-Chart.js line chart?

I would like to create a line chart using the vue-chartjs library.
What I have created so far produces no error but it also renders nothing but a blank canvas. When I switch to the developer view, I notice that all my data prints out. I'm just not sure why it's not rendering.
Here's my HTML and a snippet of the Vue code:
<div class="app">
<h1>Line Chart</h1>
<line-chart></line-chart>
</div>
<script>
Vue.component('line-chart', {
extends: VueChartJs.Line,
mounted () {
this.renderChart({
labels: this.chartDate,
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: this.expectedFund
}
]
}, {responsive: true, maintainAspectRatio: false})
}
})
new Vue({
el: '.app',
data: {
message: 'Hello World',
dataSetData: [],
expectedFund: '',
chartDate: '',
crossOver: '',
billing: ''
},
methods: {
getDataSet: function(dataField) {
console.log("get data sets");
console.log(this.dataSetData);
this.expectedFund = this.dataSetData.map(function(chartData) {
//alert("expected");
console.log(chartData);
return chartData.ExpectedFund;
});
this.billing = this.dataSetData.map(function(chartData) {
return chartData.Billing;
});
this.billing = this.dataSetData.map(function(chartData) {
return chartData.Billing;
});
this.chartDate = this.dataSetData.map(function(chartData) {
return chartData.date;
});
this.crossOver = this.dataSetData.map(function(chartData) {
return chartData.crossOver;
});
},
getListData: async function() {
const { data } = await axios.get(
"https://my-json-server.typicode.com/isogunro/jsondb/chartData"
);
return data;
}
},
mounted: async function() {
this.dataSetData = await this.getListData();
console.log("ok", this.dataSetData);
this.getDataSet();
}
})
</script>
If the pasted code is not enough, here's the Pen
After much struggle and bouncing around a bunch of Vue discords, I was able to figure out how to create a multi-line and bar-chart using Vue-Chartjs. It was a struggle worth it because I finally understand the use of props and how they work, which is what I was missing with the vuejs charts. Here's a pen showing the solution.
I am posting the json below because my charts use that data found in "my fake json server/typicode". It might change in the future, so I'm pasting it here.
{"chartData":
[
{
"date":"4/4/2019",
"totalCount":381,
"ExpectedFund":191,
"Funded":290,
"Billing":125,
"crossOver":241,
"AcceptedTotal":515
},
{
"date":"4/11/2019",
"totalCount":233,
"ExpectedFund":12,
"Funded":220,
"Billing":125,
"crossOver":211,
"AcceptedTotal":315
},
{
"date":"4/18/2019",
"totalCount":542,
"ExpectedFund":34,
"Funded":240,
"Billing":125,
"crossOver":125,
"AcceptedTotal":415
},
{
"date":"4/25/2019",
"totalCount":154,
"ExpectedFund":49,
"Funded":210,
"Billing":243,
"crossOver":35,
"AcceptedTotal":115
},
{
"date":"5/2/2019",
"totalCount":300,
"ExpectedFund":55,
"Funded":200,
"Billing":125,
"crossOver":145,
"AcceptedTotal":105
},
{
"date":"5/9/2019",
"totalCount":231,
"ExpectedFund":55,
"Funded":250,
"Billing":125,
"crossOver":355,
"AcceptedTotal":215
},
{
"date":"5/16/2019",
"totalCount":331,
"ExpectedFund":77,
"Funded":270,
"Billing":312,
"crossOver":15,
"AcceptedTotal":615
},
{
"date":"5/23/2019",
"totalCount":498,
"ExpectedFund":232,
"Funded":270,
"Billing":312,
"crossOver":15,
"AcceptedTotal":615
},
{
"date":"5/30/2019",
"totalCount":102,
"ExpectedFund":33,
"Funded":150,
"Billing":25,
"crossOver":155,
"AcceptedTotal":315
},
{
"date":"6/6/2019",
"totalCount":293,
"ExpectedFund":235,
"Funded":170,
"Billing":112,
"crossOver":125,
"AcceptedTotal":315
},
{
"date":"6/13/2019",
"totalCount":198,
"ExpectedFund":432,
"Funded":470,
"Billing":112,
"crossOver":315,
"AcceptedTotal":215
}
]
}