I am trying to build login with react.js and connect it to my springboot.
Here is my code, react.js:
import React from 'react';
export default class Login extends React.Component {
constructor() {
super();
this.state = {
login:"",
password:""
}
}
// This will be called when the user clicks on the login button
login(e) {
e.preventDefault();
console.log(this.state.password)
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
if (!xhr) {
throw new Error('CORS not supported');
}
fetch().then(r => r.json())
.then(data => console.log(data))
.catch(e => console.log(e))
}
render() {
return (
<form role="form">
<div>
<input type="text" name="login" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
</div>
<button type="submit"onClick={this.login.bind(this)}>Login</button>
</form>
);
}
}
And this is my springboot code that is located as TestController:
#RestController
public class TestController {
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
public Boolean testLogin(#RequestParam String login, #RequestParam String password) {
if ( login.equals ("ajt"))
return true;
else {
return false;
}
}
Each of them are present in two different ports, react on :9000 and springboot on :8080.
Also, on my react page I get the error:
TypeError: Failed to execute 'fetch' on 'Window': 1 argument required, but only 0 present.(…)
Any ideas?
for info: I have only got 6 months coding behind me -_- please be kind!
There's a handful of small mistakes here. I'll try to point you in the right direction for a few of them.
First of all, I just wouldn't use fetch. It's listed as an expiremntal technology by MDN, and it's browser support is weak. For someone just starting out with web development, you're much better off using a more established and "safe" technology. Either simple XMLHTTP ajax or using the ajax method from jquery. You already seem to be going down the path of XMLHTTP, so I would suggest just replacing your fetch commands with the example I linked above.
Second, you're using two different HTTP methods. Your ajax object is going to send a POST command, but your server is listening for a GET command. This is set up on the following lines;
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
If you want those two pieces of code to talk to one another, they need to be set to the same method. In this case, you want a POST for both, but it's worth learning the distinction between the two for the future.
Lastly, there's the issue of getting information from your inputs and into your ajax XMLHTTP object. To do this, you're going to want to set up onChange hooks on the inputs, and attach them to a handleChange function within the react component. Use this function to save the values to the component's state, and then later take the values out of state to apply to the xmlhttp.
A simple example of what I am describing;
render() {
return (
<div>
<input type="text" name="login" onChange={this.handleChange}/>
<button onClick={this.login.bind(this)}>Login</button>
</div>
);
}
handleChange(value) {
this.setState({login: value});
}
login () {
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.open("POST", "/test/login");
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify({login: this.state.login}));
}
Related
I am trying to Push to a new page once a user has filled out a form using this.props.history.push inside the function below.
handleSubmit = async event => {
event.preventDefault()
try {
const res = await newEnquiry(this.state.formData)
this.props.history.push('/downloads')
console.log(res)
} catch (err) {
console.log(err.response.data)
}
}
The ReactJS form is working fine on the /contacts page, and submits information to my Django back-end so I know it's working OK, however I cannot get the redirect to work and it's giving me this error message.
<form onSubmit={this.handleSubmit}> is inside my form tag and that's working fine so I am pretty sure it's not a problem with my form.
Api.js
const baseUrl = '/api'
export const newEnquiry = formData => {
return axios.post(`${baseUrl}/enquiries/`, formData)
}
Views.py
class EnquiryListView(APIView):
def get(self, _request):
enquiries = Enquiries.objects.all()
serialized_enquiries = EnquirySerializer(enquiries, many=True)
return Response(serialized_enquiries.data, status=status.HTTP_200_OK)
def post(self, request):
created_enquiry = EnquirySerializer(data=request.data)
if created_enquiry.is_valid():
created_enquiry.save()
return Response(created_enquiry.data, status=status.HTTP_201_CREATED)
return Response(created_enquiry.errors)
serializers.py
class EnquirySerializer(serializers.ModelSerializer):
class Meta:
model = Enquiries
fields = '__all__'
In your case of this problem, when the error fires off, it is unable to read the err.response.data property. If there was no error, it would redirect you. In your Django app, check what the error handler is suppose to return.
Quick review of try/catch.
try {
// if everything passes, run this block
} catch (err) {
// if something goes wrong, run this block
}
In this case, be sure to check what your full error is. It might be a 404 or something totally unexpected.
I am using the Symfony form component. I have many forms in my project.
To perfectly learn the form component was a long way to go, but now I love it. I love also the automatic validation and so on.
So. now I want to learn and use React.js in my project.
But it seems, there is no way I can use the validation and form builder like before for the projects? Am I right there?
While, in an API context, you won't use the Form Component to actually render your form in HTML format ($form->createView() method), you can still benefit from all the magic it offers: validation, form events, etc. API or not, I personnally think you should always use Form Types in Controller mutations.
For example, using FOSRestBundle, consider some simple Controller action looking like this:
/**
* #Rest\Put("posts/edit/{id}", name="posts.edit", requirements={"id"="\d+"})
*
* #param Post $post
* #param Request $request
*
* #return Post
*
* #throws BadRequestException
*
*/
public function edit(Post $post, Request $request): Post
{
$form = $this->createForm(PostType::class, $user);
$form->handleRequest($request);
$form->submit($request->request->all());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return $post;
}
// Any error handling of your taste.
// You could consider expliciting form errors.
throw new BadRequestException();
}
Note that Post entity and PostType form must be created, of course. I won't be detailing it here as there is nothing special to say about them in this context.
Also note that we don't check if the form is submitted. Rendering the form as HTML being React's job in your case, this action won't be used to GET anything, it is exclusively a PUT route. This means that any Request coming there MUST be a PUT Request containing the correct data to be handled by our PostType, submitted in your case by an HTML form manually built in React.
Furthermore, slightly out of the question's scope, FOSRestBundle subscribes to whatever your action returns and automatically serializes it to the configured format (let's say JSON in your case, I guess). This means that our example of action can return two possible responses:
A Response with status code 200 containing our serialized Post. (You could also consider a 204 and return nothing.)
A Response with status code 400 containing whatever we want (let's say form errors).
Allow me to lead you to the FOSRestBundle's documentation.
You can use your form created with the formBuilder without problem.
You must get your form with axios and create a new component like this:
const url = 'localhost/post/new';
const ref = useRef(null);
const [form, setForm] = useState('');
const fetchData = () => {
axios.get(url))
.then(function (response){
setForm(response.data);
})
.catch(function (error){
//something
})
;
}
const MyComponent = () => {
useEffect(() => {
const element = ref.current.firstChild;
if (ref.current.firstChild === null)
return;
element.addEventListener('submit', () => {handleSave(event)});
return () => {
element.removeEventListener('submit', () => {handleSave(event)});
};
}, []);
return (
<div ref={ref} dangerouslySetInnerHTML={{ __html: form }} />
);
};
const handleSave = (event) => {
event.preventDefault();
let formData = new FormData(event.target)
let action = event.target.action;
let files = event.target.querySelectorAll('[type="file"]');
if (files)
files.forEach((file) => {
formData.set(file.name, file.files[0])
});
axios.post(action, formData)
.then(function (response) {
Swal.fire({
icon: 'success',
title: response.data.message,
showConfirmButton: false,
timer: 1500
})
//Do something else
})
.catch(function (error) {
error.response.status === 422 ?
setForm(error.response.data)
:
console.log(error);
});
}
return (<MyComponent/>);
So, now you can get the form with html components and render it with the React Component.
If you get some validation error you get a 422 status and you can replace the form with setForm().
In your Symfony Controller you must set something like this:
#[Route('/post/{state}/{id}', name: 'post', defaults: ['state' => null, 'id' => null])]
public function post(
?string $state,
?int $id,
Request $request,
EntityManagerInterface $em
): JsonResponse|Response
{
if ($state == 'new') {
$post = new Post();
$form = $this->createFormBuilder()
->add('title', TextType::class)
->add('content', TextareaType::class);
$form = $form->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$em->persist($post);
$em->flush();
return $this->json(['message' => 'post added!'], 200);
}
return $this->renderForm('{{ form(form) }}', [
'form' => $form
]);
}
}
I have reduced the function only for the form, but you can use it for all your requests.
Probably not because your form is intended for server use: validation/show errors/data normalization/sanatization/etc.
Usually you use React to display HTML and connect it to your server using an API.
I have this code:
await commuteReportService.getAddresses(query).pipe(debounceTime(1000))
.subscribe((response: AddressesAPIResponse) => {
console.log('execute call', response);
});
However, in the network tab I still see that all the requests are being sent. This code is used in a autocomplete component where I want to limit the calls being sent.
I'm using rxjs 5.5, React and Typescript.
debounceTime is only applied to whatever comes after it, meaning it only applies to your 'subscribe' — not getAddresses(query), which is where I assume you're making calls.
Consider this minimal example:
// html
<input type="text" id="example" />
// js
const input = document.getElementById('example');
Rx.Observable
.fromEvent(input, 'keyup')
.map(v => {
console.log('called')
return v.target.value;
})
.debounceTime(500)
.subscribe(val => {
console.log(`Debounced: ${val}`);
});
Even though Debounced... is delayed, you'll still see called being logged to the console on every keystroke. If we change the order
// js
Rx.Observable
.fromEvent(input, 'keyup')
.debounceTime(500)
.map(v => {
console.log('called')
return v.target.value;
})
.subscribe(val => {
console.log(`Debounced: ${val}`);
});
Now both Debounced... and called are delayed (Here's the fiddle for the code above).
I am trying to call REST endpoints on one application (spring-boot) from another (reactjs). The applications are running on the following hosts and ports.
REST application, using spring boot, http://localhost:8080
HTML application, using reactjs, http://localhost:9000
I am trying to send the login info from reactjs to spring-boot but without success.
Reactjs:
import React from 'react';
export default class Login extends React.Component {
constructor() {
super();
this.state = {
login:"",
password:""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState({login: this.state.login});
}
render() {
return (
<form role="form">
<div>
<input type="text" name="login" onChange={this.handleChange} />
<input type="password" name="password"/>
</div>
<button onClick={this.login.bind(this)}>Login</button>
</form>
);
}
login () {
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
var url = "http://localhost:8080/test/login"
xmlhttp.open("POST", url );
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify({login: this.state.login}));
}
}
and Spring-boot:
#CrossOrigin(origins = "http://localhost:9000")
#RequestMapping(value = "/test/login")
public Boolean testLogin(#RequestParam String login) {
if ( login.equals ("ajt"))
return true;
else {
return false;
}
}
I can see that the two apps are connecting for even though reactjs gives me error 400, when I submit, the console of the Spring-boot app tells me:
Resolved exception caused by Handler execution: org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'login' is not present
I can only assume that from the Spring-boot side, it cannot translate what ever is sent via react.js.
ps: bear with me, I have been coding for about 6 months.
I knew this looked familiar. Sorry my last answer didn't fix all your issues.
Your current problem is here;
public Boolean testLogin(#RequestParam String login) {
Should be
public Boolean testLogin(#RequestBody String login) {
EDIT:: Second problem.
Your handleChange function isn't taking in any values! It should look more like this;
handleChange(value) {
this.setState({login: value});
}
When your input field calls this function, it needs to pass a value from the input into the state. Your current code is essentially the same as this;
this.state.login = this.state.login;
Which obviously isn't going to get you anywhere.
Try that change. If it still does not work, be sure you open your dev-tools in your browser and step through the code line by line to be sure it is executing and storing the values you want it to.
Using some documentation I found online, I've written a service method for saving some data like this:
#Injectable
export class BrandService {
brands$: Observable<Brand[]>;
private _brandsObserver: Observer<Brand[]>;
private _dataStore: {
brands: Brand[]
};
constructor(private http: Http) {
this.brands$ = new Observable(observer => this._brandsObserver = observer).share();
this._dataStore = { brands: []};
}
saveBrand(brand: Brand) {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post("http://localhost:8080/api/brands", JSON.stringify(brand), { headers: headers })
.map( (response: Response) => response.json()).subscribe(
data => {
this._dataStore.brands.push(data);
this._brandsObserver.next(this._dataStore.brands);
},
error => console.log("Could not create Brand"));
}
}
What this allows me to do is push updates to my collection of Brands and my table on the view will observe these changes and update automatically so I don't have to manually refresh it. All is well with the world.
My problem is that since I'm subscribing to the http.post in the service, my component now has no way of knowing whether or not this call succeeded, which also means that, since I'm showing the form in a modal dialog, I don't know if I should close the dialog or display errors. My component simply does this...
this._brandService.saveBrand(this.brandForm.value);
So, I was thinking that I should figure out a way to a) fire an event in the service that I'm listening for in the component for when good / bad things happen and act accordingly, or b) figure out some way of observing some other properties in the service that I can act on when those changes are detected. But I'm pretty new to all this observable stuff and I don't really even know where to begin.
data => {
this._dataStore.brands.push(data);
this._brandsObserver.next(this._dataStore.brands);
// fire some success event or
// update some observed property
},
error => {
// fire some failure event or
// update some observed property
}
You could do the subscribe() at call site
saveBrand(brand: Brand) {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post("http://localhost:8080/api/brands", JSON.stringify(brand), { headers: headers })
.map( (response: Response) => response.json()).map(
data => {
this._dataStore.brands.push(data);
this._brandsObserver.next(this._dataStore.brands);
});
}
this._brandService.saveBrand(this.brandForm.value)
.subscribe(
value => onSuccess(),
error => onError());
If you still want to do some generic error handling in saveBrand you can use the catch operator (like used in Intercepting errors and subscribing to)