Rails and Angular – camelCase or underscore?

When you’re writing your APIs in Rails, you may have an app for the front end, say, in Angular. You may send data between these two in the JSON format.

The coding conventions in both these technologies are exact opposites when it comes to names.

In rails you want your hash to look like

{
    id: 1,
    first_name: 'john doe'
}

In angular you want your object to look like

{
    id: 1,
    firstName: 'john doe'
}

In order to achieve this, without having to change the names manually before/after every data transfer request, you can add a parser in your initializer sequence:

config/initializers/json_param_key_transform.rb

ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) {
    data = ActiveSupport::JSON.decode(raw_post)
    data = {:_json => data} unless data.is_a?(Hash)
    data.deep_transform_keys!(&:underscore)
}

This will first decode raw data in the body of a request into a hash. It will then transform all the keys in the hash to convert camelCase into underscore. Your controllers’ actions will receive this modified hash as params.

Now, when you are sending data from your API to your Angular app, you want it to be camelized. We can achieve this by overriding the as_json method of your models.

If you have a base class for all your models, then you can do this in it. Otherwise, write a new abstract class which would be the base class for all your models.

model_base.rb

class ModelBase < ApplicationRecord
    self.abstract_class = true

    def as_json(options={})
        json = super(options)
        if json.is_a?(Array) then
                return_hash = [];
                json.each do |x|
                    return_hash << x.deep_transform_keys { |key| key.to_s.camelize(:lower)}
                end
                return return_hash
            else

                return json.deep_transform_keys { |key| key.to_s.camelize(:lower)}
            end
    return 
  end
end

Now in your model, when you say @some_instance.as_json, the resulting hash will be camelized instead of with underscores.

If you do not want to override the as_json method of your models, you can also write this method in your ApplicationController, and call it before rendering your JSON.

application_controller.rb

def underscore_to_camel(hash_obj)
    return_obj = nil;
    if hash_obj.is_a?(Array) then
        return_obj = [];
        hash_obj.each do |x|
            return_obj << x.deep_transform_keys { |key| key.to_s.camelize(:lower)}
        end
        return return_obj
    else
        return_obj = hash_obj.deep_transform_keys { |key| key.to_s.camelize(:lower)}
    end
end

some_controller.rb

render json: underscore_to_camel(@some_instance)

Now the JSON data received by your angular app will be camelized.

Credits for the initializer idea:
http://stackoverflow.com/a/30557924

Use paperclip to upload from and display images in your Angular App

I have an Angular2 app from which I wanted to upload images to my Rails 5 API Server. I also want to show the uploaded image in another page in my app. This is how I accmoplished this:

Uploading

my.component.html

<input type="file" id="file" name="file" (change)="onLogoChange($event)" accept="image/png,image/jpeg" >

my.component.ts

In my component, I have a member variable called model, which I will eventually send to my Rails API in a POST request.

When the user selects a file, I encode it into base 64 and keep it in my model variable:

onLogoChange(e): void {
     let file: File = e.target.files[0]
     this.getFileBase64(file).then((base64: string) => {
         this.model.logoFileViewModel = new FileViewModel()
         this.model.logoFileViewModel.base64 = base64
         this.model.logoFileViewModel.name = file.name
     })
}

getFileBase64(file: File): Promise {
    let reader: FileReader = new FileReader()
    let promise = new Promise((resolve, reject) => {
        reader.onloadend = (e) => {
            resolve(reader.result)
        }
    })
    reader.readAsDataURL(file)
    return promise
}

When the user clicks on the ‘submit’ button that I have on my page, I send the model to the Rails API in a POST request. On the rails side, I have a model DefaultPageSetting and its controller DefaultPageSettingController.

default_page_setting.rb

class DefaultPageSetting < ApplicationRecord     
    has_attached_file :logo, default_url: "/images/default_page_setting/logo033.png", :styles => { :thumb => "100x100#" }
    validates_attachment :logo, presence: true
    do_not_validate_attachment_file_type :logo
end

def set_logo_from_view_model(view_model)
    file = Paperclip.io_adapters.for(view_model[:base64])
    file.original_filename = view_model[:name]
    self.logo = file
end

default_page_setting-controller.rb

def create
    @setting = DefaultPageSetting.new
    @setting.set_logo_from_view_model = params[:logo_file_view_model]
    @setting.save
end

And Voila! The file will be saved in subfolders inside app/system/default_page_settings/logos.

Displaying

default_page_setting.rb

def get_logo_file_view_model
    view_model = {}
    view_model[:name] = logo.name
    view_model[:thumb_url] = logo.url(:thumb)
    view_model[:url] = logo.url(:original)
    return view_model
end

default_page_setting_controller.rb

def show
    setting = DefaultPageSetting.first.as_json
    setting[:logo_file_view_model] = @setting.get_logo_file_view_model
    render json: setting
end

In my Angular component, once I receive the setting json from Rails using a GET request, I just prepend (not sure if its a valid English word :P) it with the base URL of the Rails API.

NOTE: You should consider keeping the Rails base URL in your environent.ts/environment.prod.ts and fetching it from there instead of hardcoding it.

my-show-page.component.ts

ngOnInit() {
    this.service.getPageSetting()
        .then((setting: DefaultPageSetting) => {
            this.setting = data.setting
            this.setting.logoFileViewModel.url = "http://localhost:3000" + data.setting.logoFileViewModel.url
    })
}

my-show-page.component.html

<img [src]="setting.logoFileViewModel.url" />

Helpful Resources:

How to use paperclip to add image attachment to your Rails model?
How to convert image to base64 in angular2?

Create a free website or blog at WordPress.com.

Up ↑