VueJS lifecycle after submit form - forms

I would like to plot some d3js chars in vuejs after performing an API call to get some data. To do so, I created a form whose input is used to collect the data from the API. Once I submit the form, I call my d3js function to plot the charts based on the retrieved data. I want the plotting functions to be called only when the data is not empty. To do so, I used the conditional rendering v-if based on the length of the data. So far so good. My problem is that once the plots are rendered if I type anything in the form, a new plot will be created as if every time the if statement is evaluated again and again, I don't know if it is related to lifecycle or not, but how can avoid this behavior?
<template>
<meta charset="utf-8">
<h4>Associate Information</h4>
<form #submit.prevent="onSubmit">
<input placeholder="Associate Id" v-model="associateId" /> <br />
<input placeholder="Starting date" v-model="initialDate" /> <br />
<input placeholder="Ending date" v-model="finalDate" /> <br />
<button v-on:click="getAssociatesbyIdAndDates()">submit</button>
</form>
<div class="chart" v-if="dailyData.length">
{{ DailyBillabilityLinePlot() }}
{{ WeeklyMonthlyQuarterlyBarPlot(weeklyData) }}
</div>
<div class="linePlot"></div>
<div class="barPlot" v-if="weeklyData.length">
<button v-on:click="WeeklyMonthlyQuarterlyBarPlot(weeklyData)">Weekly</button>
<button v-on:click="WeeklyMonthlyQuarterlyBarPlot(monthlyData)">Monthly</button>
<svg id="chart" viewBox="0 0 960 300"></svg>
</div>
</template>
<script>
import * as d3 from 'd3'
export default {
name: 'Timecard',
props: {
msg: String
},
data() {
return {
apiUrl: "",
myNumber: 0,
environment: "",
apiTst:"",
name:"",
initialDate: "",
finalDate: "",
associateId: "",
dailyData: [],
weeklyData:[],
monthlyData:[],
}
},
methods: {
WeeklyMonthlyQuarterlyBarPlot(data){
// plot a d3 bar plot
},
DailyBillabilityLinePlot() { // plot another d3 line plot}
getAssociatesbyIdAndDates() {
// Connect to the backend and get the list of associates
// http://localhost:8080/timecards/period/test/274/2020-04-14/2020-04-22
console.log("Fetching the data for an associates from the backend based on initial date and final date...");
this.axios.get(this.apiUrl + "test/period/test/" + this.associateId + "/" + this.initialDate + "/" + this.finalDate)
.then(response => {
this.dailyData = response.data;
console.log(response.status);
})
.catch(error => {
console.error(error);
})
this.axios.get(this.apiUrl + "test/weekly/" + this.associateId + "/" + this.initialDate + "/" + this.finalDate)
.then(response => {
this.weeklyData = response.data;
console.log(response.status);
})
.catch(error => {
console.error(error);
})
this.axios.get(this.apiUrl + "test/monthly/" + this.associateId + "/" + this.initialDate + "/" + this.finalDate)
.then(response => {
this.monthlyData = response.data;
console.log(response.status);
})
.catch(error => {
console.error(error);
})
},}
onSubmit() {
let consultantApi = {
name: this.name,
initialDate: this.initialDate,
finalDate: this.finalDate,
}
this.$emit('consultantApi-submitted', consultantApi)
this.name = ''
this.initialDate = ''
this.finalDate = ''
}
},
mounted: function() {
this.apiUrl = process.env.VUE_APP_BACKEND_API;
this.environment = process.env.NODE_ENV;
}
Initial Form
D3 plots after submitting the completed form
Creation of new d3 plots whenever I press any key in the form

Yes, this is because updating any model will re-render the entire component.
To get around it, I find the simplest way is to put the chart into another component so that the re-render is then guarded.
Example:
var app = Vue.createApp({
data() {
return {
abc: 'ABC',
list: [1, 2, 3]
};
}
});
app.component("my-chart", {
template: `<div >{{Math.random()}}</div>`
});
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.6/dist/vue.global.prod.js"></script>
<div id="app">
<input v-model="abc" />
<div v-if="list.length">
{{Math.random()}}
</div>
<my-chart></my-chart>
</div>

I think your bug is in the parent component. I would try to check how often does it emmit the consultantApi. It seems like your form keeps submitting on input change event and not on form submit.

Related

Vue.js: How to fill a form prepopulated with data from a get request?

I want to load data with a GET request and fill the data to the input data attributes at vue.js 3 like
<input id="name" type="text" v-bind:placeholder="$t('message.NamePlaceholder')" value="{{ name }}" required>
and this is my script part
<script>
export default {
data () {
return {
userInformation: {},
name: "",
}
},
mounted () {
this.getUserInformation();
},
methods () {
getUserInformation() {
this.$axios({
method: 'get',
url: 'http://127.0.0.1:8000/api/get_user_information',
}).then(response => {this.userInformation = response.data});
this.name = this.userInformation.Name;
}
},
}
But the input field contains only {{ name }}. I tried also v-bind:value, but this didn't solve the problem.
Whenever you need to bind values to attributes {{}} are unnecessary. You can just write v-bind:value="name" or :value="name"
E.g.:
<input id="name" type="text" :placeholder="message.NamePlaceholder" :value="name" required></input>
The mistake was that I have to set the variable this.name at the axios command:
this.$axios({
method: 'get',
url: 'http://127.0.0.1:8000/api/get_user_information',
}).then(response => {
this.userInformation = response.data;
this.name = this.userInformation.Name;
});

Adding a wp: featured image to loop when using a headless CMS via the Wordpress API and Vue.js

Would like to know how to implement the Wordpress Featured Image to multiple vue.js/nuxt.js based headless-CMS approaches using the Wordpress restful API.
Initially, I followed this immensely helpful tutorial headless-cms with nuxt and created a headless CMS via the wordpress api, and of course applied it to my use-case (here is a link to a live version nuxt-headless-cms-webapp. Unfortunately, I have been unable to figure out how to include a post's featured image as it is not covered in this particular tutorial. I then did a bit of research and ended up piecing together another project (vue.js), in which I was able to implement featured images. That being said I would like guidance with regard to implementing my working code in terms of the wp-featured image, to the original tutorial's project (as nuxt provides better routing and SEO options from my understanding. Thank you in advanced for any help!
First, here is the axios http request syntax found in the original tutorial (nuxt) project's index.js:
const siteURL = "https://indvillage.com"
export const state = () => ({
posts: [],
tags: []
})
export const mutations = {
updatePosts: (state, posts) => {
state.posts = posts
},
updateTags: (state, tags) => {
state.tags = tags
}
}
export const actions = {
async getPosts({ state, commit, dispatch }) {
if (state.posts.length) return
try {
let posts = await fetch(
`${siteURL}/wp-json/wp/v2/posts?_embed`
).then(res => res.json())
posts = posts
.filter(el => el.status === "publish")
.map(({ id, slug, title, excerpt, date, tags, content }) => ({
id,
slug,
title,
excerpt,
date,
tags,
content
}))
commit("updatePosts", posts)
} catch (err) {
console.log(err)
}
},
async getMedia({ state, commit }) {
if (state.media.length) return
try {
let media= await fetch(
`${siteURL}/wp-json/wp/v2/media?_embed`
).then(res => res.json())
commit("updatePosts", media)
} catch (err) {
console.log(err)
}
},
async getTags({ state, commit }) {
if (state.tags.length) return
let allTags = state.posts.reduce((acc, item) => {
return acc.concat(item.tags)
}, [])
allTags = allTags.join()
try {
let tags = await fetch(
`${siteURL}/wp-json/wp/v2/tags?page=1&per_page=40&include=${allTags}`
).then(res => res.json())
tags = tags.map(({ id, name }) => ({
id,
name
}))
commit("updateTags", tags)
} catch (err) {
console.log(err)
}
}
}
Next, we have the index.vue page where the above logic is implemented.
template>
<div>
<app-masthead></app-masthead>
<div class="posts">
<main>
<div class="post" v-for="post in sortedPosts" :key="post.id">
<h3>
<a :href="`blog/${post.slug}`">{{ post.title.rendered }}</a>
</h3>
<small>{{ post.date | dateformat }}</small>
<div v-html="post.excerpt.rendered"></div>
<a :href="`blog/${post.slug}`" class="readmore slide">Read more ⟶</a>
</div>
</main>
<!--<aside>
<h2 class="tags-title">Tags</h2>
<div class="tags-list">
<ul>
<li
#click="updateTag(tag)"
v-for="tag in tags"
:key="tag.id"
:class="[tag.id === selectedTag ? activeClass : '']"
>
<a>{{ tag.name }}</a>
<span v-if="tag.id === selectedTag">✕</span>
</li>
</ul>
</div>
</aside>-->
</div>
</div>
</template>
<script>
import AppMasthead from "#/components/AppMasthead.vue";
export default {
components: {
AppMasthead
},
data() {
return {
selectedTag: null,
activeClass: "active"
};
},
computed: {
posts() {
return this.$store.state.posts;
_embed = true;
},
tags() {
return this.$store.state.tags;
},
sortedPosts() {
if (!this.selectedTag) return this.posts;
return this.posts.filter(el => el.tags.includes(this.selectedTag));
}
},
created() {
this.$store.dispatch("getPosts");
},
methods: {
updateTag(tag) {
if (!this.selectedTag) {
this.selectedTag = tag.id;
} else {
this.selectedTag = null;
}
}
}
};
Here is a link to my project with working wordpress featured images! https://indvillage.netlify.app/
And here is the logic associated with the axious http request I used.
The question is how, do I include my logic in terms of the wp featured image to the initial nuxt tutorial without breaking things:
export default {
data() {
return {
postsUrl: "https://indvillage.com/wp-json/wp/v2/posts",
queryOptions: {
per_page: 6,
page: 1,
_embed: true
},`
posts: []
};
},
methods: {
// Get recent posts from wp
getRecentMessages() {
axios
.get(this.postsUrl, { params: this.queryOptions })
.then(response => {
this.posts = response.data;
console.log("Posts retrieved!");
})
//document.getElementById("test").id = "testing";
.catch(error => {
console.log(error);
});
},
getPostDate(date) {
return moment(date).format("111");
},
},
mounted() {
this.getRecentMessages();
}
}
Next, here is the App.vue template that displays the parsed information:
<template id="app">
<body>
<div class="row container">
<!-- looping through and displaying the array bound posts in HTML -->
<div class="col s4 m4" v-for="(post, index) in posts" :key="index" :id="'post'+index">
<div class="card" id="test">
<div class="card-image">
<!-- rendering the post's wp:featuredmedia in the image portion of the html/css card -->
<img
v-if="post._embedded['wp:featuredmedia']"
:src="post._embedded['wp:featuredmedia'][0].source_url"
/>
</div>
<!-- rendering the post.excerpt to the html/css card container -->
<div class="card-content" v-html="post.excerpt.rendered"></div>
<div class="card-action">
<!-- rendering the post title to the action portion of the html/css card -->
{{ post.title.rendered }}
</div>
</div>
</div>
</body>
</template>
Please let me know if anyone has any suggestions with regard to implementing wp:featuredmedia to the code derived from the first project/tutorial (nuxt.js)
Thanks again! Feel free to email with further questions

How to submit radio button value + additional info about the form to Redux

This is a bit longwinded so I'll do my best to explain clearly.
I'm making a simple poll app and on the home page is an array of polls where you can vote on each poll.
Each poll is on a card and there will be different radio buttons representing the different voting options for that poll.
I'm trying to set up a form for each poll which contains radio button inputs for each of the different options and push that onSubmit to an action creator.
However, I would also like to pass that title of the poll as well as an argument to the action creator so that I can create a single action creator that will help me submit the votes for all the polls. Something like submitVote(title, option).
Here is my polls page:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import Loading from '../Loading';
class MyPolls extends Component {
constructor(props) {
super(props);
this.state = {
skip: 0,
isLoading: true,
isLoadingMore: false,
value: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.fetchMyPolls(this.state.skip)
.then(() => {
setTimeout(() => {
this.setState({
skip: this.state.skip + 4,
isLoading: false
});
}, 1000);
});
}
sumVotes(acc, cur) {
return acc.votes + cur.votes
}
loadMore(skip) {
this.setState({ isLoadingMore: true });
setTimeout(() => {
this.props.fetchMyPolls(skip)
.then(() => {
const nextSkip = this.state.skip + 4;
this.setState({
skip: nextSkip,
isLoadingMore: false
});
});
}, 1000);
}
handleSubmit(e) {
e.preventDefault();
}
handleChange(event) {
console.log(event.target.value);
this.setState({ value: event.target.value });
}
renderPolls() {
return this.props.polls.map(poll => {
return (
<div className='card' key={poll._id} style={{ width: '350px', height: '400px' }}>
<div className='card-content'>
<span className='card-title'>{poll.title}</span>
<p>Total votes: {poll.options.reduce((acc, cur) => { return acc + cur.votes }, 0)}</p>
<form onSubmit={this.handleSubmit}>
{poll.options.map(option => {
return (
<p key={option._id}>
<input
name={poll.title}
className='with-gap'
type='radio'
id={option._id}
value={option.option}
onChange={this.handleChange}
/>
<label htmlFor={option._id}>
{option.option}
</label>
</p>
)
})}
<button
type='text'
className='activator teal btn waves-effect waves-light'
style={{
position: 'absolute',
bottom: '10%',
transform: 'translateX(-50%)'
}}
>
Submit
<i className='material-icons right'>
send
</i>
</button>
</form>
</div>
<div className='card-reveal'>
<span className='card-title'>{poll.title}
<i className='material-icons right'>close</i>
</span>
<p>
dsfasfasdf
</p>
</div>
</div>
)
})
}
render() {
return (
<div className='center-align container'>
<h2>My Polls</h2>
{this.state.isLoading ? <Loading size='big' /> :
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-evenly', alignItems: 'center', alignContent: 'center' }}>
{this.renderPolls()}
</div>}
<div className='row'>
{this.state.isLoadingMore ? <Loading size='small' /> :
<button
className='btn red lighten-2 wave-effect waves-light' onClick={() => this.loadMore(this.state.skip)}>
Load More
</button>}
</div>
</div>
);
}
}
function mapStateToProps({ polls }) {
return { polls }
}
export default connect(mapStateToProps, actions)(MyPolls);
Demo of the app so far: https://voting-app-drhectapus.herokuapp.com/
(use riverfish#gmail.com and password 123 to login).
Github repo: https://github.com/drhectapus/Voting-App
I'd like to program it so that when form is submitted via this.handleSubmit, the handleSubmit function can take 2 arguments, title and option and pass that onto an action creator in redux.
How do I do this?
It's a little difficult to understand everything going on here, but I get the sense that your main goal is to pass two args to this.handleSubmit. You may instead consider just passing poll.title and grabbing the selected option from state. Try something like this:
this.handleSubmit(title) {
// this.state.value should already have the selected option!
let obj = {
title,
option: this.state.value
};
// dispatch the object to redux, update your reducer, etc.
}
And in your render, be sure to bind poll.title as the argument:
render() {
...
<form onSubmit={this.handleSubmit.bind(this, poll.title)}>
}
Does that help at all? Let me know if I'm totally missing the mark on what you intend. With .bind() you pass the this context to use followed by a list of common separated args, so you could pass multiple args, but it's much easier to just grab option from state in this case.
Edit
If you want to access the SyntheticEvent that gets fired on submit, you simple specify it as the second argument to this.handleSubmit like so:
this.handleSubmit(title, event) {
// prevent form submit
event.preventDefault();
}
// this is the exact same as above, no need to pass event
render() {
...
<form onSubmit={this.handleSubmit.bind(this, poll.title)}>
}
In React, synthetic events are always passed as the last argument to a bound function and simply need to be specified to be in the method definition (no need to specify in render). This is Function.prototype.bind way of working with functions and events in React. Here are the supporting docs: https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers

React|Rest API: Storing form data into an object on the REST API

I've set up a react web application that's currently listing all "Employees" from a mongodb.
I'm now trying to "add" employees to the database through a react frontend form.
I've managed to pass the data from the form to the application but I'm unsure of the process I need to go through to actually get that data solidified into an object and stored in the api.
Please excuse my code, it's disgusting as this is my first week learning react(honestly with little js knowledge, that's another story) and I've just patched together like 20 tutorials....
Here's my Form class:
class Form extends React.Component {
state = {
fullname: '',
}
change = e => {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state)
this.setState({
fullname: ''
})
}
render() {
return <div>
<form>
<input name="fullname" placeholder="Full Name" value={this.state.fullname} onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
</div>
}
}
and my Listing(?) class:
class EmployeeList extends React.Component {
constructor(props) {
super(props);
this.state = {employee: []};
this.EmployeeList = this.EmployeeList.bind(this)
this.componentDidMount = this.componentDidMount.bind(this)
}
componentDidMount() {
this.EmployeeList();
}
EmployeeList() {
fetch('/api/employees').then(function(data){
return data.json();
}).then( json => {
this.setState({
employee: json
});
console.log(json);
});
}
onSubmit = fields => {
console.log('app component got: ', fields)
}
render() {
//return a mapped array of employees
const employees = this.state.employee.map((item, i) => {
return <div className="row">
<span className="col-sm-6">{item.fullname}</span>
<span className="col-sm-2" id={item.action1}></span>
<span className="col-sm-2" id={item.action2}></span>
<span className="col-sm-2" id={item.action3}></span>
</div>
});
return <div>
<Form onSubmit={fields => this.onSubmit(fields)}/>
<div className="container">
<div className="row">
<div className="col-sm-6 bg-warning"><h3>Full Name</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 1</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 2</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 3</h3></div>
</div>
</div>
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ employees }</div>
</div>
</div>
}
}
I've managed to pass the data to the listing app evident by
onSubmit = fields => {
console.log('app component got: ', fields)
}
But how can I go about making a post request to store this data I send into an object on the db? And then also reload the page so that the new list of all employee's is shown?
Thanks so much for your time!
You can use fetch API to make POST request as well. Second parameter is the config object wherein you can pass the required request configurations.
fetch('url', {
method: 'post',
body: JSON.stringify({
name: fields.fullname
})
})
.then(response) {
response.json();
}
.then( json => {
this.setState({
employee: json
});
});
Additional Request Configs which can be used :
method - GET, POST, PUT, DELETE, HEAD
url - URL of the request
headers - associated Headers object
referrer - referrer of the request
mode - cors, no-cors, same-origin
credentials - should cookies go with the request? omit, same-origin
redirect - follow, error, manual
integrity - subresource integrity value
cache - cache mode (default, reload, no-cache)

Angular 2 : How to make POST call using form

I am completely new to Angular 2 and form concept. I am trying to POST form data to a POST API call. like this
POST API : http://localohot:8080/**********
Component :
user: any = {
id: null,
gender: null,
mstatus: null,
birthdate: null,
bloodgroup: null
}
userName: any = {
id: null,
personId: null,
displayName: '',
prefix: null,
givenName: null
}
userAddressJSON: any = {
id: null,
personId: null,
address1: null,
address2: null,
cityVillage: null
}
var form = new FormData();
form.append('userid', new Blob(['' + uid], { type: 'application/json' }));
form.append('user', new Blob([JSON.stringify(this.user)], { type: 'application/json' }));
form.append('userName', new Blob([JSON.stringify(this.userName)], { type: 'application/json' }));
form.append('userAddress', new Blob([JSON.stringify(this.userAddressJSON)], { type: 'application/json' }));
Here, I don't know how to make API call.
In our old application they used form data POST in jQuery. Now I am trying to do the same in Angular 2. When I do the form POST in old application they are sending like this
------WebKitFormBoundarybAWvwmP2VtRxvKA7
Content - Disposition: form - data; name = "userid"; filename = "blob"
Content - Type: application / json
------WebKitFormBoundarybAWvwmP2VtRxvKA7
Content - Disposition: form - data; name = "user"; filename = "blob"
Content - Type: application / json
------WebKitFormBoundarybAWvwmP2VtRxvKA7
Content - Disposition: form - data; name = "userName"; filename = "blob"
Content - Type: application / json
------WebKitFormBoundarybAWvwmP2VtRxvKA7
Content - Disposition: form - data; name = "userAddress"; filename = "blob"
Content - Type: application / json
Can any one help me how to do that form POST in Angular 2.
Here is how I currently make a POST call in my Angular 2 app, because it sounds like you could use a simple example of how to setup a form. Here is the Angular 2 documentation on How to Send Data to the Server.
For even more high level documentation on making AJAX requests in Angular 2 visit this URL.
in my app/app.module.ts
...
import { HttpModule } from '#angular/http';
...
#NgModule({
imports: [
...
HttpModule
...
],
declarations: [
...
],
providers: [ ... ],
bootstrap: [AppComponent],
})
export class AppModule { }
app/system-setup/system-setup.ts
export class SystemSetup {
system_setup_id: number;
name: string;
counter: number;
}
app/form-component/form.component.html (Notice the [(ngModel)], that is what binds the property of the object to the html input element)
<form class="form" (ngSubmit)="saveEdits()" #editSystemSetupForm="ngForm">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="theName" name="name" [(ngModel)]="selectedItem.name" #itemsName="ngModel" required minlength="3"/>
<div [hidden]="itemsName.valid || itemsName.pristine" class="alert alert-danger">Name is required! Min length of 3.</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="">Counter</label>
<input type="number" step=0.01 class="form-control" name="counter" [(ngModel)]="selectedItem.counter" />
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button type="submit" class="btn btn-success" style="float: right; margin-left: 15px;" [disabled]="!editISystemSetupForm.form.valid" >Save</button>
<button type="button" class="btn btn-danger" style="float: right;" (click)="cancelEdits()">Cancel</button>
</div>
</div>
</form>
in my app/form-component/form.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
import { Headers, RequestOptions, Http, Response } from '#angular/http';
import { SystemSetup } from '../system-setup/system-setup';
#Component({
selector: 'app-setup-form',
templateUrl: 'setup-form.component.html',
styleUrls: ['setup-form.component.css']
})
export class SetupFormComponent implements OnInit {
#Input() selectedItem: SystemSetup; // The object to be edited
#Output() finishedEditing = new EventEmitter<number>(); // When the POST is done send to the parent component the new id
// Inject the Http service into our component
constructor(private _http: Http) { }
// User is finished editing POST the object to the server to be saved
saveEdits(): void {
let body = JSON.stringify( this.selectedItem );
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this._http.post('http://localhost:8080/**********', body, options)
.map(this.extractData)
.do(
data => {
this.finishedEditing.emit(data.system_setup_id); // Send the parent component the id if the selectedItem
})
.toPromise()
.catch(this.handleError);
}
/**
* Gets the data out of the package from the AJAX call.
* #param {Response} res - AJAX response
* #returns SystemSetup - A json of the returned data
*/
extractData(res: Response): SystemSetup {
let body = res.json();
if (body === 'failed') {
body = {};
}
return body || {};
}
/**
* Handles the AJAX error if the call failed or exited with exception. Print out the error message.
* #param {any} error - An error json object with data about the error on it
* #returns Promise - A promise with the error message in it
*/
private handleError(error: any): Promise<void> {
// In a real world app, we might use a remote logging infrastructure
// We'd also dig deeper into the error to get a better message
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Promise.reject(errMsg);
}
}
This URL is the link to the official Angular 2 documentation site, which is a very good reference for anything an Angular 2 developer could want.