Does Vue have bilateral bind? - vue.js

<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.

Related

Generate Vue Component with vanilla JS

I am currently using the bootstrap table component to display a list (bootstrap-table.com)
This component (or jQuery plugin wrapped within a vue component) has a formatter for each column.
example with source code: https://examples.bootstrap-table.com/#welcomes/vue-component.html#view-source
I need to build a more complex set of links which involve some js processing that could easily be done with vue.
Instead of the formatter returning the <a> tag I would like to return the contents of a <component>. I have tried this but haven't been able to render a component.
I'm almost sure this is impossible because the child component is not previously binded which means the component won't be "reactive".
As an alternative I can build a js function that generates all the required links.
Another phrasing for this question would be: Is there any possibility to generate a vue component from vanilla js?
The above was not possible the way was tried because the components cannot be simply rendered like html tags as they can have methods and computed data therefore they must be compiled.
It looks like the solution for the above situation is using the Vue.compile() method:
const template = `
<ul>
<li v-for="item in items">
{{ item }}
</li>
</ul>`;
const compiledTemplate = Vue.compile(template);
new Vue({
el: '#app',
data() {
return {
items: ['Item1', 'Item2']
}
},
render(createElement) {
return compiledTemplate.render.call(this, createElement);
}
});
Demo:
https://codepen.io/couellet/pen/ZZNXzy
Demo reference:
https://snipcart.com/blog/vue-render-functions
Vue docs:
https://v2.vuejs.org/v2/api/#Vue-compile

V-model binding to generated input text field

Dynamic V-models created during an ajax request doesn't update when I try inputting a value
I'm using vue2.x and axios. I want to get the value set in generated input when user submit the form. I managed to set v-model on this input during ajax request
I receive this HTLM as response:
<input type="text" value="" v-model="generatedcode">. But after submitting the form the value is still empty. Looks like Vue ignore the v-model directive. How can I fix it ?
Here is my code :
VUE
var app = new Vue({
el: '#subcribtionform',
data: {
generatedform:'',
generatedcode:''
},
methods:{
OnSuccess(response){
this.generatedform = response.data;
},
OnclickSub(){
axios.post('/submitformURL',{
lastname: this.lastname,
generatedcode: this.generatedcode,
})
}
created: function () {
axios.get('/generate_inputURL').then(this.OnSuccess);
}
HTML
<div v-html="generatedform"></div>
GENERATED INPUT
<input type="text" value="" v-model="generatedcode"/>
Try:
created: function () {
axios.get('/generate_inputURL').then(res => this.OnSuccess(res));
}
Component data must be function not object. You should be seeing warning about this in console. I guess your component is not reactive data then, which means that is not redrawn after on request done.
data(): {
return {
generatedform:'',
generatedcode:''
}
}
As I can see, you want to change the vue-app template using v-html attr - I think this is not possible. While mounting the application, the template compiles into render function, so your trick does not make any sense. You can try to do the following:
Construct the template (using html recieved from server as you want ) as string or as hidden el in the DOM
Set it in your app object - template:your_html_template
Create vue app

How do I update semantic ui dropdown list with vue?

I'm trying to update the semantic ui dropdown with new values. Vue is correctly being updated and I'm refreshing the semantic ui dropdown but it still isn't updating. I saw another post which mentioned the use of key, but it still fails.
Template
<div id=root>
<label>Type:</label>
<select id="app_type" class="ui search selection dropdown" v-model="model_type_val">
<option v-for="model_type in model_types" v-bind:value="model_type.value" v-bind:key="model_type.value">{{model_type.text}}</option>
</select>
<p>
selected: {{model_type_val}}
</p>
</div>
Code
var model_types2= [
{value:"",text:"Type"},
{value:"type1",text:"Type1a"},
{value:"type2",text:"Type2a"},
{value:"type3",text:"Type3a"},
{value:"type4",text:"Type4"}
];
var vm2= new Vue({
el:'#root',
data:{
model_type_val:"",
model_types:[
{value:"",text:"Type"},
{value:"type1",text:"Type1"},
{value:"type2",text:"Type2"},
{value:"type3",text:"Type3"}
]
},
mounted: function(){
$('#app_type').dropdown();
setTimeout(function() {
this.model_types=model_types2;
alert(this.model_types[1].text);
$('#app_type').dropdown('refresh');
}, 1000);
}
});
I've tried to reproduce the code in this jsfiddle.
You have a this problem. When you have a callback inside a Vue method or lifecycle hook in which you use this, you need to make sure that this points to the correct object (the Vue). You do that with an arrow function, a closure, or bind.
setTimeout(() => {
this.model_types=model_types2;
$('#app_type').dropdown('refresh');
}, 1000);
Here is your fiddle updated.
Note: In the fiddle, I also converted your selector to use a ref. Typically you want to start weaning yourself off jQuery when working with Vue.
See How to access the correct this inside a callback.

Load Vue view-model with data from existing HTML

I'd like to use Vue with pre-rendered HTML. In the long run we might not, but for now it seems easier to leave the server code in place and use Vue to manage the client logic of the web app.
I can't seem to find anything that describes how to have the view model load its data from the HTML. My example will be the same as https://github.com/vuejs/Discussion/issues/56. Even though that issue was closed (and fixed?), I can't seem to get that to work in either 1.x or 2.x.
When I set up some HTML
<div id="myvue">
<span v-text="message">Hello!</span>
</div>
and then create a view-model for it
var vm = new Vue({
el: '#myvue'
});
I'm hoping to have the vm object hydrated with data from the HTML so that vm.message == 'Hello!' is true. But that doesn't happen. I just end up with an error saying that message is not defined with version 1. With version 2 I get a warning that says Property or method "message" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
But when I declare the data property in the vm like so
var myvue = new Vue({
el: '#myvue',
data: {
message: <something>
}
});
no matter what <something> is (null, a string, undefined), it always replaces what's in the HTML element. Is there any way to have the Vue object load data from the HTML?
It is theoretically impossible to hydrate your state directly from HTML, because rendering is not a bijective function. Two simple examples:
<p>{{ foo }}</p> renders to <p>2</p>. Is foo a string or a number?
<p>{{ foo + bar }}</p> renders to <p>6</p>. What are the values of foo and bar?
The simplest way to hydrate your state is to take the data you use for rendering on the server, serialize it to JSON and render it using <script>window.hydrationData = ...</script> directly in your document. This way, you can use it for data when you create the Vue instance.
This approach is also used in the official Vue demo app.
It's not related to Vue, but Knockout.js can kind of do this when used with ErikSchierboom's knockout-pre-rendered custom binding.
This combo essentially lets you define an input or something
<div id="mydiv">
<input data-bind="init, textInput: content" value="foo"/>
</div>
And then bind a model object to it
function ViewModel() {
this.content = ko.observable();
}
var vm = new ViewModel();
ko.applyBindings(vm, $('#mydiv')[0])
With the init binding on the input, the vm content gets loaded with the value from the input when it gets bound.
Again, it's not Vue. But thought I'd put it up incase it helps someone.
One option, if your rendered HTML doesn't depend on computed properties, might be to parse your generated HTML to grab the values you need for your data (say using jQuery or vanilla-js to create a data object to pass to your VM when you instantiate it:
$(function(){
var app = '#app';
var $app = $(app);
var data = {
a: 0, // some initial value not present in HTML
b: $app.find('.foo').text(),
c: $app.find('input[name=c]').val()
};
var vm = new Vue({
el: app,
template: '...template...', // if needed
data: data,
...
});
});
When instantiated, the data model should match the rendered HTML. You may need to supply the template if not all template components are part for the rendered HTML. Note that this most likely will cause the app to re-render once instantiated. You might be able to add the server-rendered="true" attribute to your root app element to prevent this if the pre-rendered HTML is identical to what the vm would render.
Another, not so automated option would be to have your template generate a <script> tag just below your element which the data model in it:
<script>
window.MYSTATE = window.MYSTATE || {};
window.MYSTATE['#app'] = {
"a": 0,
"b": "Some text",
"c": "Some value"
};
</script>
Make sure your data is properly escaped into JSON format. Then in your page scripts where you instantiate your app , use the window.MYSTATE['#app'] as the data object (doing a deep clone to prevent changes in the window.MYSTATE instance affecting your model.
var vm = new Vue({
el: '#app',
template: '...template if needed...',
data: $.extend(true, {}, window.MYSTATE['#app'])
...
});
Just make sure you do the instantiation after the page has loaded (i.e. by placing this at the bottom of the page before the closing body tag)

vuejs 2 v-for :key not working, html being replaced?

I'm rendering some HTML in a v-for
But everytime I change any of the data, all my html gets replaced (input fields lose their values)
I tried giving the :key all kinds of different values
I didn't have this problem in vue v1, only in v2
http://jsbin.com/jomuzexihu/1/edit?html,js,output
I had a little play with this and it appears that Vue does not re-render the entire list when using <input /> or if you use a component but it does with v-html. Heres the fiddle for the comparison:
https://jsfiddle.net/cxataxcf/
The key actually isn't needed here because the list isn't being re-ordered, so your issue isn't to do with :key but rather with v-html. Heres what the docs say about v-html:
The contents are inserted as plain HTML - data bindings are ignored. Note that you cannot use v-html to compose template partials, because Vue is not a string-based templating engine. Instead, components are preferred as the fundamental unit for UI reuse and composition.
So I guess this is where the problem lies.
It might be worth raising an issue on Vue's github page to see whether this is the expected behavior for v-html, but Vue 2.0 is much more heavily focused on components than vue 1.x and doesn't appear to recommend using v-html, so it may just be that you need to re-factor your code to use components instead.
Edit
The solution to this problem is to simply wrap the code in a component and pass the HTML as a prop:
Vue.component('unknown-html', {
props: {
html: ""
},
template: '<div><div v-html="html"></div>'
})
The markup:
<unknown-html :html="thing.html"></unknown-html>
And the View model:
var app = new Vue({
el: '#app',
data: {
numInputs: 1,
stuff: [{
'html':'<input />'
}, {
'html':'<button>Foo</button>'
}]
}
})
Here's the JSFiddle: https://jsfiddle.net/wrox5acb/
You are trying to inject raw html directly into the DOM. Probably it was possible in earlier versions of Vue.js, but it is definitely not the recommended way.
You can instead have an array of objects and bind it to html as shown in this jsFiddle: https://jsfiddle.net/43xz6xqz/
Vue.js version: 2.0.3
In the example above, vue.js is responsible for creating the input elements and also for binding these input elements to the object values using v-model.
To extract these values, you may use a computed property as shown in the sample code.
I guess, for performance optimization, when the key is not change, Vue will not rerender the dom, but will update the data import through directive.So when your input element is import through an directive (v-html), it will be rerendered everytime when stuff changes.
Due to the vue is not a string template engines, but template based on dom, so in the case of #craig_h 's example , to use the incomming html in a string template within a component:
Vue.component('unknown-html', {
props: {
html: ""
},
template: '<div><div v-html="html"></div>'
})
view:
<unknown-html :html="thing.html"></unknown-html>
So when the stuff changes, it will not to rerender the template declare in string, for vue is not a string template engine.