I am using vuesax 4 and I have select options in my form but each time I select an option it submits my form! how to prevent that of happening?
Code
HTML
<form
class="mt-2"
ref="form"
:model="form"
enctype="multipart/form-data"
>
<vs-select
:key="tags.length"
filter
:label="$t('posts.tags')"
v-model="form.tags"
multiple
>
<vs-option
v-for="tag in tags"
:key="tag.id"
:label="tag.name"
:value="tag.id"
>
{{ tag.name }}
</vs-option>
</vs-select>
<vs-button #click="onSubmit" native-type="submit" gradient>
{{ $t("posts.save") }}
</vs-button>
</form>
SCRIPT
data() {
return {
categories: [],
tags: [],
form: {
name: "",
slug: "",
image: "",
icon: "",
body: "",
online: "",
template: "",
quote: "",
video: "",
tags: [],
categories: [],
metaTags: [],
metaDescription: "",
},
};
},
mounted() {
this.fetchTags();
},
methods: {
fetchTags() {
axios
.get("/api/admin/tags", {
headers: {
Authorization: localStorage.getItem("access_token"),
},
})
.then((response) => {
this.tags = response.data.data;
})
.catch(function (error) {
console.log("error", error);
});
},
onSubmit(e) {
e.preventDefault();
let formData = new FormData();
formData.append("name", this.form.name);
formData.append("slug", this.form.slug);
formData.append("image", this.form.image);
formData.append("icon", this.form.icon);
formData.append("body", this.form.body);
formData.append("online", this.form.online);
formData.append("template", this.form.template);
formData.append("quote", this.form.quote);
formData.append("video", this.form.video);
formData.append("tags", this.form.tags);
formData.append("categories", this.form.categories);
formData.append("metas", this.form.metaTags);
formData.append("metas", this.form.metaDescription);
axios
.post("/api/admin/posts/add", formData, {
headers: {
Authorization: localStorage.getItem("access_token"),
},
})
.then((res) => {
this.$router.push({ name: "adminPosts" });
this.form = {
name: "",
slug: "",
image: "",
icon: "",
body: "",
online: "",
template: "",
quote: "",
video: "",
tags: [],
categories: [],
metaTags: [],
metaDescription: "",
};
const noti = this.$vs.notification({
position: "top-right",
color: "success",
icon: "<i class='fas fa-check'></i>",
title: "Done!",
text: res.data.message,
});
})
.catch((error) => {
var errors = error.response.data;
let errorsHtml = "<ol>";
$.each(errors.errors, function (k, v) {
errorsHtml += "<li>" + v + "</li>";
});
errorsHtml += "</ol>";
const noti = this.$vs.notification({
position: "top-right",
color: "danger",
icon: "<i class='fas fa-bug'></i>",
title: "Oops!",
text: errorsHtml,
});
});
},
}
Any idea?
In the Vuesax 4 <vs-select> docs, there is no <form> tag and perhaps you shouldn't have one either. (In a SPA, you don't typically submit the form using the browser's built in form handling anyway, and you are not using it like that either, nor the ref or the model.)
If you still want to continue using the <form> tag, you can use the #submit.prevent modifier:
<form
class="mt-2"
ref="form"
:model="form"
enctype="multipart/form-data"
#submit.prevent
>
Related
I have a vue application where I am right now getting this kind of an json object:
[
{
"meetingName":"ewq",
"meetingUrl":"",
"meetingPw":"",
"date":"2021-05-30",
"times":[
{
"startTime":"15:30",
"endTime":"16:30"
},
{
"startTime":"17:30",
"endTime":"18:30"
}
]
},
{
"meetingName":"ewq",
"meetingUrl":"",
"meetingPw":"",
"date":"2021-05-31",
"times":[
{
"startTime":"15:30",
"endTime":"16:30"
}
]
}
]
But I am aiming for something like this:
{
"meetingName":"Test",
"meetingPw":"test22",
"meetingUrl":"localhost",
"meetingTimes":[
{
"date":"15.12.2020",
"times":[{
"startTime":"15:00",
"endTime":"16:00"
}]
},
{
"date":"25.12.2020",
"times":[
{
"startTime":"17:00",
"endTime":"18:00"
},
{
"startTime":"19:00",
"endTime":"2:00"
}
]
}
]
}
But I just cant change it to get like this in my code, could someone look at my code and tell me where my mistake is?
<script>
import DatePickerComponent from "#/components/DatePickerComponent";
export default {
name: "GenerateMeetingSettings",
data: () => ({
selectedTime: [],
dates: new Date().toISOString().substr(0,10),
datesFinal: [],
meetingSettingUrl: "",
meetingPW: "",
generatedLink: false,
meetingName: "",
dialog: false,
menu: false,
modal: false,
menu2: false,
menu3: false
}),
methods:{
addTimeFields(){
this.selectedTime.push({
startTime:"",
endTime: "",
})
},
saveDateAndTIme(e) {
this.datesFinal.push({
meetingName: this.meetingName,
meetingUrl: this.meetingSettingUrl,
meetingPw: this.meetingPW,
date: this.dates,
times: this.selectedTime
}
)
this.selectedTime = [];
},
generateMeetingLink(){
let meetinId = this.meetingName
console.log(this.meetingName)
this.meetingSettingUrl = "http://localhost:8080/" + meetinId
this.generatedLink = true
console.log(JSON.stringify(this.datesFinal))
}
}
I just posted the script part as there the logic happens for the array which is generated
this is the solution I could find:
<script>
import DatePickerComponent from "#/components/DatePickerComponent";
export default {
name: "GenerateMeetingSettings",
data: () => ({
selectedTime: [],
finalMeeting: [],
datesFinal: [{meetingName: "",
meetingTime: []}] ,
dates: new Date().toISOString().substr(0,10)})
,
methods:{
addTimeFields(){
this.selectedTime.push({
date: this.dates,
startTime:"",
endTime: "",
})
},
saveDateAndTIme(e) {
this.datesFinal[0].meetingTime.push(this.selectedTime),
this.selectedTime = []
},
As the header describes, I would like to append input elements to some fieldset in my form.
I build a staged form with 3 fieldsets which appear only when "Next" button is clicked.
Now in fieldset number 3 I want to input elements based on keys I'm extracting from a external json object.
Data:
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
fieldsetThree: document.getElementById("fieldset3"),
}),
Fieldset(only appears when currentPage value is 2):
<div v-if="currentPage === 2">
<fieldset id="fieldset3">
<h2 class="fs-title">API Credentials</h2>
<h3 class="fs-subtitle">Step 3- Add any parameter for the integration</h3>
</fieldset>
</div>
Function to append the input fields after Stage 2 is done:
appendFields() {
let check = json[this.platform];
console.log(this.fieldsetThree);
for (const [key, value] of Object.entries(check)) {
let inputfield = document.createElement("input");
this.inputfield.setAttribute("type", "text");
this.inputfield.setAttribute("name",`${key}`);
this.inputfield.setAttribute("placeholder",`${key}`);
console.log("Input FIeld \n", this.inputfield)
this.fieldsetThree.appendChild(this.inputfield);
}
//Build it before.
},
The goal is to create an input field for every key in the JSON, I will add the json format for example:
"exmaple": {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
},
FUll code(without Template):
export default {
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
}),
computed: {
platformData: function () {
return json[this.platform];
}
},
methods: {
isNextClicked() {
var nextStage = this.currentPage;
this.currentPage++;
console.log("CurrentPage =>", this.currentPage);
$("#progressbar li").eq($("fieldset").index(nextStage)).addClass("active");
return this.currentPage;
},
isPreviousClicked() {
this.currentPage--;
var previousStage = this.currentPage;
console.log("CurrentPage at decrease =>", this.currentPage);
$("#progressbar li").eq($("fieldset").index(previousStage)).removeClass("active");
return this.currentPage;
},
appendFields() {
// let check = json[this.platform];
// console.log(this.fieldsetThree);
// for (const [key, value] of Object.entries(check)) {
//
// let inputfield = document.createElement("input");
// this.inputfield.setAttribute("type", "text");
// this.inputfield.setAttribute("name",`${key}`);
// this.inputfield.setAttribute("placeholder",`${key}`);
// console.log("Input FIeld \n", this.inputfield)
// this.fieldsetThree.appendChild(this.inputfield);
// }
//Build it before.
},
nextPlusappend() {
//this.appendFields();
this.isNextClicked();
}
}
}
If you work out the structure you want to keep your data, then you don't need to mess with DOM elements: just add the JSON to the data() & let Vue do its thing.
So, if you take the JSON data structure as the base for the form fields & fieldsets, then you can just add it to the data item holding the form input fields:
const example = {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
}
Vue.component("formFieldset", {
props: ["fields"],
methods: {
handleInput(val, key) {
this.$emit("update:fields", { key, val })
},
},
template: `
<div>
<label
v-for="(val, key) in fields"
:key="key"
>
{{ key }}:
<input
type="text"
:placeholder="key"
#input="(e) => handleInput(e.target.value, key)"
/>
</label>
<hr>
</div>
`
})
new Vue({
el: "#app",
data() {
return {
fieldsets: [
{
field1_1: "",
field1_2: "",
},
{
field2_1: "",
field2_2: "",
},
],
}
},
// just to add the items later to the data():
mounted() {
this.fieldsets.push(example)
},
methods: {
handleUpdateFields({ key, val, idx }) {
this.fieldsets[idx][key] = val
},
},
template: `
<div>
Putting out the data():<br />
{{ fieldsets }}
<hr>
<form>
<form-fieldset
v-for="(fieldset, i) in fieldsets"
:key="i"
:fields="fieldset"
#update:fields="(e) => handleUpdateFields({...e, idx: i})"
/>
</form>
</div>
`
})
label {
display: block;
padding: 8px 16px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
I am trying to implement list post api in dynamic form but i can't correctly post data in dynamic form here it is my all code what i did.when i send post request from my form all data display but only prizes list item not showing i tried to use mapping but still i can resolve my issue pleaase guide me where i am doing mistake
import React, { Component } from "react";
import { Helmet } from "react-helmet";
import Form from "./Form";
import Additional from "./Additional";
import Prize from "./Prize";
import Swal from "sweetalert2";
import axios from "axios";
import { withTranslation } from "react-i18next";
class Create extends Component {
constructor(props) {
super(props);
this.state = {
isHidden: true,
title: "",
lead: "",
span: "",
without: "",
startDate: null,
endDate: null,
parameter: "",
prizes: [
{
number_list: [],
prize_type: "",
name: "",
value: null,
quantity: ""
}
]
};
this.handleSubmit = this.handleSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.toggleHie = this.toggleHie.bind(this);
}
async onChange(event) {
await this.setState({
[event.target.name]: event.target.value
});
console.log(this.state);
}
handleSubmit(e) {
let authToken = localStorage.getItem("Token");
e.preventDefault();
const data = {
title: this.state.title,
lead: this.state.lead,
span: this.state.span,
startDate: this.state.startDate,
endDate: this.state.endDate,
parameter: this.state.parameter,
prizes: [
this.state.prizes.map(c => {
c.number_list = c.number_list;
c.prize_type = c.prize_type;
c.name = c.name;
c.value = c.value;
c.quantity = c.quantity;
})
]
};
axios({
method: "post",
url: `https://digitalfleet.eu/api/1/campaigns/`,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + JSON.parse(authToken)
},
data
})
.then(res => {
this.setState({
title: "",
lead: "",
span: "",
startDate: "",
endDate: "",
parameter: "",
prizes: [
{
number_list: [],
prize_type: "",
name: "",
value: null,
quantity: ""
}
]
});
this.props.history.push("/createcampaign");
Swal.fire({
title: "Campaign",
type: "success",
text: " Added Successfully !",
showConfirmButton: false,
timer: 2000
});
})
.catch(err => {
console.log(err);
Swal.fire({
title: "Campaign",
type: "error",
text: "Error while Creating new!",
timer: 2000
});
});
}
Javascript map returns an array, so you don't need to define data.prizes as array like you did.
Can you construct your data object like this, and try?
const {title, lead, span, startDate, endDate, parameter, prizes} = this.state;
const data = {title, lead, span, startDate, endDate, parameter, prizes};
In your handleSubmit function. Construct the data object like this:
const data = {
title: this.state.title,
lead: this.state.lead,
span: this.state.span,
startDate: this.state.startDate,
endDate: this.state.endDate,
parameter: this.state.parameter,
prizes: [
...this.state.prizes
]
}
Following your current approach, return is missing inside map which might be causing the values to not come. You need to do the following 2 things inorder for code to work. return from map properly and use spread operator otherwise the value will go inside prize[0] instead of prize array.
The code will be:
prizes: [...this.state.prizes.map(c => ({ //Note '(' bracket before '{' for returning object
c.number_list = c.number_list;
c.prize_type = c.prize_type;
c.name = c.name;
c.value = c.value;
c.quantity = c.quantity;
}))]
However, there is simplified version as well, if your data values are equivalent to this.state. You can simply do the following and every other value will sit fine:
data = { ...this.state }
Even for prizes, you can simply do the following:
data = {
...
prizes: [ ...this.state.prizes ]
}
Example snippet demonstrating all of above (including your current approach):
const state = {
title: "title prize",
lead: "lead prize",
span: "span prize",
prizes: [{
prize_type: "type1",
name: "Type 1"
}, {
prize_type: "type2",
name: "Type 2"
}]
}
const data1 = { ...state }
const data2 = {
title: state.title,
lead: state.lead,
span: state.span,
prizes: [...state.prizes]
}
const data3 = {
title: state.title,
lead: state.lead,
span: state.span,
prizes: [...state.prizes.map(c => ({
prize_type: c.prize_type,
name: c.name,
}))]
}
console.log(data1)
console.log(data2)
console.log("Your approach")
console.log(data3)
.as-console-wrapper {
top: 0;
max-height: 250px !important;
}
you can directly use this
const {title, lead, span, startDate, endDate, parameter, prizes} = this.state;
const data = { title, lead, span, startDate, endDate, parameter, prizes }
In my vue-app I have an array of job-postings, which have different states, such as "active", "rejected", "draft", "not_active" etc. Now I have a TabMenu: All Jobs, Drafts and To Be Approved. Each of those MenuTabs, have their own Dropdown menu, where you are supposed to filter the jobpostings. I've realized that this feature is more complex than expected, or maybe I have spend too much time with the issues, but for some reason, I cannot manage, to show "all" for the individual MenuTab. For example, when I click on the "To Be Approved" MenuTab, I want to see all the jobpostings, with the status "Not approved" and "Rejected" (See data below in the code).
So my question is, how to solve this properly? Does the job-posting data object need to have a category too?
Any help is most welcome!
So, here is my component:
<template>
<div>
<div class="tabs">
<ul>
<li v-for="(tab, index) in menuTabs” :key="tab.id" :class="{ 'active': activeTab === index }"
#click="toggleList(tab, index)” >
<span>{{tab.label}}</span>
</li>
</ul>
</div>
<div class="dropdown has-prepend col-8" :class="{ active: isOpen }">
<div :class="{ active: isOpen }" class="dropdown-select" #click="toggle">
{{ selectedOption }}
<i class="icon-chevron_down" />
</div>
<div class="dropdown-options" v-show="isOpen">
<div class="option" v-for="tab in dropDownTabs" #click="select(tab)" :key="tab.id">
<span>{{ tab.status }}</span>
</div>
</div>
</div>
<div class="block">
<DataTable :data="filteredData" :columns="tableColumns" :filter="search" />
</div>
</div>
</template>
import DataTable from '../../snippets/DataTable';
export default {
components: { DataTable },
data() {
return {
isOpen: false,
search: "",
tableData: [
{
id: 1,
title: "Salesperson",
publish_date: "2019-07-10",
status: "active",
applicants: 23,
expiration_date: "2020-02-21"
},
{
id: 2,
title: "Developer",
publish_date: "2019-11-12",
status: "not_active",
applicants: 111,
expiration_date: "2020-02-21"
},
{
id: 3,
title: "Freelanceer",
publish_date: "2019-06-10",
status: "need_approval",
applicants: 222,
expiration_date: "2020-01-10"
},
{
id: 4,
title: "Construction worker",
publish_date: "2019-12-06",
status: "active",
applicants: 76,
expiration_date: "2020-03-15"
},
{
id: 5,
title: "IT support”
publish_date: "2019-11-20",
status: "draft",
applicants: 103,
expiration_date: "2020-04-31"
},
],
menuTabs: [
{
label: "All jobs",
options: [
{
status: "all",
},
{
status: "active",
},
{
status: "not_active"
}
]
},
{
label: "Drafts",
options: [
{
status: "all"
},
{
status: "draft"
}
]
},
{
label: "To Be Approved",
options: [
{
status: "all",
},
{
status: "need_approval",
},
{
status: "rejected"
}
]
},
],
dropDownTabs: [],
selectedOption: "",
selectedTabCategory: "",
category: "",
activeTab: "",
tableColumns: [
"id",
"title",
"publish_date",
"status",
"applicants",
"expiration_date"
]
}
},
computed: {
filteredData() {
let status = this.selectedOption;
let category = this.category;
let filtered = this.tableData.filter(data => {
if (status == "all") {
return data;
}
return data.status === status;
});
return filtered;
}
},
methods: {
toggleList(tab, index) {
this.category = tab.options[0].category;
this.selectedTabCategory = tab;
let currentTabOptions = this.selectedTabCategory.options;
this.clearDropDown();
this.selectedOption = currentTabOptions[0].status;
currentTabOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = index;
},
toggle() {
this.isOpen = !this.isOpen;
},
select(tab) {
this.selectedOption = tab.status;
let category = tab.category;
let filtered = this.tableData.filter(data => {
return data.status === this.selectedOption;
});
this.isOpen = false;
return filtered;
},
clearDropDown() {
this.dropDownTabs = [];
}
},
created() {},
mounted() {
this.selectedOption = this.menuTabs[0].options[0].status;
this.selectedTabCategory = this.menuTabs[0].label;
this.category = this.menuTabs[0].options[0].category;
let defaultOptions = this.menuTabs[0].options;
defaultOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = 0;
}
};
I am not sure if it will help you at all. However I will try anyway.
You should store the selected tab when you click on it. Then filter the this.tableData based on the selected tab options. Also you will need map the tab option options to array of strings, so you can check if the posting status is in there.
methods: {
toggleList (tab, index) {
this.selectedTabObject = tab
// rest of your code...
}
},
computed: {
filteredData () {
return this.tableData.filter(data => {
const states = this.selectedTabObject.options.map(opt => opt.status)
return states.includes(data.status)
})
}
}
Also I have created simple fiddle to mimic your problem.
https://jsfiddle.net/3hqnp7u2/7/
I had a very long form which around 20 different fields and I displayed those input area using map function. I want to valid the input data when I click the submit button and jump to the corresponding input required box.
const ReportFields = [
{
title: "Report Title*",
field: "report_title",
type: "text",
required: true
},
{
title: "Submitting Agency*",
field: "submitting_agency",
type: "text",
required: true
},
{
title: "Division*",
field: "division",
type: "select",
required: true
},
{
title: "Committee*",
field: "committee",
type: "select",
required: true
},
{
title: "Assigned Contact*",
field: "assigned_contact",
type: "select",
required: true
},
{
title: "Other Recipients",
field: "other_recipients",
type: "text",
required: false
}];
class App extends Component {
state = {
report: {
report_title: "",
submitting_agency: "",
division: "",
committee: "",
assigned_contact: "",
other_recipients: ""
},
errorMessage: "",
refs: {}
}
componentDidMount() {
this.registerRefs();
}
registerRefs = () => {
const refs = ReportFields.reduce((acc, current) => {
const ref = React.createRef();
acc[current.field] = ref;
return acc;
}, {});
this.setState({ refs });
}
onSubmit = (e) => {
e.preventDefault();
for (let i = 0; i < ReportFields.length; i++) {
const curt = ReportFields[i];
if (curt.required && this.state.report[curt.field] === "") {
this.setState({errorMessage: `${curt.title} cannot be empty!`});
this.state.refs[curt.field].current.focus();
break;
}
}
}
render() {
const display = ReportFields.map((field, idx) => {
return (
<div key={idx}>
<p>{field.title}</p>
<input
type={field.type}
onChange={(e) => {
this.setState({
report: {...this.state.report, [field.field]: e.target.value}
})
}}
ref={this.state.refs[field.field]}
></input>
</div>
);
})
return (
<div className="App">
{display}
<input type="button" value="submit" onClick={this.onSubmit}/>
</div>
);
}
}
export default App;
I tried to use react refs but it doesn't work, any idea?
Also, I am actually using these content in react modal, will this be one of the reason why it doesn't work?
Ok here is a solution I know who can work but I don't say it's the best one. A working example here https://codesandbox.io/s/94v4r6w7kr. As you can see when you click submit you jump to password input.
How do that work ? First as you can see we need a way to save all the ref we gonna create. I save it in the state refs here. The way that work is a loop over each field and for each one I createRef and add this to an object. I use this object inside the state. When you want to use it after that, you then can call this.state.refs[thenameoftheinput].current.focus().
This is an example, and I let you make it work with your own data. But I hope that can give you an idea :)
const ReportFields = [
{
title: "Report Title*",
field: "report_title",
type: "text",
required: true
},
{
title: "Submitting Agency*",
field: "submitting_agency",
type: "text",
required: true
},
{
title: "Division*",
field: "division",
type: "select",
required: true
},
{
title: "Committee*",
field: "committee",
type: "select",
required: true
},
{
title: "Assigned Contact*",
field: "assigned_contact",
type: "select",
required: true
},
{
title: "Other Recipients",
field: "other_recipients",
type: "text",
required: false
}
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
refs: {}
};
}
componentDidMount() {
this.registerRefs();
}
registerRefs = () => {
const refs = ReportFields.reduce((acc, current) => {
const ref = React.createRef();
acc[current.field] = ref;
return acc;
}, {});
this.setState({ refs });
};
focusTextInput = () => {
this.state.refs.division.current.focus();
};
render() {
const inputs = ReportFields.map(el => {
return <input placeholder={el.title} ref={this.state.refs[el.field]} />;
});
return (
<div>
<form>
{inputs}
<input type="button" value="submit" onClick={this.focusTextInput} />
</form>
</div>
);
}
}
Iterate through all fields and create a separate ref for each one of them. Use a unique identifier (as a suggestion - the name property) to access the ref later.
class App extends Component {
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
// Fields
this.ReportFields = [
{
type: "text",
name: "firstname",
title: "First Name"
},
{
type: "text",
name: "lastname",
title: "Last Name"
}
];
this.inputRefs = this.ReportFields.reduce((acc, field) => ({
...acc,
[field.name]: React.createRef()
}), {});
}
state = {
a: {
b: "",
c: "",
d: "",
e: "",
f: "",
g: "",
h: "",
i: "",
j: "",
k: "",
l: "",
m: "",
n: "",
o: "",
p: "",
q: "",
r: "",
s: "",
t: "",
u: "",
v: "",
w: "",
x: "",
y: ""
},
errorMessage: ""
};
focusTextInput() {
// Focus on the input you wish, in this case "firstname"
console.log(this.inputRefs["firstname"].current.focus());
}
render() {
const display = this.ReportFields.map((field, idx) => {
return (
<div key={idx}>
<p>{field.title}</p>
<input
type={field.type}
onChange={e => {
this.setState({
report: { ...this.state.report, [field.field]: e.target.value }
});
}}
ref={this.inputRefs[field.name]}
/>
</div>
);
});
return (
<div className="App">
{display}
<input type="button" value="submit" onClick={this.focusTextInput} />
</div>
);
}
}