Assign Uploaded File to Variable Rails Carrierwave
Nosotros're going to build an app to manage breakfast recipes. We won't focus on developing all Crud functionality, because information technology's non in our scope. Instead, we're going to focus on iv main steps:
- Create a elementary API for recipes. We're going to talk near serialization a little bit.
- Add Bulma to brand our app look good.
- Consume API from React components. We're going to use axios library to make requests to the backend and create React component from scratch. Additionally, you'll see the render prop blueprint in action.
- Upload files. We're diving into Active Storage characteristic and react-dropzone-component library.
The app nosotros're going to create looks something like this:
If you desire to skip ahead to the finished repo, you can practise so here.
Prerequisites
Before we begin, make sure you lot have scarlet version >= 2.5.0 and Rails version 5.2.0. The ActiveStorage precious stone is included on Rails 5.two by default. Y'all can simply check your version:
cherry -v # cerise 2.5.0 rails -v # Runway five.2.0
[ If your reddish version is not up to appointment, yous can update it with a ruby version manager like rvm or rbenv. ] You can find how to integrate Runway app with React in our previous articles here (with webpacker jewel) and here (with create-react-app tool). There are a bunch of interesting articles on the Cyberspace that you can help yous do it and so experience gratis to choose the all-time for you.
Step 1: Create a simple API to update recipes
The reason for this tutorial is to demonstrate how to use Active Storage together with a React library so we won't spend time building full Grime. Let's generate Recipemodel.
rails k model Recipe title:string description:string teaching:cord
and populate db/seeds.rb. To save time, you tin use these recipes examples. Then run:
rail db:create db:drift db:seed
The command respectively creates our DB, executes migrations and populates tables with data.
API Endpoints
In one case we finished with modeling, now it'southward time to take care of API endpoints. In our routes, nosotros'll need to claw up the recipes resource. We'll focus only on listing recipes and updating a unmarried recipe. So add these lines to config/routes.rb: [ scope :api changes the resources path and adds /api prefix before /recipes. It helps to differ what are API paths. Here is a nice article what you can do with scope. ] Our API will betrayal the post-obit RESTful endpoints: We notwithstanding need to create methods for these routes; so permit's do information technology. Create recipes controller app/controllers/recipes_controller.rb, and add the following in the file: Next up, restart your server, and yous should be able to visit http://localhost:3000/api/recipes to come across the recipes we'd populated indb/seeds.rb:
Serialization
Fifty-fifty though Track provides JSON serialization by default, nosotros're going to use ActiveModel::Serializer, because in a real application the framework feature is ordinarily not enough. ActiveModel::Serializer encapsulates the JSON serialization of object. It allows defining attributes and relationships which should be included in a JSON response. It as well acts every bit a presenter, then you tin can define custom methods to override how object properties are displayed or show boosted information nearly an object. Add the post-obit gem to your application's Gemfile:
jewel 'active_model_serializers'
And run package to install this jewel:
bundle install
In one case nosotros have the precious stone installed, we can generate the Recipe serializer:
rails g serializer Recipe
The generator will create a new serializer within app/serializers/ binder. Fill the file with the following: attributes defines what should be included in a JSON response. Moreover, we override updated_at to have nicely formatted output. If you lot make a request again to http://localhost:3000/api/recipes, you should get a serialized response:
Step 2: Add Bulma to make our app looks good
Bulma is an open source CSS framework based on Flexbox. To make it work, only run the control in your console:
yarn add bulma
or
npm install bulma
After installation, yous can import the CSS file into the project using this snippet inside packs/awarding.js:
import 'bulma/css/bulma.css';
Now nosotros can start building our React components!
Step 3: Consume API from React components
Start create App.jsx file nether app/javascript/packs. It's going to exist our parent component. Fill the file with the following: Now we accept to tell React where inside the DOM tree the App component should be rendered. Jump into packs/application.js. Our App component will exist rendered inside div chemical element with 'root' id. Make sure your rails server and web pack are running. When you hit localhost, y'all should see "Hello at that place". Now it would exist nice to display our recipes right? For at present, nosotros're going to mock recipes data. Change the App component to wait like: We nowadays the mocked data as key pair value objects. The reason is to have the power to expect for recipes while updating easily. As nosotros take plenty of recipes, we need to iterate over them. We'll put this logic to RecipeList component: Our primary event is to split up concerns of displaying and editing a recipe. To achieve this, first create a NonEditableRecipe functional component that will be responsible only for displaying information about a single recipe. React allows creating reusable components and then we can employ NonEditableRecipe component and display information technology in every iteration passing single recipe as props. Now NonEditableRecipe has access to recipe property taken from its parent component. Equally you can encounter we use bulma styles and a sample paradigm. After these steps, you should go something like to: And then we need to take care of updating recipes. Create EditableRecipe component. It contains a form that you can make full in to set up a recipe's properties and several methods: handleInputChange()– chosen every time a course input inverse and sync local state with new values.
handleSubmit() – for now merely logs a text, later should submit a grade.
To connect both recipes functionalities, we're going to innovate Recipe component which returns EditableRecipe or NonEditableRecipe component based on the state given from its parent (RecipeToogle component). This approach is called return prop pattern. The idea behind it is that RecipeToogle is but responsible for managing isEditable state that is passed downwards to its children together with toogle() method. It gives us a nice separation of concerns and loose coupling. Nosotros also have to add together a button for toggling between EditableRecipe and NonEditableRecipe components. Don't forget to change RecipeList to display Recipe component rather than NonEditableRecipe. Toggle functionality is ready! Next, we desire to allow our React component to talk with Runway API. To brand it easier allow's install axios – promise based HTTP client to make requests from the browser.
yarn add axios
Create recipeApi.js file under app/javascript/packs/api/ folder and make full with the following: We will need two methods:
getRecipes() – to fetch all recipes,
updateRecipe(formData, recipeId) – to update a single recipe with data from the form.
Since App is our main parent component, it'south a expert place to communicate with exposed endpoints and care for it as a container. fetchRecipes() – loads recipes from the backend and assign to the component country, invoked inside lifecycle React component method componentDidMount().
mapIntoObject() – helper method to map array elements into central pair value objects.
update() – sends recipes data getting from the class to the backend to be saved and updates internal component state with updated recipe; we need to call a callback() that will be toogle() method in our case, because we want to display NonEditableRecipe component but after a recipe is updated.
Pass downwards update() method from App component through RecipeList to Recipe and then to RecipeToogle component. Add submit() method inside RecipeToogle and use update() method. Make the submit() method available for RecipeToogle children. Now EditableRecipe component should have access to it equally well. Dorsum to EditableRecipe component, let's update handleSubmit() method to collect recipe class data and send to the submit() that you tin become from the props. Nosotros have quite a lot of work behind the states. I'one thousand glad that you've gotten this far. :). You should be able to update recipes now. Check information technology out and let'south move to the adjacent pace: Uploading images.
Step 4: Upload files
Agile storage
Our next goal is to upload images for recipes. Time to see Active Storage in action. It's a congenital-in solution for handling file uploads for Rails five.2. It's like to popular third party gems such as Paperclip or Carrierwave. To start with Agile Storage, run the command:
rails active_storage:install
The command creates the migration to add together active storages ii tables to your application: blobs and attachments. This migration looks like this: Hither are their responsibilities, according to Runway README:
Active Storage uses polymorphic associations via the Attachment join model, which so connects to the actual Blob. Blob models store attachment metadata (filename, content-type, etc.), and their identifier key in the storage service.
So the blobs are going to store all the file-related metadata similar the file proper noun, file size, content type, etc. The attachments is the join tabular array between your models and your uploads. What it means is that you don't demand to make any databases changes, no additional migrations needed, whenever y'all want to add a new upload to your models. This nifty arroyo distinguish Active Storage from its competitors such as Paperclip or Carrierwave which on the contrary crave to add a new column to the existing model. With Active Storage you tin keep your data on local hard drive also as public cloud providers. Most popular deject providers Amazon S3, Google Cloud Storage and Azure Blob Storage are supported out of the box. Let'due south attach a single paradigm to our recipe. Go to Recipe model and add:
Let's examination if nosotros tin can add image to our existing recipe. Download an example epitome, discover a recipe in the panel and attempt to attach downloaded epitome:
recipe.paradigm.adhere io: File.open("/file/to/path"), filename: "image.jpg", content_type: "epitome/jpg"
Then bank check if the epitome is fastened correctly:
recipe.epitome.attached?
To delete image apply:
recipe.image.purge
Now, nosotros need to update recipes_controller.rb. Add together :image parameter to the white-list. Employ with_attached_image method delivered past Active Storage inside alphabetize controller method to prevent N+1 while getting recipes with their images. Change update controller method to await like: Nosotros're going to move concern logic for updating a recipe to the divide service. Create UpdateRecipeService class under app/services/ folder: To get a recipe image included in JSON result, we have to set it up in RecipeSerializer. prototype – returns formatted object if an prototype is attached to a recipe. The object includes image information such as file name, byte size, and URL to epitome source.
url_for – helper method that gives a source path to a given paradigm.
You have to include Track.awarding.routes.url_helpers to get admission to the url_for method.
[ If you get an fault: ArgumentError (Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to truthful)you lot probably don't have correct environments configurations. Add these two lines with your host: config.action_mailer.default_url_options = { host: 'localhost:3000' } Rails.awarding.routes.default_url_options[:host] = 'localhost:3000' to every config/{surroundings}.rb file ]
React Dropzone Component
Once nosotros consummate the backend side, we can take care of the frontend. First we demand to install the react-dropzone-component library:
yarn add react-dropzone-component
It provides a React Dropzone Component which allows drag and driblet files uploads with image previews. The component is based on an open source Dropzone.js library. We're going to start from EditableRecipe component. Information technology volition proceed track of the selected prototype and attaches the epitome to a recipe grade while submitting. Let's add selectedImage field to the component state and initialize with nothing. Update handleSubmit() method to append an prototype to a recipe class when the prototype is selected. Add two methods: selectImage() and unselectImage() which are going to deal with updating selectedImage state. We will invoke selectImage() method every time when we drag and drop a file within an upload expanse. There will be as well a possibility to remove image completely; so nosotros're going to set selectedImage value every bit an empty string to send information technology later to backend and delete from our database. The last thing we're going to change in this component is render() method to brandish drag and drop area for uploading files. To brand this functionality reusable we'll create ImageUploader component that will employ underneath react-dropzone-component library. Let's place ImageUploader nether app/javascript/packs/components/shared/ binder and fill with the post-obit:
Dropzone Component Configuration
DropzoneComponent has defined customizable configuration:
componentConfig – required configuration object which the DropzoneComponent is initialized with, eq. you can enable to testify file types icons or forestall posting files automatically to a URL by filling postUrl with meaningless string and set up autoProcessQueue to false inside djsConfig object.
djsConfig – optional configuration object for dropzone.js, eq. you tin can restrict file types you are allowed to upload or enable the remove button.
eventHandlers – optional list of event handlers;
we apply three of available events:
init() – the callback receives a reference to the dropzone object as a parameter; dropzone operations require accessing the dropzone object so it's a proficient place to assign dropzone object to a variable that you volition have access to (in our example we assign dropzone object to an instance variable myDropzone). As the name says init() is called when a component is initialized and so you can phone call your methods there that should be invoked first. Nosotros want to brandish a thumbnail if an image was already uploaded. This is what showPreview()method is doing. The prototype is taken from the parent component.
addedfile() – is invoked right after selecting image to be uploaded; the image is automatically added to the files array belonging to the dropzone object (you lot can access it with this.myDropzone.files). Nosotros desire to let upload of only one image for a unmarried recipe. In the event of a callback, we invoke additional removePrevAndAddNew() method that removes previous paradigm if it was already selected and call parent selectImage() method with given new selected imate to update the parent state.
removedfile() – is invoked after clicking the remove push button. In the callback we invoke the parent unselectImage() to set parent state for selectedImage to empty string. That'southward all! Now endeavor to upload an epitome. If everything works correctly, you should go something similar to:
And that's all! If yous're interested in more tutorials like this, make sure to check out our other blog posts.
Source: https://www.nopio.com/blog/upload-files-with-rails-active-storage/
0 Response to "Assign Uploaded File to Variable Rails Carrierwave"
Postar um comentário