Skip to content

Instantly share code, notes, and snippets.

@stungeye
Last active March 5, 2024 08:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stungeye/65558d2d7d03356754a3ede55f6fcf31 to your computer and use it in GitHub Desktop.
Save stungeye/65558d2d7d03356754a3ede55f6fcf31 to your computer and use it in GitHub Desktop.
Rails with Vue JS

Installing Yarn

You may need to install yarn (Javascript package manager) before you can create Rails apps that involve Javascript frontends.

For the WSL run the following commands:

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn

The password you are prompted for is your WSL root password.

Creating A new Rails App with VueJs

Create a new app: rails new vue_todo --webpack=vue

If you have an existing Rails app you can add Vue support by running: rails webpacker:install:vue

Rename the generated app/javascript/packs/hello_vue.js to better fit with your project. (For example: todo_vue.js) And then ensure that you include the associated javascript and stylesheet packs in your application layout:

<%= javascript_pack_tag 'todo_vue' %>
<%= stylesheet_pack_tag 'todo_vue' %>

Scaffold a Resource and a Pages Controller

Use Rails to create the following:

rails g scaffold ToDo description:string done:boolean
rails g controller pages home about

Remove the scaffold CSS:

rm app/assets/stylesheets/scaffold.scss 

Update the routes in config/routes.rb:

Rails.application.routes.draw do
  resources :to_dos

  root to: 'pages#home'
  get 'about', to: 'pages#about', as: 'about'
end

Add the vue app div to the home view (app/views/pages/home.html.erb):

<h1>Your To Do List</h1>

<div id="vue-todo-app"></div>

Adding Support for Turbolinks

If you are going to be using the rails routing system to navigate around (i.e. you aren't building a SPA) you'll want to add support for turbolinks.

Command line: ./bin/yarn add vue-turbolinks

In your js file that loads your view app:

import TurbolinksAdapter from 'vue-turbolinks';

import Vue from 'vue';
import App from '../app.vue';    

Vue.use(TurbolinksAdapter);

document.addEventListener('turbolinks:load', () => {
  const el = document.getElementById('vue-todo-app');

  if (el !== null) {
    new Vue({
      el,
      render: h => h(App)
    });
  }
});

Running Your App

You'll still need to run the Rails server:

rails s

But you'll also want the webpack dev server running in another terminal:

./bin/webpack-dev-server

Configuring Vs Code to play nicely with Vue

  • Install the following plugins: Prettier, Eslint, Vetur

  • Install eslint and its vue plugin: yarn add -D eslint eslint-plugin-vue

  • Create a .eslintrc.js config file in the project root:

      module.exports = {
        env: {
          browser: true,
          es6: true
        },
        extends: ["eslint:recommended", "plugin:vue/recommended"],
        rules: {
          // override/add rules settings here, such as:
          // 'vue/no-unused-vars': 'error'
        }
      };
    
  • Update the VS Code settings.json file:

      "vetur.validation.template": false, // Allow eslint / eslint-plugin-vue handle the linting.
      "vetur.format.defaultFormatter.html": "js-beautify-html",
      
      "eslint.validate": [
          "javascript",
          "javascriptreact",
          { "language": "vue", "autoFix": true }
      ],
    
      "[javascript]": {
          "editor.formatOnSave": true,
      },
    
      "[vue]": {
          "editor.formatOnSave": true,
      },
    

Adding the Bulma CSS Framework

Add bulma to the project Gemfile:

gem 'bulma-rails', '~> 0.7.4' # Or whatever happens to be the latest version.

Run: bundle install

Rename app/assets/stylesheets/application.css to applications.scss and import bulma:

@import 'bulma';

Restart the rails sever and see if the bulma styles have been applied.

Add the Vue Browers Dev Tools

Install the Vue Deve Tools for Chrome or Firefox.

No longer needed?

Add babel polyfill: ./bin/yarn add -D @babel/polyfill

Load this from your app/javascript/packs/application.js file:

import "@babel/polyfill";
{
"name": "vue_todo_retry",
"private": true,
"dependencies": {
"@rails/actioncable": "^6.0.0-alpha",
"@rails/activestorage": "^6.0.0-alpha",
"@rails/ujs": "^6.0.0-alpha",
"@rails/webpacker": "^4.0.7",
"turbolinks": "^5.2.0",
"vue": "^2.6.10",
"vue-loader": "^15.7.1",
"vue-template-compiler": "^2.6.10",
"vue-turbolinks": "^2.0.4"
},
"version": "0.1.0",
"devDependencies": {
"eslint": "^6.5.1",
"eslint-plugin-vue": "^5.2.3",
"webpack-dev-server": "^3.8.2"
}
}

Initial Setup

git clone https://github.com/StungEye-RRC/Rails-Vue-ToDo-F2019.git
cd Rails-Vue-ToDo-F2019
bundle install
yarn install
rails db:migrate
rails db:seed

Installing VueJS

rails webpacker:install:vue
yarn add vue-turbolinks
  • Rename app/javascript/packs/hello_vue.js => to_do_vue.js

  • Edit the app/views/layout/application.html.erb

    <%= javascript_pack_tag 'to_do_vue' %>
    <%= stylesheet_pack_tag 'to_do_vue' %>
    
  • Edit the app/javascript/packs/to_do_vue.js

    import TurbolinksAdapter from 'vue-turbolinks';
    import Vue from 'vue';
    import App from '../app.vue';    
    
    Vue.use(TurbolinksAdapter);
    
    document.addEventListener('turbolinks:load', () => {
      const el = document.getElementById('vue-todo-app');
    
      if (el !== null) {
        new Vue({
          el,
          render: h => h(App)
        });
      }
    });
    
  • The pages controller:

    class PagesController < ApplicationController
      def home
        @todos = ToDo.all
      end
    
      def about
      end
    end
    
  • The Vue component app/javascript/app.vue

    <template>
      <div>
        <ol>
          <li v-for="todo in todos" :key="todo.id">
            <label>
              <input
                type="checkbox"
                v-model="todo.done"
              >
              <del v-if="todo.done">{{ todo.description }}</del>
              <span v-else>{{ todo.description }}</span>
            </label>
          </li>
        </ol>
      </div>
    </template>
    
    <script>
      export default {
        data: function () {
          return {
            todos: [] 
          }
        },
        mounted: function () {
          const data_element = document.getElementById('todo-data');
          this.todos = JSON.parse(data_element.getAttribute('data-todos'));
        }
      }
    </script>
    
    <style scoped>
    </style>
    
  • The pages controller's home view:

    <h1>To Do</h1>
    <div id="todo-data" data-todos="<%= @todos.to_json %>">
    <div id="app"></div>
    

Requestion the To Do data via an API Request

  • Remove the data attribute from the pages controller's home view.

  • Remove the Model query from the pages controller's home action.

  • Create a new file app/javascript/RailsApi.js

    const rails_api = {
      getToDos: async function() {
        const response = await fetch("/to_dos.json");
        return response.json();
      }
    };
    
    export default rails_api;
    
  • Update script tag in app/javascript/app.vue

    import RailsAPI from "RailsApi.js";
    
    export default {
      data: function () {
        return {
          todos: [] 
        }
      },
      
      mounted: async function () {
        this.todos = await RailsAPI.getToDos(); 
      }
    }
    

Submitting New To Do Items Using An API POST

  • Update app/javascript/app.vue

    <template>
      <div>
        <ol>
          <li v-for="todo in todos" :key="todo.id">
            <label>
              <input
                type="checkbox"
                v-model="todo.done"
              >
              <del v-if="todo.done">{{ todo.description }}</del>
              <span v-else>{{ todo.description }}</span>
            </label>
          </li>
        </ol>
        <input 
          ref="newItem" 
          v-model.trim="newToDo"
          @keyup.enter="createToDo"
          placeholder="New To Do Item"
          autofocus
        />
      </div>
    </template>
    
    <script>
    import RailsAPI from "RailsApi.js";
    
    export default {
      data: function () {
        return {
          newToDo: "",
          todos: []
        }
      },
      methods: {
        createToDo: async function() {
          const todo = await RailsAPI.createToDo(this.newToDo);
          this.todos.push(todo);
          this.newToDo = "";
        }
      },
      mounted: async function () {
        this.todos = await RailsAPI.getToDos(); 
      }
    }
    </script>
    
    <style scoped>
    div > input {
      margin-left: 2em;
    }
    </style>
    
  • Update app/javascript/RailsApi.js

    const rails_api = {
      headers: {
        "Content-type": "application/json; charset=utf-8",
        "X-CSRF-Token": document
          .querySelector('meta[name="csrf-token"]')
          .getAttribute("content")
      },
    
      getToDos: async function() {
        const response = await fetch("/to_dos.json");
        return response.json();
      },
    
      createToDo: async function(description) {
        const response = await fetch("/to_dos.json", {
          method: "POST",
          body: JSON.stringify({ description: description, done: false }),
          headers: rails_api.headers
        });
    
        return response.json();
      },
    };
    
    export default rails_api;
    
@rusikf
Copy link

rusikf commented May 15, 2020

Hi, how to run eslint when browser load page ?

@lunaisnotaboy
Copy link

Hi, how to run eslint when browser load page ?

@rusikf eslint is a command-line tool. This means that it formats code on the server, not the client (a.k.a. the browser). Unfortunately, there's no way to do what you're asking.

@rusikf
Copy link

rusikf commented Mar 17, 2021

ou t's a bit outdated comment, sure, thanks!
I configured eslint in vim editor and has no problems, thanks! @lunaisnotaboy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment