The Vue input fields (v-text-field) automatically show the previous input when I click on the text field.
Actually, I delete the value from all inputs with the blue button (which also creates additional random tables). The reset works, but why is the value displayed again?
What I tried: I used v-form to delete the value of all inputs with this.$refs.form.reset(); (see https://vuetifyjs.com/en/components/forms/#misc). That didn't solve the problem.
This is how it looks:
GIF
HTML
<v-simple-table>
<template v-slot:default>
...
<tbody>
<tr :class="verb.ich">
<td>
<v-text-field label="ich"></v-text-field>
</td>
</tr>
<tr :class="verb.du">
<td>
<v-text-field label="du"></v-text-field>
</td>
</tr>
<tr :class="verb.er">
<td>
<v-text-field label="er/sie/es"></v-text-field>
</td>
</tr>
...
...
</tbody>
</template>
</v-simple-table>
...
<v-btn #click="next"> Weiter </v-btn>
Method next():
next: function () {
var allInputs = document.getElementsByTagName("input");
for (var i = 0, max = allInputs.length; i < max; i++) {
allInputs[i].value = "";
allInputs[i].style.backgroundColor = "white";
}
...
}
Solution
(with the help of Michael LevĂ˝'s answer)
v-model
<td>
<v-text-field v-model="verb.val[0]"></v-text-field>
</td>
...
reset value
for (var i = 0; i < this.verben.length; i++) {
this.verben[i].val[0] = "";
...
}
in each array
verben: [
{
val: ["", "", "", "", "", ""],
...
}
v-text-field has some internal variable which saves the value when you are typing. Your next() method doesn't clear this value but only sets value of the rendered <input> control - when you click on the control, v-text-field is re-rendered and shows the value stored inside
Your next method is an example how not to work with form items in Vue. Vue is data driven. For each control you need a variable in data() section of your component and use v-model to bind the control to that variable.
Now when you want to change the value programmatically, you change the data in your component (not in the DOM)
Form Input Bindings
Vuetify form controls works same way - examples
Related
I am creating the table with the looping, and if the loop value matches with the particular character I want to set the Reactive Form default value of Repeat else I want to set the empty value in the Reactive Form. Following Is my code
typescript
rDefault:string = "";
create(){
let form = this.fb.group({
rp:[this.rDefault]});
return form;
}
template
<tbody *ngFor="let c of ch.vl; let i = index" [formGroupName]="i">
<tr class="ui-widget-content ui-datatable">
<td>
{{rpm[ofs+i].label}}
</td>
<td>
<p-dropdown formControlName="rp" [options]="rpm[ofs+i].label =='REPEAT'? compOpt : []" appendTo="body" [disabled]='rpm[ofs+i].label == "REPEAT"?false:true'></p-dropdown>
</td>
</tr>
</tbody>
If {{rpm[ofs+i].label}} this value is equal to "Repeat" I want to set the default form values as "Repeat", else empty value. How can I achieve this?
Got the solution for my question but I don't know this is the correct way or not?
In the every loop I am calling the another method from [option] and setting the value in that method.
template file
<td>
<p-dropdown formControlName="rp" [options]="changeDefault(rpm[ofs+i].label)" appendTo="body" [disabled]='rpm[ofs+i].label == "REPEAT"?false:true'></p-dropdown>
</td>
Updated TS file
rDefault:string = "";
//this method will change value of rDefault
changeDefault(val){
this.rDefault = val == "REPEAT" ? "REPEAT" : "";
let rtnar = 'REPEAT'? this.compOpt : [];
return rtnar;
}
//
create(){
let form = this.fb.group({
rp:[this.rDefault]});
return form;
}
What's the best way to accomplish something like this. I have a credit note that I wish to edit the unit price. I'll pull in the invoice details then the user can edit/change the unit price. When I do this, all fields are changing. Somehow I need to put this in its own state? I have no idea how.
Parent Component using React.createClass:
line_items = [
{
id: 1,
unit_amount: "250.00",
...
},
{
...,
unit_amount: "20.00",
...
}
]
//render
<LineItems items={this.props.line_items} />
Child Component using React.createClass:
getInitialState(){
return{
creditedValues: []
}
},
_valueChange(e){
this.setState({creditedValues: this.state.creditedValues.concat([e.target.value])})
},
render(){
var items = this.props.items;
if (items.length !== 0) {
var lineItems = items.map(function(l){
return(
<tr key={l.id}>
<td data-label="Payment">{l.description}</td>
<td className="credit-note-qty" data-label="Issue Date">{l.quantity}</td>
<td className="credit-note-up" data-label="Amount">{l.unit_amount}</td>
<td className="credit-note-amt" data-label="Period">
<input type="number" value={this.state.newValue} onChange={this._valueChange} />
</td>
</tr>
)
}.bind(this));
};
return(<tbody>{lineItems}</tbody>)
}
Before I changed to concat, when I change a value, all values change at the same time. How to prevent this?
Edit:
I have updated the _valueChange with:
_valueChange(i, e){
e.preventDefault();
var obj = this.props.creditNote.line_items;
var num = obj.find(p => i === p.id);
num.unit_amount = e.target.value;
this.setState({creditedValues : num});
}
Input now changes to:
<input type="number" value={this.state.newValue} onChange={this._valueChange.bind(this, l.id)} />
Now I have exactly what I want but there is a but. If the user update another input field, it replace the entire state. True, as the code I have replaces the entire state. If the user update two or more fields, how to keep the new objects?
I'm doing angular table filter. My first attempt is to try to include it on the header. The icon show on the wrong position and when I focus the text box the sorting change.
So I move the box to the <tfoot> but it looks like it is somehow disabled because even when I change the content on text box the ng-model="filter_id" it doesnt change.
The outside text box, works perfect.
In the picture:
press 1 on <tfoot input text>: the textbox change to 1, event updateFilteredList trigger but filter_id is empty, neither <p input text> nor {{filter_id}} change.
press 1 again on <tfoot input text>: the textbox change to 11, same behaviour
press 1 on <p input text>: the textbox change to 1, {{filter_id}} update to 1 and updateFilteredList also receive filter_id=1 (even when filter for 1 return the same list);
EDIT
I found out when app start if I type on the external text box the interal get updated. But when I type a single char on the internal one, stop the updates.
<div ng-controller="eventCtrl">
<table class="table table-striped" at-table at-list="filteredList"
at-config="config" at-paginated>
<thead></thead>
<tfoot>
<tr>
<th><input type="text" ng-change="updateFilteredList()"
ng-model="filter_id" style="width: 50px" />
</th>
</tr>
</tfoot>
<tbody></tbody>
</table>
<at-pagination at-config="config" at-list="filteredList"></at-pagination>
<p>
<input type="text" ng-change="updateFilteredList()"
ng-model="filter_id" />
{{filter_id}}
</p>
</div>
I'm using this plug-in for my table. Is this a bug or maybe is some behaviour of the plugin? Is there a way to detect if plug-in overwrite some events?
Controller:
app5.controller('eventCtrl', ["$scope", "$filter", "$http" , function ($scope, $filter, $http) {
$scope.cars = [
{ Car_ID: 1, X: null, Y: null, RoadName: null, Azimuth: null, DateTime: null, Adress: null }
];
$scope.filteredList = $scope.cars;
$scope.filter_id = "";
$scope.updateFilteredList = function () {
console.log('filter_id: ' + $scope.filter_id);
$scope.filteredList = $filter("filter")($scope.cars, $scope.filter_id);
console.log('filteredList.length:' + $scope.filteredList.length);
};
Probably there is a better way, but this is working. Because the event was already triggering, I just get the value from the DOM before run the filter
$scope.filter_id = $('#filter_id').val();
Function
$scope.updateFilteredList = function () {
$scope.filter_id = $('#filter_id').val();
console.log('filter_id: ' + $scope.filter_id);
$scope.filteredList = $filter("filter")($scope.cars, $scope.filter_id);
console.log('filteredList.length:' + $scope.filteredList.length);
};
I have an table element where the declaration is as follows
<euro-table id="euroTable" number-visible-rows="10">
<euro-column title="Id" type="text" key="Id"></euro-column>
<euro-column title="Descripcion" type="text" key="Descripcion"></euro-column>
<euro-column title="Abreviatura" type="text" key="ShortName"></euro-column>
<euro-column title="Tipo" type="object" key="FeeType" objectkey="Descripcion"></euro-column>
<euro-column title="Monto($)" type="text" key="Monto"></euro-column>
<euro-column title="Cobrar a" type="array" key="NivelesEscolares" objectkey="Descripcion"></euro-column>
</euro-table>
Data is added using javascript after an iron-ajax request. Everything works as it should work, except for one thing: when I use dom-repeat to bind added data I use <dom-if> template because depending on the type of column, I must access and display the corresponding information. The code I use to do that is the following:
<template is="dom-repeat" items="{{visibleRows}}" id="tableRow" as="row">
<tr on-tap="rowSelected" class$="{{getClass(item.active)}}">
<template is="dom-repeat" items="{{headers}}" id="tableRow2" as="column">
<template is="dom-if" if="{{getType(column.type, 'object')}}">
<td>
<li>{{getObjectValue(column,row)}}</li>
</td>
</template>
<template is="dom-if" if="{{getType(column.type, 'array')}}">
<td>
<template is="dom-repeat" items="{{getDataArray(column,row)}}">
<li>{{getObjectValue(column,row)}}</li>
</template>
</td>
</template>
<template is="dom-if" if="{{getType(column.type, 'text')}}">
<td>{{getValue(column,row)}}</td>
</template>
</template>
</tr>
</template>
So my problem is that I can not display the information correctly and I think the reason is for the use of dom-repeat. My information is displayed as follows:
The information is out of the table, I'm a checking my getType function but I think its ok. Any idea about to fix my bug? Thanks!
After I had researched a little more, I found here that my problem was a bug of polymer. To solve it, is necessary to change two functions in Polymer.html file. The functions are _wrapTextNodes and _showHideChildren. I will leave the functions here just in case anyone have the same problem.
_wrapTextNodes: function(root) {
// wrap text nodes in span so they can be hidden.
for (var n = root.firstChild; n; n=n.nextSibling) {
if (n.nodeType === Node.TEXT_NODE && n.textContent.trim.length) {
var s = document.createElement('span');
root.insertBefore(s, n);
s.appendChild(n);
n = s;
}
}
},
_showHideChildren: function() {
var hidden = this._hideTemplateChildren || !this.if;
if (this._instance) {
var c$ = this._instance._children;
for (var i=0; i<c$.length; i++) {
var c = c$[i];
if (c.nodeType !== Node.TEXT_NODE) {
c.style.display = hidden ? 'none' : '';
c._hideTemplateChildren = hidden;
}
}
}
},
Here you can find dom-if.html file.
To elaborate on the title, what I am trying to achieve is the following.
I am building an interactive table component in Ember. Here is the stripped template:
<table>
<thead>
<tr>
{{#each header in headers}}
<th>{{header}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each row in rows}}
<tr>
{{#each header in headers}}
<td>{{input value=row.[header]}}</td> <!-- can I bind to row.header here somehow? -->
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
I want each input field for a specific row and specific column, to be bound to that row's row object, specifically to a property named the way the header column is named.
Essentially, I want to use the value of the header variable to bind a property in the row object called by that value (If current header has value 'title' then I want to bind to row.title)
Here is an example of how I initialize these objects:
var headers = ['title','description'];
var rows = [],
row = {};
for(var i = 0; i < headers.length; i++) {
row[headers[i]] = ''; // this line does similar thing to what I am trying to achieve
}
rows.push(row);
/* This gives
rows = [
{
title: '',
description: ''
}
]
*/
After researching, I found this in the Handlebars documentation that says I can access properties like this:
{{#each articles.[10].[#comments]}}
...
{{/each}}
Which is, according to the docs, pretty much the same as:
articles[10]['#comments']
However, using:
rows.[header]
doesn't work for me because it tries to literally access the 'header' property of the rows object (i.e. rows.header) and not the value contained in the header variable.
You can always extend the textfield component and at least get the value you are looking for.
App.DynamicInputComponent = Ember.TextField.extend({
row: null,
col: null,
value: function(){
var row = this.get('row');
var col = this.get('col');
return row[col];
}.property('row', 'col')
});
Then, in your template you can do:
<table>
{{#each item in model}}
<tr>
{{#each col in columns}}
<td> {{ dynamic-input type='text' row=item col=col }} </td>
{{/each}}
</tr>
{{/each}}
</table>
(Partially) working solution here
This functionality can now easily be achived in Ember (since 1.13) using the new and awesome inline helpers mut and get:
The way to achieve this is in two basic steps:
Use get to dynamically lookup a property from r named as whatever the value of h is at that each iteration. For example, if h = 'title', then this would return r['title'].
Use mut to specify that this extracted value is mutable by our input component (specifically its value property).
This is how the whole each looks:
{{#each rows as |r|}}
<tr>
{{#each headers as |h|}}
<td>
<input onkeyup={{action (mut (get r h)) value="target.value" }}>
</td>
{{/each}}
</tr>
{{/each}}
Detailed example on Ember Twiddle