vue.js: ":class" cause the classList is wrong - vue.js

as cood like this
'<div class="item" :class="{'editable':canEdit}"></div>';
set 'canEdit' to false -------the classes of 'div' are "item"
'$("div").addClass("on")' ------the classes of 'div' are "item on"
set 'canEdit' to true -------the classes of 'div' are "item editable"
question:why the classes of 'div' are not "item on editable"

Because you are not using Vue to add class to the element. So Vue doesn't know you have added a new class to the element.
See this example where I add a class NOT using Vue:
new Vue({
el: '#app',
template: `
<div>
<div ref="e" class="a" :class="{ b: b }"></div>
<button #click="toggleB">Toggle B</button>
<button #click="addC">Add C</button>
</div>
`,
data(){
return {
b: true
}
},
methods: {
toggleB(){
this.b = !this.b;
},
addC(){
this.$refs.e.classList.add('c');
}
}
});
.a {
width: 100px;
height: 100px;
}
.b {
border: 1px solid black;
}
.c {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app"></div>
Now if I use Vue to add class to it, it will work because now Vue knows:
new Vue({
el: '#app',
template: `
<div>
<div class="a" :class="{ b: b, c: c }"></div>
<button #click="toggleB">Toggle B</button>
<button #click="addC">Add C</button>
</div>
`,
data(){
return {
b: true,
c: false
}
},
methods: {
toggleB(){
this.b = !this.b;
},
addC(){
this.c = true;
}
}
});
.a {
width: 100px;
height: 100px;
}
.b {
border: 1px solid black;
}
.c {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app"></div>

Related

Why does Bootstrap Autocomplete send vue.js router go to /#/exclamationmark?

Why does it happen that my Vue router navigates to /#/! without apparent reason?
This seems to happen when I fire an event from an autocomplete form built with Bootstrap Autocomplete and trigger a function.
Calling the same function by clicking a button does not lead to the problem.
This is the parent component where the event is emitted to
<style scoped>
</style>
<template>
<div id="appspace">
<div id="leftbar">
</div>
<div id="workarea">
<div id="mapblock">
</div>
<div id="infoblock">
<div class="form-group"><label for="gotoff">Go to</label>
<autosuggest #locselect="locSelect($event)" id="gotoff"></autosuggest>
</div>
<button v-on:click="searchAround()" type="button" class="btn btn-primary">Search</button>
</div>
</div>
<div id="rightbar">
</div>
</div>
</template>
<script>
module.exports = {
data: function () {
return {
};
},
components: {
autosuggest: httpVueLoader('components/base/autosuggest.vue'),
},
mounted: function(){
},
destroyed: function(){
},
methods: {
setMarkerInCenter: function(){
this.locSelect({ value: { lng: 12, lat: 14 }})
},
locSelect: function(e) {
console.log('locSelect');
console.log(e);
},
},
}
</script>
and this is the component emitting the event:
<style scoped>
.autocomplete {
position: relative;
width: 130px;
}
.autocomplete-results {
padding: 0;
margin: 0;
border: 1px solid #eeeeee;
height: 120px;
overflow: auto;
}
.autocomplete-result {
list-style: none;
text-align: left;
padding: 4px 2px;
cursor: pointer;
}
.autocomplete-result:hover {
background-color: #4AAE9B;
color: white;
}
</style>
<template>
<div class="input-group">
<input ref="ac" class="form-control">
<div class="input-group-append">
<div class="input-group-text"><i class="fa fa-compass" style="height:0.5em;padding:0;margin:0;margin-bottom:4px"></i></div>
</div>
</div>
</template>
<script>
module.exports = {
mounted: function(){
var i = this.$refs.ac;
var c = this
$(i).autoComplete({ resolverSettings: { url: '/api/gc/autocomplete' } });
$(i).on('autocomplete.select', function(e, sel) {
e.preventDefault();
c.$emit('locselect', sel);
e.preventDefault();
});
},
}
</script>
Any leads as to how to debug this?
I couldn't find the reason why this autocomplete changes the route, that behavoir
seems to be undocumented. But here's a method to temporarily prevent this behavior until you find the solution, add this global route guard to your router to prevent navigation to '/#/!' route:
router.js
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
console.log(to)
// TEMP PATCH:
// Autocomplete changes route to '/#/!'
if (to.path !== '/#/!') {
next()
}
})
Just make sure that console.log(to) actually has a property path === '/#/!'

Avoid infinite loading spinner in vue js

I have a preloader in Vue js but it's infinite, how could I fix it? I was thinking about doing something to hide the div that contains the preloader when the page is loaded using mounted(), but I am not sure what should I do exactly. I am working in the App.vue file.
EDIT: Tried to add the class d-none when the component is mounted but that didn't work
new Vue({
el: "#app",
data(){
return{
classes: '',
}
},
mounted(){
this.classes='d-none';
}
});
/* Loader */
#preloader {
position: fixed;
left: 0;
top: 0;
z-index: 99999;
width: 100%;
height: 100%;
overflow: visible;
display: table
}
.loader {
display: table-cell;
vertical-align: middle;
position: relative;
width: 200px;
height: 200px
}
#preloader,
.contact-box,
.scroll-to-top {
background: #fff;
text-align: center
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="preloader" v-bind:class="classes">
<div class="row loader">
<img src="https://media2.giphy.com/media/3oEjI6SIIHBdRxXI40/giphy.gif" alt="logo" width="220" height="110">
<br><br>
</div>
</div>
<h1>content</h1>
</div>
the best way to do this task in vuejs is using a v-if.
e.g
<div v-if="!isLoaded" id="preloader">
<div class="row loader">
<img src="https://media2.giphy.com/media/3oEjI6SIIHBdRxXI40/giphy.gif" alt="logo" width="220" height="110">
<br><br>
</div>
</div>
new Vue({
el: "#app",
data(){
return{
isLoaded: false,
}
},
mounted: function() {
this.$nextTick(q => {
// Will be executed when the DOM is ready
this.isLoaded = true;
// UPD : you can use this instead for your tests ..
// setTimeout(function () { this.isLoaded = true; }, 2000)
})
}
});

Hide div when clicking the main div, but not when clicking child divs, hrefs, spans etc

So, when you click the blue content, I want to hide it, but not if you click a child element, like a h2, a, p, etc. Is this possible?
Please see the fiddle
html
<html>
<head></head>
<body>
<div id="app">
<div v-if="modal" id="content" #click="toggle">
<h3>
Name
</h3>
<span>
Like this (don't close if clicked)
</span>
</div>
<button #click="toggle">Toggle</button>
</div>
</body>
</html>
.js
new Vue({
el: '#app',
data: {
modal: true
},
methods: {
toggle: function( ){
this.modal = !this.modal
}
}
});
.css
a {
color: white
}
#content {
width: 300px;
height: 300px;
background: blue;
color: white;
}
Fiddle: http://jsfiddle.net/awu752mr/6/
I believe that is what the v-on:click.self modifier does. See here in the docs.
As #LShapz answered, the modifier=.self should be the recommended approach (Vue Style).
Another way is compare $event.target === $event.currentTarget (not recommended for your use case, but you may use these two properties for some other situations in future).
Vue.config.productionTip = false
new Vue({
el: '#app',
data: {
modal: true
},
methods: {
toggle: function(ev){
if(ev.target === ev.currentTarget) this.modal = !this.modal
}
}
});
a {
color: white
}
#content {
width: 300px;
height: 300px;
background: blue;
color: white;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div v-if="modal" id="content" #click="toggle($event)">
<h3>
Name
</h3>
<span>
Like this
</span>
</div>
<button #click="toggle">Toggle</button>
</div>

Accessing Vue Component scope from external javascript

Can I access Vue components data properties and methods from external javascript? I am trying to create a hybrid application where a portion of the screen is a Vue component, and I want to call a method inside that component on click of a button which is handled by pure js. Is there a way to achieve this?
Thanks!
Yes. You need to assign your Vue object to a variable (i.e. vue) and then you can access vue.methodName() and vue.propertyName:
// add you new Vue object to a variable
const vue = new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
});
// Add event listener to outside button
const button = document.getElementById('outsideButton');
button.addEventListener('click', function() {
vue.toggle(vue.todos[1]);
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
<div class="outside">
<button id="outsideButton">
Click outside button
</button>
</div>
Yes, you can add an event listener to the Vue component that listens for the button click's event. See example here on codepen.
JS
new Vue({
el: '#app',
methods: {
clickMethod(event){
if (event.target.id === 'outsideButton') {
alert('button clicked')
}
}
},
created(){
let localThis = this
document.addEventListener('click', this.clickMethod)
}
})
HTML
<div>
<button id="outsideButton">Button</button>
<div id="app">
</div>
</div>

Use method in template, out of instance vue

This is warning when i click on go to contact in tab about: "Property or method "switchTo" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in component )."
How do I fix this?
new Vue({
el: '#app',
data: {
currentPage: 'home',
},
methods: {
switchTo: function(page) {
this.currentPage = page;
}
},
components: {
home: {
template: `#home`,
},
about: {
template: `#about`,
},
contact: {
template: '#contact'
}
}
})
.navigation {
margin: 10px 0;
}
.navigation ul {
margin: 0;
padding: 0;
}
.navigation ul li {
display: inline-block;
margin-right: 20px;
}
input, label, button {
display: block
}
input, textarea {
margin-bottom: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div class="navigation">
<ul>
<li>Home</li>
<li>About</li>
</ul>
</div>
<div class="pages">
<keep-alive>
<component v-bind:is="currentPage">
</component>
</keep-alive>
</div>
</div>
<template id="home">
<p>home</p>
</template>
<template id="about">
<p>about go to contact</p>
</template>
<template id="contact">
<p>contact</p>
</template>
Just change your about template to this
<template id="about">
<p>about go to contact</p>
</template>
new Vue({
el: '#app',
data: {
currentPage: 'home',
},
methods: {
switchTo: function(page) {
this.currentPage = page;
}
},
components: {
home: {
template: `#home`,
},
about: {
template: `#about`,
},
contact: {
template: '#contact'
}
}
})
.navigation {
margin: 10px 0;
}
.navigation ul {
margin: 0;
padding: 0;
}
.navigation ul li {
display: inline-block;
margin-right: 20px;
}
input, label, button {
display: block
}
input, textarea {
margin-bottom: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div class="navigation">
<ul>
<li>Home</li>
<li>About</li>
</ul>
</div>
<div class="pages">
<keep-alive>
<component v-bind:is="currentPage">
</component>
</keep-alive>
</div>
</div>
<template id="home">
<p>home</p>
</template>
<template id="about">
<p>about go to contact</p>
</template>
<template id="contact">
<p>contact</p>
</template>
I already solved a problem like this in this question: Calling methods in Vue build
It's not the same problem so it's not a repeated question, but the answer is the same:
In the created hook, add the component to window.componentInstance like this:
methods: {
foo () {
console.log('bar')
}
},
created () {
window.componentInstance = this
}
Then you can call the method anywhere like this:
window.componentInstance.foo()