Skip to content

Instantly share code, notes, and snippets.

@przbadu
Last active July 16, 2022 21:48
Show Gist options
  • Save przbadu/084197ea821a98b0e177b266b41ba0a2 to your computer and use it in GitHub Desktop.
Save przbadu/084197ea821a98b0e177b266b41ba0a2 to your computer and use it in GitHub Desktop.
Vue js and Rails integration

Setup Rails and Vuejs

  1. Generate new rails app using --webpack flag
rails new myApp --webpack=vue

Note:

  1. You can use --webpack=angular for angular application and --webpack=react for react.
  2. you can also pass -J flag like rails new myApp -J --webpack=vue to ignore turbolink, but strongly recommend you don't do this, I will see how to make vue js compitable with turbolinks.
  1. Keep all of the vue js related codes in app/javascript/pack/ directory. Anything that needs to be compiled by webpack should go inside this directory.

  2. app/assets/javascript is still there for your js and coffescript code that are independed of webpack modules.

  3. vue-turbolinks npm module to fix turbolink problems

import TurbolinksAdapter from 'vue-turbolinks';

document.addEventListener('turbolinks:load', () => {
  // This code will setup headers of X-CSRF-Token that it grabs from rails generated token in meta tag.
  axios.defaults.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content')

  new Vue({
    ...
    mixin: [TurbolinksAdapter],
    ...
  })
})
yarn add vue-turbolink
  1. axios npm module for making server request (alternative to vue-resource or $.ajax)
yarn add axios

Usage

axios.post('/users', {
  firstName: 'John',
  lastName: 'Doe',
})
 .then(res => console.log(res))
 .catch(err => console.log(err));

Example application below:

<!-- app/views/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>DemoTurbolinks</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application' %>
</head>
<body>
<%= yield %>
</body>
</html>
// app/javascript/packs/application.js
import Employee from './employee';
// app/javascript/packs/employee.js
import Vue from 'vue/dist/vue.esm';
import TurbolinksAdapter from 'vue-turbolinks';
import axios from 'axios';
export default document.addEventListener('turbolinks:load', () => {
axios.defaults.headers.common['X-CSRF-Token'] = document
.querySelector('meta[name="csrf-token"]')
.getAttribute('content');
const element = document.getElementById('employees');
if (element !== null) {
Vue.component('employee-row', {
template: '#employee-row',
mixin: [TurbolinksAdapter],
props: {
employee: Object,
},
data() {
return {
editMode: false,
errors: {},
employeeRow: this.employee,
};
},
computed: {
changeEmployeeRow: {
get() {
this.employeeRow;
},
set(data) {
this.employeeRow = data;
},
},
},
methods: {
// toggle the employee status which also updates
// the employee in the database
toggleManagerStatus() {
this.employee.manager = !this.employee.manager;
this.updateEmployee();
},
// ajax call to update an employee
updateEmployee() {
axios
.put(`/employees/${this.employee.id}.json`, {
employee: this.employee,
})
.then(response => {
// Turbolinks.visit(`/employees`);
this.errors = {};
this.employeeRow = response.data;
this.editMode = false;
})
.catch(error => {
this.errors = error.response && error.response.data;
});
},
fireEmployee() {
axios
.delete(`/employees/${this.employee.id}.json`)
.then(response => Turbolinks.visit(`/employees`))
.catch(error => console.log(error));
},
},
});
const employees = new Vue({
el: element,
mixin: [TurbolinksAdapter],
data() {
return {
employees: [],
employee: {
name: '',
email: '',
manager: false,
},
errors: {},
};
},
created() {
axios
.get('/employees.json')
.then(response => (this.employees = response.data))
.catch(error => console.log(error));
},
methods: {
hireEmployee() {
axios
.post('/employees.json', {
employee: this.employee,
})
.then(response => {
this.errors = {};
this.employees.push(response.data);
})
.catch(error => {
this.errors = error.response.data;
});
},
},
});
}
});
# app/controllers/employees_controller.rb
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
# GET /employees
# GET /employees.json
def index
@employees = Employee.all
end
# GET /employees/1
# GET /employees/1.json
def show
end
# GET /employees/new
def new
@employee = Employee.new
end
# GET /employees/1/edit
def edit
end
# POST /employees
# POST /employees.json
def create
@employee = Employee.new(employee_params)
respond_to do |format|
if @employee.save
format.html { redirect_to @employee, notice: 'Employee was successfully created.' }
format.json { render :show, status: :created, location: @employee }
else
format.html { render :new }
format.json { render json: @employee.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /employees/1
# PATCH/PUT /employees/1.json
def update
respond_to do |format|
if @employee.update(employee_params)
format.html { redirect_to @employee, notice: 'Employee was successfully updated.' }
format.json { render :show, status: :ok, location: @employee }
else
format.html { render :edit }
format.json { render json: @employee.errors, status: :unprocessable_entity }
end
end
end
# DELETE /employees/1
# DELETE /employees/1.json
def destroy
@employee.destroy
respond_to do |format|
format.html { redirect_to employees_url, notice: 'Employee was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_employee
@employee = Employee.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:name, :email, :manager)
end
end
<!-- app/views/employees/index.html.erb -->
<p id="notice"><%= notice %></p>
<h1>Employees</h1>
<%= link_to 'New Employee', new_employee_path %>
<br>
<div id="employees">
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Manager?</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" v-model="employee.name"/> <br/>
<span style="color:red">{{errors.name}}</span>
</td>
<td>
<input type="email" v-model="employee.email"/> <br/>
<span style="color:red">{{errors.email}}</span>
</td>
<td>
<input type="checkbox" v-model="employee.manager"/>
</td>
<td>
<button @click="hireEmployee">Save</button>
</td>
</tr>
</tbody>
<tbody
is="employee-row"
v-for="employee in employees"
:employee="employee">
</tbody>
</table>
</div>
<script type="text/x-template" id="employee-row">
<tr>
<td>
<div v-if="editMode">
<input type="text" v-model="employeeRow.name"/> <br/>
<span style="color:red">{{errors.name}}</span>
</div>
<div v-else>{{ employeeRow.name }}</div>
</td>
<td>
<div v-if="editMode">
<input type="email" v-model="employeeRow.email"/> <br/>
<span style="color:red">{{errors.email}}</span>
</div>
<div v-else>{{ employeeRow.email }}</div>
</td>
<td>
<div v-if="editMode">
<input type="checkbox" v-model="employeeRow.manager"/>
</div>
<div v-else>{{ employeeRow.manager ? '&#10004;' : '' }}</div>
</td>
<td>
<button v-if="editMode" @click="updateEmployee">Save</button>
<button v-else @click="editMode = true">Edit</button>
<button v-if="!editMode" @click="toggleManagerStatus">
{{ employeeRow.manager ? 'Demote' : 'Promote' }}
</button>
<button v-if="!editMode" @click="fireEmployee" style="color:red">Fire</button>
</td>
</tr>
</script>
@xlanex6
Copy link

xlanex6 commented May 24, 2018

Thanks so much.
It was really helpful

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