How to upload images in react via api? - reactjs

Today I saw a number of tutorials on how to upload photos in react via the api.
I did everything, tried all the methods. But in the end I get stuck.
(During the whole explanation I will focus only on the features of the image upload)
In Models I have groups and variable -
[NotMapped]
public IFormFile ImageFile {get; set; }
In api I get
[Route ("Add")]
[HttpPost]
public void Post (Group group)
And I have in state-
const initialFieldValues ​​= {
GroupName: '',
GroupAbout: '',
imageName: '',
imageSrc: defaultImageSrc,
imageFile: null
}
const [values, setValues] = useState (initialFieldValues)
When changing the image has a function-
const handleImg = (e) => {
if (e.target.files && e.target.files [0]) {
let imageFile = e.target.files [0];
const reader = new FileReader ();
reader.onload = x => {
setValues ​​({
... values,
imageFile,
imageSrc: x.target.result
})
}
reader.readAsDataURL (imageFile)
SetDisplayImg ("block");
}
else {
setValues ​​({
... values,
imageFile: null,
imageSrc: defaultImageSrc
})
}
};
And when submitting the form
const handleFormSubmit = e => {
e.preventDefault ()
const formData = new FormData ()
.append ('groupImage', values.imageFile)
addOrEdit (formData)
}
const addOrEdit = (formData) => {
axios.post ('api / groups / add', formData) .catch (error => {
console.log (error.response.data);
console.log (error.response.status);
console.log (error.response.headers);
});
}
In this code -makes error 415 (even regardless of uploading the image but, even if I put it only other variables that get stringed and work normally.)
If I add [FromForm] in the api it does not respond to me, i.e. it does not write me an error message nor does it reach the api (I checked in debugging)
If I change the axios to
const obj = {'groupImage': values.imageFile
}
axios.post ('api / groups / add', obj) .catch (error =>
I get an error message 400-
"The JSON value could not be converted to System.String. Path: $ .groupImage
And if I send the value from state
axios.post ('api / groups / add', values)
I get an error message System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'. Path: $ .imageFile | LineNumber: 0 | BytePositionInLine: 6939781.
---> System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'.
Anything I try to fix, it causes another error, I'm really at a loss.
Regards

>.append ('groupImage', values.imageFile)
Firstly, please make sure the key of your formdata object can match your model class property's name.
formData.append('imageName', values.imageName);
//...
//it should be imageFile, not groupImage
formData.append('imageFile', values.imageFile);
Besides, please apply the [FromForm] attribute to action parameter, like below.
public void Post([FromForm]Group group)
{
//...
Test Result

Usually a 415 means you aren't setting the right Content-Type header. Does the API you are trying to upload to mention acceptable types or encodings it expects?

Related

How can I know if the save method worked in mongo

im new using mongo and when i save a new object in my db i use the save method, but when using it and then printing the result, if it was successful i get the object, not someting that i can use to handle any error in the front end
router.post("/post_recipe", async (request, response) => {
const {title, content, author} = request.body;
const new_post = new Posts({title, content, author});
new_post.save(sdfs).then((response) => {
response.json(response);
}).catch(error => response.json(error));
});
doing this on purpose i get the error in the console but its not sending it to the front end to handle it and tell the user that there was a problem
Posts its a scheme, dont know if it has something to do with
Problem is you are using same variable name for your router response and save method response.
Solution
router.post("/post_recipe", async (req, res) => {
const {title, content, author} = req.body;
const new_post = new Posts({title, content, author});
// Getting rid of .then and .catch method
new_post.save((err, savedPost) => {
// Your custom error message
if (err) return res.status(400).json('post not saved due to some problem! Please try again');
// Post that you just saved in db
return res.json(savedPost);
});
This happen due to scope of variable.
For more detail check this article from w3schools.com

Allowing a multipart (image) upload through GraphQL for Elixir backend

I was hoping to get a clearer understand where I may be going wrong. Sorry if I ask a lot of questions since I feel a bit lost and been stuck for about a week on this one.
Currently, I've changed the package I was using for linking with apollo-client. The previous package was apollo-link-http and now I'm using apollo-absinthe-upload-link since I read it allows for image upload.
It's done as follows
const httpLink = createLink({
uri: 'http://localhost:4000/api',
credentials: 'same-origin',
fetch,
fetchOptions
});
There wasn't any change to sending information to the backend but I do continue to be lost in regards to uploading an image. The purpose of this image is to upload to the cloud then save the url information with the product details.
I'm using a hook as const [ images, setImages ] = useState([]); and an input of <input type="file" placeholder="Upload Image" onChange={handleUploadImage} />
The purpose of the onChange function is to set the image information to the images property. When we send the mutation to the backend, the way it's done is as follows
const buildForm = () => {
let storeID = 2;
const fileData = fileList.fileList.map((file) => {
return file.originFileObj;
});
const data = new FormData();
data.append('fileData', images);
debugger;
return {
productName,
productDescription,
productPrice,
productType,
isReturnable,
storeID,
fileData: data
};
};
when it goes, within my console I'm getting an error of Uncaught (in promise) Error: GraphQL error: Argument "fileData" has invalid value $fileData. and I'm seeing on the backend the key fileData having an empty object as its value. I was hoping to get some advice on what might be wrong or what I should consider. If someone mentioned CURL please explain since I have no idea what that means in regards to GraphQL and sending a mutation. Thank you for the help on this matter.
P.S - The mutation call that is being used is
export const CREATE_PRODUCT_MUTATION = gql`
mutation CreateProduct(
$storeID: Int!
$productName: String!
$productDescription: String!
$productPrice: Decimal!
$productType: Int!
$isReturnable: Boolean!
$fileData: Upload!
) {
createProduct(
product: {
productName: $productName
productDescription: $productDescription
productPrice: $productPrice
productType: $productType
isReturnable: $isReturnable
}
storeId: $storeID
fileData: $fileData
) {
id
productName
productDescription
productPrice
}
}
`;
UPDATE - Network return request
{"errors":[{"locations":[{"column":0,"line":2}],"message":"Argument \"fileData\" has invalid value $fileData."}]}
Backend Schema
#desc "List a new product"
field :create_product, :product do
arg(:product, :new_product)
arg(:store_id, :integer)
arg(:file_data, non_null(:upload))
apollo-absinthe-upload-link expects a variable of type File or Blob (see here), but you are passing fileData as type FormData.
Since your input type is file, you could do:
const handleUploadImage = (event) => setImages(event.target.files);
const buildForm = () => ({
productName,
productDescription,
productPrice,
productType,
isReturnable,
storeID: 2,
fileData: images[0],
});
References:
file-selector
react-dropzone

Is it possible to use the Symfony form component with React.js?

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.

Perform Asynchronous Decorations in DraftJS?

I'm trying to perform real-time Named Entity Recognition highlighting in a WYSIWYG editor, which requires me to make a request to my back-end in between each keystroke.
After spending about a week on ProseMirror I gave up on it and decided to try DraftJS. I have searched the repository and docs and haven't found any asynchronous examples using Decorations. (There are some examples with Entities, but they seem like a bad fit for my problem.)
Here is the stripped down Codepen of what I'd like to solve.
It boils down to me wanting to do something like this:
const handleStrategy = (contentBlock, callback, contentState) => {
const text = contentBlock.getText();
let matchArr, start;
while ((matchArr = properNouns.exec(text)) !== null) {
start = matchArr.index;
setTimeout(() => {
// THROWS ERROR: Cannot read property '0' of null
callback(start, start + matchArr[0].length);
}, 200) // to simulate API request
}
};
I expected it to asynchronously call the callback once the timeout resolved but instead matchArr is empty, which just confuses me.
Any help is appreciated!
ok, one possible solution, a example, simple version (may not be 100% solid) :
write a function take editor's string, send it to server, and resolve the data get from server, you need to figure out send the whole editor string or just one word
getServerResult = data => new Promise((resolve, reject) => {
...
fetch(link, {
method: 'POST',
headers: {
...
},
// figure what to send here
body: this.state.editorState.getCurrentContent().getPlainText(),
})
.then(res => resolve(res))
.catch(reject);
});
determine when to call the getServerResult function(i.e when to send string to server and get entity data), from what I understand from your comment, when user hit spacebar key, send the word before to server, this can done by draftjs Key Bindings or react SyntheticEvent. You will need to handle case what if user hit spacebar many times continuously.
function myKeyBindingFn(e: SyntheticKeyboardEvent): string {
if (e.keyCode === 32) {
return 'send-server';
}
return getDefaultKeyBinding(e);
}
async handleKeyCommand(command: string): DraftHandleValue {
if (command === 'send-server') {
// you need to manually add a space char to the editorState
// and get result from server
...
// entity data get from server
const result = await getServerResult()
return 'handled';
}
return 'not-handled';
}
add entity data get from server to specific word using ContentState.createEntity()
async handleKeyCommand(command: string): DraftHandleValue {
if (command === 'send-server') {
// you need to manually add a space char to the editorState
// and get result from server
...
// entity data get from server
const result = await getServerResult()
const newContentState = ContentState.createEntity(
type: 'string',
mutability: ...
data: result
)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
// you need to figure out the selectionState, selectionState mean add
// the entity data to where
const contentStateWithEntity = Modifier.applyEntity(
newContentState,
selectionState,
entityKey
);
// create a new EditorState and use this.setState()
const newEditorState = EditorState.push(
...
contentState: contentStateWithEntity
)
this.setState({
editorState: newEditorState
})
return 'handled';
}
return 'not-handled';
}
create different decorators find words with specific entity data, and return different style or whatever you need to return
...
const compositeDecorator = new CompositeDecorator([
strategy: findSubjStrategy,
component: HandleSubjSpan,
])
function findSubjStrategy(contentBlock, callback, contentState) {
// search whole editor content find words with subj entity data
// if the word's entity data === 'Subj'
// pass the start index & end index of the word to callback
...
if(...) {
...
callback(startIndex, endIndex);
}
}
// this function handle what if findSubjStrategy() find any word with subj
// entity data
const HandleSubjSpan = (props) => {
// if the word with subj entity data, it font color become red
return <span {...props} style={{ color: 'red' }}>{props.children}</span>;
};

Success / Error Reporting from Service with Events / Observables

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)

Resources