How to bind dynamically added form input with vue.js - javascript

I want to render some forms and bind them to Vue.
forms are listed on json Like this.
formslist:[
"<input type='text' name='item1' v-model='item1'>",
"<input type='text' name='item2' v-model='item2'>",
]
I could render them but couldn't bind.
Template is this.
<div v-for="form in formslist">
<div v-html="form"></div>
</div>
Later, I understood that html added by "v-html" can't use data bindings.
But then I don't have no ideas how to do this.
Would anyone teach me good solution?
Sorry for lack of words.
I wrote code like this but "item1" and "item2" are not binded.
js:
new Vue({
el: '#app',
data() {
return {
formData: {},
formslist:
// this list is got by ajax as neccessary
{
"item1": "<input type='text' name='item1' v-model='formData.item1'>",
"item2": "<input type='text' name='item2' v-model='formData.item2'>",
}
};
},
methods: {
submit() {
alert('formdata: ' + JSON.stringify(this.formData));
// submit data to backend
// e.g. axios.post(...).then(...).catch(...);
}
}
})
html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<form name="myForm" #submit.prevent="submit">
<div class="form-group">
<label>Firstname</label>
<input class="form-control" v-model="formData.firstName" type="text">
</div>
<div class="form-group">
<label>Lastname</label>
<input class="form-control" v-model="formData.lastName" type="text">
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" v-model="formData.description"></textarea>
</div>
<div class="form-group" v-for="(form, name) in formslist">
<label>{{name}}</label>
<div v-html="form"></div>
</div>
<button type="submit">
Submit
</button>
</form>
</div>

Vue.js is model/data oriented - have a look at the docs to learn more about it.
So create a model where you're storing your form data and then use that model to submit the data or do anything you like with the data.
Please have a look at a basic example below or at this fiddle.
Update 01.07.2017
OK, now I understand what you're trying to do.
Please find my updated code below or at the fiddle (same link).
You could also use a form generator like vue-form-generator. It's doing the same as my demo - just fewer coding required.
How does it work?
The form will be rendered with a template that is getting the data from a model that you can get from the server.
The type of the input can't be bound with :type because that's not supported with v-model - there was an error message when I tried that. So it's a bit more to write but it should be clear how this works. Add similar template tags for other types.
To further improve the model, you could also store the top-level of your form, so you can customize that display class as well. e.g. something like:
{
formName: 'myForm',
className: 'form-horizontal',
items: [...] // same array as before
}
If you need to group some inputs or display them differently you can add more properties to the model and more logic to the template.
const formRenderModel = [{
type: 'text',
modelName: 'firstName',
className: 'form-control',
labelText: 'First name',
required: true
}, {
type: 'text',
modelName: 'lastName',
className: 'form-control',
labelText: 'Last name',
required: true
}, {
type: 'text',
modelName: 'description',
className: 'form-control',
labelText: 'Description',
required: true,
pattern: '.{10,}', // minimum 10 characters
validationMessage: 'Minimum 10 characters required'
}];
new Vue({
el: '#app',
data() {
return {
formData: {}
};
},
created() {
// later axios.get('/form-render).then(...).catch(...);
// now just a constant
this.formRenderModel = formRenderModel;
},
methods: {
submit() {
console.log('formdata', this.formData);
// submit data to backend
// e.g. axios.post(...).then(...).catch(...);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<form name="myForm" #submit.prevent="submit">
<div v-for="inputModel in formRenderModel">
<label>{{inputModel.labelText}}</label>
<template v-if="inputModel.type == 'text'">
<input type="text" v-model="formData[inputModel.modelName]"
:class="inputModel.className"
:pattern="inputModel.pattern"
:title="inputModel.validationMessage"
:required="inputModel.required" />
</template>
<!-- add more templates for radios, checkboxes, .. -->
</div>
<button type="submit">
Submit
</button>
</form>
</div>

Related

Problem incorporating data got from API vue.js

After get API call with a string input attached, I get the result alright. The problem I have is incorporating it to my frontend. I have tried a lot of solutions that I found online but nothing working and cannot understand. So far I have done this:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type = text/javascript src = https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/vue.resource/1.0.3/vue-resource.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<form action="http://127.0.0.1:1880/" target="_self">
<label for="request"><strong>Please insert the input here:</strong></label><br>
<input type="text" id="request" name="input"><br>
<button v-on:click="getOverview($event)">Submit</button>
</form>
<h1 id="results" v-for="overview in overview">
{{overview}}
</h1>
<script type = text/javascript >
new Vue({
el: "#results",
data() {
return {
overview: []
}
},
methods: {
async getOverview(event) {
try {
const {data:{json:{sub_regions}}} = await axios.get('http://127.0.0.1:1880/');
console.log('results data', sub_regions);
this.overview = sub_regions;
}
catch (error) {
console.log(error);
return [];
}
}
},
created(){
this.getOverview()
}
})
</script>
</body>
</html>
I am a bit lost with javascript as I am new to it, all kinds of help are welcome,
Thank you in advance! :)
EDIT: the file I get from the API is JSON
Looks as though you are trying to call a method outsides of the vue app itself.
You have the el: "results" but you are trying to invoke a vue method within your button outside of its context.
Try something like this:
<div id="results">
<form action="http://127.0.0.1:1880/" target="_self">
<label for="request"><strong>Please insert the input here:</strong></label><br>
<input type="text" id="request" name="input"><br>
<button v-on:click="getOverview($event)">Submit</button>
</form>
<h1 v-for="overview in overview">
{{overview}}
</h1>
</div>
Some problems here...
You're including Vue twice (https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js and https://unpkg.com/vue/dist/vue.js). Don't do that
You cannot use a v-for directive on your root Vue element
Your form is outside your Vue root so you won't be able to use v-on
Your submit button will submit the form normally. There's nothing stopping that from happening
Your input value is never used. You should try binding it to a data property
Change your HTML to
<div id="results">
<form action="http://127.0.0.1:1880/" #submit.prevent="getOverview">
<label for="request">
<strong>Please insert the input here:</strong>
</label>
<br>
<input type="text" id="request" name="input" v-model="input">
<br>
<button type="submit">Submit</button>
</form>
<h1 v-for="item in overview">
{{ item }}
</h1>
</div>
and in your JS, replace data with
data: () => ({
overview: [],
input: ''
})
Then you can use this.input if you ever need to get the value the user types in.
Here's an example using a placeholder API
new Vue({
el: "#results",
data: () => ({
overview: [],
input: ''
}),
methods: {
async getOverview ($event) {
let url = 'https://jsonplaceholder.typicode.com/users'
if (this.input) {
url += `/${encodeURIComponent(this.input)}`
}
try {
const { data: sub_regions } = await axios.get(url)
console.log('results data', sub_regions);
this.overview = Array.isArray(sub_regions) ? sub_regions : [ sub_regions ]
} catch (error) {
console.log(error);
this.overview = []
}
}
},
created() {
this.getOverview()
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<div id="results">
<form action="http://127.0.0.1:1880/" #submit.prevent="getOverview">
<label for="request">
<strong>Please insert the user ID here:</strong>
</label>
<br>
<input type="number" id="request" name="input" v-model.number="input">
<br>
<button type="submit">Submit</button>
</form>
<h1 v-for="item in overview">
{{ item.name }}
</h1>
</div>
Other notes:
Avoid using the same variable name for your array and iterable.
Bad - v-for="overview in overview"
Good - v-for="thing in things"
VueResource is long dead. Avoid using it or at least update to the latest version (1.5.1)

w2ui ignoring value in html form

w2ui is ignoring the value on the input tag.
How do I get it to use the value?
It reads the selects just fine.
jsfiddle.net
<div id="form" style="width: 750px;">
<div class="w2ui-page page-0">
<div class="w2ui-field">
<label>First Name:</label>
<div>
<input name="first_name" type="text" value="John" />
</div>
</div>
</div>
<div class="w2ui-buttons">
<button class="w2ui-btn" name="reset">Reset</button>
<button class="w2ui-btn" name="save">Save</button>
</div>
</div>
$(function() {
$('#form').w2form({
name: 'form',
url: 'server/post',
fields: [
{ field: 'first_name', type: 'text', required: true }
],
actions: {
reset: function() {
this.clear();
},
save: function() {
this.save();
}
}
});
});
If I have to write JavaScript. How would I access the fields?
You can access the value of the input with form.record.
In your case w2ui.form.record.first_name (where form is the name of your w2form).
In your save event you can access the record with this.record, e.g.:
save: function() {
console.log(this.record);
console.log(this.record.first_name);
this.save();
}
additionally, As documentation of w2ui , you can set the value of input field
w2ui.Your_Form_Name.record['You_InputField_Name'] = The_New_value;
and then call form refresh to update both html and object, but this has issue where it clear the previous drop list select , so do set the new value with code as the following to avoid use refresh and keep pre-select in drop list
$('#InputFiled_Name').val(The_New_Value);
w2ui.Your_Form_name.record['Your_InputField_Name'] = The_New_Value;

Checkbox array in Vue Js

I have an array of checkboxes, coming from a main system object where I store all system setting. (called getSystem{}).
In this form, Im accessing a User, which has an array of roles [].
How can I check this array of roles, against the getSystem.user_roles?
I know how to do it normally, in javascript obviously. But what would I put in the checkbox input Vue.js wise?
<b-form-group>
<label for="company">Email Address</label>
<b-form-input type="text" id="email" v-model="user.email" placeholder="Enter a valid e-mail"></b-form-input>
</b-form-group>
// Here i can do user.roles to get the array of roles.
// What can I do to loop through the roles and check the box if it exists in the user roles??
<b-form-group v-for="resource, key in getSystem.user_roles" v-if="getSystem.user_roles">
<label>{{resource.role_name}}</label>
<input type="checkbox" [ what do I put here to compare against user.roles, and check the box if exists??] >
</b-form-group>
This behavior is well documented on the Checkbox binding Docs.
Here a little example emulating your logic
new Vue({
el: '#app',
data: {
user: {
email: 'test#test.com',
roles: [{id: 1, name: 'Client'}]
},
roles: [
{
id: 1,
name: 'Client',
},
{
id: 2,
name: 'Admin',
},
{
id: 3,
name: 'Guest',
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<div>
<label>Email</label>
<input type="text" v-model="user.email" />
</div>
<div v-for="role in roles" :key="role.id">
<label>{{role.name}}</label>
<input type="checkbox" v-model="user.roles" :value="role"/>
</div>
<p>User's selected roels</p>
{{user.roles}}
</div>
<input type="checkbox" v-model="userRoles" :true-value="[]" :value="resource.role_name">
You should add :true-value="[]".
<b-form-group v-for="resource, key in getSystem.user_roles" v-if="getSystem.user_roles" :key="key">
<label>{{resource.role_name}}</label>
<input type="checkbox" v-model="userRoles" :value="resource.role_name" >
</b-form-group>
<script>
data(){
return {
userRoles: []
}
}
</script>
We can use dynamic check box input rendering with the condition to select values from the customized function (in my example selectUsers). In that function, we can write conditions that we need to compare before append to the selected array.
Demo:
This is the full NuxtJs(vue) component with dummy data.
<template>
<v-container fluid>
<p>{{selected }}</p>
<div v-for="user in user_roles" :key="user[0]">
<input type="checkbox"
#click="()=>{selectUsers(user[0])}"
:value="user[0]"
>
{{user[1]}}
</div>
</v-container>
</template>
<script>
import VueLodash from 'vue-lodash'
import lodash from 'lodash'
export default {
data() {
return {
user_roles:[[1,"dd"],[2,"ddd"],[3,"kksse"],[4,"kske"]] ,
selected:[],
};
},
methods: {
selectUsers(id){
//in here you can check what ever condition before append to array.
if(this.selected.includes(id)){
this.selected=_.without(this.selected,id)
}else{
this.selected.push(id)
}
}
}
}
</script>
In my case, as I am server rendering the checkboxes, what worked for me is to replace :value with just value
#foreach($carOptions as $option)
<div class="form-check">
<input class="mx-2 form-check-input cursor-pointer"
type="checkbox"
value="{{$option}}" <------ HERE in order for vue to work I had to change from :value to value
v-model="offer.carOptions">
<label class="custom-control-label cursor-pointer"
for="leading">{{ __($option) }}</label>
</div>
#endforeach
NOTE: The code snippet is a Laravel Blade Templates.

Stop multiple field filling with v-model?

I'm pretty new to Vue.js and while I've figured most of the problems I'm hitting out, I can't get my head around this one.
I'm displaying a list of posts based on API output, and want to have a comments box for each post. Posting to the API is working fine and it adds it without a problem, but because I'm using the same v-model for each input in the loop any text entered is replicated throughout all other matching inputs due to the binding.
<div class="row" v-if="">
<div class="col-md-11">
<input type="text" class="form-control" value="" title=""
placeholder="Add comments here.." v-model="note.note">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-default" style="margin-left: -1.5rem" v-on:click="addNote(task.id)">Add Comment</button>
</div>
</div>
JS:
addNote: function (id) {
if (this.note.note) {
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');
this.$http.post('api/note/create/' + id, this.note).then(function (response) {
this.fetchTaskList();
});
}
}
Screenshot:
Is there a different directive I should be using? Or is there another way around this?
You can do this by using index in v-for and then binding each box to the notes comment like so:
<!-- use index, so we know where we are in the loop -->
<div v-for="(note, index) in notes">
{{ note.note }}
<!-- Bind to the comment at the given index -->
<input v-model="notes[index].comment" />
</div>
Now you just need to set that up in data:
data: {
notes: [
{note: 'note 1', comment: ''},
{note: 'note 2', comment: ''},
{note: 'note 3', comment: ''}
]
}
Here's the JSFiddle: https://jsfiddle.net/efxzmq9s/

ListView with NumericTextBox Data Initialization

I have a listview that I bind manually:
$("#List").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataSource: new kendo.data.DataSource({
data: []
})
});
Initially I bind the list and later I load it with some data:
$("#ActButton").on("click", function(e) {
.
.
var list = $("#List").data("kendoListView");
list.dataSource.data(data);
list.refresh();
In my template being bound I have a control that I want to initialize as a numeric textbox. I was hoping to use data attributes, but it doesn't recognize:
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="#= Amount #" value="" />
How can I initialize the numeric textbox within a listview?
Note: I'm not using MVVM so I was using kendo.bind("body") to wire up the UI initially, but not using data-bind for the additional wire up of data.
I'm previously work on similar problem, i have a dojo example where i have a list view (kind of) and custom template with show/hide textbox and numeric textbox input. Click on the Product Name or Amount will make the textbox or numericTextbox visible, changing the textbox or numericTextbox value will also update the label value as it refering to the same thing on the dataSource.
First of all i'm usually work with kendo observable as vm like
var vm = kendo.observable({
dataSource: new kendo.data.DataSource({
Id: "id",
data: []
}),
act: function(){
var data = [
{id:1, productName: "Item A", amount: 1, isEditName: false, isEditAmount: false },
{id:2, productName: "Item B", amount: 2, isEditName: false, isEditAmount: false },
];
var list = $("#list").data("kendoListView");
list.dataSource.data(data);
list.refresh();
},
toggleProductName : function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditName;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditName",!editable);
},
toggleAmount: function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditAmount;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditAmount",!editable);
},
});
Create the listView and bind the page to vm
$("#list").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataBound: function(e){ kendo.bind($("#list"),vm); },
dataSource: vm.dataSource
});
kendo.bind($("#example"),vm);
Then here goes the html :
<div id="example">
<div id="list"></div>
<button id="act" data-bind="click:act">Act now</button>
</div>
My item Template :
<script type="text/x-kendo-template" id="ItemTemplate">
<tr>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-bind="visible: dataSource.get(#=id#).isEditName, value: dataSource.get(#=id#).productName " style="width:100px"/>
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditName, click: toggleProductName"> close </label>
<label data-id="#=id#" data-bind="invisible: dataSource.get(#=id#).isEditName, click: toggleProductName, text: dataSource.get(#=id#).productName"> #= productName #</label>
</td>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="100" style="width:100px" data-bind="visible: dataSource.get(#=id#).isEditAmount, value: dataSource.get(#=id#).amount" />
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditAmount, click: toggleAmount"> close </label>
<label data-id="#=id#" data-bind="invisible:dataSource.get(#=id#).isEditAmount, click: toggleAmount, text: dataSource.get(#=id#).amount"></label>
</td>
</tr>
</script>
The main thing here is that binding the template with the vm again on databound (so it can access the vm properties as well as updating the value from the input)
I bind the row to their respective record on dataSource
This is just a workaround for me, i hope it could help you as well

Categories

Resources