Basically, I want a dynamic link on my a href based on the code. Where I want my link to be to example2.com when the website using my NuxtJS code is example1.com.. However, it will always get linked to / instead of example2.com.. It is really weird as it can clearly be seen in Vue Inspection Tool and in {{ backHref }} that it is already returning example2.com.. However it does not reflect in :href
/* eslint-disable vue/no-v-html */
<template>
<div class="h-100 bg-ededed">
<a :href="backHref">{{ backHref }}</a>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
meta: { noFooter: true },
computed: {
...mapState({
chatRooms: (state) => state.chat.chatRooms
}),
backHref() {
let link
if (this.chatRooms.length === 1) {
console.log(`this.isExample: ${this.isExample}`)
if (this.isExample) {
console.log('went here 1')
link = 'https://example2.com'
} else {
console.log('went here 2')
link = '/'
}
} else {
link = '/chat/'
}
console.log(
`this.addChannelCodeLink(link): ${this.addChannelCodeLink(link)}`
)
return this.addChannelCodeLink(link)
},
getDomain() {
let host
if (process.client && window) {
host = window.location.host
}
return host
},
isExample() {
return this.getDomain ? this.getDomain.includes('example1.com') : false
}
},
methods: {
addChannelCodeLink(link) {
if (this.$route.query.r) {
link += `?r=${this.$route.query.r}`
}
return link
}
}
}
</script>
<style lang="scss" scoped></style>
Console.log will be something like this:
- While in SSR
this.isExample: false
went here 2
this.addChannelCodeLink(link): /
- In Client
this.isExample: true
went here 1
this.addChannelCodeLink(link): http://example2.com
Related
I have an array with a variable ecoveneer_magma_oak that I want to change by pressing a button.
When I change the variable in console, everything is good but I don't see my value on the page.
PS: I am a beginner in Vue.
Here is my code
<template>
<div>
<swiper-slide
v-for="door in doors"
:key="door.id"
class="card"
:door="doors"
></swiper-slide>
<div class="modelName">{{ door.model }}</div>
</div>
</template>
<script>
export default {
data() {
return {
ecoveneer_magma_oak: 'Дуб Магма',
doors: [
{
image: '1.jpg',
model: '018 BG',
ecoveneer_magma_oak: 'Дуб Бордо',
decor: this.ecoveneer_magma_oak,
id: '1',
},
],
}
},
methods: {
changeLange(lang) {
this.ecoveneer_magma_oak = 'Ecoveneer Magma Oak'
},
},
}
</script>
Ok I have spend 2 work days for find answer but nothing Now I declare variable not in data then I change variable and refresh DOM but variable "decor" not changing
var ecoveneer_magma_oak= 'Дуб Бордо'
export default {
data() {
doors: [
{
image:"1.jpg",
model:"018 BG",
decor: ecoveneer_magma_oak,
id:"1"
}],
methods: {
changeLange(lang){
if(lang==='RU'){
this.renderComponent = false;
this.$nextTick(() => {
// Add the component back in
this.renderComponent = true;
});
ecoveneer_magma_oak="Ecoveneer Magma Oak"
}
}
I am new to Vuejs and come across this bug which I have no idea what I have done wrong. I am not receiving any console errors. It doesn't work on initial page load but it seems to work after I comment something out (or make a minor change). It will still then continue to work if I reverse the changes I just made and put it back to the original code. But once again on a fresh page load it won't work.
The issue: I am making a to do list and on page load when I add new tasks through the input field, the list does not appear on the page like it should be. I also console log the data array for this and it shows it is getting added to the array but is not getting rendered to the page. No console errors. In my code I will comment out some other data property (there are 2 additional ones below todosList in the TodoList.vue file that are currently not being used yet) and save and then the tasks will automatically appear on the page. So I think oh ok that might be the issue so with this new minor change I decide to refresh the page to see if it works as expected. Nope it doesn't so I then uncomment out what I previously commented out and save and the list appears again. But once again if I refresh the page it doesn't work. It only seems to be if I make a change inside the data function in the TodoList.vue file.
Additional info: The data is stored in the parent todos[] (App.vue), updated/pushed to array in a child (TodoCreate.vue) and sent back to the parent using $emit. This data is then sent through to another child (TodoList.vue) using props so that it can be rendered on the page.
Wondering if there is something that is not quite right in my code which is causing this to bug out like that. I will include everything in case it is something that looks unrelated to me but could be causing it.
Here is also a link to a code sandbox where the issue can be replicated by following the instructions on the page https://codesandbox.io/s/adding-new-todo-not-working-properly-jwwex?file=/src/components/TodoList.vue
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<template>
<div :class="currentMode">
<the-header #modeToggled="updateMode($event)"></the-header>
<main>
<todo-create #addedTodos="updateTodos"></todo-create>
<todo-list :todos="todos"></todo-list>
</main>
</div>
</template>
<script>
import TheHeader from './components/TheHeader.vue';
import TodoCreate from './components/TodoCreate.vue';
import TodoList from './components/TodoList.vue';
export default {
name: 'App',
components: {
TheHeader,
TodoCreate,
TodoList,
},
data() {
return {
currentMode: {
dark_mode: true,
light_mode: false
},
todos: [],
}
},
methods: {
updateMode(mode) {
this.currentMode = mode;
},
updateTodos(data) {
this.todos = data;
console.log(this.todos);
},
toggleCompleted() {
}
},
// provide() {
// return {
// todos: this.todos,
// };
// }
}
</script>
TheHeader.vue
<template>
<h1>To-do App</h1>
<div>
<label for="toggle-mode" aria-label="Toggle light and dark mode"></label>
<input type="checkbox" id="toggle-mode" #change="toggleMode">
</div>
</template>
<script>
export default {
emits: ['modeToggled'],
data() {
return {
toggleState: false,
}
},
methods: {
toggleMode() {
this.toggleState = !this.toggleState;
this.$emit('modeToggled', this.modeClasses);
}
},
computed: {
modeClasses() {
return {
dark_mode: !this.toggleState,
light_mode: this.toggleState
}
}
}
}
</script>
TodoCreate.vue
<template>
<div>
<label for="newtodo" class="sr-only">Create new to do</label>
<input type="text" id="newtodo" placeholder="Create a new todo..." v-model="todoval" v-on:keyup.enter="addTodo" >
</div>
</template>
<script>
export default {
emits: ['addedTodos'],
data() {
return {
todoval: '',
taskNumber: 0,
todos: [],
};
},
methods: {
addTodo() {
const val = this.todoval;
const taskNumber = this.taskNumber;
this.todos.push({ taskID: taskNumber, value: val, complete : 'not-completed'});
this.todoval = '';
this.taskNumber++;
console.log(this.todos);
this.$emit('addedTodos', this.todos);
},
}
}
</script>
TodoList.vue
<template>
<ul class="todo-items" :class="filterClass">
<li class="drop-zone" v-for="(listItem, index) in todosList" :class="listItem.complete" :key="listItem.taskID"
#drop='onDrop($event, index)'
#dragover.prevent
#dragenter.prevent>
<div class="drag-el" draggable="true"
#dragstart='startDrag($event, index)'>
<label :for="'checkbox-'+index" :aria-label="'Mark task ' + listItem.value + ' as completed'"></label>
<input type="checkbox" :id="'checkbox-'+index" #change="toggleCompleted(index, listItem.value, listItem.complete, listItem.taskID)">
<input type="text" disabled :value="listItem.value">
<img src="../assets/icon-cross.svg" #click="removeTask(index)">
</div>
</li>
</ul>
</template>
<script>
export default {
props: {
todos: Object,
filterClass: String
},
// inject: ['todos'],
data() {
return {
todosList: this.todos,
// completedTodos: [],
// activeTodos: [],
};
},
// watch: {
// todosList(data) {
// data.filter(function(todo) {
// if(todo.completed == 'completed') {
// completedTodos.push(todos);
// }
// });
// }
// },
methods: {
startDrag: (evt, item) => {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('itemID', item)
},
onDrop (evt, list) {
const itemID = evt.dataTransfer.getData('itemID');
const movedData = this.todosList[itemID];
this.todosList.splice(itemID,1);
this.todosList.splice(list,0, movedData);
},
toggleCompleted() {
// still need to write this method
},
removeTask() {
// still need to write this method
}
}
}
</script>
I am trying to make a timeline website. I would like to click on a Interval component (such as Russo-Persian War in the code below) and a text-description to show on the sidebar. There is currently no text description set. The sidebar is currently set to "Lorem Ipsum". How can I dynamically change the text in the sidebar based on the Interval component I click on? Thank you.
Here is my code:
Sidebar.vue
<div id="side-bar">
<hr />
{{text}}
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
data() {
return {
text: `Lorem Ipsum`,
}
},
methods: {
},
})
</script>
Helper.js
export class Mark {
name = ''
tags = []
constructor(date) {
this.year = date
}
}
/* An interval consist of two marks (dates) in time-history */
export class Interval {
from = null
to = null
subIntervals = null
name = ''
i18n = null
tags = []
constructor() {
if (arguments.length === 1) {
let data = arguments[0]
this.from = new Mark(data.from)
this.to = new Mark(data.to)
if (data.subIntervals !== undefined) {
this.subIntervals = data.subIntervals
}
this.i18n = {
messages: {
es: { message: { title: data.title } }
}
}
} else {
this.from = arguments[0]
this.to = arguments[1]
}
}
}
/* A 'Timeline' consist of a 'name' and a
* property 'events', which consists of an ordered list
* of 'Interval's AND/OR 'Mark's (or even other 'Timeline's) */
export class Timeline {
events = []
name = ''
tags = []
constructor(_name) {
this.name = _name
}
}
Data.js
import { Interval, Timeline, century } from './Helper'
...
new Interval({
title: '1800s',
from: 1800,
to: 1899,
})
let lower = new Timeline()
let ninteenthcentury = new Timeline('1900s')
ninteenthcentury.events.push(
new Interval({
title: 'Russo-Persian War',
from: 1804,
to: 1813,
}),
new Interval({
title: 'Russo-Turkish War',
from: 1806,
to: 1812,
})
(etc...)
)
lower.events.push(
ninteenthcentury
)
export { lower }
Format.vue
<template>
<div class="interval"
:style="{
width: width + 'px',
marginLeft: marginLeft + 'px',
display: width < 51 ? 'none' : 'flex'
}"
:title="title">
<span v-on:click= "" class="name" >{{$t('message.title')}}</span>
<!-- sub intervals -->
<div class="lane" v-if="data.subIntervals">
<interval
v-for="(subInterval, index) in data.subIntervals"
:key="index"
:data="subInterval"
:left="last(index)"
:ratio="ratio" />
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'interval',
props: ['data', 'ratio', 'left'],
i18n: {
},
created: function() {
if (this.data.i18n) {
this.$i18n.setLocaleMessage('es', this.data.i18n.messages.es)
}
},
methods: {
last(index) {
if (index > 0) {
return this.data.subIntervals[index - 1].to.year
}
return this.data.from.year
},
},
computed: {
marginLeft() {
if (this.left) {
return Math.abs(this.left - this.data.from.year) * this.ratio
}
return 0
},
width() {
return (this.data.to.year - this.data.from.year) * this.ratio
},
title() {
return this.$i18n.t('message.title') + ` (${this.data.from.year},${this.data.to.year})`
},
}
})
</script>
First of all you should consider migrating your helper to vuex, using store features will help you in every thing.
You will have all intervals in one array and, for instance, have a prop in your store for currentInterval that changes every time you click an interval on the timeline. You can achieve this by dispatching a vuex action to the store that will mutate the state of your prop, ex currentInterval.
In the component where you show the current interval data you will ...mapGetters from the store that will always return that prop currentInterval of the store.
More information about store management here: enter link description here
I'm setting up a Vue.js project and connecting it to Firebase for the real time database.
Problem: I am able to save the data to the Firebase database however I am not able to render it to the view.
Error Message:
[Vue warn]: Property or method "names" is not defined on the instance
but referenced during render.
I have tried to adjust the vue instance "names" property by adding it the data function instead of making it a separate property in the instance, but that is not working.
<div id="app">
<label for="">Name</label>
<input type="text" name="" id="" v-model="name">
<button #click="submitName()">Submit</button>
<div>
<ul>
<li v-for="personName of names"
v-bind:key="personName['.key']">
{{personName.name}}
</li>
</ul>
</div>
</div>
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
firebase: {
names: namesRef
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
<style>
Expected Result: Data saved to Firebase is rendered on the view
Actual result:
[Vue warn]: Property or method "names" is not defined on the instance
but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property.
Essentially, you have an incorrect attribute in your Vue instance.. You need to move firebase into data..
([CodePen])
I was unable to get this working in a Stack Snippet..
~~~THE FIX~~~
VUE/JS
firebase.initializeApp({
databaseURL: "https://UR-DATABASE.firebaseio.com",
projectId: "UR-DATABASE"
});
const database = firebase.database().ref("/users");
const vm = new Vue({
el: "#app",
data: {
firebase: {
names: []
},
name: "SomeName"
},
methods: {
getFirebaseUsers() {
this.firebase.names = [];
database.once("value", users => {
users.forEach(user => {
this.firebase.names.push({
name: user.child("name").val(),
id: user.child("id").val()
});
});
});
},
handleNameAdd() {
let id = this.generateId();
database.push({
name: this.name,
id: id
});
this.name = "";
this.getFirebaseUsers();
},
generateId() {
let dt = new Date().getTime();
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
let r = ((dt + Math.random() * 16) % 16) | 0;
dt = Math.floor(dt / 16);
return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
});
}
},
mounted() {
this.getFirebaseUsers();
}
});
HTML
<script src="https://www.gstatic.com/firebasejs/6.1.1/firebase.js"> .
</script>
<div id="app">
<label for="">Name</label>
<input type="text" name="" id="" v-model="name">
<button #click="handleNameAdd">Submit</button>
<div>
<ul>
<li v-for="(person, index) in firebase.names"
v-bind:key="person.id">
{{person.name}} | {{person.id}}
</li>
</ul>
</div>
</div>
OLD ANSWER:
This is what it should look like inside of data:
...
data() {
firebase: {
names: [],
}
}
...
Therefore, the data in your v-for would be referenced via firebase.names like:
...
<li v-for="(personName, index) in firebase.names"
:key="index"> // <<-- INDEX IS NOT THE BEST WAY TO STORE KEYS BUT ITS BETTER THAN NOTHING
//:key="personName.id // <<-- YOU COULD ALSO DO SOMETHING LIKE THAT, IF YOU HAVE A UNIQUE ID PER PERSON
{{personName.name}}
</li>
...
OPTIMAL FIX:
You could use a computed property if you wanted to automatically save/retrieve data from firebase each time a user adds a new name...as outlined in the CodePen and Code Snippet..
THE ISSUE:
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
firebase: { // <<--- THIS IS INVALID, AND WHY IT'S NOT RENDERING
names: namesRef // CHECK YOUR CONSOLE FOR ERRORS
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Try this.
You need to return the object in data
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
firebase: {
names: namesRef
},
}
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
<style>
Use a computed property for names. Computed is more appropriate than data in this case, mainly because the component does not own the data. If it eventually resided in a vuex store, for instance, it would then react to external changes.
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
computed: {
names() {
return namesRef
}
}
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Try this
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
cumputed: {
names: namesRef
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Got mine working.
The solution is pretty simple.
Add names:[] to data object so it looks like:
...
data () {
return {
name: "levi",
names:[]
}
},
....
That's pretty much it.
Explaination
The firebase object data needs to be defined in order to use it
If you have more issues check the vuex documentation replicate that to your code.
i have a form which have a couple of comps for inputs and inside each there is another comp for error, so i have
// input comp
<template></template>
<script>
import Store from '../../store'
export default {
props:['errors'],
data() {
return {
input: ''
}
},
computed: {
showError() {
if (this.errors && !this.input) {
return true;
}
}
}
}
</script>
// error comp
<template>
<span class="help-block">
<strong v-for="error in errors">
{{ error }}
</strong>
</span>
</template>
<script>
export default {
props: ['errors'],
watch: {
errors: (val) => {
this.$emit('newError')
}
},
}
</script>
// display the error
<form-errors :errors="errors" v-if="showError" v-on:newError="showError = !showError"></form-errors>
so what am after is
get the error watch to actually work as so far i don't know how to hook into the component update
how to override the computed prop of showError
No you can not overwrite the computed property like this: showError = !showError, You have to use some other approach.
Given that you want to show both errors: errors related to form input and error coming from backend: You can have following structure of your error variable:
errors: {
"backendErrors": [],
"formErrors" : []
}
Now you can have your computed property show error like following:
showError() {
if (this.errors.backendErrors || (this.errors.formErrors && !this.input) ) {
return true;
}
else{
return false
}
}
ot whatever other logic suits you.