I am really confused with the radio input in Vue.js - vue.js

From the Vue guide, I saw these things:"For radio, checkbox and select options, the v-model binding values are usually static strings (or booleans for checkbox):" and
<!-- `picked` is a string "a" when checked -->
<input type="radio" v-model="picked" value="a">
But when I try to render picked in the tag , it does not work. why?
Here's my code.(in this case, it's one)
When I click the radio, it does not reflect in the tag span.

Your missing data, try:
new Vue({
el:'#app',
data: {
picked: 'One'
}
})
Example: http://codepen.io/anon/pen/NpPmgp

You have to add data as well in the vue instance:
new Vue({
el:'#app',
data: {
picked: 'Two'
}
})
Check this:
http://codepen.io/anon/pen/XMJQRB

Related

Vue replaces "open" attribute to value "open" in any tag

I'm using vue.js (v2.6.12) components in laravel blade templates.
For the project, I'm also using MathML in which I need to use the open attribute of <mfenced> tag to be set to some custom values. Here is the example of the math expressing in mathml.
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="[">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
But as soon as the page renders, the open attribute is converted into this open="open". I'm 100% sure there is no other library or script is loaded that updates like so, just plain vue. This actually breaks the math expression. So it looks like this:
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="open">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
Later I realized that not only in math expression, litaratily any tag, be it <div open="anything">...</div>, <span open="anything">...</span>, <custom-element open="something">...</custom-element> having open attribute behaves the same. even if I use v-pre attribute to exclude it from vue js templete compiler.
And this do not happen, as soon I disable the vue app initialization.
The question here are:
Why vue is changing the open attribute like so?
How can I stop this behaviour, to the entire page within the vue application area or at least where I choose (something like using v-pre), is there ary config or any other way around?
Why
In HTML spec there are some attributes called boolean attributes. Spec dictates what can be a value of such attribute:
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
open is one of the boolean attributes - it is defined for the <details> element
Problem with Vue 2 is, that it treats most of the boolean attributes as global - without considering the element it is placed on. Result is that open attribute is always rendered with value "open" or removed if the value is falsy (when v-binding). This is fixed in Vue 3 as shown in 2nd example...
How
The use of v-pre is the way to go but unfortunately for you there is a bug.
See this issue. The bug was already fixed with this commit(Sep 21, 2020) but it was not released yet...
example - the "With v-pre" should work in Vue version > 2.6.12
const vm = new Vue({
el: '#app',
data() {
return {
message: 'Hi!',
html: `<div open="[" close="]">Hi from html</div>`
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<div v-html="html"></div>
<div v-pre>
<p open="[" close="]">With v-pre</p>
</div>
</div>
example - it works in Vue 3 - open is treated as boolean attribute only if placed on <details>
const app = Vue.createApp({
data() {
return {
message: 'This works in Vue 3!',
}
},
})
app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<details open="[">
<summary>Details</summary>
open attribute on details element is treated as boolean (renders empty value)
</details>
</div>
One workaround is to create a directive (named "attr") that sets the attribute:
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
Then use it in your template like v-bind but with v-attr:
<mfenced v-attr:open="'['">
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
new Vue({ el: '#app' })
<script src="https://unpkg.com/vue#2.6.12"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="app">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" v-attr:open="'['">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
</div>
I've found a simple hack to solve this problem.
Why hack?
Because it is eventually going to be fixed in the comming release as pointed by #Michal, so just a quick & dirty hack is enough for now to go for it.
What I did is I placed the math content in the content and also added it to the data attribute and replacing the original content after vue has done its bad work (sorry just using blade syntax here, but it will make sense). I keep it in both places just for SEO purposes.
The template where I need math expression to be displayed.
...
<div class="proxy-content" data-proxy-content="{{ $article->content }}">
{!! $article->content !!}
</div>
...
I was using it along with jQuery, but you can easily substitute with vue.js' $el. This is what it looks in my app.js file.
...
const app = new Vue({
el: '#app',
methods: {
proxyContent() {
// Set Proxy Content.
jQuery('.proxy-content').each((i, el) => {
const $el = jQuery(el);
$el.html( jQuery('<textarea />').html( $el.data('proxy-content')).text() );
});
}
loadMathJax() {
// Load & Initialize MathJax Library.
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-mml-chtml.js";
document.getElementsByTagName("head")[0].appendChild(script);
}
}
mounted(){
// Enable proxy content after mount, so we are sure no more rendering issue for templates.
this.proxyContent();
// Load MathJax library with a little delay to make sure everything is ready before loading the library.
setTimeout(() => this.loadMathJax(), 10);
}
});
...
One might argue, that I'm mixing up things outside of the scope of the vue application. For me that is not an issue, as the whole page is using vue.js and also the single thing don't make any harm even if there is another scope that is using mathml (though it depends on actual implementation).
In that case, if you want to scope it well, just use $el of vue.

Vuetify v-text-field not updating value when it is changed. Even on $forceUpdate()

Vuetify v-text-field not updating value when it is changed. Even on $forceUpdate() and when using $set
I have tried to use it without $forceUpdate and $set but even with both of these methods it does not work
Example what DOES work WITHOUT vuetify:
https://jsfiddle.net/gu273qy0/3/
Example of what DOES NOT work WITH Vuetify:
https://jsfiddle.net/gu273qy0/5/
This javascript can be used in both examples
new Vue({
el: "#app",
data: () => ({
todos: ['']
}),
methods: {
setText(index, todo){
if (todo.toString().match(/^((([A-Z]{3})[UJZ](\d{0,7}))|[A-Z]{0,3})$/)) {
this.$set(this.todos, index, todo)
}
this.$forceUpdate()
},
addText() {
console.log(this.todos)
this.todos.push('')
}
}
})
I expect that the exact same implementation works with vuetify and without vuetify. Unfortunately this is not the case. Only the non vuetify example works.
An example of a valid value or todo is AAAU0101201.
An example of a invalid value is AAAA
I cannot really explain why you have not the same results with or without Vuetify, but in fact, with Vuetify example, if you enter a wrong value, then the todos array won't be modified, and so the model won't be updated
I think you should for example handle #blur event:
https://jsfiddle.net/v6m2tyrs/1/

Vuejs problem ; How to bind new elements added to DOM?

I Want to add new element to the DOM after new Vue instance created.
I know I can re initiate the instance of vue but it seems it is not good idea because vue have to re render all elements again. So is there a method to bind just new element to the data scope?
<body>
<div id="app">
<span>{{ a }}<span>
<div id="newElem"></div>
</div>
</body>
<script>
$(document).ready(function () {
var mainApp = new Vue({
el: "#app",
data: {
"a": "aa",
"b": "bb"
}
});
$("#newElem").html("<span>{{ b }}</span>")
});
</script>
This code renders {{ b }} unbinded. I am looking for a method for preventing this.
Thanks
Have you tried something like this?
<div v-html="newElem"></div>
el: "#app",
data: {
"a": "aa",
"b": "bb"
},
created() {
this.newElem = "<span>{{ b }}</span>";
}
It's really hard to tell your ultimate goal here but I'll take a shot.
Ideally, you wouldn't touch the DOM, you'd want to update the data and let Vue handle the DOM for you.
I think you want to initialize b as undefined and set that when you're ready for it to be displayed.
data: {
a: 'aa',
b: undefined,
}
Then in your template you can have something like this:
<span v-if="b">{{ b }}</span>
This way the span won't display until b is truthy.
Then, later, when b is ready, you would mainApp.b = 'bb'.
Here's a quick demo: http://jsfiddle.net/crswll/vtu8nzea/1/
If you make add a more complete use case I can likely help further.
Also, this answer might help as well. It's kind of like the next level of this thinking: Vuejs: v-model array in multiple input

Vue-select bind v-model to save value, not label

I am new to VueJs (currently working on: 2.5.8) and trying to implement vue-select.
I have successfully integrated the plugin in my app and using it with:
<v-select :options="oData" v-model="selected">
new Vue({
el: "#app",
data: {
oData: ["foo", "bar"],
selected: null
}
});
This seems to work fine, but whenever I select foo or bar from the options, the selected contains foo/bar based on selected, whereas I want to store it's respective index, for ex: if foo is selected, the selected should contain 0 instead of foo.
Can anyone please help me to achieve that?
The following code worked for me.
oData = [{label:"foo", value:"1"},{label:"bar", value:"2"}]
<v-select
v-model="selected"
:items="oData"
item-text="label"
item-value="value"
>
</v-select>

Does Vue have bilateral bind?

<p contenteditable="true" v-bind="message"></p>
Object #message does not change when I edit the HTML element. Is there any simple way to do this using Vue.js?
Unfortunately, contenteditable doesn't work with Vue bindings such as v-model, it's recommended that you use a library such as medium.js to build your own component.
However, if you're just trying to do something simple and keep the data in sync you can probably do that yourself:
View Model
new Vue({
el: '#app',
methods: {
updateMessage() {
this.message = this.$refs.message.innerText;
}
},
watch: {
message() {
this.$refs.message.innerText = this.message;
}
},
data: {
message: 'Hello'
}
})
HTML
<p ref="message" contenteditable="true" #keyup="updateMessage" class="editable">{{message}}</p>
 
As you can see you have to deal with updating the DOM yourself when using contenteditable. What I'm actually doing here is using a ref to target the contenteditable, so I can get the inner text via this.$refs.message.innerText. I've then added a #keyup event which calls the updateMessage method to update the message data property. I've then added a watcher which reverses this process, so when message is updated it updates the contenteditable.
Here's the JSFiddle: https://jsfiddle.net/3ngc9486/
Yes it has a two-way binding directive v-model, but it works only with input elements. So, instead of using a p element and handling that with complex JS, use a textarea with v-model and it will work out of the box.
<textarea v-model="message"></textarea>
here is an example.