I would like to do something like this:
<input type="text" value={ value1 } >
<input if={ value1 != ''} type="text">
So, adding to DOM the second input when the first input has any value different than empty. I know 2-way data binding is not supported by Riot.js
I have tried to do the following:
<input ref="first" type="text" >
<input if={ this.refs.first.value != '' } type="text">
But it does not work.
You are correct, Riot does not do 2-way data binding for reasons I'll leave to the reader to research. It's fascinating stuff, but in my opinion, most web development can be made drastically simpler by following a few simple patterns. Riot is great for this reason. Simply put, the tag is just markup with event listeners that can update the tag.
<my-tag>
<input name='username' type='text' onKeyup={ checkVal } value={ opts.val }>
<input if={ usernameEntered } name='password' type='password'>
<script>
this.checkVal = (event) => {
this.usernameEntered = event.target.value !== ''
// this.update() is implicitly called in event listeners
}
</script>
</my-tag>
In your case (I don't know your exact use case), you may want to populate the first input element with a value passed into the tag (opts.val). Then we can assign some variable (usernameEntered) in an event listener (checkVal) and update the tag.
See Riot JS Guide for more detailed examples
Related
I am pretty new to vue, and am trying to use it in a bootstrap modal. The relevant div in the modal is as follows.
<div class="form-group row">
<label for="priceQCField" class="col-sm-2 col-form-label">Price<span class="red"> *</span></label>
<input type="number" step="0.01" class="form-control col-sm-4" id="priceQCField" name="priceQCField" min="0" v-model="job.price">
</div>
I read some other questions about vue returning strings rather than numbers, so I have converted the job.price to a number inside my method to call the modal
showPriceJob: function (job) {
this.job = job;
this.job.price = parseFloat(this.job.price);
$('#mdlPriceJob').modal('show');
},
However, job.price refuses to appear in the input field either as a string or a number. I know it is available to the modal as I can see it using <span>{{job.price}}</span>.
Can anyone advise me please?
Additional - I think it is a display issue - if I change the input field, the entry in the <span> changes
2nd update - initial table
<tr class="light-grey" v-for="job in jobs" v-on:click="viewJob(job)">
<td>{{job.id}}</td>
<td>{{job.customerName}}</td>
<td>{{job.description}}</td>
<td v-bind:class="job.dueDate | dateColour">{{job.dueDate | dateOnly}}</td>
<td>£{{job.price}} {{job.isEstimate | priceEstimated}}</td>
<td>{{job.delivery}}</td>
</tr>
Upd.
According to your comments to my answer you are using v-for and you can't use this.job within your method. You should give us more code to see the whole picture.
Upd.2
You have showed more code but I didn't see any v-for so I am confused. You can try to use something like this if job is a property of appData.jobs:
showPriceJob: function (job) {
this.appData.jobs.job = Object.assign({}, job);
this.appData.jobs.job = parseFloat(this.appData.jobs.job.price);
$('#mdlPriceJob').modal('show');
},
But I'm not sure about this because I don't see where job is declared.
Upd.3
Oh! Wait! You have this code:
data: appData.jobs, but data should be in this format:
data: function(){
return {
appData: {
jobs: [],
},
}
},
Or show me what is your appData.jobs variable is.
If i open https://v2.vuejs.org/v2/guide/forms.html#Text and edit text - no effect on typing text in mobile chrome. #keyup #input #keypress - v-model does not change when I'm typing
<input v-model="message" #keyup="log" placeholder="Edit">
<p>Edited: {{ message }}</p>
How can i fix it? I need get input value on typing (#keyup #input)
Update: After a lot of discussion, I've come to understand that this is a feature, not a bug. v-model is more complicated than you might at first think, and a mobile 'keyboard' is more complicated than a keyboard. This behaviour can surprise, but it's not wrong. Code your #input separately if you want something else.
Houston we might have a problem. Vue does not seem to be doing what it says on the tin. V-model is supposed to update on input, but if we decompose the v-model and code the #input explicitly, it works fine on mobile. (both inputs behave normally in chrome desktop)
For display on mobiles, the issue can be seen at...
https://jsbin.com/juzakis/1
See this github issue.
function doIt(){
var vm = new Vue({
el : '#vueRoot',
data : {message : '',message1 : ''}
})
}
doIt();
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id='vueRoot'>
<h1>v-model</h1>
<div>
<input type='text'
v-model='message'
>
{{message}}
</div>
<h1>Decomposed</h1>
<div>
<input type='text'
:value='message1'
#input='evt=>message1=evt.target.value'
>
{{message1}}
</div>
</div>
I tried all solutions I could find on the internet, nothing worked for me. in the end i came up with this, finally works on android!
Trick is to use compositionupdate event:
<input type="text" ... v-model="myinputbox" #compositionupdate="compositionUpdate($event)">
......
......
methods: {
compositionUpdate: function(event)
{
this.myinputbox = event.data;
},
}
Ok, I dont know if there is another solution for this issue, but it can be solved with a simple directive:
Vue.directive('$model', {
bind: function (el, binding, vnode) {
el.oninput = () => (vnode.context[binding.expression] = el.value)
}
})
using it just like
<input v-$model="{toBind}">
There is an issue on the oficial repo, and they say this is the normal behavior (because the composition mode), but I still need the functionality
EDIT: A simpler solution for me was to just use #input.native. Also, the this event has (now?) a isComposing attribute which we can use to either take $event.data into account, or $event.target.value
In my case, the only scheme that worked was handling #keydown to save the value before the user action, and handling #keyup to process the event if the value had changed. NOTE: the disadvantage of this is that any non-keyboard input (like copy/paste with a mouse) will not work.
<md-input
v-else
:value="myValue"
ref="input"
#keydown="keyDownValue = $event.target.value"
#keyup="handleKeyUp($event)"
#blur="handleBlur()"
/>
With handleKeyUp in my case being:
handleKeyUp(evt){
if(evt.target.value !== this.keyDownValue){
this.$emit('edited', evt);
}
}
My use case was the following:
I requested a search endpoint in the backend to get suggestions as the user typed. Solutions like handling #compositionupdate lead to sending several several requests to the backend (I also needed #input for non-mobile devices). I reduced the number of requests sent by correctly handling #compositionStarted, but there was still cases where 2 requests were sent for just 1 character typed (when composition was left then, e.g. with space character, then re-entered, e.g. with backspace character).
I am facing a problem with my page with VueJS. It's a page for different translations of the website. It has a dropdown on the top for the language selection that once switched will update the fields with the current language.
The problem starts when it loads, because my form is like this:
<form id="trForm">
...
<input type="text" name="header_title" class="form-control" v-model="translations.header.header_title" />
...
</form>
It's trying to access these attributes before the method returns any data, but somehow it will still show the data once it is complete, but it becomes troublesome when I try to switch the language, it won't because of this problem and also, if I do the following:
<form id="trForm">
...
<input type="text" name="header_title" v-if="translations.header" class="form-control" v-model="translations.header.header_title" />
...
</form>
on each field, those that aren't populated will display no field at all for a new input value. I tried something like translations.features || '', but no success.
I also tried to put on the parent block a condition that if the loading is false will display the form, but since the page is loaded first than the method is executed, it will always be false for the first microsecond.
methods: {
fetchTranslations(e) {
let vm = this;
vm.loaded = false;
$.get('/ajax/admin/translations', { 'locale': e }).done((data) => {
if (data.success) {
vm.translations = JSON.parse(data.translations.translation);
vm.loaded = true;
} else {
toastr.error('Something went wrong');
}
});
},
Please, what do I do? It'd be good to show the form after there is data.
Introduce a new variable, e.g. loaded that defaults to false
Use this variable as a v-if condition on the form
In the callback of your data fetch, set loaded to true.
I want to create a tag like this
<mytag>
<p class={ hide: doEdit }>
<a class="icon1" onmousedown={ startEdit }></a>
<input type="text" value={ opts.value } readonly/>
</p>
<p class={ hide: !doEdit }>
<a class="icon2 onmousedown={ endEdit }></a>
<input name="editfield" type="text" value={ opts.value }/>
</p>
this.doEdit = false
startEdit(e){
this.doEdit = true
this.update()
}
endEdit(e){
this.doEdit = false
opts.value = this.editfield.value // this does not change opts.value, unfortunately
this.update()
}
</mytag>
If it would have worked, i could have used it like
var mydatamodel = {"input1":"email","input2":"name"}
<mytag value={ mydatamodel.input1 }></mytag>
<mytag value={ mydatamodel.input2 }></mytag>
Unfortunately, this does not seem to work. mydatamodel.xy does not get updated, i cannot assign a new value to opts.value (there is no exception, opts.value simply won't change its value).
What would a good way be to update the parents model according to the new values of the "editfield" in the children?
It is possible to access the data using this.mytag[i].editfield. But this is no good solution for larger forms.
I also tried using a custom event and trigger it in the child tag. However, i did not yet find a proper generic solution to update the model in the parent tag. This approach led to something clumsy as the "this.mytag[i].editfield"-way.
Is there a method to create the child tags in such a way that it is possible to write
<mytag value={ mydatamodel.input1 }></mytag>
where mydatamodel.input1 is updated as soon as it changes in the child tag?
Thanks for your thoughts.
Set the callback through an attribute:
<my-tag
title={ globalModel.input1.title }
val={ globalModel.input1.val }
onendedit={ endEdit1 }></my-tag>
Then send back the value through callback.
endEdit (e) {
this.doEdit = false
if (opts.onendedit)
opts.onendedit(this.editfield.value)
}
It seems better to add an outer tag. See detail on plunkr
You may have an interest on RiotControll, too. This is a kind of Flux solution for Riot.js, FYI.
Update: rewrote the answer after the information via comment.
i would like to know the syntax for removing a row which was added by appendchild.
There is also a removechild, but I am not sure how to operate.
<input type="button" id='submitlink' value="ADD_AGENDA" onClick="generateRowAgenda()" name="AGENDA"/>
<input type="button" id='submitlink' value="" onClick="removeRow()" name="AGENDA"/>
<script language="">
function generateRowAgenda() {
var temp ="<p><input type='text' class='textinputagenda' name='MM_AGENDA[]'></p>";
var newdiv = document.createElement('AGENDA');
newdiv.innerHTML = temp;
var yourDiv = document.getElementById('AGENDA');
yourDiv.appendChild(newdiv);
}
function removeRow(){
yourDiv.appendChild.deleteRow(newdiv);
}
</script>
<br>
<div id="AGENDA" align="center"></div>
That would be:
function removeRow(element){
element.parentNode.removeChild(element);
}
removeRow(newDiv);
That is, the removeChild method actually belongs to the element's parent, so you need to reference the parent first (by using parentNode) and then call the method removeChild over the newDiv element.
Also, you have two elements with the same id: submitlink. And that is not good.
If you rename your element, you could add a listener that call the removeRow function.
<input type="button" id='doremove' value="" onClick="removeRow()" name="AGENDA"/>
(Now the id is doremove)
Now do something like this to make the removeRow function to be executed on click:
document.getElementById('doremove').addEventListener('click', function(e) {
removeRow(newDiv);
});
Here is an example on jsfiddle: http://jsfiddle.net/5TmQC/
You'll notice that remove only works with one agenda item. You want to work with several agendas?
Try this then: http://jsfiddle.net/5TmQC/1/
Almost same code, but this one can delete several, in order by using pop()