Transition Component API
Matestack's transition component enables switching between pages without a full website reload. It works similar to links, but instead of reloading the complete website, including the layout like Rails usually does, it only asynchronously reloads the page without the layout and replaces it dynamically.
Since 3.0.0 transitions require a wrapping matestack_vue_js_app component and a page_switch component as seen below
1
class Shop::Layout < Matestack::Ui::Layout
2
3
def response
4
matestack_vue_js_app do # required to wrap transitions and page_switch
5
nav do
6
transition 'Matestack Shop', path: root_path
7
transition 'Products', path: products_path
8
end
9
page_switch do # required to wrap the yield
10
yield
11
end
12
end
13
end
14
15
end
Copied!
Let's add the products page which simply lists all products and adds a transition to their show page for each one.
1
class Shop::Pages::Products::Index < Matestack::Ui::Page
2
3
def response
4
Products.all.each do |product|
5
div do
6
paragraph product.name
7
transition 'Details', path: product_path(product)
8
end
9
end
10
end
11
12
end
Copied!

Parameters

Except for id and class, the transition component can handle additional parameters:

path - required

As the name suggests, the path expects a path within our application. If you want to route to a link outside our application, use the a method, rendering a typical HTML a tag
1
transition path: page1_path do
2
button 'Page 1'
3
end
Copied!
If the path input is a string it just uses this string for the transition target.
You can also just use the Rails url helper methods directly. They will return a string which is then used as the transition target without any further processing.

text

If the transition component receives a text via the first argument, it gets rendered as shown here:
1
transition 'Click me for a transition', path: page1_path
Copied!
1
<a href='my_example_app/page1'>Click me for a transition</a>
Copied!
If no text is present, the transition component expects a block that it then yields the usual way.

delay

You can use this attribute if you want to delay the actual transition. It will not delay the page_loading_triggered event
1
delay: 1000 # means 1000 ms
Copied!

Active class

The transition component automatically gets the active class on the clientside when the current path equals the target path.
When a sub page of a parent transition component is currently active, the parent transition component gets the active-child class. A sub page is recognized if the current path is included in the target path of the parent transition component:
Parent target: /some_page
Currently active: /some_page/child_page --> Parent gets child-active
Query params do not interfere with this behavior.

Events

The transition component automatically emits events on:
  • transition triggered by user action -> "page_loading_triggered"
  • optional client side delay via delay attribute
  • start to get new page from server -> "page_loading"
  • server side/network delay
  • successfully received new page from server -> "page_loaded"
  • failed to receive new page from server -> "page_loading_error"

DOM structure and loading state element

app/matestack/example_app/layout.rb
1
class ExampleApp::Layout < Matestack::Ui::Layout
2
3
def response
4
#...
5
main do
6
page_switch do
7
yield
8
end
9
end
10
#...
11
end
12
13
def my_loading_state_slot
14
span class: "some-loading-spinner" do
15
plain "loading..."
16
end
17
end
18
19
end
Copied!
which will render:
1
<main>
2
<div class="matestack-page-container">
3
<div class="loading-state-element-wrapper">
4
<span class="some-loading-spinner">
5
loading...
6
</span>
7
</div>
8
<div class="matestack-page-wrapper">
9
<div><!--this div is necessary for conditonal switch to async template via v-if -->
10
<div class="matestack-page-root">
11
your page markup
12
</div>
13
</div>
14
</div>
15
</div>
16
</end>
Copied!
and during async page request triggered via transition:
1
<main>
2
<div class="matestack-page-container loading">
3
<div class="loading-state-element-wrapper loading">
4
<span class="some-loading-spinner">
5
loading...
6
</span>
7
</div>
8
<div class="matestack-page-wrapper loading">
9
<div><!--this div is necessary for conditonal switch to async template via v-if -->
10
<div class="matestack-page-root">
11
your page markup
12
</div>
13
</div>
14
</div>
15
</div>
16
</end>
Copied!
You can use the loading class and your loading state element to implement CSS based loading state effects. It may look like this (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!

Examples

The transition core component renders the HTML <a> tag and performs a page transition

Perform transition from one page to another without full page reload

First, we define our routes (config/routes.rb) and the corresponding endpoints in our example controller:
1
get 'my_example_app/page1', to: 'example_app_pages#page1', as: 'page1'
2
get 'my_example_app/page2', to: 'example_app_pages#page2', as: 'page2'
Copied!
1
class ExampleAppPagesController < ExampleController
2
3
include Matestack::Ui::Core::Helper
4
5
matestack_layout ExampleApp::Layout
6
7
def page1
8
render ExampleApp::Pages::ExamplePage
9
end
10
11
def page2
12
render ExampleApp::Pages::SecondExamplePage
13
end
14
15
end
Copied!
Then, we define our example app layout with a navigation that consists of two transition components!
1
class ExampleApp::Layout < Matestack::Ui::Layout
2
3
def response
4
h1 'My Example App Layout'
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
page_switch do
16
yield
17
end
18
end
19
end
20
end
21
22
end
Copied!
Lastly, we define two example pages for our example application:
1
class ExampleApp::Pages::ExamplePage < Matestack::Ui::Page
2
3
def response
4
div id: 'my-div-on-page-1' do
5
h2 'This is Page 1'
6
plain "#{DateTime.now.strftime('%Q')}"
7
end
8
end
9
10
end
Copied!
and
1
class ExampleApp::Pages::SecondExamplePage < Matestack::Ui::Page
2
3
def response
4
div id: 'my-div-on-page-2' do
5
h2 'This is Page 2'
6
plain "#{DateTime.now.strftime('%Q')}"
7
end
8
transition path: page1_path do
9
button 'Back to Page 1'
10
end
11
end
12
13
end
Copied!
Now, we can visit our first example page via localhost:3000/my_example_app/page1 and see our two buttons (Page 1 and Page 2) and the content of page 1 (My Example App Layout and This is Page 1).
After clicking on the Page 2-button, we get transferred to our second page (This is Page 2) without re-loading the whole page.
If we then click the other button available (Back to Page 1), we get transferred back to the first page, again without re-loading the whole page. This behavior can save quite some request payload (and therefore loading time) as only the relevant content on a page gets replaced!