5 video tutorial series
Original LoopBack & React tutorial from Traversy for comparison
I've followed the tutorial and got it to function on Cloud 9 in developement..
I wasn't sure about how to set the port to an Environment variable, so I hardcoded my ports with 8080 for Cloud 9.. now I'm trying to run it on Heroku and all my axios posts are broken.
I think I changed all the links back from
axios.get(`http://foood-liberation-front-turtlewolfe.c9users.io:8080/api/Barrels/${barrelID}`)
to axios.get(`http://localhost:3000/api/Barrels/${barrelID}`)
but I'm still missing something, I can get it to compile on Heroku at
https://food-liberation-frontz.herokuapp.com
but when I click on the save link to add a new barrel, it's broken.
```
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
class AddBarrel extends Component {
addBarrel(newBarrel){
console.log(newBarrel);
axios.request({
method:'post',
url:'http://foood-liberation-front-turtlewolfe.c9users.io:8080/api/Barrels',
data: newBarrel
}).then(response => {
this.props.history.push('/');
}).catch( err => console.log(err));
}
onSubmit(e){
const newBarrel = {
Name: this.refs.Name.value,
barrel_number: this.refs.barrel_number.value,
contents: this.refs.contents.value,
date_last_checked: this.refs.date_last_checked.value,
date_planted: this.refs.date_planted.value,
location: this.refs.location.value,
size: this.refs.size.value,
notes: this.refs.notes.value
}
this.addBarrel(newBarrel);
e.preventDefault();
}
render () {
return (
<div className = "container green lighten-3" >
<br />
<Link className = "btn grey" to = "/">back</Link>
<h6>add a Barrel</h6>
<form onSubmit = {this.onSubmit.bind(this)}>
<div className = "input-field" >
<input type = "text" name = "Name" ref = "Name" />
<label htmlFor = "Name" >Name</label>
</div>
<div className = "input-field" >
<input type = "text" name = "barrel_number" ref = "barrel_number" />
<label htmlFor = "barrel_number" >barrel number</label>
</div>
<div className = "input-field" >
<input type = "text" name = "contents" ref = "contents" />
<label htmlFor = "contents" >contents</label>
</div>
<div className = "input-field" >
<input type = "date" name = "date_planted" ref = "date_planted" />
<label htmlFor = "date_planted" ></label>
</div>
<div className = "input-field" >
<input type = "date" name = "date_last_checked" ref = "date_last_checked" />
<label htmlFor = "date_last_checked" ></label>
</div>
<div className = "input-field" >
<input type = "text" name = "location" ref = "location" />
<label htmlFor = "location" >location</label>
</div>
<div className = "input-field" >
<input type = "text" name = "size" ref = "size" />
<label htmlFor = "size" >size</label>
</div>
<div className = "input-field" >
<input type = "text" name = "notes" ref = "notes" />
<label htmlFor = "notes" >notes</label>
</div>
<input type = "submit" value = "Save" className = "btn" />
</form>
</div>
)
}
}
export default AddBarrel;
```
Have you defined what your application should do when the server receives the POST request you've defined above from the front end? For example...
In your React component above your POST request might look like this. I've modified your code above, specifically the URL. Note that your browser is already pointed to your application's URL, but you're looking to make a POST request to a specific route within your application.
axios.request({
method:'post',
url:'/api/Barrels',
data: newBarrel
})
Your server would receive the request to that route, do some stuff, and respond accordingly. The code below would probably live in server.js.
app.post('/api/Barrels', function (req, res) {
res.send('STUFF BACK TO FRONT END')
})
Related
I'm using a react form and hooks to send a post to my backend that's running Spring boot. When I send the form information through axios post it sends a formatted JSON which is perfect, but one portion of the JSON needs to be an array of strings instead it sends one long String in the array. How can I format my code to send an array of string inputs and not one large string? I know my backend works as i've tested the end point using swaggerUi and Postman, The issue is the missile array sending back a single string array element instead of multiple strings. Jackson on the backend will only parse each individually declared string
.
import React, {useEffect, useState} from "react";
import {Link, useNavigate} from "react-router-dom";
import EmpireService from "../service/EmpireService";
const CreateOrder = () => {
const [empireName, setEmpireName] = useState("");
const [hullName, setHullName] = useState("");
const [shieldType, setShieldType] = useState("");
const [weaponType, setWeaponType] = useState("");
const [missiles, setMissiles] = useState([]);
const shipOrder ={empireName,hullName,shieldType,weaponType,missiles};
const saveOrder = (e) => {
EmpireService.createOrder(shipOrder).then((response)=> {
console.log(response.data);
}).catch(error => {
console.log(error);
})
}
return (
<div>
<br></br>
<div className = "container">
<div className = "row">
<div className = "card col-md-6 offset-md-3 offset-md-3 border-success">
<div className = "card-body ">
<form>
<div className = "form-group mb-2">
<label className = "form-label">Empire Name: </label>
<input
type = "text"
placeholder='Enter Empire name'
name = "empireName"
className='form-control border-success'
value = {empireName}
onChange = {(e) => setEmpireName(e.target.value)}>
</input>
</div>
<div className = "form-group mb-2">
<label className = "form-label">Hull Name: </label>
<input type = "text"
placeholder='Enter Hullname'
name = "hullName"
className='form-control border-success'
value = {hullName}
onChange = {(e) => setHullName(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2 ">
<label className = "form-label">Shield: </label>
<input type = "text"
placeholder='Enter Shield type'
name = "shieldType"
className='form-control border-success'
value = {shieldType}
onChange = {(e) => setShieldType(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2 ">
<label className = "form-label">Weapon: </label>
<input type = "text"
placeholder='Enter desired weapon'
name = "weaponType"
className='form-control border-success'
value = {weaponType}
onChange = {(e) => setWeaponType(e.target.value)}
>
</input>
</div>
<div className = "form-group mb-2 ">
<label className = "form-label">Missiles: </label>
<input type = "text"
placeholder='Enter desired missile'
name = "missiles"
className='form-control border-success'
value = {missiles}
onChange = {(e) => setMissiles([e.target.value])}
>
</input>
</div>
<button className='btn btn-success ' onClick={(e) => saveOrder(e)} >Submit</button>
<Link to = "/dawn-parts" className='btn btn-danger'>Cancel</Link>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default CreateOrder;
This is the raw JSON generated after submitting the form I need the missiles array to be separate strings not a single string.
{
"empireName": "Tano",
"hullName": "hull",
"shieldType": "phase_shield",
"weaponType": "ion_cannon",
"missiles": [
"voice_of_Grace, Vengeance_of_Ended_Dreams, plasma_missile"
]
}
My axios service function to connect to the order endpoint.
createOrder(shipOrder){
return axios.post(CREATE_ORDER_URL,shipOrder);
}
I need to search two addresses on the same webpage, one for location, one for correspondence. The first Google API Address works fine, I then tried duplicating the function and form modifying it, but it doesn't populate the second address, it always tries to populate the first address, can anyone tell me where I am going wrong please? Thanks for your help.
function initMap() {
const componentForm = [
'street_number',
'route',
'location',
'locality',
'administrative_area_level_2',
'postal_code',
];
const autocompleteInput = document.getElementById('location');
const options = {
types: ['(cities)'],
componentRestrictions: { country: 'gb' }
};
const autocomplete = new google.maps.places.Autocomplete(autocompleteInput);
autocomplete.addListener('place_changed', function () {
const place = autocomplete.getPlace();
if (!place.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert('No details available for input: \'' + place.name + '\'');
return;
}
fillInAddress(place);
});
function fillInAddress(place) { // optional parameter
const addressNameFormat = {
'street_number': 'short_name',
'route': 'long_name',
'locality': 'long_name',
'administrative_area_level_2': 'short_name',
'postal_code': 'short_name',
};
const getAddressComp = function (type) {
for (const component of place.address_components) {
if (component.types[0] === type) {
return component[addressNameFormat[type]];
}
}
return '';
};
document.getElementById('location').value = getAddressComp('street_number') + ' '
+ getAddressComp('route');
for (const component of componentForm) {
// Location field is handled separately above as it has different logic.
if (component !== 'location') {
document.getElementById(component).value = getAddressComp(component);
}
}
}
}
function initMapAddress2() {
const componentForm = [
'street_number',
'route',
'location',
'locality',
'administrative_area_level_2',
'postal_code',
];
const autocompleteInput = document.getElementById('location2');
const options = {
types: ['(cities)'],
componentRestrictions: { country: 'gb' }
};
const autocomplete2 = new google.maps.places.Autocomplete(autocompleteInput);
autocomplete2.addListener('place_changed', function () {
const place2 = autocomplete2.getPlace();
if (!place2.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert('No details available for input: \'' + place2.name + '\'');
return;
}
fillInAddress(place2);
});
function fillInAddress(place2) { // optional parameter
const addressNameFormat = {
'street_number2': 'short_name',
'route2': 'long_name',
'locality2': 'long_name',
'administrative_area_level_22': 'short_name',
'postal_code2': 'short_name',
};
const getAddressComp = function (type) {
for (const component of place2.address_components) {
if (component.types[0] === type) {
return component[addressNameFormat[type]];
}
}
return '';
};
document.getElementById('location2').value = getAddressComp('street_number2') + ' '
+ getAddressComp('route2');
for (const component of componentForm) {
// Location field is handled separately above as it has different logic.
if (component !== 'location2') {
document.getElementById(component).value = getAddressComp(component);
}
}
}
}
<div class="card-container">
<div class="panel">
<div>
<img class="sb-title-icon" src="https://fonts.gstatic.com/s/i/googlematerialicons/location_pin/v5/24px.svg" alt="">
<span class="sb-title">Correspondence Address</span>
</div>
<input type="text" placeholder="Search Address" id="location" />
<input type="text" placeholder="" id="street_number" />
<input type="text" placeholder="" id="route" />
<input type="text" placeholder="" id="locality" />
<div class="half-input-container">
<input type="text" class="half-input" placeholder="" id="administrative_area_level_2" />
<input type="text" class="half-input" placeholder="" id="postal_code" />
</div>
</div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=****************Zv_k&libraries=places&callback=initMap&channel=GMPSB_addressselection_v1_cA" async defer></script>
<div class="card-container">
<div class="panel">
<div>
<img class="sb-title-icon" src="https://fonts.gstatic.com/s/i/googlematerialicons/location_pin/v5/24px.svg" alt="">
<span class="sb-title">Location Address</span>
</div>
<input type="text" placeholder="Search Address" id="location2" />
<input type="text" placeholder="" id="street_number2" />
<input type="text" placeholder="" id="route2" />
<input type="text" placeholder="" id="locality2" />
<div class="half-input-container">
<input type="text" class="half-input" placeholder="" id="administrative_area_level_22" />
<input type="text" class="half-input" placeholder="" id="postal_code2" />
</div>
</div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=****************Zv_k&libraries=places&callback=initMapAddress2&channel=GMPSB_addressselection_v1_cA" async defer></script>
For anyone else having the same issue, I found a solution here Multiple Address on same page
I'm new to vueJS.
I want to add a required validator in image using vee-validate.
Built-in required validador isn't working so I created a custom validator img_required.
here's what I've done so far.
.vue html part
<ValidationProvider rules="image|img_required" bail="false" v-slot="{ errors, validate }">
<div class="row row-xs mg-t-20 mx-0">
<label class="col-sm-4 form-control-label">
<span class="tx-danger">*</span> Image:
</label>
<div
#dragover.prevent
#change="validate"
#drop.prevent
class="file-wrapper col-sm-8 mg-t-10 mg-sm-t-0"
>
<div v-if="imgUrl">
<button #click="imageNull" class="img-close">
<b-icon icon="x"></b-icon>
</button>
<img style="height: 127px" :src="imgUrl" />
</div>
<input type="text" hidden v-model="imgUrl" />
<div v-if="!imgUrl" #drop="handleImage($event, 'drop')">
<input
type="file"
class="form-control"
name="file"
accept="image/*"
#change="handleImage($event, 'input'); validate()"
/>
Drop image
</div>
</div>
</div>
<div v-for="error in errors" :key="error">{{ error }}</div>
</ValidationProvider>
I can't use v-model in input type file so I created a dummy hidden input field and passed imgUrl in v-model <input type="text" hidden v-model="imgUrl" />
imgUrl gets image src from file drop or input file.
I added a close button to nullify imgUrl variable.
<button #click="imageNull" class="img-close">
<b-icon icon="x"></b-icon>
</button>
I pass this imgUrl to vee-validate extend method.
.vue script part
data() {
return {
imgUrl: ""
}
},
methods: {
handleImage(e, action) {
var file;
if (action == "input") file = e.target.files[0];
else if (action == "drop") file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.onload = (e) => {
this.imgUrl = e.target.result;
};
reader.readAsDataURL(file);
},
imageNull() {
this.imgUrl = "";
},
}
here's validation.js file
extend('img_required', {
validate(imgUrl) {
console.log(imgUrl);
return imgUrl !== "";
},
message() {
return "Image is required!";
}
});
Here I'm checking if imgUrl is an empty base64 string or not.
And when I hit close button, imgUrl nullifies.
The issue here is when I print this imgUrl it shows event.target.files.
I'm new to vue.js. Please tell me if I'm doing something wrong
I think the problem is that you're handleImage function is not correct
Try this;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
this.imgUrl = reader.result;
};
I am trying to populate a select menu in redux forms dynamically.
I've been using the debugging tools in chrome and can see that the 'departments' variable sees the array list
({departments.map(department => <option key={department} value={department}>{department}</option>)}
but the final choice list isn't populating. I'm guessing it has something to do with the renderSelectField function, but I'm not sure what I am overlooking?
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import isValidEmail from 'sane-email-validation';
class SimpleReduxForm extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.renderSelectField = this.renderSelectField.bind(this);
}
onSubmit = async (data) => {
try {
let response = await fetch('/api/getRecords', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-type': 'application/json'
}
});
let responseJson = await response.json();
//display success message to user
alert('Form successfully submitted')
return responseJson;
//reset form
} catch (error) {
alert(error);
}
}
renderInputField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<input placeholder={field.label} {...field.input} className="form-control" type={field.input.type} />
</div>
</div>
)
}
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option></select>
</div>
</div>
)
}
render() {
const { handleSubmit, pristine, reset, submitting, invalid } = this.props;
//Options for select - this should be an AJAX call to a table to get options list
const departments = ["Dept 1", "Dept 2", "Dept 3"]
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
label="Username"
name="username"
component={this.renderInputField}
type="text"
/>
<Field
label="Email"
name="email"
component={this.renderInputField}
type="email"
/>
<Field
label="Age"
name="num_field"
component={this.renderInputField}
type="text"
/>
<Field
label="Department"
name="department"
defaultselection="Select..."
component={this.renderSelectField}>
{departments.map(department => <option key={department} value={department}>{department}</option>)}
</Field>
<div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
<button type="button" className="btn btn-warning" disabled={pristine || submitting} onClick={reset}> Clear Values </button>
</div>
</form >
)
}
}
//Validate Errors Before Submission
const validate = (values) => {
//create errors object
const errors = {}
/*Example showing to check is a field contains data
* if no, submission == invalid*/
if (!values.username) {
errors.username = 'Required!'
}
/*check to see if email is provided and that submission is an actual email address*/
if (!values.email) {
errors.email = 'Required!'
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid Email!'
}
/* Example to show that the length of a field
* can be checked as part of validation */
if (values.num_field < 2) {
errors.num_field = "Must be at least 10 years old"
}
return errors
}
const mapStateToProps = state => ({
SimpleReduxForm: state.form.SimpleReduxForm
});
export default reduxForm({
validate,
form: 'SimpleReduxForm',
enableReinitialize: true,
keepDirtyOnReinitialize: true,
})(connect(mapStateToProps)(SimpleReduxForm));
I figured it out. Just in case anyone else runs into this issue. I needed to add {field.children} into the renderSelectField function. So the final function looks like:
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option>{field.children}</select>
</div>
)
}
I'm using redux-form and on blur validation. After I type the first character into an input element, it loses focus and I have to click in it again to continue typing. It only does this with the first character. Subsequent characters types remains focuses. Here's my basic sign in form example:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../actions/authActions';
require('../../styles/signin.scss');
class SignIn extends Component {
handleFormSubmit({ email, password }) {
this.props.signinUser({ email, password }, this.props.location);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
{this.props.errorMessage}
</div>
);
} else if (this.props.location.query.error) {
return (
<div className="alert alert-danger">
Authorization required!
</div>
);
}
}
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.email) {
errors.email = 'Enter a username';
}
if (!values.password) {
errors.password = 'Enter a password'
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error }
}
SignIn = reduxForm({
form: 'signin',
validate: validate
})(SignIn);
export default connect(mapStateToProps, actions)(SignIn);
This happens because you're re-defining renderField as a new component every time you render which means it looks like a new component to React so it'll unmount the original one and re-mounts the new one.
You'll need to hoist it up:
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
class SignIn extends Component {
...
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
...
As #riscarrott mentioned, put renderField outside of component class .
But I am still losing focus .. And after testing, I concluded the re-rendering is done because of using curried function (return another function, and not return element . directly) .
const const renderField = (InputComponent = 'input') => ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<InputComponent {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
Then, if your renderField is a curried function :
then , don't do 😔😔😔😔:
//.....
<Field name="email" type="text" component={renderField('input')} label="Email Address" />
<Field name="desc" component={renderField('textarea')} label="Email Address" />
But , do the following 🙂🙂🙂🙂 :
// outside component class
const InputField = renderField('input');
const TextAreaField = renderField('textarea');
// inside component class
<Field name="email" type="text" component={InputField} label="Email Address" />
<Field name="desc" component={TextAreaField} label="Email Address" />
What worked for me was refactoring arrowFunction-based Component to class-based Component as the behavior of InputForm components was weird. Every time the value of each input was changed they all rerendered even after splitting each inputType to separated components. There was nothing else left to fix but changing main component to class-based. I guess it may be caused by redux-form itself.
This can also happen if you have defined styled-components inside your render function.
You should define them outside your class.
Like this:
const Row = styled.div`
justify-content:center;
`;
const Card = styled.div`
width:18rem;
padding:1rem;
`;
class Login extends Component{
i have the same problem. i resolved mine by changing the component to Class component and i removed all the css style config from render().
I had the same problem. I solved it when I added my react redux form to the store in the createForms():
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
tasks: Tasks,
task: Task,
image: Image,
admin: Admin,
pageId: PageID,
fieldValues: FieldValues,
formValues: FormValues,
...createForms({
createTask: initialTask,
editTask: initialEditTask
})
}),
applyMiddleware(thunk, logger)
);
return store;
}
I had the same problem, and none of the answers worked for me.
But thanks to Advem's answer I got an idea of what could be wrong:
My form required accordion UI, and for that I had state variable in it:
const ConveyorNotificationSettingsForm = (props) => {
const {handleSubmit, formValues, dirty, reset, submitting} = props;
const [expandedSection, setExpandedSection] = useState(null);
...
with only one expanded section, that with its index equal to expandedSection .
After I extracted the accordion to a separate functional component and moved useState there, the problem was gone.
actually, this is a problem with the function component. I used a class-based component with redux form and my problem solved. I don't know the exact reason but redux form re-renders when we enter the first word and losses focus. use class-based components whenever you want to use redux form.
class StreamCreate extends React.Component{
rendorInput(formProps){
return <input {...formProps.input} />;
}
render(){
return (
<Container maxWidth="lg">
<form action="">
<Field name="title" component={this.rendorInput}/>
<Field name="description" component={this.rendorInput} />
</form>
</Container>
)
}
}
export default reduxForm({
form: 'createStream'
})( StreamCreate);