How to set pageCount manually in vuejs-paginate? - vue.js

My HTML:
<paginate
:pageCount="max"
:containerClass="'pagination'"
:clickHandler="redirectToPage"
:force-page="selectedPage">
</paginate>
max is getter from vuex. When paginate is loading max = undefined, so I need to set pageCount after value max is changed.
In offical docs only static value:

Taking Belmin Bedak's comment into account, here it is working with a v-if to make it wait for pageCount to load. In testing, I found that updating pageCount works as expected: the number of pages available is updated.
vm = new Vue({
el: '#app',
data: {
max: null
},
components: {
paginate: VuejsPaginate
},
methods: {
clickCallback: function(page) {
console.log(page);
}
},
mounted() {
// Pretend we're getting from vuex
setTimeout(() => {
this.max = 7;
}, 1000);
}
});
<link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<script src="https://unpkg.com/vuejs-paginate#0.8.0"></script>
<div id="app">
<paginate v-if="max"
:page-count="max"
:container-class="'pagination'"
:click-handler="clickCallback">
</paginate>
<div v-else>
Loading...
</div>
</div>

Related

Why doesn't Nuxtjs update template when data changes?

I have a component in Nuxt with the following HTML in the template:
<div class="slider" id="slider" ref="slider" :style="'transform: transitionX(-' + sliderPosition + '%)'">
Then I also have a data variable:
export default {
data() {
return {
sliderPosition: 0
}
},
methods: {
moveLeft() {
this.sliderPosition -= 25
console.log(this.sliderPosition)
},
Now, every time I click the button that triggers the method, the console logs out the incremented sliderPosition value.
But in the template it always stays 0 and never updates - the transitionX is never changed (shows 0% in Chrome inspector and doesn't change).
Why could this be?
There is no transform function transitionX - docs. I guess you wanted translateX
Your sliderPosition is negative and you have a - in the style expression. So if the value of sliderPosition is -25 the style is translateX(--25) which is not valid
So just to be clear, this is not a problem of Vue not updating the DOM but instead problem of browser not accepting invalid style...
const vm = new Vue({
el: '#app',
data() {
return {
sliderPosition: 0
}
},
methods: {
move(percent) {
this.sliderPosition += percent
console.log(this.sliderPosition)
},
},
computed: {
sliderStyle() {
return {
transform: `translateX(${this.sliderPosition}%)`
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app">
<button #click="move(-5)">Move left</button>
<button #click="move(5)">Move right</button>
<div class="slider" id="slider" ref="slider" :style="sliderStyle">This is OK!
</div>
<div class="slider" id="slider" ref="slider" :style="`transform: translateX(-${this.sliderPosition}%)`">This is not!
</div>
</div>

Computed property setter creates Maximus stack exceeded

I have such code:
<div id="app">
<b-form-group label="Sorting">
<b-form-checkbox-group
v-model="sorting"
:options="filterData.sorting"
/>
</b-form-group>
</div>
new Vue({
el: '#app',
computed: {
sorting: {
get: function () {
return this.filterInput.sorting
},
set: function (value) {
// this array needs to always have only one value
this.filterInput.sorting = [value[0]]
}
}
},
data () {
return {
filterData: {
sorting: ['PRICE_ASC', 'PRICE_DESC']
},
filterInput: {
sorting: []
}
}
}
})
https://jsfiddle.net/pum86bsx/1/
Error seems to be in computed getter. When I comment it out it's all good. I have no idea why it's like that.
You setting the value of the v-model (the value returned in the getter), which will cause the setter to fire again.. and again.. and again.. Causing your stack to overflow.
Using checkboxes
You could instead use the #change event, which should only fire when the value actually changes.
new Vue({
el: '#app',
data () {
return {
filterData: {
sorting: ['PRICE_ASC', 'PRICE_DESC']
},
filterInput: {
sorting: []
}
}
},
methods: {
onChange(value) {
if(value.length === 0) {
this.filterInput.sorting = value
} else {
this.filterInput.sorting = [value[value.length - 1]]
}
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-form-group label="Sorting">
<b-form-checkbox-group
v-model="filterInput.sorting"
:options="filterData.sorting"
#change="onChange"
></b-form-checkbox-group>
</b-form-group>
</div>
Using radio buttons
Alternatively you could use radio buttons, which only allows one to be selected at a time, so you don't have to handle that yourself.
new Vue({
el: '#app',
data () {
return {
filterData: {
sorting: ['PRICE_ASC', 'PRICE_DESC']
},
filterInput: {
sorting: []
}
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-form-group label="Sorting">
<b-form-radio-group
v-model="filterInput.sorting"
:options="filterData.sorting"
></b-form-radio-group>
</b-form-group>
</div>

vuejs data doesn't change when property change

I am very new to Vuejs so although I can probably devise a solution myself by using a watcher or perhaps a lifecycle hook I would like to understand why the following does not work and what should be done instead.
The problem is that the mutated local data doesn't update whenever the component consumer changes the property cellContent. The parent owns cellContent so using the property directly is a no-no (Vue seems to agree).
<template>
<textarea
v-model="mutableCellContent"
#keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
#keyup.esc="$emit('cancel')">
</textarea>
</template>
<script>
export default {
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data () {
return {
mutableCellContent: this.cellContent
}
}
}
</script>
<style>
...
</style>
In data (mutableCellContent: this.cellContent) you are creating a copy of the prop, that's why when the parent changes, the local copy (mutableCellContent) is not updated. (If you must have a local copy, you'd have to watch the parent to update it.)
Instead, you should not keep a copy in the child component, just let the state be in the parent (and change it through events emitted in the child). This is a well known the best practice (and not only in Vue, but in other frameworks too, if I may say it).
Example:
Vue.component('cell-editor', {
template: '#celleditor',
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data () {
return {}
}
});
new Vue({
el: '#app',
data: {
message: "Hello, Vue.js!"
}
});
textarea { height: 50px; width: 300px; }
<script src="https://unpkg.com/vue"></script>
<template id="celleditor">
<textarea
:value="cellContent"
#keyup.ctrl.enter="$emit('value-submit', $event.currentTarget.value)"
#keyup.esc="$event.currentTarget.value = cellContent">
</textarea>
</template>
<div id="app">
{{ message }}
<br>
<cell-editor :cell-content="message" #value-submit="message = $event"></cell-editor>
<br>
<button #click="message += 'parent!'">Change message in parent</button>
</div>
You have to create a watcher to the prop cellContent.
Vue.config.productionTip = false
Vue.config.devtools = false
Vue.config.debug = false
Vue.config.silent = true
Vue.component('component-1', {
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data() {
return {
mutableCellContent: this.cellContent
}
},
template: `
<textarea
v-model="mutableCellContent"
#keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
#keyup.esc="$emit('cancel')">
</textarea>
`,
watch: {
cellContent(value) {
this.mutableCellContent = value;
}
}
});
var vm = new Vue({
el: '#app',
data() {
return {
out: "",
cellContent: ""
}
},
methods: {
toOut(...args) {
this.out = JSON.stringify(args);
},
changeCellContent() {
this.cellContent = "changed at " + Date.now();
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<component-1 :cell-content="cellContent" #value-submit="toOut" #cancel="toOut"></component-1>
<p>{{out}}</p>
<button #click="changeCellContent">change prop</button>
</div>

Use Materializecss Alpha with Vue

Hearing of Materializecss Alpha release I was excited because I was a huge fan of it. But I am confused on how to import it into a typical vue.js app and initializing it plugins
for example, how we implement this in Vue:
var instance = new M.Carousel({
fullWidth: true,
indicators: true
})
Without JQuery and with webpack component
0) I tried to use 1.0.0-alpha.2 with npm (npm install materialize-css#next) but...
1) 1.0.0-alpha.2 has an error so meanwhile I cloned the last repo version directly from github and used with npm as local dependency (this will not be needed as soon as alpha.3 will be released):
2) I generated the webpack template with vue-cli
3) I substituted the HelloWorld component with the following one... where I put the carousel initialization code in the mounted method of the component
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<div class="carousel">
<div v-for="elem in images">
<a class="carousel-item" :href="elem.link"><img :src="elem.img" :alt="elem.link"></a>
</div>
</div>
</div>
</template>
<script>
import M from 'materialize-css'
export default {
name: 'Carousel',
mounted () {
var elem = document.querySelector('.carousel')
// eslint-disable-next-line
var t = new M.Carousel(elem, {
indicators: true
})
},
data () {
return {
msg: 'Welcome to Your Carousel Component',
images: [
{
img: 'http://quintagroup.com/cms/technology/Images/materialize.png',
link: 'https://github.com/Dogfalo/materialize'
},
{
img: 'https://vuejs.org/images/logo.png',
link: 'https://vuejs.org/'
},
{
img: 'https://avatars1.githubusercontent.com/u/9919?s=200&v=4',
link: 'https://github.com'
}
]
}
}
}
</script>
4) I added import 'materialize-css/dist/css/materialize.min.css' to main.js
I think that it could be as simple as that:
(function($){
$(function(){
$('.carousel').carousel({
fullWidth: true,
indicators: true
});
}); // end of document ready
})(jQuery); // end of jQuery name space
new Vue({
el: '#app',
data: {
images: [{
img: "http://quintagroup.com/cms/technology/Images/materialize.png",
link: "#link1"
},
{
img: "http://quintagroup.com/cms/technology/Images/materialize.png",
link: "#link2"
},
{
img: "http://quintagroup.com/cms/technology/Images/materialize.png",
link: "#link3"
},
{
img: "http://quintagroup.com/cms/technology/Images/materialize.png",
link: "#link4"
},
{
img: "http://quintagroup.com/cms/technology/Images/materialize.png",
link: "#link5"
}
]
}
})
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-alpha.1/css/materialize.min.css">
<script src="https://unpkg.com/vue"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-alpha.1/js/materialize.min.js"></script>
<div id=app>
<div class="carousel">
<div v-for="elem in images">
<a class="carousel-item" :href="elem.link"><img :src="elem.img" :alt="elem.link"></a>
</div>
</div>
</div>

VueJS 2 update contenteditable in component from parent method

I have editable element updated by component method, but i have also json import and i want to update element my parent method. I can update model, but editable element doesn´t bind it. If i insert content to component template, it will bind updated model, but then i can´t really edit it.
Here´s my example: https://jsfiddle.net/kuwf9auc/1/
Vue.component('editable', {
template: '<div contenteditable="true" #input="update"></div>', /* if i insert {{content}} into this div, it wil update, but editing behave weird */
props: ['content'],
mounted: function () {
this.$el.innerText = this.content;
},
methods: {
update: function (event) {
console.log(this.content);
console.log(event.target.innerText);
this.$emit('update', event.target.innerText);
}
}
})
var app = new Vue({
el: '#myapp',
data: {
herobanner: {
headline: 'I can be edited by typing, but not updated with JSON upload.'
}
},
methods: {
uploadJSON: function (event) {
var input = event.target;
input.src = URL.createObjectURL(event.target.files[0]);
var data = input.src;
$.get(data, function(data) {
importdata = $.parseJSON(data);
this.$data.herobanner = importdata.herobanner;
}.bind(this));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<main id="myapp" class="container-fluid">
<input type="file" id="file" name="file" #change="uploadJSON" style="display: none; width: 1px; height: 1px"/>
<a href="" onclick="document.getElementById('file').click(); return false" title="Import settings from JSON file">
upload JSON
</a>
<h1>
<editable :content="herobanner.headline" #update="herobanner.headline = $event"></editable>
</h1>
Real value of model:
<br>
<h2>{{herobanner.headline}}</h2>
</main>
Working example:
Vue.component('editable', {
template: `
<div contenteditable="true" #blur="emitChange">
{{ content }}
</div>
`,
props: ['content'],
methods: {
emitChange (ev) {
this.$emit('update', ev.target.textContent)
}
}
})
new Vue({
el: '#app',
data: {
herobanner: {
headline: 'Parent is updated on blur event, so click outside this text to update it.'
}
},
methods: {
async loadJson () {
var response = await fetch('https://swapi.co/api/people/1')
var hero = await response.json()
this.herobanner.headline = hero.name
},
updateHeadline (content) {
this.herobanner.headline = content
}
}
})
<main id="app">
<button #click="loadJson">Load JSON data</button>
<h1>
<editable
:content="herobanner.headline"
v-on:update="updateHeadline"
>
</editable>
</h1>
<h2>{{herobanner.headline}}</h2>
</main>
<script src="https://unpkg.com/vue#2.5.3/dist/vue.min.js"></script>