link1644 link1645 link1646 link1647 link1648 link1649 link1650 link1651 link1652 link1653 link1654 link1655 link1656 link1657 link1658 link1659 link1660 link1661 link1662 link1663 link1664 link1665 link1666 link1667 link1668 link1669 link1670 link1671 link1672 link1673 link1674 link1675 link1676 link1677 link1678 link1679 link1680 link1681 link1682 link1683 link1684 link1685 link1686 link1687 link1688 link1689 link1690 link1691 link1692 link1693 link1694 link1695 link1696 link1697 link1698 link1699 link1700 link1701 link1702 link1703 link1704 link1705 link1706 link1707 link1708 link1709 link1710 link1711 link1712 link1713 link1714 link1715 link1716 link1717 link1718 link1719 link1720 link1721 link1722 link1723 link1724 link1725 link1726 link1727 link1728 link1729 link1730 link1731 link1732 link1733 link1734 link1735 link1736 link1737 link1738 link1739 link1740 link1741 link1742 link1743 link1744 link1745 link1746 link1747 link1748 link1749 link1750 link1751 link1752 link1753 link1754 link1755 link1756 link1757 link1758 link1759 link1760 link1761 link1762 link1763 link1764 link1765 link1766 link1767 link1768 link1769 link1770 link1771 link1772 link1773 link1774 link1775 link1776 link1777 link1778 link1779 link1780

[Vue.js] VueJs get url query

I’m developing a website with vuejs and at this moment I’m in a trouble, I need get the url query (page) from a url like this websitename.com/user/?page=1 but the this.$router.query.page get me a error (Uncaught TypeError: Cannot read property ‘page’ of undefined)

someone know something about this problem and can help me?

PS: I can set the query page using

this.$router.push({name: ‘userIndex’, query: { page: ‘123’ } });

and I can get the url normal params like the

userID -> (websitename.com/user/:userId | websitename.com/user/1)

but I can’t get any query parameter

Solution :

I think you can simple call like this, this will give you result value.

this.$route.query.page

Look image $route is object in vue.js Instance and you can access with this keyword and next you can select object properties like above one :

Have a look Vue-router document for selecting queries value :

vue.js Router Object

Solution 2:

Current route properties are present in this.$route, this.$router is the instance of router object which gives the configuration of the router. You can get the current route query using this.$route.query

Solution 3:

In my case I console.log(this.$route) and returned the fullPath:

console.js:
fullPath: “/solicitud/MX/666”,
params: {market: “MX”, id: “666”},
path: “/solicitud/MX/666”

console.js: /solicitud/MX/666

[Vue.js] VueJs how to make pagination with limiter and range..?

there is code like this :

var demoList = new Vue({
el: ‘#demoList’,
data: {
items: [{
“id”: 1,
“name”: “Tom”
}, {
“id”: 2,
“name”: “Kate”
}, {
“id”: 3,
“name”: “Jack”
}, {
“id”: 4,
“name”: “Jill”
}, {
“id”: 5,
“name”: “aa”
}, {
“id”: 6,
“name”: “bb”
}, {
“id”: 7,
“name”: “cc”
}, {
“id”: 8,
“name”: “dd”
}, {
“id”: 1,
“name”: “Tom”
}, {
“id”: 2,
“name”: “Kate”
}, {
“id”: 3,
“name”: “Jack”
}, {
“id”: 4,
“name”: “Jill”
}, {
“id”: 5,
“name”: “aa”
}, {
“id”: 6,
“name”: “bb”
}, {
“id”: 7,
“name”: “cc”
}, {
“id”: 8,
“name”: “dd”
}, ],
loading: false,
order: 1,
searchText: null,
ccn: null,
currentPage: 0,
itemsPerPage: 2,
resultCount: 0
},
computed: {
totalPages: function() {
console.log(Math.ceil(this.resultCount / this.itemsPerPage) + “totalPages”);
return Math.ceil(this.resultCount / this.itemsPerPage);

}
},
methods: {
setPage: function(pageNumber) {
this.currentPage = pageNumber;
console.log(pageNumber);
}
},
filters: {
paginate: function(list) {
this.resultCount = this.items.length;
console.log(this.resultCount + “ Result count”);
console.log(this.currentPage + “ current page”);
console.log(this.itemsPerPage + “ items per page”);
console.log(this.totalPages + “ Total pages 2”);
if (this.currentPage >= this.totalPages) {
this.currentPage = Math.max(0, this.totalPages - 1);
}
var index = this.currentPage * this.itemsPerPage;
console.log(index + “ index”);
console.log(this.items.slice(index, index + this.itemsPerPage));
return this.items.slice(index, index + this.itemsPerPage);
}
}
});
a {
color: #999;
}
.current {
color: red;
}
ul {
padding: 0;
list-style-type: none;
}
li {
display: inline;
margin: 5px 5px;
}
<script src=”https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<div id=”demoList”>
<div class=”containerTable”>
<table class=”table”>
<thead>
<tr class=”header”>
<th>
<div><a @click=”sortvia(‘provider_name’)”>Provider</a>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for=”item in items | paginate”>
<td>{item.name}</td>
</tr>
</tbody>
</table>
</div>
<ul>
<li v-for=”pageNumber in totalPages”>
<a href=”#” @click=”setPage(pageNumber)”>{ pageNumber+1 }</a>
</li>
</ul>
</div>

Im stuck trying to create a pager with vuejs, so I was wonder if anyone can appoint an example of how to make a pager like this if is possible “1-2-3-4-5 … 55” ??, thanks again for any help.

Solution :

Check out this code:

https://jsfiddle.net/os7hp1cy/48/

html:

<ul>
<li v-for=”pageNumber in totalPages”
v-if=”Math.abs(pageNumber - currentPage) < 3 || pageNumber == totalPages - 1 || pageNumber == 0”>
<a href=”#” @click=”setPage(pageNumber)”
:class=”{current: currentPage === pageNumber, last: (pageNumber == totalPages - 1 && Math.abs(pageNumber - currentPage) > 3), first:(pageNumber == 0 && Math.abs(pageNumber - currentPage) > 3)}”>
{ pageNumber+1 }</a>
</li>
</ul>

css:

a.first::after {
content:’…’
}

a.last::before {
content:’…’
}

Basically, it only shows pagination that is within 2 pages of the current page. Then it will also show page 1 and the last page, and will put “…” before or after the number with CSS. So if you are on page 10, it will show:

1… 8 9 10 11 12 …21

Solution 2:

I’ve forked @Jeff’s code and make a working version for vue.js 2 due to this filter migration https://vuejs.org/v2/guide/migration.html#Filters-Outside-Text-Interpolations-removed.

paginate method is moved to computed:

paginate: function() {
if (!this.users || this.users.length != this.users.length) {
return
}
this.resultCount = this.users.length
if (this.currentPage >= this.totalPages) {
this.currentPage = this.totalPages
}
var index = this.currentPage * this.itemsPerPage - this.itemsPerPage
return this.users.slice(index, index + this.itemsPerPage)
}

WARNING: I didn’t apply the text filter, just the pagination first.

https://jsfiddle.net/c1ghkw2p/

[Vue.js] How can I share a method between components in Vue.js?

Check out this simple shopping cart demo:

http://plnkr.co/edit/CHt2iNSRJAJ6OWs7xmiP?p=preview

A user can pick a veggie and a fruit, and it will be added into the cart array. The function that adds a fruit/veggie is very similar, and to combine it into a function that can be shared across both components.

selectFruit: function(product){
var cart = this.cart
for(p in cart){
if (cart[p][“type”] == “fruit”){
console.log(“We already got a fruit!, Let’s remove “ + cart[p][“name”] + “ and add in “ + product[“name”]);
this.cart.$remove(cart[p])
}
}
console.log(“Adding “ + product.name + “ to cart.”);
var productName = product.name
var cartFruit = {name: product.name, type: ‘fruit’}
this.cart.push(cartFruit)
}

selectVeggie: function(product){
var cart = this.cart
for(p in cart){
if (cart[p][“type”] == “veggie”){
console.log(“We already got a veggie!, Let’s remove “ + cart[p][“name”] + “ and add in “ + product[“name”]);
this.cart.$remove(cart[p])
}
}
console.log(“Adding “ + product.name + “ to cart.”);
var productName = product.name
var cartVeggie = {name: product.name, type: ‘veggie’}
this.cart.push(cartVeggie)
}

How can I make it so I can alter this method and have it used globally? I’m using the vue.js Router with this project btw, thanks for any help!

Solution :

Option 1

One approach for sharing the method across components is to use a mixin. Here’s a cartMixin that contains a selectProduct method:

var cartMixin = {
methods: {
selectProduct: function (product) {
var cart = this.cart
for(p in cart){
if (cart[p][“type”] == product.type){
console.log(“We already got a “+ product.type +”!, Let’s remove “ + cart[p][“name”] + “ and add in “ + product[“name”]);
this.cart.$remove(cart[p])
}
}
console.log(“Adding “ + product.name + “ to cart.”);
var productName = product.name
var cartProduct = {name: product.name, type: product.type}
this.cart.push(cartProduct)
}
}
};

You can reference this in each component like this:

var Vegetable = Vue.extend({
template: ‘#vegetable’,
mixins: [cartMixin],
data: function(){
return sourceOfTruth
}
})

… and then use it in the templates like this:

<li v-for=”product in food | showOnly ‘fruit’” @click=”selectProduct(product)”>
{product.name}
</li>

Here’s a fork of the Plunker.

Option 2

After thinking about this some more, another option you might consider is to create a base Product component and extend that to create the Fruit and Vegetable components. You would then put the common functionality in the base component.

var Product = Vue.extend({
data: function(){
return sourceOfTruth
},
methods: {
selectProduct: function (product) {
var cart = this.cart
for(p in cart){
if (cart[p][“type”] == product.type){
console.log(“We already got a “+ product.type +”!, Let’s remove “ + cart[p][“name”] + “ and add in “ + product[“name”]);
this.cart.$remove(cart[p])
}
}
console.log(“Adding “ + product.name + “ to cart.”);
var productName = product.name
var cartProduct = {name: product.name, type: product.type}
this.cart.push(cartProduct)
}
}
})

var Vegetable = Product.extend({
template: ‘#vegetable’,
});
var Fruit = Product.extend({
template: ‘#fruit’,
});

Here’s a Plunker with this approach.

Given that the Fruit and Vegetable templates are so similar, you might be able to take this idea even further and use a common template from the base component.

Solution 2:

I found this technique to be more simple/satisfactory, as I prefer composition over inheritance:

src/shared.js

export default {
foo: function() { alert(“foo!”) }
}

src/yourcomponent.vue

<template>…</template>

<script>
import shared from ‘./shared’

export default {
mounted: function() {
this.foo = shared.foo // now you can call this.foo() (in the functions/template)
}
}
</script>

This will also allow you to write Vue-agnostic tests.

NOTE: if you need foo to run in Vue-scope replace this.foo = shared.foo with this.foo = shared.foo.bind(this)

Solution 3:

You can put the method in the root vue.js instance and then dispatch an event from the child instance when a veggie is selected, or when a fruit is selected. Events look for a handler on their parent component, and if they don’t find an event handler they keep going up the chain until they do. So on the root instance:

events: {
‘choose-fruit’:function(fruit){

//handle the choosing of fruit

}
}

Then on the child instance:

selectFruit: function(product){

this.$dispatch(‘choose-fruit’, product);

}

[Vue.js] Vue.js + Webpack + vue-loader + bootstrap-sass + sass-loader

I’m just getting started with Vue.js + Webpack + vue-loader + bootstrap-sass + sass-loader and I’m a little lost.

What I’d like to do is use the SASS version of bootstrap with my SPA Vue.js code. to do this so my bootstrap customisations can be done using SASS. Here is what I’ve done:

Created a new Vue.js + webpack project with vue-cli.
Installed bootstrap-sass and sass-loader.
Added the following to build/webpack.base.conf.js:

{ test: /\.scss$/, loaders: [ ‘style’, ‘css’, ‘sass’ ] },
{ test: /\.(woff2?|ttf|eot|svg)$/, loader: ‘url’, query: { limit: 10000 } }

Created src/style.scss with one line: @import ‘bootstrap’;
Added this line to the top of src/main.js: import ‘./style.scss’

When I now run npm run dev I get the following error:

ERROR in ./src/main.js
Module not found: Error: Cannot resolve module ‘style’ in Users/rstuart/Workspace/javascript/kapiche-demo/src
@ ./src/main.js 3:0-25

I’m not sure why this isn’t working.

Also, related to this question, how do I get access to Bootstrap SASS variables inside my vue.js components? If I understand what is going on here, the SASS will be compiled to CSS before being included inline in main.js meaning there is no access to any Bootstrap variables in my components. Is there a way to achieve this?

Solution :

I managed to solve this problem myself. Instead of trying to directly import style.scss, I deleted the file entirely and I replaced the <style> element of App.vue.js with the following:

<style lang=”sass”>
$icon-font-path: “../node_modules/bootstrap-sass/assets/fonts/bootstrap/“;
@import ‘../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap’;

.wrapper {
margin-top: $navbar-height;
}
</style>

This has the added bonus of making Bootstrap variables available in the style block of vue.js components. Also, I removed { test: /\.scss$/, loaders: [ ‘style’, ‘css’, ‘sass’ ] } from webpacker.base.conf.js entirely but kept the bit dealing with fonts. The loader for .vue.js files already deals with sass.

Solution 2:

I managed to worked it the right way like this:

Vue:

<style lang=”sass”>
$icon-font-path: “~bootstrap-sass/assets/fonts/bootstrap/“;
@import “~bootstrap-sass/assets/stylesheets/bootstrap”;
</style>

webpack config loader:

{
test: /\.(png|jpg|gif|svg)$/,
loader: ‘file-loader’,
options: {
name: ‘[name].[ext]?[hash]‘
}
},
{
test: /\.scss$/,
loaders: [ ‘style-loader’, ‘css-loader’, ‘sass-loader’ ]
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: “url-loader?limit=10000&minetype=application/font-woff”
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: “file-loader”
}

Take note that I use the bootstrap-sass and not the default boostrap

Solution 3:

It’s trying to resolve the style module that you specified as a loader in this section of the webpack.base.conf.js:

{ test: /\.scss$/, loaders: [ **‘style’**, ‘css’, ‘sass’ ] }

Given that you have a css and sass loader, that’s likely an erroneous entry that you can remove to get yourself going.

[Vue.js] Vuex state on page refresh

My app uses the Firebase API for User Authentication, saving the Login status as a boolean value in a Vuex State.

When the user logs in I set the login status and conditionally display the Login/Logout button accordingly.

But when the page is refreshed, the state of the vue.js app is lost and reset to default

This causes a problem as even when the user is logged in and the page is refreshed the login status is set back to false and the login button is displayed instead of logout button even though the user stays logged in….

What shall I do to prevent this behavior

Shall i use cookies
Or any other better solution is available…

-

Solution :

This is a known usecase. There are different solutions.

For example, one can use vuex-persistedstate. This is a plugin for vuex to handle and store state between page refreshes.

Sample code:

import { Store } from ‘vuex’
import createPersistedState from ‘vuex-persistedstate’
import * as Cookies from ‘js-cookie’

const store = new Store({
// …
plugins: [
createPersistedState({
getState: (key) => Cookies.getJSON(key),
setState: (key, state) => Cookies.set(key, state, { expires: 3, secure: true })
})
]
})

What we do here is simple:

you need to install js-cookie
on getState we try to load saved state from Cookies
on setState we save our state to Cookies

Docs and installation instructions: https://www.npmjs.com/package/vuex-persistedstate

Solution 2:

I think use cookies/localStorage to save login status might cause some error in some situation.
Firebase already record login information at localStorage for us include expirationTime and refreshToken.
Therefore I will use vue.js created hook and Firebase api to check login status.
If token was expired, the api will refresh token for us.
So we can make sure the login status display in our app is equal to Firebase.

new Vue({
el: ‘#app’,
created() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
log(‘User is logined’);
// update data or vuex state
} else {
log(‘User is not logged in.’);
}
});
},
});

Solution 3:

I’ve solved this by resetting my headers every time I re-load also fetch user data, I don’t know what is better …

new Vue({
el: ‘vue’,
render: h => h(VueBox),
router,
store,

computed: {
tokenJWT () {
return this.$store.getters.tokenCheck
},
},

created() {
this.setAuth()

},

methods:
Object.assign({}, mapActions([‘setUser’]), {

setAuth(){
if (this.tokenJWT) {
if (this.tokenJWT === ‘expired’) {
this.$store.dispatch(‘destroyAuth’)
this.$store.dispatch(‘openModal’)
this.$store.dispatch(‘setElModal’, ‘form-login’)

} else {
window.axios.defaults.headers.common = {
‘Accept’: ‘application/json’,
‘Authorization’: ‘Bearer ‘ + this.tokenJWT
}
axios.get( api.domain + api.authApi )
.then(res => {
if (res.status == 200) {
this.setUser( res.data.user )
}
})
.catch( errors => {
console.log(errors)
this.destroyAuth()
})
}
}
}
})

})

Solution 4:

put on state:

producer: JSON.parse(localStorage.getItem(‘producer’) || “{}”)

put on mutations:

localStorage.setItem(“producer”,JSON.stringify(state.producer)) // OR
localStorage.removeItem(“producers”);

works fine for me!

[Vue.js] forEach is not a function error with JavaScript array

I’m trying to make a simple loop:

const parent = this.el.parentElement
console.log(parent.children)
parent.children.forEach(child => {
console.log(child)
})

But I get the following error:

VM384:53 Uncaught TypeError: parent.children.forEach is not a function

Even though parent.children logs:

What could be the problem?

Note: Here’s a JSFiddle.

Solution :

The parent.children is an Array like object. Use the following solution:

const parent = this.el.parentElement;
console.log(parent.children);
Array.prototype.forEach.call(parent.children, child => {
console.log(child)
});

The parent.children is NodeList type, which is an Array like object because:

It contains the length property, which indicates the number of nodes
Each node is a property value with numeric name, starting from 0: {0: NodeObject, 1: NodeObject, length: 2, …}

See more details in this article.

Solution 2:

parent.children is not an array. It is HTMLCollection and it does not have forEach method. You can convert it to the array first. For example in ES6:

Array.from(parent.children).forEach(function (child) {
console.log(child)
});

or using spread operator:

[…parent.children].forEach(function (child) {
console.log(child)
});

Solution 3:

parent.children will return a node list, technically a html element Collection. That is an array like object, but not an array, so you cannot call array functions over it directly. At this context you can use Array.from() to convert that into a real array,

Array.from(parent.children).forEach(child => {
console.log(child)
})

Solution 4:

parent.children is a HTMLCollection which is array-like object. First, you have to convert it to a real Array to use Array.prototype methods.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
console.log(child)
})

Solution 5:

That’s because parent.children is a NodeList, and it doesn’t support the .forEach method (as NodeList is an array like structure but not an array), so try to call it by first converting it to array using

var children = [].slice.call(parent.children);
children.forEach(yourFunc);

Solution 6:

A more naive version, at least the sure that it’s work on all device, without conversion and ES6 :

const children = parent.children;
for (var i = 0; i < children.length; i++){
console.log(children[i]);
}

https://jsfiddle.net/swb12kqn/5/

Solution 7:

If you are trying to loop over a NodeList like this:

const allParagraphs = document.querySelectorAll(“p”);

I highly recommend loop it this way:

Array.prototype.forEach.call(allParagraphs , function(el) {
// Write the code here
})

Personally, I’ve tried several ways but most of them didn’t work as I wanted to loop over a NodeList, but this one works like a charm, give it a try!

The NodeList isn’t an Array, but we treat it as an Array, using Array. So, you need to know that it is not supported in older browsers!

Need more information about NodeList? Please read its documentation on MDN.

Solution 8:

There is no need for the forEach, you can iterate using only the from’s second parameter, like so:

let nodeList = [{0: [{‘a’:1,’b’:2},{‘c’:3}]},{1:[]}]
Array.from(nodeList, child => {
console.log(child)
});

Solution 9:

Since you are using features of ES6 (arrow functions), you may also simply use a for loop like this:

for(let child of [{0: [{‘a’:1,’b’:2},{‘c’:3}]},{1:[]}]) {
console.log(child)
}

Solution 10:

This has always worked for me

const someElement = document.querySelectorAll(‘.someElement’);

someElement.forEach((element) => {
// the code here
});

[Vue.js] Vuex - Computed property name was assigned to but it has no setter

there is a component with some form validation. It is a multi step checkout form. The code below is for the first step. I’d like to validate that the user entered some text, store their name in the global state and then send then to the next step. when using vee-validate and vuex

<template>
<div>
<div class=’field’>
<label class=’label’ for=’name’>Name</label>
<div class=”control has-icons-right”>

<input name=”name” v-model=”name” v-validate=”‘required|alpha’” :class=”{‘input’: true, ‘is-danger’: errors.has(‘name’) }” type=”text” placeholder=”First and Last”>
<span class=”icon is-small is-right” v-if=”errors.has(‘name’)”>
<i class=”fa fa-warning”></i>
</span>
</div>
<p class=”help is-danger” v-show=”errors.has(‘name’)”>{ errors.first(‘name’) }</p>

</div>
<div class=”field pull-right”>
<button class=”button is-medium is-primary” type=”submit” @click.prevent=”nextStep”>Next Step</button>
</div>
</div>
</template>

<script>
export default {
methods: {
nextStep(){
var self = this;

// from baianat/vee-validate
this.$validator.validateAll().then((result) => {
if (result) {
this.$store.dispatch(‘addContactInfoForOrder’, self);
this.$store.dispatch(‘goToNextStep’);
return;
}
});
}
},
computed: {
name: function(){
return this.$store.state.name;
}
}
}
</script>

there is a store for handling order state and recording the name. Ultimately I would like to send all of the info from multi step form to the server.

export default {
state: {
name: ‘’,
},

mutations: {
UPDATE_ORDER_CONTACT(state, payload){
state.name = payload.name;

}
},

actions: {
addContactInfoForOrder({commit}, payload) {
commit(‘UPDATE_ORDER_CONTACT’, payload);
}
}
}

When I run this code I get an error that Computed property “name” was assigned to but it has no setter.

How do I bind the value from the name field to the global state? I would like this to be persistent so that even if a user goes back a step (after clicking “Next Step”) they will see the name they entered on this step

Solution :

If you’re going to v-model a computed, it needs a setter. Whatever you want it to do with the updated value (probably write it to the $store, considering that’s what the getter pulls it from) you do in the setter.

If writing it back to the store happens via form submission, you don’t want to v-model, you just want to set :value.

If you want to have an intermediate state, where it’s saved somewhere but doesn’t overwrite the source in the $store until form submission, you’ll need to create such a data item.

Solution 2:

`

computed: {
isSubmitAttemped() {
return Object.keys(this.fields).every(field => {
return this.fields[field] && this.fields[field].valid;
});
}
}

<button class=”btn btn-warning btn-block” v-bind:disabled=”!isSubmitAttemped” >Login</button>

`

[Vue.js] Vue JS mounted()

when creating a game in VueJS, where, when the page loads, a method to fire, make an ajax call to an external API and create a bunch of data properties. When the player wins the round, to them to be able to see a button that allows them to restart the game. when using a mounted() hook to fire the method on page load.

My question is when not sure how to implement the restart functionality if the game setup and API call are within the mounted() function. Is there a way to run the mounted() function again?

Thanks!

Solution :

Abstract the initialization into a method, and call the method from mounted and wherever else you want.

new Vue({
methods:{
init(){
//call API
//Setup game
}
},
mounted(){
this.init()
}
})

Then possibly have a button in the template to start over.

<button v-if=”playerWon” @click=”init”>Play Again</button>

In this button, playerWon represents a boolean value in the data that you would set when the player wins the game so the button appears. You would set it back to false in init.

[Vue.js] How can I solve Uncaught TypeError Cannot read property 'get' of undefined in the vuex store?

If I try this .$session.get(SessionKeys.Cart) in my component like this :

<template>

</template>
<script>

export default {

methods: {
add(item) {
console.log(this.$session.get(SessionKeys.Cart)

}
}
}
</script>

It works. I success get session cart

But if I try it in the my vuex store like this :

import { set } from ‘vue’
// initial state
const state = {
list: {}
}
// getters
const getters = {
list: state => state.list
}
// actions
const actions = {
addToCart ({ dispatch,commit,state },{data})
{
console.log(this.$session.get(SessionKeys.Cart))

}
}
// mutations
const mutations = {

}
export default {
state,
getters,
actions,
mutations
}

There exist error : Uncaught TypeError: Cannot read property ‘get’ of undefined

How can I solve this error?

Solution :

You can pass the component this into the dispatch function, called dispatching with a payload. like so:

<template>

</template>
<script>

export default {

methods: {
this.$store.dispatch(‘addToCart’, { data: {}, ctx: this })

// add(item) {
// console.log(this.$session.get(SessionKeys.Cart)
// …
//}
}
}
</script>

import { set } from ‘vue’

// initial state
const state = {
list: {}
}

// getters
const getters = {
list: state => state.list
}

// actions
const actions = {
addToCart ({ dispatch, commit, state }, { data, ctx })
{
console.log(ctx.$session.get(SessionKeys.Cart))

}
}

// mutations
const mutations = {

}

export default {
state,
getters,
actions,
mutations
}

[Vue.js] Force download GET request using axios

I’m using vuejs 2 + axios.
I need to send a get request, pass some params to server, and get a PDF as a response. Server uses Laravel.

So

axios.get(`order-results/${id}/export-pdf`, { params: { … })

makes successful request but it does not start force downloading, even though server returns correct headers.

I think this is a typical situation when you need to, say, form a PDF report and pass some filters to server. So how could it be accomplished?

Update

So actually I found a solution. However the same approach didn’t work with axios, don’t know why, that’s why I used raw XHR object. So the solution is to create a blob object and user createUrlObject function. Sample example:

let xhr = new XMLHttpRequest()
xhr.open(‘POST’, Vue.config.baseUrl + `order-results/${id}/export-pdf`, true)
xhr.setRequestHeader(“Authorization”, ‘Bearer ‘ + this.token())
xhr.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”)
xhr.responseType = ‘arraybuffer’

xhr.onload = function(e) {
if (this.status === 200) {
let blob = new Blob([this.response], { type:”application/pdf” })
let link = document.createElement(‘a’)
link.href = window.URL.createObjectURL(blob)
link.download = ‘Results.pdf’
link.click()
}
}

Important: you should have array buffer as response type

However, the same code written in axios returns PDF which is empty:

axios.post(`order-results/${id}/export-pdf`, {
data,
responseType: ‘arraybuffer’
}).then((response) => {
console.log(response)

let blob = new Blob([response.data], { type: ‘application/pdf’ } ),
url = window.URL.createObjectURL(blob)

window.open(url); // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
})

Solution :

You’re getting empty PDF ‘cause no data is passed to the server. You can try passing data using data object like this

axios
.post(`order-results/${id}/export-pdf`, {
data: {
firstName: ‘Fred’
},
responseType: ‘arraybuffer’
})
.then(response => {
console.log(response)

let blob = new Blob([response.data], { type: ‘application/pdf’ }),
url = window.URL.createObjectURL(blob)

window.open(url) // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
})

By the way I gotta thank you so much for showing me the hint in order to download pdf from response. Thank ya :)

var dates = {
fromDate: 20/5/2017,
toDate: 25/5/2017
}

The way in which there is used is,

axios({
method: ‘post’,
url: ‘/reports/interval-dates’,
responseType: ‘arraybuffer’,
data: dates
}).then(function(response) {
let blob = new Blob([response.data], { type: ‘application/pdf’ })
let link = document.createElement(‘a’)
link.href = window.URL.createObjectURL(blob)
link.download = ‘Report.pdf’
link.click()
})

Solution 2:

Try this:
It works perfectly for me with compatibility for Internet Explorer 11 (createObjectURL doesn’t work on Explorer 11)

axios({
url: ‘http://vvv.dev',
method: ‘GET’,
responseType: ‘blob’, // important
}).then((response) => {
if (!window.navigator.msSaveOrOpenBlob){
// BLOB NAVIGATOR
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement(‘a’);
link.href = url;
link.setAttribute(‘download’, ‘download.pdf’);
document.body.appendChild(link);
link.click();
}else{
// BLOB FOR EXPLORER 11
const url = window.navigator.msSaveOrOpenBlob(new Blob([response.data]),”download.pdf”);
}
});

https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

Solution 3:

I don’t think its possible to do this in axios or even AJAX. The file will be kept in memory, i.e. you cannot save file to disk. This is because JavaScript cannot interact with disk. That would be a serious security issue and it is blocked in all major browsers.

You can construct the URL in front-end and download it in the following way:

var url = ‘http://example.com/order-results/' + id + ‘/export-pdf?’ + ‘..params..’

window.open(url, ‘_blank’);

Hope this helps!

Solution 4:

I had similar issues- I ended up creating link and downloading from there.

I put more details of how in the answer on another stackoverflow question.