Create Seamless Loading in Ruby on Rails
Tags: ruby, webdev, programming, rails
Published: 2022-03-05 02:25:33

Do you want your rails app to automatically load the next page when scrolled to the bottom? That's what we're covering in this post. We will create a rails app like Pinterest in Rails 6.

1. Create the Image scaffold.
    ```console
    $ rails g scaffold Image url:string author:string
    ```
    This will generate the necessary files for the rails app. You 
    don't need to know most of them. We will be editing only some 
    of the files.

    To create the image table, run this on your terminal:
    ```ruby
    $ rails db:migrate
    ```
2. Edit config/routes.rb.
    ```ruby
    Rails.application.routes.draw do
     root to: "images#index"
    end
    ```
    This way the root page would use image#index by 
    default (localhost:3000).

3. Edit db/seeds.rb 
    ```ruby
    require "net/http"

    url = "https://picsum.photos/v2/list?page=2&limit=100"
    resp = Net::HTTP.get_response(URI.parse(url))
    data = JSON.parse(resp.body)

    data.each do |d|
     url = "https://picsum.photos/200/#{300+(rand(1..5)*20)}" #This will use random heights for the image
     p Image.where(author: d['author'], url: url).first_or_create
    end
    ```
    The URL, "https://picsum.photos/v2/list?page=2&limit=100" will return 100 random images that we will be using as seed for our rails app. For more information about this you could check it [here](https://picsum.photos/).

    Then run in your terminal:
    ```terminal
     $ rails db:seed
    ```

4. Update app/views/images/index.html.erb.

    ```html
    <header class="container">
     <h1 class="element-stack">Pinterest Flow</h1>
     <p class="element-stack">Scroll down to load more data</p>
    </header>

    <div id="columns">
     <%- @images.each do |image| %>
      <figure>
        <img src=<%= image.url %>>
        <figcaption><%= image.author%></figcaption>
      </figure>
     <% end %>
    </div>
    ```
5. Edit app/assets/stylesheets/images.scss or app/assets/stylesheets/images.css, depending on what's generated by the scaffold.

    ```css
    @font-face{font-family:'Calluna';
    src:url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/callunasansregular-webfont.woff') format('woff');
    }
    body {
	 background: url(//subtlepatterns.com/patterns/scribble_light.png);
     font-family: Calluna, Arial, sans-serif;
     min-height: 1000px;
    }
    #columns {
        column-width: 150px;
        column-gap: 15px;
        width: 90%;
        max-width: 1100px;
        margin: 50px auto;
    }

    div#columns figure {
        background: #fefefe;
        margin: 0 2px 15px;
        padding-bottom: 10px;
        transition: opacity .4s ease-in-out;
        display: inline-block;
        column-break-inside: avoid;
        min-height: 100px;
    }

    div#columns figure img {
        width: 100%; height: auto;
        border-bottom: 1px solid #ccc;
        margin-bottom: 5px;
        border-radius: 15px;
    }

    div#columns figure figcaption {
        font-size: .9rem;
        color: #444;
        line-height: 1.5;
    }

    div#columns small {
        font-size: 1rem;
        float: right;
        text-transform: uppercase;
        color: #aaa;
    }

    div#columns small a {
        color: #666;
        text-decoration: none;
        transition: .4s color;
    }

    @media screen and (max-width: 750px) {
        #columns { column-gap: 0px; }
        #columns figure { width: 100%; }
    }
    ```
    This CSS is from [this](https://codepen.io/dudleystorey/pen/AqzKdv).

    Also import it in application.css.
    ```css
    /*
    * This is a manifest file that'll be compiled into application.css, which will include all the files
    * listed below.
    *
    * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
    * vendor/assets/stylesheets directory can be referenced here using a relative path.
    *
    * You're free to add application-wide styles to this file and they'll appear at the bottom of the
    * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
    * files in this directory. Styles in this file should be added after the last require_* statement.
    * It is generally better to create a new file per style scope.
    *
    *= require card
    *= require_tree .
    *= require_self
    */

    @import "images";

    ```

    This is the styling we would be using. You could check out your localhost to check if it is all okay. 

6. Add paginate for images using [will_paginate](https://github.com/mislav/will_paginate) gem. Add this to the Gemfile.

    ```ruby
    gem 'will_paginate'
    ```

    Then run bundle install.
    ```console
    $ bundle install
    ```
7.  Edit index action in app/controllers/images_controller.rb.
    ```ruby
    # GET /images or /images.json
    def index
     @images = Image.all.paginate(page: params[:page], per_page: 15)
    end
    ```
    Create _images.html.erb in app/views/images with this code.
    ```html
    <%- images.each do |image| %>
     <figure>
       <img src=<%= image.url %>>
       <figcaption><%= image.author%></figcaption>
     </figure>
    <% end %>
    ```
    Then edit app/views/images/index.html.erb.
    ```html
    <div id="columns">
     <%=render partial: "images", locals:{images: @images} %>
    </div>

    <%= will_paginate @images %>
    ```

    This would make paginate working in our rails app.

8. Add Jquery to Rails. Run this in your terminal.
    ``` console
    $ yarn add jquery
    ```

    Then add the code below in config/webpack/environment.js.
    ```js
    const webpack = require('webpack')
    environment.plugins.prepend('Provide',
     new webpack.ProvidePlugin({
      $: 'jquery/src/jquery',
      jQuery: 'jquery/src/jquery'
    })
    )
    ```
    Require jquery in application.js file.
    ```js
    require("jquery")
    ```
9. Create jquery.pageless.js in app/javascript/packs with this [code](https://github.com/jney/jquery.pageless/blob/master/lib/jquery.pageless.js)

    Then require it in the application.js file.
    ```js
    require("jquery")
    require("packs/jquery.pageless")
    ```

10. Add this code in app/views/images/index.html.erb.
    ```html
    <script type="text/javascript" charset="utf-8">
     var totalPages = <%= @images.total_pages %>;
     var url = '<%= root_path%>';
     $('#columns').pageless({ totalPages: totalPages
                       , url: url
                       , loaderMsg: 'Loading more results'
                       });
    </script>
    ```
11. Edit app/controllers/images_controller.rb.
    ```rb
    def index
     @images = Image.all.paginate(page: params[:page], per_page: 15)

     if request.xhr?
       render partial: 'images/images', locals:{images: @images}
     end
    end
    ```

Now check your localhost to see if it's running.

And that's it. We're done! 😃

If you want to check the code. You could do it here:
[Github Repository](https://github.com/jkevinbaluyot/seamless-loading-rails)