matestack-ui-vuejs
Boost your productivity & easily create reactive web UIs in pure Ruby.
matestack-ui-vuejs ships all you need to build reactive UIs in pure Ruby orchestrating prebuilt Vue.js components with a simple Ruby DSL.
The prebuilt reactive components built on top of Vue.js are covering typical features of a reactive web UI, such as async form submission, dynamic page transitions or async partial UI updates. No Opal involved
If required, it can be easily extended with pure JavaScript.

Compatibility

matestack-ui-vuejs requires matestack-ui-core
matestack-ui-vuejs is tested against:
  • Rails 7.0.1 + Ruby 3.0.0 + Vue.js 3.2.26
  • Rails 6.1.1 + Ruby 3.0.0 + Vue.js 3.2.26
  • Rails 6.1.1 + Ruby 2.7.2 + Vue.js 3.2.26
  • Rails 6.0.3.4 + Ruby 2.6.6 + Vue.js 3.2.26
  • Rails 5.2.4.4 + Ruby 2.6.6 + Vue.js 3.2.26
Rails versions below 5.2 are not supported.
Vue.js 2.x is supported when using the Compat build of Vue.js

Documentation/Installation

Detailed documentation can be found here

Feature walk-through

1. Use reactive UI components in pure Ruby

Matestack's generic, reactive components can be placed on your UI with a simple Ruby DSL. While putting these generic components in your UI code, you inject some configuration in order to adjust the behaviour to your needs. This enables you to create reactive UIs without touching JavaScript!
Behind the scenes: When calling a reactive component with the Ruby DSL, Matestack will render a special component tag with a bunch of attributes to the resulting, server-side rendered HTML. Within the browser, Vue.js will pick up these component tags and mount the JavaScript driven components on it.
Toggle parts of the UI based on events
matestack-ui-vuejs offers an event hub. Reactive components can emit and receive events through this event hub. "onclick" and "toggle" calling two of these reactive core components. "onclick" emits an event which causes the body of the "toggle" component to be visible for 5 seconds in this example.
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
onclick emit: "some_event" do
5
button "click me"
6
end
7
toggle show_on: "some_event", hide_after: 5000 do
8
plain "Oh yes! You clicked me!"
9
end
10
end
11
12
end
Copied!
Call controller actions without JavaScript
Core components offer basic dynamic behaviour and let you easily call controller actions and react to server responses on the client side without full page reload. The "action" component is configured to emit an event after successfully performed an HTTP request against a Rails controller action, which is received by the "toggle" component, displaying the success message.
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
action my_action_config do
5
button "click me"
6
end
7
toggle show_on: "some_event", hide_after: 5000 do
8
plain "Success!"
9
end
10
end
11
12
def my_action_config
13
{
14
path: some_rails_route_path,
15
method: :post,
16
success: {
17
emit: "some_event"
18
}
19
}
20
end
21
22
end
Copied!

Dynamically handle form input without JavaScript

Create dynamic forms for ActiveRecord Models (or plain objects) and display server side responses, like validation errors or success messages, without relying on a full page reload. Events emitted by the "form" component can be used to toggle parts of the UI.
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
matestack_form my_form_config do
5
form_input key: :some_model_attribute, type: :text
6
button "click me", type: :submit
7
end
8
toggle show_on: "submitted", hide_after: 5000 do
9
span class: "message success" do
10
plain "created successfully"
11
end
12
end
13
toggle show_on: "failed", hide_after: 5000 do
14
span class: "message failure" do
15
plain "data was not saved, please check form"
16
end
17
end
18
end
19
20
def my_form_config
21
{
22
for: MyActiveRecordModel.new,
23
path: some_rails_route_path,
24
method: :post,
25
success: {
26
emit: "submitted"
27
},
28
failure: {
29
emit: "failed"
30
}
31
}
32
end
33
34
end
Copied!
Implement asynchronous, event-based UI rerendering in pure Ruby
Using Matestack's built-in event system, you can rerender parts of the UI on client side events, such as form or action submissions. Even server side events pushed via ActionCable may be received! The "async" component requests a new version of its body at the server via an HTTP GET request after receiving the configured event. After successful server response, the DOM of the "async" component gets updated. Everything else stays untouched.
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
matestack_form my_form_config do
5
#...
6
end
7
#...
8
async rerender_on: "submitted", id: "my-model-list" do
9
ul do
10
MyActiveRecordModel.last(5).each do |model|
11
li model.some_attribute
12
end
13
end
14
end
15
end
16
17
def my_form_config
18
{
19
#...
20
success: {
21
emit: "submitted"
22
},
23
failure: {
24
emit: "failed"
25
}
26
}
27
end
28
29
end
Copied!
Manipulate parts of the UI via ActionCable
"async" rerenders its whole body - but what about just appending the element to the list after successful form submission? The "cable" component can be configured to receive events and data pushed via ActionCable from the server side and just append/prepend new chunks of HTML (ideally rendered through a component) to the current "cable" component body. Updating and deleting is also supported!
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
matestack_form my_form_config do
5
#...
6
end
7
#...
8
cable prepend_on: "new_element_created", id: "some-cable-driven-list" do
9
MyActiveRecordModel.last(5).each do |model|
10
Components::SomeListItemComponent.call(content: model.some_content)
11
end
12
end
13
end
14
15
end
Copied!
app/controllers/some_controller.rb
1
# within your controller action handling the form input
2
ActionCable.server.broadcast("matestack_ui_core", {
3
event: "new_element_created",
4
data: Components::SomeListItemComponent.call(content: model.some_content) # or plain HTML
5
})
Copied!
Easily extend with Vue.js
Matestack's dynamic parts are built on Vue.js. If you want to implement custom dynamic behaviour, you can simply create your own Vue components and use them along Matestack's components. It's even possible to interact with Matestack's components using the built-in event bus.
app/matestack/components/some_component.rb
1
class Components::SomeComponent < Matestack::Ui::Component
2
3
def response
4
Components::MyVueJsComponent.call()
5
toggle show_on: "some_event", hide_after: "3000" do
6
span class: "message success" do
7
plain "event triggered from custom vuejs component"
8
end
9
end
10
end
11
12
end
Copied!
app/matestack/components/my_vue_js_component.rb
1
class Components::MyVueJsComponent < Matestack::Ui::VueJsComponent
2
3
vue_name "my-vue-js-component"
4
5
def response
6
div class: "my-vue-js-component" do
7
button "@click": "vc.increaseValue"
8
br
9
plain "{{ vc.dynamicValue }}!"
10
end
11
end
12
13
end
Copied!
app/matestack/components/my_vue_js_component.js
1
import MatestackUiVueJs from 'matestack-ui-vuejs'
2
3
const myComponent = {
4
mixins: [MatestackUiVueJs.componentMixin],
5
template: MatestackUiVueJs.componentHelpers.inlineTemplate,
6
data: () => {
7
return {
8
dynamicValue: 0
9
};
10
},
11
methods: {
12
increaseValue(){
13
this.dynamicValue++
14
MatestackUiVueJs.eventHub.$emit("some_event")
15
}
16
}
17
}
18
19
export default myComponent
20
21
// and then in your application pack file you register the component like:
22
23
appInstance.component('my-vue-js-component', myComponent) /
Copied!

3. Create whole SPA-like apps in pure Ruby

The last step in order to leverage the full Matestack power: Create a Matestack layout (~Rails layout) and Matestack page (Rails ~view) classes (as seen on matestack-ui-core) and implement dynamic page transitions with components coming from matestack-ui-vuejs without any custom JavaScript implementation required.
Create your layouts and views in pure Ruby
The layout class is used to define a layout, usually containing some kind of header, footer and navigation. The page class is used to define a view. Following the same principles as seen on components, you can use components (core or your own) in order to create the UI. The matestack_vue_js_app page_switch and transition components enable dynamic page transition, replacing the yielded content with new serverside rendered content rendered by the requested page.
app/matestack/some_app/some_layout.rb
1
class SomeApp::SomeLayout < Matestack::Ui::Layout
2
3
def response
4
h1 "My App"
5
matestack_vue_js_app do
6
nav do
7
transition path: page1_path do
8
button "Page 1"
9
end
10
transition path: page2_path do
11
button "Page 2"
12
end
13
end
14
main do
15
div class: "container" do
16
page_switch do
17
yield
18
end
19
end
20
end
21
end
22
end
23
24
end
Copied!
app/matestack/some_app/pages/page1.rb
1
class SomeApp::Pages::Page1 < Matestack::Ui::Page
2
3
def response
4
div class: "row" do
5
div class: "col" do
6
plain "Page 1"
7
end
8
end
9
end
10
11
end
Copied!
app/matestack/some_app/pages/page2.rb
1
class SomeApp::Pages::Page2 < Matestack::Ui::Page
2
3
def response
4
div class: "row" do
5
div class: "col" do
6
plain "Page 2"
7
end
8
end
9
end
10
11
end
Copied!
Layouts and pages are referenced in your Rails controllers and actions
Instead of referencing Rails layouts and views on your controllers, you just use apps and pages as substitutes. Work with controllers, actions and routing as you're used to! Controller hooks (e.g. devise's authenticate_user) would still work!
app/controllers/some_controller.rb
1
class SomeController < ApplicationController
2
3
include Matestack::Ui::Core::Helper
4
5
matestack_layout SomeApp::SomeLayout
6
7
def page1
8
render SomeApp::Page1
9
end
10
11
def page2
12
render SomeApp::Page2
13
end
14
15
end
Copied!
app/config/routes.rb
1
Rails.application.routes.draw do
2
3
root to: 'some#page1'
4
5
get :page1, to: 'some#page1'
6
get :page2, to: 'some#page2'
7
8
end
Copied!
Use CSS animations for fancy page transition animations
Use Matestack's css classes applied to the wrapping DOM structure of a page in order to add CSS animiations, whenever a page transition is performed. You can even inject a loading state element, enriching your page transition effect.
app/matestack/some_app/some_layout.rb
1
class SomeApp::SomeLayout < Matestack::Ui::Layout
2
3
def response
4
h1 "My App"
5
matestack_vue_js_app do
6
nav do
7
transition path: page1_path do
8
button "Page 1"
9
end
10
transition path: page2_path do
11
button "Page 2"
12
end
13
end
14
main do
15
div class: "container" do
16
page_switch do
17
yield
18
end
19
end
20
end
21
end
22
end
23
24
def loading_state_element
25
div class: 'some-loading-element-styles'
26
end
27
28
end
Copied!
app/assets/stylesheets/application.scss
1
.matestack-page-container{
2
3
.matestack-page-wrapper {
4
opacity: 1;
5
transition: opacity 0.2s ease-in-out;
6
7
&.loading {
8
opacity: 0;
9
}
10
}
11
12
.loading-state-element-wrapper{
13
opacity: 0;
14
transition: opacity 0.3s ease-in-out;
15
16
&.loading {
17
opacity: 1;
18
}
19
}
20
21
}
Copied!
Get started now --> Detailed documentation can be found here