Just started to integrate vuex to my laravel application, going through with the sample counter application in the official vuex docs.
js/components/calculate.vue
<template>
<div>
<p>{{ count }}</p>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script >
import store from '../app.js'
export default {
computed: {
count () {
return store.state.count
}
},
methods: {
increment () {
store.commit('increment')
},
decrement () {
store.commit('decrement')
}
}
}
</script>
js/app.js
const calculate = Vue.component('calculate', require('./components/calculate.vue'));
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
});
const app = new Vue({
el: '#app'
});
Using webpack build
I got store.state.count not defined error in this line return store.count.
If you are creating your store in app.js file, (since you haven't mentioned) you need to add
const calculate = Vue.component('calculate', require('./components/calculate.vue'));
Vue.use(Vuex) // this line
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
});
const app = new Vue({
el: '#app'
});
This goes without saying that you need to import Vuex in this file.
after which you need to modify, this part of your code:
const app = new Vue({
el: '#app'
});
to
const app = new Vue({
el: '#app',
store // if you're vue-cli/babel else use store: store
});
And in js/components/calculate.vue
<template>
<div>
<p>{{ count }}</p>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script >
// import store from '../app.js' this import is not required
export default {
computed: {
count () {
return this.$store.state.count
}
},
methods: {
increment () {
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
}
}
</script>
The import is not required as the store is supposed to be accessible to each component of the vue instance, to access it, you just need to have access to $store property of the vue instance which in your component is this (as long as the context doesn't change by means of a callback or something similar.)
Related
I am learning laravel with Vue js I am implementing a search engine with vue js components, I hope you can help me in advance thanks.
This is my Vue js component where my input search is located.
<template>
<div class="container">
<form>
<input
class="form-control"
placeholder="buscar"
type="search"
v-model="buscador"
#keyup="buscarProductos"
>
</form>
</div>
</template>
This is my app.js file.
const { default: Axios } = require('axios');
require('./bootstrap');
window.Vue = require('vue');
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
const app = new Vue({
el: '#app',
created(){
this.producto();
},
data: {
libros:[],
buscador: '',
setTimeoutbuscador:''
},
methods: {
producto(){
Axios.get('./sales', {
params:{
filtro: this.buscador
}
})
.then(res => {
this.libros = res.data.data ;
})
.catch( error => {
console.log( error.response )
});
},
buscarProductos(){
clearTimeout( this.setTimeoutbuscador = setTimeout(this.producto, 360) )
}
}
});
I have a blade view where I place my component in this way.
<div id="app">
<example-component></example-component>
</div>
Your ExampleComponent.vue file should look like below
<template>
<div class="container">
<form>
<input
class="form-control"
placeholder="buscar"
type="search"
v-model="buscador"
#keyup="buscarProductos"
>
</form>
</div>
</template>
<script>
import Axios from 'axios';
export default {
created(){
this.producto();
},
data(){
return {
buscador: ''
}
},
methods: {
producto(){
Axios.get('./sales', {
params:{
filtro: this.buscador
}
})
.then(res => {
this.libros = res.data.data ;
})
.catch( error => {
console.log( error.response )
});
},
buscarProductos(){
clearTimeout( this.setTimeoutbuscador = setTimeout(this.producto, 360) )
}
}
}
And your app.js would be
require('./bootstrap');
import Vue from 'vue';
import ExampleComponent from './ExampleComponent';
Vue.component('example-component', ExampleComponent);
const app = new Vue({
el: '#app'
});
Then you can use the ExampleComponent as
<div id="app">
<example-component></example-component>
</div>
Data properties defined on root component cannot be accessed as local properties on a vue component.
Data properties of root component can be accessed as $root.bucador for example.
However as far as possible Vue components should be defined as decoupled as possible - it should contain properties required in it's template. For properties required to be passed from parent component they can be passed as props.
Read Vue docs https://v2.vuejs.org/v2/guide/ it has plenty of examples
I'm getting started with Vue, I need to create a form of tiered select fields. That is the selected option for A, uses that to call the API to get the options for B, which determines options for C.
I'm still pretty new to frontend frameworks so this might be a terrible design. However not every inclusion of A (SelectState.vue) in a view requires all the children so making them modular was my first thought.
Currently I have a top level component that displays the select options:
SelectState.vue
<template>
<div id="select-state">
<span>{{ label }}</span>
<select v-model="selectedState">
<option v-for="state in states" :key="state">
{{ state }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'select-state',
data: function () {
return {
selectedState: '',
states: ['TX']
}
},
props: ['label']
// this.states = axios.get('xxx')
}
</script>
Index.vue
<template>
<div id="form">
<v-select-state label="State"></v-select-state>
<v-select-zip label="Zip"></v-select-zip>
</div>
</template>
<script>
import SelectState from './SelectState.vue'
import SelectZip from './SelectZip.vue'
export default {
name: 'Index',
components: {
'v-select-state': SelectState,
'v-select-Zip': SelectZip
}
}
</script>
Then I have a SelectZip.vue that is identical to SelectState.vue except that it has a parameter for its axios.get('XXX', params = {'state': ???}). But I'm stuck on how to "pass" that necessary parameter.
Thanks in advance!
edit: In conjunction with #dziraf's answer my working although verbose SelectedZip.vue is as follows:
<template>
<div id="select_zip">
<span>{{ label }}</span>
<select v-model="selected_zip">
<option v-for="zip in zips" :key="zip">
{{ zip }}
</option>
</select>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'select_zip',
data: function () {
return {
zips: []
}
},
props: ['label'],
computed: {
selected_zip: {
get () { return this.$store.state.formModule.zip },
set (value) { this.$store.commit('formModule/setZips', value) }
},
selected_state: {
get () { return this.$store.state.formModule.state }
}
},
methods: {
getValidZips (state) {
axios.post('/api/v1/get_valid_zips', {
params:{'state': state }})
.then(response => {
this.zips = response.data
})
.catch(error => {
console.log(error)
})
}
},
watch: {
selected_state (value) {
this.getValidZips(value)
}
}
}
</script>
You can pass it by adding 'state' props to your select components from your main form component, but I think it isn't a good long-term solution.
Instead, consider using Vuex. An example configuration would look like this:
#/store/modules/form.js
const Form = {
namespaced: true,
state: {
state: '',
zip: ''
},
getters: {},
mutations: {
setState (state, payload) {
state.state = payload
},
setZip (state, payload) {
state.zip = payload
}
},
actions: {}
}
export default Form
#/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import Form from './modules/form'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
formModule: Form,
}
})
export default store
#/main.js
// your impots
import store from './store/index'
// your configs
new Vue({
el: '#app',
router,
store, // add store to your main Vue instance so it's accessible with this.$store
axios,
components: { App },
template: '<App/>'
});
This would be your SelectState.vue:
<template>
<div id="select-state">
<span>{{ label }}</span>
<select v-model="selectedState">
<option v-for="state in states" :key="state">
{{ state }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'select-state',
data: function () {
return {
states: ['TX']
}
},
computed: {
selectedState: {
get() { return this.$store.state.formModule.state },
set(value) { this.$store.commit('formModule/setState', value) }
}
},
props: ['label']
}
</script>
Your SelectZip.vue would be the same, except you would instead use your store's zip as your v-model.
Your store variables are accessible across your app and you can access them with computed properties in your components.
I am working on a Vue Js 2 application and I'm currently building the store and the different modules to separate out the code.
Is there a way to write a common function and share it across all modules?
For example, I have a function truncate() that I need to be use in customer.js, cart.js, address.js.
If I declare it in store.js and try to use in modules, it throws an error.
Is export and import the only way?
What is the best way to share the function?
The simplest case is, naturally, to just define a regular function in a js file and import/use it anywhere you need it.
There are Vue-specific approaches, though:
For common reusable functions in Vuex modules, you can use Vuex Plugins.
Check an example below. Mind the usage at the root store: plugins: [myTruncatePlugin].
const myTruncatePlugin = store => {
store.truncate = function(str) {
return str.replace(/-/g, '') + ' ...was truncaaaated!'; // example implementation
}
}
const moduleA = {
namespaced: true,
state: {name: "name#moduleA"},
mutations: { changeName(state, data) { state.name = this.truncate(data); } },
}
const moduleB = {
namespaced: true,
state: {title: "title#moduleB"},
mutations: { changeTitle(state, data) { state.title = this.truncate(data); } },
}
const myStore = new Vuex.Store({
strict: true,
modules: {
aaa: moduleA,
bbb: moduleB
},
plugins: [myTruncatePlugin] // IMPORTANT: YOU MUST DECLARE IT HERE
});
new Vue({
store: myStore,
el: '#app',
mounted: function() {
setTimeout(() => {
this.changeName("-n-e-w-N-A-M-E-");
this.changeTitle("-n-e-w-T-I-T-L-E-");
}, 200);
},
computed: {
...Vuex.mapState('aaa', ['name']),
...Vuex.mapState('bbb', ['title'])
},
methods: {
...Vuex.mapMutations('aaa', ['changeName']),
...Vuex.mapMutations('bbb', ['changeTitle'])
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>moduleA's name: {{ name }}</p>
<p>moduleB's title: {{ title }}</p>
</div>
For common reusable functions in Vue instances, you can use Mixins. For the most general case there's the Global Mixin (use with care):
Vue.mixin({
methods: {
truncate(str) {
return str.replace(/-/g, '') + ' ...was truncaaaated!'; // example implementation
}
}
})
// this.truncate() will be available in all Vue instances...
new Vue({
el: '#app1',
data: {myStr1: '-o-n-e-'},
mounted() { this.myStr1 = this.truncate(this.myStr1); }
})
new Vue({
el: '#app2',
data: {myStr2: '-t-w-o-'},
mounted() { this.myStr2 = this.truncate(this.myStr2); }
})
// ...and components
Vue.component('my-comp', {
template: '#t3',
data() { return {myStr3: '-t-h-r-e-e-'} },
mounted() { this.myStr3 = this.truncate(this.myStr3); }
});
new Vue({
el: '#app3',
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.min.js"></script>
<div id="app1">App1: "{{ myStr1 }}"</div>
<div id="app2">App2: "{{ myStr2 }}"</div>
<template id="t3">
<div>App3's component: "{{ myStr3 }}"</div>
</template>
<div id="app3"><my-comp></my-comp></div>
#acdcjunior has the best answer using Mixins, but I'm giving you another option by just declaring method in your Vue instance.
So belows example, I am creating doTruncate method in Vue instance then the components are calling them by this.$parent.doTruncate
// register
Vue.component('cart-component', {
template: '<button #click="doTruncate">Cart Truncate!</button>',
methods: {
doTruncate: function() {
this.$parent.doTruncate("Hello from cart");
}
}
})
// register
Vue.component('customer-component', {
template: '<button #click="doTruncate">Customer Truncate!</button>',
methods: {
doTruncate: function() {
this.$parent.doTruncate("Hello from customer");
}
}
})
var app3 = new Vue({
el: '#app',
methods: {
doTruncate: function(params) {
alert(params);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.15/dist/vue.js"></script>
<div id="app">
<cart-component></cart-component>
<br>
<customer-component></customer-component>
<br>
<button #click="doTruncate('Hello from parent')">
Parent!
</button>
</div>
you can use vue js events to share function like
eventBus.js // it will create common instance
import Vue from 'vue';
export const eventBus = new Vue();
common.js // your common functions will go into this file
import { eventBus } from '<path of file>';
mounted() {
eventBus.$on('truncate',()=> {
this.truncate();
})
}
methods: {
truncate(){
//truncate code
}
}
customer.js // call your common truncate function from customer.js
import { eventBus } from '<path of file>';
eventBus.$emit('truncate');
With some help from StackOverflow I got the following to run my loadData when the page loads and when the button is clicked.
However the text on the page is not updating. Something is wrong about my syntax with this.text = xhr.data
index.html:
<div id="app"></div>
app.js:
const Vue = window.Vue = require("vue");
Vue.prototype.$http = require("axios");
const App = require("./components/App.vue");
window.app = new Vue({
el: "#app",
render: h => h(App)
});
components/app.vue:
<template>
<div>
<h1>Test</h1>
<p>{{text}}</p>
<button #click="this.loadData">Reload</button>
</div>
</template>
<script>
export default {
mounted() {
this.loadData();
},
methods: {
loadData() {
this.$http.get("https://icanhazip.com")
// This fails
.then(xhr => this.text = xhr.data);
}
}
};
</script>
You must to define your text property in components data.
From Vue.js documentation:
Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive. For example:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
In your case your component should look like this:
<script>
export default {
created() {
this.loadData();
},
data() {
return {
text: '',
};
},
methods: {
loadData() {
this.$http.get("https://icanhazip.com")
// This fails
.then(xhr => this.text = xhr.data);
}
}
};
</script>
I'm new to vue, so I'm probably making a rookie error.
I have a root vue element - raptor.js:
const Component = {
el: '#app',
store,
data: {
productList: store.state.productlist
},
beforeCreate: function () {
return store.dispatch('getProductList', 'getTrendingBrands');
},
updated: function (){
console.log(111);
startSlider();
}
};
const vm = new Vue(Component);
Using this template
<div class="grid-module-single popular-products" id="app">
<div class="row">
<div class="popular-items-slick col-xs-12">
<div v-for="product in productList">
...
</div>
</div>
</div>
My store is very simple store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import model from '../../utilities/model';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
productlist: []
},
mutations: {
setProductList(state, data) {
state.productlist = data;
}
},
actions: {
getProductList({ commit }, action) {
return model.products().then(data => commit('setProductList', data));
}
}
});
In my vuex devtool, I can see, that the store is being updated
https://www.screencast.com/t/UGbw7JyHS3
but my component is not being updated:
https://www.screencast.com/t/KhXQrePEd
Question:
I can see from the devtools, that my code is working. The store is being updated with data. My component is not being updated,however. I thought it was enough just to add this in the data property on the component:
data: {
productList: store.state.productlist
}
but apparently the data object doesn't seem to be automatically synced with the store. So either I'm doing a complete vue no-no somewhere, or I need to tweak the code a bit. Anyway can anyone help me in the right direction.
Thanks a lot.
UPDATE
Figured it out myself. Just had to replace the components data part with a computed method:
data:
data: {
productList: store.state.productlist
}
and replace it with.
computed: {
productList () {
return store.state.productlist;
}
},
data only work once on component before render, so you can use computed instead.
like above answer, or you can use mapstate
import {mapState} from 'vuex'
...
computed: mapState({
productList: state => state.productList
})
First - use getter to do this mapGetters, also you need to watch this property somehow, you can set store subscription or just with watch method trough component.
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'UPDATE_DATA') {
...
}
}
You are calling the store into the productList data property in the wrong way.
You can try it:
data: {
productList: $store.state.productlist
}
Otherwise you have to import store in each component that are using the store.