Issue while binding the dynamic form field with v-model - javascript

I am trying to prepare a dynamic form that generates a form based on the columns present in the database table. Each form field is based on the columns present in the database table.
import { mapGetters } from 'vuex'
export default {
name: 'NewRecord',
data () {
return {
columnName: {
}
}
},
computed: {
...mapGetters(['columns'])
},
}
I am using mapGetters to pull columns from state. Structure of columns is :
[
{ "name": "id", "type": "varchar(255)", "label": "Id", "align": "left", "field": "id", "sortable": true, "__iconClass": "q-table__sort-icon q-table__sort-icon--left", "__thClass": "text-left sortable", "__tdClass": "text-left" },
{ "name": "username", "type": "varchar(80)", "label": "Username", "align": "left", "field": "username", "sortable": true, "__iconClass": "q-table__sort-icon q-table__sort-icon--left", "__thClass": "text-left sortable", "__tdClass": "text-left" }
{.......
.......}
]
I am generating form via following code:
<div class='columns row'>
<div class='col-3'
v-for='col in columns'
:key='col.name'>
<input standard type='text' :label='capital_letter(col.label)'
v-model="col.name"></input>
</div>
</div>
How can I use that v-model="col.name" in data property for two way binding.
Any help will be very much appreciated.

To manage two way binding to the state in a loop, I think you'll need to avoid v-model. Instead, you probably want to call a mutation on the change event. So your input will look like this:
<div class='col-3' v-for='(col, index) in columns' :key='col.id'>
<input
standard
type='text'
:label='capital_letter(col.label)'
:value="col.name"
#input="changeColumn($event, index)"
>
</div>
Notice that we're passing in the index from the loop, so you can target the appropriate column in the method below.
Then, in methods:
changeColumn(event, index) {
this.$store.commit('updateColumn', {i: index, value: event.target.value})
}
Then, using the payload object, change the appropriate column in your store (put this in mutations in the store):
updateColumn(state, payload) {
state.columns[payload.i].name = payload.value
}
Remember that in your view, you'll also need "...mapMutations(['updateColumn'])" etc...
You can read more about v-model with the state in the Vuex docs:
https://vuex.vuejs.org/guide/forms.html

Related

How can I update the button clicked in bootstrap-vue

I am using Bootstrap-Vue to display a table, and I've added in an extra column with an update button using vue-slot. I have this displaying fine, and I have a method being called when you click the button. Within that method I can access all the information on the item however I can't seem to find a way to access the button. I want to disable it and change the contents of it. How can I access the button element? I have created a codepen example here that shows what I have set up and need to do.
HTML
<div id='app'>
<div>{{ this.output }}</div>
<b-table hover head-variant="dark"
id="pages-table"
:items="items"
:fields="fields">
<template slot="actions" slot-scope="data">
<button class="btn btn-dark" #click="update(data)">Update</button>
</template>
</b-table>
</div>
JavaScript
new Vue({
el: "#app",
data: {
output: null,
items: [
{
id: 1,
name: "Tony"
},
{
id: 2,
name: "John"
},
{
id: 3,
name: "Paul"
}
],
fields: [
{
key: "id",
label: "ID",
sortable: true
},
{ key: "name" },
{ key: "actions" }
]
},
methods: {
update(data) {
// I need to disable the button here
this.output = data;
data.item.name = "Dave";
}
}
});
You could add a dynamic ref to the button
<button class="btn btn-dark" #click="update(data)" :ref="'btn' + data.index">Update</button>
And then just access the button by that ref
this.$refs["btn" + data.index].disabled = true
This is a codepen with the example
https://codepen.io/vlaem/pen/gNjGQE
Instead of the index you could also use the id property of your data to create the ref (data.item.id)
Though personally this doens't feel right, I think it would be better if we could track the status of all the buttons on the same or a different array, maybe like in the following example
https://codepen.io/vlaem/pen/GbBMLe

Trying to bind the query to the json string for every request in to the api in Vue.js

I'm trying to get results from an api based on the user search box. When the user enters a value 'en' or 'de'. They should get the result from that search. I need to bind the user input into my query string. This works when I manually code the country into the template, but not when I bind the value into the string after the user inputs a value for the second time. The 'get' request that uses the user input value 'query' works fine. But not when I bind this a second time
I want to be fit to access
results[i].query.name
But '.query' is not working when I query the data unless I enter the value manually '.en'
I have a json file that looks like the following
[
{
"en": {
"name": "testone",
"id": 5363289,
"location": "messages_en.properties1"
},
"de": {
"name": "testonede",
"id": 5363289,
"location": "messages_en.properties2"
}
},
{
"en": {
"name": "test2",
"id": 5363289,
"location": "messages_en.properties3"
},
"de": {
"name": "test2de",
"id": 5363289,
"location": "messages_en.properties4"
}
}
]
Below is my index.html vue.js template
<div id=#app>
<input type="text" v-model="query" placeholder="Choose Language" />
<div class="medium-6 columns">
<a #click="getResult(query)" class="button expanded">Retrieve</a>
</div>
<template v-for="(result, i) in results">
<div class="card" style="width: 20rem; display:inline-block;">
<div class="card-block"></div>
<p> {{results[i].query}} </p>
<!-- works when I manually code in the 'en' query but when ran with 'query' it returns an error 'Cannot read property 'name' of undefined"' second time it returns that the value is -->
<!-- <p> {{results[i].en.name}} </p> -->
<!-- <p> {{results[i].query.name}} </p> -->
</div>
</template>
</div>
Vue.js
el: '#app',
data () {
return {
search: '',
query: 'en',
results: '',
title: '',
items: '',
section: ''
}
},
methods: {
getResult(query) {
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
console.log(this.results);
});
},
You need to use bracket notation to access a property using a param, so:
results[i][query].name
The second issue is that results[i][query] will be undefined until the async call has completed, so you will need to check that the property is not undefined or use a boolean flag. So, to check that it is not undefined you could do something like:
<p v-if="!!results[i][query]">{{results[i][query].name}}</p>
<p v-else>Loading...</p>
Here's a simplified JSFiddle for that: https://jsfiddle.net/4w3dxm22/
Or you could just use a dataLoaded flag:
new Vue({
el: '#app',
methods:{
getResult(query) {
this.dataLoaded = false; // set dataLoaded to false
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
this.dataLoaded = true; // Data has loaded so set dataLoaded to true
});
},
data: {
dataLoaded: false
}
})
Then you can do:
<span v-if="dataLoaded">{{results[i][query].name}}</span>
<span v-else>Loading Data...</span>
Here's the simplified JSFiddle for that: https://jsfiddle.net/99ydx82u/

how to datalink the index with the json object?

I have the following JSON object which maintain the sequence in it.
var sample={
"sample": [
{
"example": [
{
"sequence": 1,
},
{
"sequence":2
},
{
"sequence":3
}
]
},
{
"example": [
{
"sequence": 1,
}
]
}
]
};
$.templates("testingTemplate", "#testingSection");
var html=$.link.testingTemplate("#htmlHolder", sample);
$("#insert").click(function(){
var childIndexVal=parseInt($("#childIndex").val());
var x= {
"sequence": childIndexVal+1,
"xxx":"yyy"
};
var parentIndexVal=parseInt($("#parentIndex").val());
$.observable(sample.sample[parentIndexVal].example).insert(childIndexVal,x);
console.log(sample);
});
.parentHolder
{
border:1px solid red;
padding:5px;
margin:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.80/jsviews.js"></script>
<script id="testingSection" type="text/x-jsrender">
{{if sample && sample.length}}
{^{for sample }}
<div class="parentHolder">
{^{for example }}
<div><span data-link="#index+1" ></span>
<input type="text" value="{{:sequence}}" id="sequence" data-link="#index+1"></div>
{{/for}}
</div>
{{/for}}
{{/if}}
</script>
<div id="htmlHolder">
</div>
<div class="messages">
</div>
<span>Parent Index:</span><input type="text" id="parentIndex"/>
<span>Child Index:</span><input type="text" id="childIndex"/>
<button id="insert" >Insert</button>
so whenever I insert the object it should update the squence accodring to the index.
In the given example, if I am inserting an object into the 1st index of example in sample[0].(consider example array index starts from zero).
So when I am inserting one object into first parameter, the sequence in the remaining object should updated according to the index.
How can I achieve it.
give parent Index as 0 and child index as 1.
expected output,
Note: extra "xxx":"yyy" for differntiation purpose.
var sample={
"sample": [
{
"example": [
{
"sequence": 1,
},
{
"sequence": 2,
"xxx":"yyy"
}
{
"sequence":3
},
{
"sequence":4
}
]
},
{
"example": [
{
"sequence": 1,
}
]
}
]
};
Update: : tried with linkTo also.
<input type="text" data-link="linkTo=sequence #index+1" ></div>
Still not getting the expected output.
Thanks in advance.
Neither JsRender nor JsViews will modify the JSON data that they are rendering. This is by design: there are no side-effects on the data...
On the other hand, you can write code to create side-effects on the data, or you can use two-way binding so a user can modify data values. But two-way binding will only change the targetted data value, not other values elsewhere, and will do so only when the user triggers a change event on that <input> for example.
In your case you want any observable changes to an examples array to trigger changes to all examples in the array such as to ensure the example.sequence value is always equal to the index+1 for each example. In that case you have to write code to do that.
One way you can achieve that is to add the following code using observeAll:
$.observable(sample).observeAll(function(ev, eventArgs) {
if (ev.type==="arrayChange" && ev.data.observeAll.path().slice(-7)==="example") {
$.each(ev.currentTarget, function(i, item) {
$.observable(item).setProperty("sequence", i+1);
})
}
});
That will ensure that the sequence value stays in sync not only for insert, but also when items are removed or for changes in the the order of items, etc.

Using Knockout.js mapping plugin with an array in json

I am trying to set up an example in which a series of news items will be passed in using ajax in a json format. At the moment I am just using a function to simulate returned data.
Here is the jsfiddle: http://jsfiddle.net/c8b4naL5/
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
<span data-bind="foreach: { data: newsItems, as: 'item' }" >
<!-- <span data-bind="foreach: { data: items, as: 'item' }"> -->
<div class="news-item">
<span data-bind='text:item.title'></span>
</div>
</span>
<script type="text/javascript">
function NewsItemsCall(){
return {
newsItemsFromCall: [
{title:'First Title From call'},
{title:'Second Title From call'}
]
}
}
function NewsItem(newsItemsCall){
var map = ko.mapping.fromJS(newsItemsCall);
return map;
}
var viewModel = {
newsItems:ko.observableArray([new NewsItem(new NewsItemsCall())])
}
ko.applyBindings(viewModel);
</script>
The ko.toJSON displays the following:
{
"newsItems": [
{
"newsItemsFromCall": [
{
"title": "First Title From call"
},
{
"title": "Second Title From call"
}
],
"__ko_mapping__": {
"ignore": [],
"include": [
"_destroy"
],
"copy": [],
"observe": [],
"mappedProperties": {
"newsItemsFromCall[0].title": true,
"newsItemsFromCall[1].title": true,
"newsItemsFromCall": true
},
"copiedProperties": {}
}
}
]
}
At this point I am just trying to get it to work to display the data in the template. Any insights would be appreciated. Thanks in advance.
Well the modification required could be approached from either the data side or the client side. At face value, your view isn't matched up to the data due to newsItems containing an array of newItemsFromCall. If the data is in the correct format, then just add another foreach binding.
Modifying the data
NewsItemsCall could return an array instead of an object
be aware of the return of the mapping call depending on how you will be using that value elsewhere
Modifying the UI
<span data-bind="foreach: { data: newsItems, as: 'item' }" >
<div data-bind='foreach: item.newsItemsFromCall'>
<span data-bind='text: title'></span>
</div>
</span>
Modified fiddle with changes to the data structure. I also included an alternate approach that maps the fromJS call directly as a viewmodel.
Example of mocking json calls in a fiddle.

Knockout: Select in simpleGrid example

I am using the knockout simple grid found here:
http://knockoutjs.com/examples/grid.html
I want to be able to add a select into the grid, which has a data-bind attribute assigned to an object array in my vm.
So I have added another column from the example:
this.gridViewModel = new ko.simpleGrid.viewModel({
data: this.items,
columns: [
{ headerText: "Item Name", rowText: "name" },
{ headerText: "Sales Count", rowText: "sales" },
{ headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } },
*{ headerText: "Select", rowText: function (item) { return "<select data-bind=\"options:items, optionsText: 'name', optionsValue: 'name'\"></select>" } }*
],
pageSize: 4
});
And changed the text attribute to html within the control:
<td data-bind=\"*html*: typeof rowText == 'function' ? rowText($parent) : $parent[rowText] \"></td>\
The Selects appear, but not populated with data from my object array.
JSFiddle found here:
http://jsfiddle.net/vwj2p/1/
(I have pasted in the code from the simple grid above as I made a change to the simplegrid code).
{ headerText: "Select", rowText: function (item) { return "<select data-bind=\"options:$root.items, optionsText: 'name', optionsValue: 'name'\"></select>" } }
I'm assuming each item object doesn't have an items property and you're trying to reference the viewModel's items array? If so, change your code to the above.
This still won't work, however. If you look at the html binding documentation, you'll see that it's only going to spit out static html. During the binding process when this is all getting rendered, KO doesn't applyBindings to the generated HTML.
I tried playing around with the code a bit to try and do ko.applyBindingsToDescendants(viewModel, {td element}), where {td element} is a reference to the parent element with the html binding on it, when the items observableArray updated but that didn't seem to do anything.
Bottom line, I don't think you're going to get this to work without doing a lot of plumbing work to the simpleGrid. It is just a simple grid, after all.

Categories

Resources