Vue.js render v-container only if fullWidth setting is not true - vue.js

I have the following structure and I want to include the container only if the website setting is not fullWidth.
For the moment, if I set the fullWidth=false, I am unable to see the HelloWorld component, but I want to see it, only not within a container.
<v-content>
<v-container v-if="!layoutIsFullWidth()">
<HelloWorld/>
</v-container>
</v-content>
I know this is a stupid question, but I've tried everything, and didn't find a situation similar to it, or I didn't knew what to search.
Thank you!

As I said, use CSS for showing/hiding content depending on the window width.
However, if you want a JS/Vue solution, then remove the braces, as it says in your console:
[Vue warn]: Error in render: "TypeError: layoutIsFullWidth is not a function"
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
settings: {
layout: {
darkTheme: false,
fullWidth: false
}
},
leftDrawer: {
activated: true,
opened: false
}
},
methods: {
hasLeftDrawer() {
return this.leftDrawer.activated;
},
layoutIsFullWidth() {
return this.settings.layout.fullWidth;
},
layoutIsDarkTheme() {
return this.settings.layout.darkTheme;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-if="!layoutIsFullWidth">{{ message }}</p>
</div>
Also, better use a computed property for this purpose:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
settings: {
layout: {
darkTheme: false,
fullWidth: false
}
},
leftDrawer: {
activated: true,
opened: false
}
},
computed: {
hasLeftDrawer() {
return this.leftDrawer.activated;
},
layoutIsFullWidth() {
return this.settings.layout.fullWidth;
},
layoutIsDarkTheme() {
return this.settings.layout.darkTheme;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-if="!layoutIsFullWidth">{{ message }}</p>
</div>

Related

vuejs get value from main model to object?

I have a basic ask. I am trying to get the data from the main area of my vue model to inside an object. I tried using app.$data.name i also tried this.name, but I cannot get it to work without error.
new Vue({
el: "#app",
data: {
name:"Bobby",
currentCard: {},
currentCard: {
author: this.name,
},
},
methods: {
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>
I think that is not possible, you could use a computed value.
new Vue({
el: "#app",
data() {
return {
name: "Bobby"
};
},
computed: {
currentCard() {
return {
author: this.name
};
}
},
methods: {}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{currentCard.author}}
</div>

how to reverse message on clik button vue js

i want to reserve paragraph after click button
reserve paragraph is showing in second paragraph
for my case is i have to use template ?
here is my code
<div id="example">
<p>Original Message: "{{ message }}"</p>
<p id="pReserve"></p>
<button #click="reserve">Reserve</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>
<script>
Vue.component('coba',{
});
var vm = new Vue({
el: '#example',
data: {
message: 'Hai'
},
computed: {
// a computed getter
reversedMessage: function () {
// `this` mengarah ke instance vm
return this.message .split('').reverse().join('')
}
},
methods: {
reserve:{
}
},
})
</script>
Here is the solution.
var vm = new Vue({
el: '#example',
data: {
message: 'Hai'
},
methods: {
reversedMessage: function() {
this.message = this.message.split('').reverse().join('');
}
},
});
<div id="example">
<p>Original Message: "{{ message }}"</p>
<p id="pReserve"></p>
<button #click="reversedMessage">Reserve</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>

Unable to trigger render when switching between multiple objects containing content in different languages

I read Reactivity in Depth but can't solve the issue.
I'm creating a small single page app that contains images and text.
When the user clicks a button I want the language to change.
Currently I am storing the content in two files that export an object.
export default {
projects: [
{
title: 'Project Title',
year: 2016,
...
},
]
}
and importing that
import contentEn from './assets/content.en.js'
import contentDe from './assets/content.de.js'
new Vue({
el: '#app',
components: { App },
data: {
mainContent: {
content: contentEn
}
},
methods: {
switchToGerman(){
this.mainContent.content = contentDe
}
},
template: '<App :mainData="mainContent"/>',
})
When I assign another object to mainContent.content the rendering is not triggered.
I understand that adding and deleting properties from object don't lead to change detection but I switch out a whole object. I tried assigning it with this.$set with no success.
I also tried this and googled a lot but can't get it work.
Or is my approach just wrong?
Thank you for helping,
best,
ccarstens
EDIT:
See below the code for the App component and the ProjectElement
// App.vue
<template>
<div id="app">
<button #click="switchGerman">Deutsch</button>
<ProjectElement v-for="(project, key) in fullData.content.projects" :key="key" :content="project"/>
</div>
</template>
<script>
import ProjectElement from './components/ProjectElement'
export default {
name: 'App',
props: [
'mainData'
],
data () {
return{
fullData: {}
}
},
methods: {
switchGerman(){
this.$root.switchToGerman()
}
},
created(){
this.fullData = this.$props.mainData
},
watch: {
mainData: {
handler: function(newData){
this.fullData = newData
},
deep: true
}
},
components: {
ProjectElement,
}
}
</script>
And the ProjectElement
//ProjectElement.vue
<template>
<article :class="classObject" v-observe-visibility="{
callback: visibilityChanged,
throttle,
intersection: {
threshold
}
}">
<header v-html="description"></header>
<div class="content">
<carousel :per-page="1" :pagination-enabled="false">
<slide v-for="(slide, index) in projectContent.media" :key="index">
<VisualElement :content="slide" ></VisualElement>
</slide>
</carousel>
</div>
</article>
</template>
<script>
import {Carousel, Slide} from 'vue-carousel'
import VisualElement from './VisualElement'
export default {
name: "ProjectElement",
components: {
Carousel,
Slide,
VisualElement
},
props: [
'content'
],
data () {
return {
projectContent: {},
isVisible: false,
throttle: 300,
threshold: 0.8
}
},
created(){
this.projectContent = this.content
},
methods: {
visibilityChanged(isVisible){
this.isVisible = isVisible
}
},
computed: {
description(){
return `
<p>${ this.projectContent.title } - ${this.projectContent.year}</p>
<p>${ this.projectContent.description }</p>
`
},
classObject(){
return {
visible: this.isVisible,
'project-element': true
}
}
}
}
</script>
Did you try doing deep copy:
switchToGerman () {
const copyContent = JSON.parse(JSON.stringify(this.mainContent))
copyContent.content = contentDe
this.mainContent = copyContent
}
I found the solution (thank you #EricGuan for pointing out that the mistake must lay somewhere else)
As you can see in the original post I created a watcher for the mainData property and expected that this would trigger the re render.
What was missing is, that I didn't watch the content property on the ProjectElement component, thus not triggering a re render there.
I added this to ProjectElement.vue and now it works like a charm:
watch: {
content(newContent){
this.projectContent = newContent
}
},
Thank you everybody for helping me! <3

How to defer form input binding until user clicks the submit button?

I wanted to make a two-way data binding on my form input in Vue.js 2.3. However, I cannot use the v-model directive, because I want the data to be updated only on clicking the submit button. Meanwhile, the input value may be updated from another Vue method, so it should be bound to the data property text. I made up something like this jsFiddle:
<div id="demo">
<input :value="text" ref="input">
<button #click="update">OK</button>
<p id="result">{{text}}</p>
</div>
new Vue({
el: '#demo',
data: function() {
return {
text: ''
};
},
methods: {
update: function () {
this.text = this.$refs.input.value;
}
}
});
It works, but it does not scale well when there are more inputs. Is there a simpler way to accomplish this, without using $refs?
You can use an object and bind its properties to the inputs. Then, in your update method, you can copy the properties over to another object for display purposes. Then, you can set a deep watcher to update the values for the inputs whenever that object changes. You'll need to use this.$set when copying the properties so that the change will register with Vue.
new Vue({
el: '#demo',
data: function() {
return {
inputVals: {
text: '',
number: 0
},
displayVals: {}
};
},
methods: {
update() {
this.copyObject(this.displayVals, this.inputVals);
},
copyObject(toSet, toGet) {
Object.keys(toGet).forEach((key) => {
this.$set(toSet, key, toGet[key]);
});
}
},
watch: {
displayVals: {
deep: true,
handler() {
this.copyObject(this.inputVals, this.displayVals);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
<input v-model="inputVals.text">
<input v-model="inputVals.number">
<button #click="update">OK</button>
<input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>
If you're using ES2015, you can copy objects directly, so this isn't as verbose:
new Vue({
el: '#demo',
data() {
return {
inputVals: { text: '', number: 0 },
displayVals: {}
};
},
methods: {
update() {
this.displayVals = {...this.inputVals};
},
},
watch: {
displayVals: {
deep: true,
handler() {
this.inputVals = {...this.displayVals};
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
<input v-model="inputVals.text">
<input v-model="inputVals.number">
<button #click="update">OK</button>
<input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>
You can use two separate data properties, one for the <input>'s value, the other for the committed value after the OK button is clicked.
<div id="demo">
<input v-model="editText">
<button #click="update">OK</button>
<p id="result">{{text}}</p>
</div>
new Vue({
el: '#demo',
data: function() {
return {
editText: '',
text: ''
};
},
methods: {
update: function () {
this.text = this.editText;
}
}
});
Updated fiddle
With a slightly different approach than the other answers I think you can achieve something that is easily scalable.
This is a first pass, but using components, you could build your own input elements that submitted precisely when you wanted. Here is an example of an input element that works like a regular input element when it is outside of a t-form component, but only updates v-model on submit when inside a t-form.
Vue.component("t-input", {
props:["value"],
template:`
<input type="text" v-model="internalValue" #input="onInput">
`,
data(){
return {
internalValue: this.value,
wrapped: false
}
},
watch:{
value(newVal){
this.internalValue = newVal
}
},
methods:{
update(){
this.$emit('input', this.internalValue)
},
onInput(){
if (!this.wrapped)
this.$emit('input', this.internalValue)
}
},
mounted(){
if(this.$parent.isTriggeredForm){
this.$parent.register(this)
this.wrapped = true
}
}
})
Here is an example of t-form.
Vue.component("t-form",{
template:`
<form #submit.prevent="submit">
<slot></slot>
</form>
`,
data(){
return {
isTriggeredForm: true,
inputs:[]
}
},
methods:{
submit(){
for(let input of this.inputs)
input.update()
},
register(input){
this.inputs.push(input)
}
}
})
Having those in place, your job becomes very simple.
<t-form>
<t-input v-model="text"></t-input><br>
<t-input v-model="text2"></t-input><br>
<t-input v-model="text3"></t-input><br>
<t-input v-model="text4"></t-input><br>
<button>Submit</button>
</t-form>
This template will only update the bound expressions when the button is clicked. You can have as many t-inputs as you want.
Here is a working example. I included t-input elements both inside and outside the form so you can see that inside the form, the model is only updated on submit, and outside the form the elements work like a typical input.
console.clear()
//
Vue.component("t-input", {
props: ["value"],
template: `
<input type="text" v-model="internalValue" #input="onInput">
`,
data() {
return {
internalValue: this.value,
wrapped: false
}
},
watch: {
value(newVal) {
this.internalValue = newVal
}
},
methods: {
update() {
this.$emit('input', this.internalValue)
},
onInput() {
if (!this.wrapped)
this.$emit('input', this.internalValue)
}
},
mounted() {
if (this.$parent.isTriggeredForm) {
this.$parent.register(this)
this.wrapped = true
}
}
})
Vue.component("t-form", {
template: `
<form #submit.prevent="submit">
<slot></slot>
</form>
`,
data() {
return {
isTriggeredForm: true,
inputs: []
}
},
methods: {
submit() {
for (let input of this.inputs)
input.update()
},
register(input) {
this.inputs.push(input)
}
}
})
new Vue({
el: "#app",
data: {
text: "bob",
text2: "mary",
text3: "jane",
text4: "billy"
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<t-form>
<t-input v-model="text"></t-input><br>
<t-input v-model="text2"></t-input><br>
<t-input v-model="text3"></t-input><br>
<t-input v-model="text4"></t-input><br>
<button>Submit</button>
</t-form>
Non-wrapped:
<t-input v-model="text"></t-input>
<h4>Data</h4>
{{$data}}
<h4>Update Data</h4>
<button type="button" #click="text='jerome'">Change Text</button>
</div>

How to passing data from Vue instance into a Vue.component?

I have a Vue instance with two components. If the user clicks on a button in the second component I want to hide the template of first ones.
I have this code:
<div id="app">
<mycomp-one :active="active"></mycomp-one>
<mycomp-two></mycomp-two>
</div>
<template id="mycomponent-one">
<div v-show="!active">
<!-- ... --->
</div>
</template>
<template id="mycomponent-two">
<button v-on:click="setActive">Click Me</button>
</template>
With this script code:
Vue.component('mycomp-one', {
template: '#mycompnent-one',
// etc...
});
Vue.component('mycomp-two', {
template: '#mycomponent-two',
data: function() {
return {
active: false
};
},
methods: {
setActive: function() {
this.$parent.$options.methods.setActive();
},
},
});
new Vue({
el:'#app',
data:{
active: false
},
methods: {
setActive: function() {
this.active = !this.active;
},
},
});
If the button is clicked it working good to pass the information from the component to the instance. But here is stopping the data flow, the mycomp-one component did not get the change. How can I fix that? I want to hide the mycomp-one if the active is true.
this.$parent.$options.methods.setActive() is not a method bound to the Vue. I'm not sure how you got here, but this is not how you call a method on the parent.
console.clear()
Vue.component('mycomp-one', {
props:["active"],
template: '#mycomponent-one',
});
Vue.component('mycomp-two', {
template: '#mycomponent-two',
data: function() {
return {
active: false
};
},
methods: {
setActive: function() {
this.$parent.setActive();
},
},
});
new Vue({
el:'#app',
data:{
active: false
},
methods: {
setActive: function() {
this.active = !this.active;
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<mycomp-one :active="active"></mycomp-one>
<mycomp-two></mycomp-two>
</div>
<template id="mycomponent-one">
<div v-show="!active">
Stuff
</div>
</template>
<template id="mycomponent-two">
<button v-on:click="setActive">Click Me</button>
</template>
Beyond that, components shouldn't call methods on their parent. They should emit events the parent listens to. This is covered well in the documentation.
console.clear()
Vue.component('mycomp-one', {
props:["active"],
template: '#mycomponent-one',
});
Vue.component('mycomp-two', {
template: '#mycomponent-two',
data: function() {
return {
active: false
};
},
methods: {
setActive: function() {
this.active = !this.active
this.$emit("set-active", this.active)
},
},
});
new Vue({
el:'#app',
data:{
active: false
},
methods: {
setActive: function() {
this.active = !this.active;
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<mycomp-one :active="active"></mycomp-one>
<mycomp-two #set-active="active = $event"></mycomp-two>
</div>
<template id="mycomponent-one">
<div v-show="!active">
Stuff
</div>
</template>
<template id="mycomponent-two">
<button v-on:click="setActive">Click Me</button>
</template>