Migrating to 3.0

Installation & setup changes

Core/Vuejs repo and gem split

  • matestack-ui-core previously contained logic for
    • Ruby -> HTML conversion
    • Reactivity via prebuilt and custom Vue.js components
  • in order to have better seperation of concerns, we've moved the reactivity related things to its own repository/gem -> matestack-ui-vuejs
  • matestack-ui-core is now meant to be combined with any reactivity framework or none at all
If you've used reactivity features of matestack-ui-core 2.x you now have to install matestack-ui-vuejs (Ruby Gem & NPM Package) additionally:
Gemfile
1
gem 'matestack-ui-core', '~> 3.0.0.rc2'
2
gem 'matestack-ui-vuejs', '~> 3.0.0.rc3'
Copied!

Remove matestack-ui-core JavaScript package

  • matestack-ui-core does not ship a JavaScript package anymore
  • please remove the package from your application and switch to matestack-ui-vuejs for the VueJs driven reactivity if required
1
yarn remove matestack-ui-core
Copied!
  • and add matestack-ui-vuejs:
package.json
1
{
2
"name": "my-app",
3
"dependencies": {
4
"matestack-ui-vuejs": "^3.0.0-rc3", // <-- new package name
5
"..."
6
}
7
}
Copied!

IE 11 support dropped

  • vue3 dropped IE 11 support
  • when using babel alongside webpacker, please adjust your package.json or .browserslistrc config in order to exclude IE 11 support:
1
{
2
"name": "my-app",
3
"...": { },
4
"browserslist": [
5
"defaults",
6
"not IE 11" // <-- important!
7
]
8
}
Copied!
Otherwise you may encounter issues around regeneratorRuntime (especially when using Vuex)

Setup via webpacker

config/webpack/environment.js
1
const { environment } = require('@rails/webpacker')
2
const webpack = require('webpack');
3
4
const customWebpackConfig = {
5
resolve: {
6
alias: {
7
vue: 'vue/dist/vue.esm-bundler',
8
}
9
},
10
plugins: [
11
new webpack.DefinePlugin({
12
__VUE_OPTIONS_API__: true,
13
__VUE_PROD_DEVTOOLS__: false
14
})
15
]
16
}
17
18
environment.config.merge(customWebpackConfig)
19
20
module.exports = environment
Copied!
(don't forget to restart webpacker when changing this file!)
and then just use import { whatever } from 'vue' instead of import { whatever } from 'vue/dist/vue.esm'
Optional: vue3 compat build usage
  • if you're using any vue2 APIs (or one of the libraries you're using), you can use the vue3 compat build
  • this enables you to use both vue2 and vue3 APIs and migrate step by step
  • usage via webpack config when using webpacker 5.x and up:
config/webpack/environment.js
1
const { environment } = require('@rails/webpacker')
2
const webpack = require('webpack')
3
4
const customWebpackConfig = {
5
resolve: {
6
alias: {
7
vue: '@vue/compat/dist/vue.esm-bundler',
8
}
9
},
10
plugins: [
11
new webpack.DefinePlugin({
12
__VUE_OPTIONS_API__: true,
13
__VUE_PROD_DEVTOOLS__: false
14
})
15
]
16
}
17
18
environment.config.merge(customWebpackConfig)
19
20
module.exports = environment
Copied!

Ruby related changes

Matestack::Ui::App is now called Matestack::Ui::Layout

  • Matestack::Ui::App was always meant to be a layout wrapping pages, but was supercharged with some vuejs logic before splitting the core and vuejs repos
  • now Matestack::Ui::App is only a layout, that's why it should be named like that: Matestack::Ui::Layout
-> Search&Replace

matestack_app method is renamed to matestack_layout

  • following the above mentioned naming adjustment, the matestack_app method used on controller level is renamed to matestack_layout
app/controllers/demo_controller.rb
1
class DemoController < ActionController::Base
2
include Matestack::Ui::Core::Helper
3
4
layout "application" # root rails layout file
5
6
matestack_layout DemoApp::Layout # <-- renamed from matestack_app
7
8
def foo
9
render DemoApp::Pages::Foo
10
end
11
12
end
Copied!

Matestack::Ui::Layout Matestack::Ui::Page wrapping DOM structures

  • previously, Matestack::Ui::App added some wrapping DOM structure around the whole layout and around it's yield
  • this enabled dynamic page transition and loading state animations
  • Matestack::Ui::Layout now purely renders the layout and yields a page without anything in between
  • the wrapping DOM structres required for dynamic page transitions and loading state animations needs to be added via two new components if you want to use these features via matestack-ui-vuejs (see section below!)
matestack/some/app/layout.rb
1
class Some::App::Layout < Matestack::Ui::Layout
2
def response
3
h1 "Demo App"
4
main do
5
yield
6
end
7
end
8
end
Copied!
matestack/some/app/pages/some_page.rb
1
class Some::App::Pages::SomePage < Matestack::Ui::Page
2
def response
3
h2 "Some Page"
4
end
5
end
Copied!
will just render:
1
<body> <!-- coming from rails layout if specified -->
2
<!-- no wrapping DON structure around the layout -->
3
<h1>Demo App</<h1>
4
<main>
5
<!-- page markup without any wrapping DOM structure -->
6
<h2>Some Page</h2>
7
<main>
8
</body>
Copied!

Matestack::Ui::Layout adjustments when using matestack-ui-vuejs

  • Matestack::Ui::Layout classes are no longer automatically wrapped by a component tag meant to mount the matestack-ui-core-app component on it.
  • this has to be done manually via the matestack_vue_js_app component, which is more explicit and gives more flexibility
  • additionally, the page_switch component has to wrap the yield in order to support dynamic page transitions
matestack/some/vue_js/app/layout.rb
1
class Some::VueJs::App::Layout < Matestack::Ui::Layout
2
3
def response
4
h1 "Demo VueJs App"
5
matestack_vue_js_app do # <-- this one
6
main do
7
page_switch do # <-- and this one
8
yield
9
end
10
end
11
end
12
end
13
14
end
Copied!
  • using these components will add the original wrapping DOM structres which enables loading state animations
  • new <matestack-component-tempate> will be rendered coming from these two new components
1
<body> <!-- coming from rails layout if specified -->
2
<div id="matestack-ui"> <!-- coming from rails layout if specified -->
3
<h1>Demo VueJs App</h1>
4
<matestack-component-tempate> <!-- new tag rendered since 3.0, should not break anything -->
5
<div class="matestack-app-wrapper">
6
<main>
7
<matestack-component-tempate> <!-- new tag rendered since 3.0, should not break anything -->
8
<div class="matestack-page-container">
9
<div class="matestack-page-wrapper">
10
<div> <!--this div is necessary for conditonal switch to async template via v-if -->
11
<div class="matestack-page-root">
12
your page markup
13
</div>
14
</div>
15
</div>
16
</div>
17
</matestack-component-tempate>
18
</main>
19
</div>
20
</matestack-component-tempate>
21
</div>
22
</body>
Copied!

VueJs related changes in order to support vue3

MatestackUiCore is now MatestackUiVueJs

  • following the repo/gem split, the Vue.js related libary is now called MatestackUiVueJs
-> Search&Replace

app definition and mount

javascript/packs/application.js
1
import { createApp } from 'vue'
2
import MatestackUiVueJs from 'matestack-ui-vuejs'
3
4
const appInstance = createApp({})
5
6
document.addEventListener('DOMContentLoaded', () => {
7
MatestackUiVueJs.mount(appInstance) // use this mount method
8
})
Copied!

custom component registration

some/component/file.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
foo: "bar"
9
};
10
},
11
mounted(){
12
console.log("custom component mounted")
13
}
14
};
15
16
export default myComponent
Copied!
javascript/packs/application.js
1
import { createApp } from 'vue'
2
import MatestackUiVueJs from 'matestack-ui-vuejs'
3
4
import myComponent from 'some/component/file.js' // import component definition from source
5
6
const appInstance = createApp({})
7
8
appInstance.component('my-component', myComponent) // register at appInstance
9
10
document.addEventListener('DOMContentLoaded', () => {
11
MatestackUiVueJs.mount(appInstance)
12
})
Copied!

component template

  • For application components, apply template: MatestackUiVueJs.componentHelpers.inlineTemplate
some/component/file.js
1
import MatestackUiVueJs from 'matestack-ui-vuejs'
2
3
const myComponent = {
4
mixins: [MatestackUiVueJs.componentMixin],
5
template: MatestackUiVueJs.componentHelpers.inlineTemplate, // this one!
6
data() {
7
return {
8
foo: "bar"
9
};
10
},
11
mounted(){
12
console.log("custom component mounted")
13
}
14
};
15
16
export default myComponent
Copied!
  • Only for core components
    • add import import componentHelpers from 'some/relative/path/to/helpers'
    • and then apply template: componentHelpers.inlineTemplate

component scope prefix

  • use vc. (short for vue component) in order to prefix all properties references or method calls within your vue.js component response
some/component/file.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
foo: "bar"
9
};
10
},
11
mounted(){
12
console.log(this.foo) // --> bar
13
// or:
14
console.log(vc.foo) // --> bar
15
}
16
};
17
18
export default myComponent
Copied!
1
class Components::MyComponent < Matestack::Ui::VueJsComponent
2
vue_name "my-component"
3
4
def response
5
div do
6
plain "{{foo}}" # --> undefined!
7
plain "{{vc.foo}}" # --> bar
8
end
9
end
10
end
Copied!

component $refs

  • use this.getRefs() instead of this.$refs
  • use matestack_ui_vuejs_ref() when applying refs to your componen template:
1
def response
2
# ...
3
div ref: "some-ref" # <-- not picked up by this.getRefs()
4
# ...
5
div "matestack-ui-vuejs-ref": matestack_ui_vuejs_ref('some-ref')
6
end
Copied!

component $el

  • use this.getElement() instead of this.$el in order to get the root element defined in your response method
-> Search&Replace
Additional Note:
  • use this.getTemplateElement() in order to get the template element (matestack-component-template tag) wrapping the root element defined in your response method

component beforeDestroy hook

  • beforeDestroy was renamed to beforeUnmount within vue3
-> Search&Replace

$set, Vue.set

  • this.$set and Vue.set are removed in vue3 as they are not longer required for proper reactivity binding
  • If you use these methods, use plain JavaScript mutations instead

Vuex store dependency removed

  • previously a Vuex store was required and by default available. Now it's optional
  • you can add a store manually following the official Vuex 4.x docs