getElementsByClassName in context of vue - javascript

In the context of vue.js, how does getElementsByClassName work?
I have the following snippet of code below - the goal is to click a button and change the color of the h1 tag to green using vue.js methods:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1 class="main-header">{{ message }}</h1>
<button v-on:click="colorChange">Click me</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
colorChange: function() {
// what do I do here...?
}
}
})
</script>
</body>
</html>
It's to my understanding that Vue is like React with a virtual DOM and you don't modify it directly.
Obviously in vanilla JS you just do document.getElementsByClassName('main-header')[0].style.backgroundColor="green"; but that seems counterintuitive in Vue.
Am I overcomplicating this and is this in fact how it works? Or does Vue have a specific way of handling this? I've been looking at https://v2.vuejs.org/v2/guide/class-and-style.html but it just explains how to bind classes. I'm also reading through https://v2.vuejs.org/v2/guide/events.html but I'm having a hard time finding what I need in regards to targeting an element/modifying it in some way...
How do you select and/or modify an element in the context of Vue?

You are correct. In Vue, interacting directly with the DOM is counter-intuitive.
Fortunately, there is a directive that allows you to apply style changes directly by binding to a data property. (Keep in mind, you could do something similar with a class as well).
<h1 class="main-header" v-bind:style="{ color: activeColor}">{{ message }}</h1>
<button v-on:click="colorChange">Click me</button>
In your component, create a data property and update it on button click:
data: {
message: 'Hello Vue!',
activeColor: 'red'
},
methods: {
colorChange: function() {
this.activeColor = 'green'
}
}

Related

Vue.JS: Why won't parent element receive event? Does parent have to be a custom component?

I am noob to Vue.js. I thought the Events made sense: I assumed they bubbled up through the DOM tree until caught by a parent. But I must be missing something. The following code doesn't work, doesn't generate any errors or warnings, and in the Vue developer tools, I see the event being emitted. What am I missing?
index.html:
<!doctype html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
</head>
<body>
<div id="app" #button-clicky="gotevent">
<div #button-clicky="gotevent">
<h1>{{ info }} {{ counter }}</h1>
<button-component />
</div>
</div>
<script src="./main.js"></script>
</body>
</html>
main.js:
Vue.component('button-component', {
data: function() {
return {
count: 1
}
},
methods:
{
clicky() {
this.$data.count++;
this.$emit('button-clicky');
}
},
template: `<button #click="clicky">Go {{ count }}</button>`
});
const vue = new Vue({
el: '#app',
data: {
info: "this is title.",
counter: 0
},
methods: {
gotevent() {
this.$data.counter++;
alert('The event was heard!'); }
}
});
Can't I listen for an event on any parent level above the component emitting it?
I added the listener on both the direct parent <div> and also the 'app' container, but nothing happens and no errors or warnings are thrown.
I don't think Vue events bubble like DOM ones, you need to catch them on the emitting component, so <button-component #button-clicky="gotevent" />

Bind a vue input that doesn't exist yet to a data property

We are using Vex in our application for all the dialog messages. I have an input in one of the dialogs that is used to filter a list of items.
In my Vue instance, I want to listen for the changes on a data property.
The problem is that Vue doesn't get the changes as I type them in the input box. I am thinking this is due to the fact that the input is added to the DOM after the Vue initialization.
How would I approach this so my Vue instance can listen to the input changes? I've done my best to recreate the scenario in it's most simple form in the snippet below.
new Vue({
el: '#app',
data: {
filter: ''
},
methods: {
openModal: function() {
vex.dialog.open({
input: [
'<h2>Filter</h2>',
'<input v-model="filter" />',
'<p>{{filter}}</p>'
].join('')
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/js/vex.combined.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/css/vex.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-on:click="openModal">Open Modal</button>
</div>
The issue is that you are providing plain html in strings to the vex.dialog.open method. Vue won't have any idea that you have done this and so none of the Vue syntax that you've included in those strings will be interpreted as it would in a Vue component definition.
What you should do is make a Vue component for that input, and then pass that input's element via a ref to the vex.dialog.open method. That way, Vue will have compiled the template before it's used in the dialog.
Here's a simple example:
Vue.component('my-input', {
template: `
<div>
<h2>Filter</h2>
<input v-model="filter"/>
<p>{{filter}}</p>
</div>
`,
data() {
return {
filter: ''
}
}
});
new Vue({
el: '#app',
data() {
return {
modalOpened: false
}
},
methods: {
openModal() {
vex.dialog.open({
input: this.$refs.input.$el
});
this.modalOpened = true;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/js/vex.combined.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vex-js/4.0.0/css/vex.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="openModal">Open Modal</button>
<my-input v-show="modalOpened" ref="input"/>
</div>

Binding the source to an iframe with Vue JS shows up empty iframe?

Currently, I have an Vue JS app which I need to bind the iframe source to the iframe. I have used the suggested method of v-bind:src="" but using Inspect Element I can see that the body tag of the iframe is empty. Can this be done or am I better giving up my treasure hunt?
<div id="app">
<h1>{{message}}</h1>
<div class="responsive-iframe">
<iframe v-bind:src="maps.treasureMapFrame"></iframe>
</div>
</div>
</div>
Then my JS is as below;
var app = new Vue({
el: '#app',
data() {
return {
message: "Find Treasure",
maps: [
{
treasureMapFrame:"https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1230838.6815658903!2d-3.2675559393922833!3d52.948750511868894!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x4870be1abfbc770f%3A0x474d30c2c81f0893!2sTreasure%20Island%20Play%20Ltd!5e0!3m2!1sen!2sfr!4v1571778541141!5m2!1sen!2sfr"
}
]
};
}
})
you need to select the first item in your array:
<iframe v-bind:src="maps[0].treasureMapFrame"></iframe>

Vue 2.0 - How passing function to child component?

I have one issue. I want to pass function link to the child component. It's working but in HTML I get that code. It's correct how improve it?
I have Vue instance
app = new Vue({
... some code
data: {
onAppClose: null,
onAppSend: null
}
})
I want to add from global window any function. Or register function in Vue instance
app.onSend = () => console.log('data')
And pass this function to child
<div id="app">
<dynamsoft-component v-if="displayComponent"
:docs="docs"
:onAppSend="onSend"
:onAppClose="onClose"
></dynamsoft-component>
</div>
But I get this HTML template in console
<div id="app">
<div onappsend="()=>{}" onappclose="function (data) {
console.warn('dwdawad')
console.log('data')
}"></div>
</div>
You example code is not making a lot of sense - do you want to add a listener not a div or pass a function to a child component?`
I assume the latter. Vue has custom events for that .
Parent template:
<div v-on:appsend="someMethod" v-on:appclose="someOtherMethod"></div>
Parent component methods:
methods: {
someOtherMethod: function (data) {
console.warn('dwdawad')
console.log('data')
},
// ...
}
And then emit form the child:
this.$emit('appclose', {id: 'whatever'} /*pass data here*/)
Edit:
I still don't see how those functions would end up directly in the template, but the real problem is: HTML is not case-sensitive. so :onAppSend becomes :onappsend. You have to use kebap-case: :on-app-send. Vue will convert it to onAppSend in the component.
I have never used Vue.js before now..
But having a look at the how to on their site, this seems to work
In Vue style guide have recommendations about props naming
https://v2.vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended
Vue.component('dynamsoft-component', {
props: ['onAppSend'],
template: '<button v-on:click="buttonclick">click me</button>',
methods: {
buttonclick(e){
// Check if onAppSend is defined.
if(Boolean(this.onAppSend)){
this.onAppSend();
}
}
}
})
new Vue({
el: '#app',
methods: {
onSend: function(){
console.log('child clicked');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<dynamsoft-component :on-app-send="onSend"></dynamsoft-component>
</div>

How to keep data in component when using v-with in Vue js

So here is my problem:
I want to make a component that takes it's values from v-with="values" and add them to my component model after some modification, then display those modified properties.
But from what I understand, when I set values with "v-with", component data are erased so the binding between my component data (not v-with one) and my directives are lost.
I'm really new to this framework, I don't see any solution, so I guess it was time to ask my first question here !
Here is the HTML:
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentData" ></my-component>
</div>
And here is the Javascript:
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
And the js fiddle http://jsfiddle.net/KwakawK/hfj1tv4n/3/
I'm not totally clear on what you're trying to do, but I believe it can be solved by using the second form of v-with, which is v-with="childProp: parentProp". Rather than the parent property overriding all of the child data, this will replace only the property on the left of the colon.
So I think your code can be fixed by changing the v-with to this:
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
Here's the updated code as a snippet:
// register the grid component
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
// bootstrap the demo
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.11.4/vue.min.js"></script>
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
</div>
See the Vue guide for more information.

Categories

Resources