Transition-group not working in Vue render function - vue.js

I'm trying to use Nuxt.js with nuxt/tailwind to build my own Carousel.
I have done a working example, and rewrite the template part by render function.
But I found that the transition-group is not working as previous one.
Here is a simple version, the transition-group tag is not working in render function:
components/Faded.vue
<script>
export default {
render(h) {
return h(
"transition-group",
{
props: {
name: "faded",
},
},
this.$slots.default
);
},
};
</script>
<style lang="postcss" scoped>
.faded-leave {
opacity: 1;
}
.faded-leave-active {
transition: opacity 1s;
}
.faded-leave-to {
opacity: 0;
}
.faded-enter {
opacity: 0;
}
.faded-enter-active {
transition: opacity 1s;
}
.faded-enter-to {
opacity: 1;
}
</style>
index.vue
<template>
<div>
<button class="bg-green-100" #click="AddIndex">Button</button>
<Faded>
<div key="1" v-show="this.showIndex === 0" class="bg-red-100">0</div>
<div key="2" v-show="this.showIndex === 1" class="bg-yellow-100">1</div>
<div key="3" v-show="this.showIndex === 2" class="bg-blue-100">2</div>
</Faded>
</div>
</template>
<script>
export default {
data() {
return {
showIndex: 0,
};
},
methods: {
AddIndex() {
this.showIndex = (this.showIndex + 1) % 3;
},
},
};
</script>
original question
This code is working as my expect:
index.vue
<div class="h-screen flex justify-center">
<div class="h-[60%] lg:w-[50%] w-full bg-black">
<Carousel />
</div>
</div>
Carousel.vue(correct version)
<template>
<div class="wrap">
<transition-group tag="div" class="container" :name="transitionName">
<Slide v-for="(img,index) of imgs" :key="img.src" v-show="index===show">
<img :src="img.src" class="object-cover select-none"/>
</Slide>
</transition-group>
<NavigationGroup v-show="showNavigators" :show="show" :length="imgs.length" :setShow="setShow"/>
<button #click="setShow(show-1)" v-show="showArrows && (cycle || show!==0)" class="navigate-arrow left-2">
<IconFont type="fas" name="angle-left"/>
</button>
<button #click="setShow(show+1)" v-show="showArrows && (cycle || show!==imgs.length-1)" class="navigate-arrow right-2">
<IconFont type="fas" name="angle-right"/>
</button>
</div>
</template>
<script>
export default {
props :{
interval: {type: Number, default: 5000},
autoplay: {type: Boolean, default: false},
cycle: {type: Boolean, default: true},
showArrows: {type: Boolean ,default: true},
showNavigators: {type: Boolean, default: true},
},
data () {
return {
timer: undefined,
transitionName: 'left-in',
show: 0,
imgs: [
{ src: require('~/assets/images/bear.jpg') },
{ src: require('~/assets/images/all-namin.png') },
{ src: require('~/assets/images/eat.png') },
{ src: require('~/assets/images/teemo.jpg') },
{ src: require('~/assets/images/gun.png') },
]
}
},
mounted () {
if(this.autoplay && this.cycle){
this.timer = setInterval(this.nextShow, this.interval)
}
},
methods: {
setShow (index) {
this.show = index
},
nextShow () {
this.show++
}
},
watch: {
show (nVal, oVal) {
if (nVal < 0) {
this.show = this.cycle? this.imgs.length - 1: oVal
} else if (nVal > this.imgs.length - 1) {
this.show = this.cycle? 0: oVal
} else {
if (oVal < 0) this.transitionName = 'left-in'
else if (oVal > this.imgs.length - 1) this.transitionName = 'right-in'
else this.transitionName = nVal > oVal ? 'right-in' : 'left-in'
}
}
},
}
</script>
<style lang="postcss" scoped>
.right-in-enter{
#apply left-full;
}
.right-in-enter-active, .right-in-leave-active{
transition: left 0.5s;
}
.right-in-enter-to, .right-in-leave{
#apply left-0;
}
.right-in-leave-to{
#apply -left-full;
}
.left-in-enter{
#apply -left-full;
}
.left-in-enter-active, .left-in-leave-active{
transition: left 0.5s;
}
.left-in-enter-to, .left-in-leave{
left:0%
}
.left-in-leave-to{
left:100%
}
.wrap {
#apply relative flex justify-center w-full h-full;
}
.container{
#apply relative overflow-hidden w-full;
margin: 0 auto;
}
.page{
#apply absolute w-full h-full flex justify-center;
}
.navigate-container {
#apply absolute bottom-[5%] flex justify-center gap-2 lg:gap-5 select-none;
}
.navigate-dot {
#apply rounded-full w-4 h-4 bg-gray-500;
}
.navigate-dot-active {
#apply animate-pulse;
box-shadow: 0 0 2px 5px #d1d5db;
}
.navigate-arrow {
#apply absolute top-[50%] w-5 h-5 select-none flex justify-center items-center bg-gray-400 text-xl rounded-full text-white;
}
</style>
But the transition-group seems not working in the following code
index.vue
<Carousel>
<img :src="img.src" v-for="img in imgs" :key="img.src" class="object-cover">
</Carousel>
Carousel.vue (broken version)
<script>
import IconFontVue from '../IconFont.vue'
import NavigationGroupVue from './NavigationGroup.vue'
import SlideVue from './Slide.vue'
export default {
props :{
interval: {type: Number, default: 5000},
autoplay: {type: Boolean, default: false},
cycle: {type: Boolean, default: true},
showArrows: {type: Boolean ,default: true},
showNavigators: {type: Boolean, default: true},
},
data(){
return {
timer: undefined,
transitionDirection: 'left-in',
show: 0,
children: Array(0),
}
},
mounted () {
this.children = this.$slots.default
if(this.autoplay && this.cycle){
this.timer = setInterval(this.nextShow, this.interval)
}
},
methods: {
setShow(index){
this.show = index
},
nextShow () {
this.show++
}
},
watch: {
show (nVal, oVal) {
if (nVal < 0) {
this.show = this.cycle? this.children.length - 1: oVal
} else if (nVal > this.children.length - 1) {
this.show = this.cycle? 0: oVal
} else {
if (oVal < 0) this.transitionDirection = 'left-in'
else if (oVal > this.children.length - 1) this.transitionDirection = 'right-in'
else this.transitionDirection = nVal > oVal ? 'right-in' : 'left-in'
}
}
},
render(h){
return h('div',{
class:{
'wrap':true,
}
},[
// transition group slides
h('transition-group',{
props:{
name: this.transitionDirection,
},
class:{
'containing': true,
}
},this.children.map((elem,index)=>{
return h(SlideVue,{
key: `slide-${index}`,
style:{
'display': this.show===index?'':'none'
}
},[elem])
})),
// navigation button
h(NavigationGroupVue,{
class:{
'hidden':!this.showNavigators,
},
props:{
show: this.show,
length: this.children.length,
setShow: this.setShow,
},
}),
// left button
h('button',{
class:{
'navigate-arrow left-2':true,
'hidden': !(this.showArrows && (this.cycle || this.show!==0))
},
on:{
click:()=>{
this.setShow(this.show-1)
}
}
},[
h(IconFontVue,{
props:{
name:"angle-left",
type:"fas",
}
})
]),
//right button
h('button',{
class:{
'navigate-arrow right-2':true,
'hidden': !(this.showArrows && (this.cycle || this.show!==this.children.length-1))
},
on:{
click:()=>{
this.setShow(this.show+1)
}
}
},[
h(IconFontVue,{
props:{
name:"angle-right",
type:"fas",
}
})
]),
])
}
}
</script>
<style lang="postcss">
.right-in-enter{
#apply left-full;
}
.right-in-enter-to, .right-in-leave{
#apply left-0;
}
.right-in-leave-to{
#apply -left-full;
}
.left-in-enter{
#apply -left-full;
}
.left-in-enter-active,
.left-in-leave-active,
.right-in-enter-active,
.right-in-leave-active{
transition: left 0.5s;
}
.left-in-enter-to, .left-in-leave{
#apply left-0;
}
.left-in-leave-to{
#apply left-full;
}
.wrap {
#apply relative flex justify-center w-full h-full;
}
.containing{
#apply relative overflow-hidden w-full;
margin: 0 auto;
}
.page{
#apply absolute w-full h-full flex justify-center;
}
.navigate-container {
#apply absolute bottom-[5%] flex justify-center gap-2 lg:gap-5 select-none;
}
.navigate-dot {
#apply rounded-full w-4 h-4 bg-gray-500;
}
.navigate-dot-active {
#apply animate-pulse;
box-shadow: 0 0 2px 5px #d1d5db;
}
.navigate-arrow {
#apply absolute top-[50%] w-5 h-5 select-none flex justify-center items-center bg-gray-400 text-xl rounded-full text-white;
}
</style>
The two codes are similar after compiling. But the classes of transition-group are not working when I click the arrow or navigating button. Can someone please help me ?

Related

How to fix this error: "v-model cannot be used on a prop, because local prop bindings are not writable?"

I'm trying to make a dropdown sort and I get this error:
VueCompilerError: v-model cannot be used on a prop, because local prop bindings are not writable. Use a v-bind binding combined with a v-on listener that emits update:x event instead.
Here are 2 components App and MySelect:
<template>
<!-- App Component -->
<div class="app">
<h1>Страница с постами</h1>
<div class="app__btns">
<my-button #click="showDialog">Cоздать пост</my-button>
<my-select v-model="selectedSort" :options="sortOptions" />
</div>
<my-dialog v-model:show="dialogVisible">
<post-form #create="createPost" />
</my-dialog>
<post-list :posts="posts" #remove="removePost" v-if="!isPostsLoading" />
<div v-else>Идет загрузка...</div>
</div>
</template>
<script>
import axios from 'axios'
import PostForm from './components/PostForm.vue'
import PostList from './components/PostList.vue'
export default {
components: { PostList, PostForm },
data() {
return {
posts: [],
dialogVisible: false,
isPostsLoading: false,
selectedSort: '',
sortOptions: [
{ value: 'title', name: 'По названию' },
{ value: 'body', name: 'По содержанию' },
],
}
},
methods: {
createPost(post) {
this.posts.push(post)
this.dialogVisible = false
},
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id)
},
showDialog() {
this.dialogVisible = true
},
async fetchPosts() {
try {
this.isPostsLoading = true
const res = await axios.get(
'https://jsonplaceholder.typicode.com/posts?_limit=10'
)
this.posts = res.data
} catch (error) {
alert('ошибка')
} finally {
this.isPostsLoading = false
}
},
},
mounted() {
this.fetchPosts()
},
}
</script>
<!-- флаг scoped - значит, что стили будут применяться только к этому комопненту -->
<style>
.app {
padding: 20px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app__btns {
margin: 15px 0;
display: flex;
justify-content: space-between;
}
</style>
<template>
<!-- MySelect component -->
<select v-model="modelValue" #change="changeOption">
<option disabled value="">Выберите из списка</option>
<option v-for="option in options" :key="option.value" :value="option.value">
{{ option.name }}
</option>
</select>
</template>
<script>
export default {
name: 'my-select',
props: {
modelValue: {
type: String,
},
options: {
type: Array,
default: () => [],
},
},
methods: {
changeOption(event) {
this.$emit('update:modelValue', event.target.value)
},
},
}
</script>
<style lang="scss" scoped></style>
I need to update modelValue, so I tried to add
:value="modelValue"
instead of
v-model="modelValue"
and it works, but I'm not sure if this is the correct solution.
If anyone else is encountering this issue when updating their vue version. Please note that this error started to appear on version 3.2.45.
For the implementation pattern, as noted on the documentation, props should be considered readonly within the component. Vue did not enforce it enough prior to version 3.2.45.
Documentation with links to good implementation patterns : https://vuejs.org/guide/components/props.html#one-way-data-flow

canvas size is different when drawing canvas size

Could you tell me how to fix this issue: canvas has 1200x700px, but the drawing is scaled to 300x150?
<template>
<div>
<input type="text" v-model="msg"></input>
<br>
<canvas v-on:mousemove="mouse" id="c"></canvas>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
msg: 'Welcome to Your Vue.js App',
vueCanvas: null,
pixel: null
}
},
methods: {
mouse: function (event) {
this.vueCanvas.putImageData(this.pixel, event.offsetX, event.offsetY)
this.msg = event.offsetX + ":" + event.offsetY
},
init: function () {
this.vueCanvas = document.getElementById("c").getContext("2d");
this.pixel = this.vueCanvas.createImageData(1, 1);
this.pixel.data[3] = 255;
}
},
mounted() {
this.init()
}
}
</script>
<style scoped>
#c {
height: 700px;
width: 1200px;
border: 1px solid gray;
}
</style>
Snippet:
new Vue({
el: "#app",
data() {
return {
msg: 'Welcome to Your Vue.js App',
vueCanvas: null,
pixel: null,
};
},
methods: {
mouse: function(event) {
this.vueCanvas.putImageData(this.pixel, event.offsetX, event.offsetY);
this.msg = event.offsetX + ':' + event.offsetY;
},
init: function() {
this.vueCanvas = document.getElementById('c').getContext('2d');
this.pixel = this.vueCanvas.createImageData(1, 1);
this.pixel.data[3] = 255;
},
},
mounted() {
this.init();
},
})
#c {
height: 700px;
width: 1200px;
border: 1px solid gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="msg" />
<br />
<canvas v-on:mousemove="mouse" id="c"></canvas>
</div>
I have recorded a video in order to show what goes wrong.
I find solution:
this.vueCanvas.canvas.width=this.vueCanvas.canvas.clientWidth //300->1200
this.vueCanvas.canvas.height=this.vueCanvas.canvas.clientHeight //150->700
but I am not sure if is it good practice.
This is how I meant in my comment:
new Vue({
el: "#app",
data() {
return {
msg: 'Welcome to Your Vue.js App',
vueCanvas: null,
pixel: null,
};
},
methods: {
mouse: function(event) {
this.vueCanvas.putImageData(this.pixel, event.offsetX, event.offsetY);
this.msg = event.offsetX + ':' + event.offsetY;
},
init: function() {
this.vueCanvas = document.getElementById('c').getContext('2d');
this.pixel = this.vueCanvas.createImageData(1, 1);
this.pixel.data[3] = 255;
},
},
mounted() {
this.init();
},
})
#c {
border: 1px solid gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="msg" />
<br />
<canvas v-on:mousemove="mouse" id="c" width="1200" height="700"></canvas>
</div>
Is this not how you want it?

Why does my class not show in vue but my conditional class does?

I want to show two classes in my button. One that is conditional on a Boolean data value like "hideLeftArrow" and another class that shows up as the default like arrowBtn. The conditional class shows but the default class doesn't. What is wrong with my syntax in the following:
:class="[{ hideArrow: hideLeftArrow }, arrowBtn]"
My full code for reference
<template>
<div class="showcase-container">
<button
#click="showRightArrow"
:class="[{ hideArrow: hideLeftArrow }, arrowBtn]"
>
<img alt="left arrow" src="../assets/leftArrow.svg" class="arrow" />
</button>
<a
v-for="game in games"
:key="game.id"
:class="{ hideGame: game.hide }"
:href="game.link"
target="_blank"
>{{ game.name }}</a
>
<button
#click="showLeftArrow"
:class="[{ hideArrow: hideRightArrow }, arrowBtn]"
>
<img alt="right arrow" src="../assets/rightArrow.svg" class="arrow" />
</button>
</div>
</template>
<script>
export default {
data() {
return {
games: [
{
name: "Tryangle",
link: "https://google.com",
id: 1,
hide: false,
},
{
name: "BagRPG",
link: "https://youtube.com",
id: 2,
hide: true,
},
],
hideLeftArrow: true,
hideRightArrow: false,
};
},
methods: {
showLeftArrow() {
this.hideLeftArrow = !this.hideLeftArrow;
this.hideRightArrow = !this.hideRightArrow;
this.games[0].hide = true;
this.games[1].hide = false;
},
showRightArrow() {
this.hideLeftArrow = !this.hideLeftArrow;
this.hideRightArrow = !this.hideRightArrow;
this.games[0].hide = false;
this.games[1].hide = true;
},
},
};
</script>
<style lang="scss">
#import "../styles.scss";
.showcase-container {
.hideArrow {
visibility: hidden;
}
.arrowBtn {
background: none;
border: 0;
}
.hideGame {
display: none;
}
}
</style>
I think you're aiming for this, with quotes around arrowBtn:
:class="[{ hideArrow: hideLeftArrow }, 'arrowBtn']"
Otherwise arrowBtn will be treated as a property name and not a string..
That said, I'd probably do it this way instead:
class="arrowBtn"
:class="{ hideArrow: hideLeftArrow }"
class allows you to have both a static and a bound version on the same element.

Cannot read property '$i18n' of undefined when using Vue Test Utils

I'm trying to test a BaseDialog component that uses VueI18n for translations with vue-test-utils. I cannot get the test to run do the the following error:
TypeError: Cannot read property '$i18n' of undefined
at VueComponent.default (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/src/components/BaseDialog/BaseDialog.vue:2671:220)
at getPropDefaultValue (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:1662:11)
at validateProp (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:1619:13)
at loop (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4612:17)
at initProps (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4643:33)
at initState (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4586:21)
at VueComponent.Vue._init (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4948:5)
at new VueComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5095:12)
at createComponentInstanceForVnode (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3270:10)
at init (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3101:45)
at createComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5919:9)
at createElm (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:5866:9)
at VueComponent.patch [as __patch__] (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:6416:7)
at VueComponent.Vue._update (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:3904:19)
at VueComponent.updateComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4025:10)
at Watcher.get (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4426:25)
at new Watcher (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4415:12)
at mountComponent (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:4032:3)
at VueComponent.Object.<anonymous>.Vue.$mount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/vue/dist/vue.runtime.common.dev.js:8350:10)
at mount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/#vue/test-utils/dist/vue-test-utils.js:8649:21)
at shallowMount (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/#vue/test-utils/dist/vue-test-utils.js:8677:10)
at Object.it (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/src/components/BaseDialog/__tests__/BaseDialog.spec.js:22:21)
at Object.asyncJestTest (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/jasmine_async.js:108:37)
at resolve (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:56:12)
at new Promise (<anonymous>)
at mapper (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:43:19)
at promise.then (/Users/mmelv/Workspace/Projects/Vue/vue-pux-port/node_modules/jest-jasmine2/build/queue_runner.js:87:41)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
I've tried every solution listed here with no result.
Here is the relevant code:
// BaseDialog.spec.js
import { shallowMount, createLocalVue } from '#vue/test-utils'
import BaseDialog from '#/components/BaseDialog/BaseDialog'
import VueI18n from 'vue-i18n'
describe('BaseDialog', () => {
it('is called', () => {
let localVue = createLocalVue()
localVue.use(VueI18n)
const messages = {
gb: {
'ui.universal.label.ok': 'OK',
'ui.universal.label.cancel': 'Cancel'
}
}
const i18n = new VueI18n({
locale: 'gb',
fallbackLocale: 'gb',
messages
})
const wrapper = shallowMount(BaseDialog, {
i18n,
localVue
})
expect(wrapper.name()).toBe('BaseDialog')
expect(wrapper.isVueInstance()).toBeTruthy()
})
})
// BaseDialog.vue
<template>
<transition :name="animation">
<div v-if="isActive" class="dialog modal is-active" :class="size">
<div class="modal-background" #click="cancel('outside')" />
<div class="modal-card animation-content">
<header v-if="title" class="modal-card-head">
<p class="modal-card-title">{{ title }}</p>
</header>
<section
class="modal-card-body"
:class="{ 'is-titleless': !title, 'is-flex': hasIcon }"
>
<div class="media">
<div v-if="hasIcon" class="media-left">
<b-icon
:icon="icon ? icon : iconByType"
:pack="iconPack"
:type="type"
:both="!icon"
size="is-large"
/>
</div>
<div class="media-content">
<p v-html="message" />
<div v-if="hasInput" class="field">
<div class="control">
<input
ref="input"
v-model="prompt"
class="input"
:class="{ 'is-danger': validationMessage }"
v-bind="inputAttrs"
#keyup.enter="confirm"
/>
</div>
<p class="help is-danger">{{ validationMessage }}</p>
</div>
</div>
</div>
</section>
<footer class="modal-card-foot">
<button
v-if="showCancel"
ref="cancelButton"
class="button"
#click="cancel('button')"
>
{{ cancelText }}
</button>
<button
ref="confirmButton"
class="button"
:class="type"
#click="confirm"
>
{{ confirmText }}
</button>
</footer>
</div>
</div>
</transition>
</template>
<script>
import Modal from '../BaseModal/BaseModal'
import config from '../../utils/config'
import { removeElement } from '../../utils/helpers'
export default {
name: 'BaseDialog',
extends: Modal,
props: {
title: {
type: String,
default: null
},
message: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
iconPack: {
type: String,
default: null
},
hasIcon: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'is-primary'
},
size: {
type: String,
default: null
},
confirmText: {
type: String,
default: () => {
return config.defaultDialogConfirmText
? config.defaultDialogConfirmText
: this.$i18n('ui.universal.label.ok')
}
},
cancelText: {
type: String,
default: () => {
return config.defaultDialogCancelText
? config.defaultDialogCancelText
: this.$i18n('ui.universal.label.cancel')
}
},
hasInput: Boolean, // Used internally to know if it's prompt
inputAttrs: {
type: Object,
default: () => ({})
},
onConfirm: {
type: Function,
default: () => {}
},
focusOn: {
type: String,
default: 'confirm'
}
},
data() {
const prompt = this.hasInput ? this.inputAttrs.value || '' : ''
return {
prompt,
isActive: false,
validationMessage: ''
}
},
computed: {
/**
* Icon name (MDI) based on the type.
*/
iconByType() {
switch (this.type) {
case 'is-info':
return 'information'
case 'is-success':
return 'check-circle'
case 'is-warning':
return 'alert'
case 'is-danger':
return 'alert-circle'
default:
return null
}
},
showCancel() {
return this.cancelOptions.indexOf('button') >= 0
}
},
beforeMount() {
// Insert the Dialog component in body tag
this.$nextTick(() => {
document.body.appendChild(this.$el)
})
},
mounted() {
this.isActive = true
if (typeof this.inputAttrs.required === 'undefined') {
this.$set(this.inputAttrs, 'required', true)
}
this.$nextTick(() => {
// Handle which element receives focus
if (this.hasInput) {
this.$refs.input.focus()
} else if (this.focusOn === 'cancel' && this.showCancel) {
this.$refs.cancelButton.focus()
} else {
this.$refs.confirmButton.focus()
}
})
},
methods: {
/**
* If it's a prompt Dialog, validate the input.
* Call the onConfirm prop (function) and close the Dialog.
*/
confirm() {
if (this.$refs.input !== undefined) {
if (!this.$refs.input.checkValidity()) {
this.validationMessage = this.$refs.input.validationMessage
this.$nextTick(() => this.$refs.input.select())
return
}
}
this.onConfirm(this.prompt)
this.close()
},
/**
* Close the Dialog.
*/
close() {
this.isActive = false
// Timeout for the animation complete before destroying
setTimeout(() => {
this.$destroy()
removeElement(this.$el)
}, 150)
}
}
}
</script>
<style lang="scss">
.dialog {
.modal-card {
max-width: 460px;
width: auto;
.modal-card-head {
font-size: $size-5;
font-weight: $weight-semibold;
}
.modal-card-body {
.field {
margin-top: 16px;
}
}
.modal-card-foot {
justify-content: flex-end;
.button {
display: inline; // Fix Safari centering
min-width: 5em;
font-weight: $weight-semibold;
}
}
#include tablet {
min-width: 320px;
}
}
&.is-small {
.modal-card,
.input,
.button {
#include control-small;
}
}
&.is-medium {
.modal-card,
.input,
.button {
#include control-medium;
}
}
&.is-large {
.modal-card,
.input,
.button {
#include control-large;
}
}
}
</style>
I don't really know what else to try here. This is the beginning of a project where I must support 9 languages with over 500 keys a piece, so I've got to get this working. Any help is very much appreciated.
The problem was I was referencing this in the props. Props are processed before the component is instantiated and therefore I had no access to this. It's always the little things that make you bash your head into the wall hahaha.

Repeated data in vue

i`m having a problem with my vue, the problem is im trying to print 2 words, that is 'A.2' and 'B.3', but when im printing it, it just show 'B.3' and 'B.3'. here is my code
this is a simple quiz project, so everytime a user choose option a with true status it should be adding 1 point to the score, i haven`t made that yet.
<template>
<div class="hello">
<h1 v-if="show">hai</h1>
<h1 v-else>hehe</h1>
<p>{{ nama}}</p>
<input type="text" v-model="nama">
<button type="button" v-on:click="hideTitle">Click Me</button>
<h3> 1.Yang Dipakai Di sepatu adalah </h3>
<p>{{ nama}}</p>
<h3 v-for="j in jawaban">
<input type="radio">
{{j}}
</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data : function() {
return{
nama: 'Luthfi',
show: true
},
{
jawaban: 'A.2',
correct: true
},
{
jawaban: 'B.3',
correct: false
},
{
jawaban: 'C.4',
correct: false
}
},
methods: {
hideTitle() {
this.show = !this.show
}
},
mounted: function () {
this.nama = 'Fitra'
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
i expect there is 4 output from option A to D, but it kept showing me same option
In your code, data() returns only one object that contains
{
nama: 'Luthfi',
show: true
}
You must change this like:
data : function() {
return{
nama: 'Luthfi',
show: true,
jawaban: 'A.22',
correct: true,
jawabann: 'B.3'
}
}