Last week brought another successful pair-programming sprint towards ApostropheCMS 3.0, the next version of our open-source Node.js CMS. A major focus is migrating to the Vue frontend framework. And we really like Vue. Because Vue can do magic.
This time, Bob Clewell and I focused on a simple goal: editing Apostrophe’s pieces with Vue. “Pieces” are simple, standalone documents like blog posts and events.
Pieces rely heavily on schemas. So the greatest amount of work went into bringing Apostrophe’s schemas to Vue.
Magic trick #1: the Component element
Apostrophe developers expect to just list the fields they want and watch the magic happen; Vue is more commonly used for “bespoke” user interfaces where each field is hand-coded. How to bring them together?
We solved it by making liberal use of Vue’s
Component element. Here’s how we loop over all of the schema fields and output each one with the appropriate component for its field type:
component lets us delay the decision of what Vue component to use for each field until we know the field’s type. Nice one, Vue.
Magic trick #2: keeping a Rabbit inside your Hat
We also love Vue’s “slot pattern,” which allows us to wrap a component in another, reusable “layout component.” Here’s our
And now the
ApostropheString component that wears it like a tailored suit, slipping its
body in the middle:
Magic trick #3: two-way data binding
All of that is pretty magical. But our favorite bit of Vue magic, so far, is
v-model allows us to create a “two-way binding” in which a certain property of our data automatically “comes to life,” powered by a particular component.
All Vue developers have used it to power ordinary form fields; what you might not know is that you can add that same power to your own components.
What’s the big win there? We can just hand an Apostrophe document object off to an
ApostropheSchemaEditor component with
v-model, and… boom. Instant editability.
ApostrophePiecesInsertModal, a dialog box for creating a new document. We use
v-model to hand control of our “piece” off to the
ApostropheSchemaEditor component. Easy-peasy.
We also take advantage of slots again here, overriding parts of
ApostropheModal, which is based on the excellent example in the Vue documentation.
Magic trick #4: mixins save time (and prevent bugs)
There’s one more bit of Vue magic that we really like: mixins. Vue doesn’t have object-oriented inheritance, as such. But mixins provide very similar capabilities, in a way that is especially well suited to creating reusable interface components without duplicating code.
ApostropheFieldMixin.js. Here we implement all the parts of a component for an individual field that most field types won’t need to change:
The mixin does so many things for us right off the bat:
- It implements the
v-modelpattern, accepting a
valueprop and emitting an
inputevent when the value changes. It also watches the
valueprop so it can react if the field’s value is changed “upstream” in the schema editor.
- It provides a
nextdata property that’s ready to be hooked up to the actual input field, again via
- It handles defaults.
- It obtains
optionsfor us from the
apostrophe-schemasmodule. Before you ask: these don’t change during the lifetime of the page in the browser, and they are pushed down to the browser by server-side code. So yes, they do belong in
- It invokes a
validatemethod to make sure there are no errors in the input.
But wait, where is that
validate method? It is provided by each field component. Here’s the code for
Thanks to the mixin, plus the template you saw earlier that binds a text input field to
next, the only thing we have left to do is validate our input by providing a
validate method. If we don’t like it, we return an error string that will become part of a CSS class.
Upstream from us,
ApostropheSchemaEditor can easily inspect all of the values of all of the fields, spot any
error properties, and present
ApostrophePiecesInsertModal with what it really needs: the latest version of the document, plus a
hasErrors flag to help decide if we can save it yet or not.
This is some really cool magic. But Apostrophe 3.0 still isn’t a usable CMS just yet (although 2.x surely is, and will be supported for years to come). So what’s our next trick?
Sprint #3: editing content on the page
We have further to go with pieces, for sure. We still need to build a tabbed interface, and many more schema field types including complex cases like
array that can have their own schemas. But we’re confident in our direction and can work independently to take those things to the finish line.
So the next sprint will focus on a different area of Apostrophe: editing content in context on the page.
That means marrying Apostrophe’s concepts of widgets and areas on the page with Vue components that play nicely together even if some of the content inside them isn’t coming with Vue, since we don’t plan to require Vue on the front end. And that should be an interesting challenge.
Sprint #4: is it time for a new rich text editor?
One thing that will make sprint #3 a little easier: while widgets have a place on the page, they are most often edited via simple schema-based forms.
But there are exceptions. And that brings us to one of the most interesting challenges.
Is it time to ditch CKEditor?
We’ll put it right out there: CKEditor has been great for Apostrophe for many, many years. And we’re grateful. Along with TinyMCE, CKEditor has for many years been the only in-browser rich text editor with two critical features:
A permissive open source license, compatible with most projects.
Serious… even fanatical… amounts of cross-browser testing.
And we can’t stress either of those enough. Without the first, developers won’t use it. And without the second, a rich text editor is just a cute parlor trick that only works in Chrome.
So while other editors are aesthetically appealing and might load more quickly, it’s been hard to part with CKEditor.
But the latest version of CKEditor has a different license. And in recent years, Quill has broken free from the pack of flash-in-the-pan rich text editors to make a serious dent. And it sure is pretty, both for users and for developers.
So in Sprint #4, we’ll make up our minds through experiment and experience. And that should be great fun.