Using Svelve for Plone add-ons
João
Posted on June 3, 2019
Zope/Plone bashing follows. If you like Zope/Plone, that's okay, just
skip the first paragraph and I promise you'll read no bashing!
I don't like Plone. Okay, actually, I don't like Zope/ZODB. It's overly complex, VERY inneficient, difficult to maintain, quite fragile. Plone itself is a miracle. The people behind it should be canonized as saints and revered as geniuses. They managed to do something actually usable with the mess that is Zope. Why I'm bashing Zope if I'm going to talk about Plone? Well, I have a job. My job is not to only work with technologies that I like. My job is to work with technologies that I have to.
End of Zope/Plone bashing, carry on!
The problem
I needed to develop an add-on which included a view that calls an internal server to search for specific results and render them differently depending on the context. There's not much to it, except the UI, which is not too complex, but complex enough for me to not like using vanilla JS, jQuery or simple ZPT templates. I first tried React, because it Plone 6 will ship with a React-based frontend. I managed to make it work with Plone 5, for then I had a problem: I still needed to support Plone 4, and then I found my solutions too bug-prone.
A solution: Svelte
I thought to myself: "What I need is something that doesn't require any external dependency at all". Then I remembered reading something about Svelte, because, well, it promised to be a "disappearing" library that outputs just plain Javascript that I would just need to include in my add-on. Does it work? Turns out it does! Here is a sample:
<script>
export let name;
</script>
<style>
h1 {
color: purple;
}
</style>
<h1>Hello {name}!</h1>
When you build it, the output is a Javascript file that does not need Svelte itself: it's a self-contained file that can easily be used inside a page with a simple JS entry. The CSS is split into a separate file as well.
<link rel="stylesheet" href="my-built-app.css" />
<script type="text/javascript" src="my-built-app.js"></script>
<div id="appContainer"></div>
<script>
// target is the element where the component will be rendered
// props are the properties that the components need
const app = new App({
target: document.getElementById("appContainer"),
props: {
name: "Elvis Presley"
}
});
</script>
Hands-on: Using Svelte inside an add-on
For this hands-on, you will need:
- The latest Node.js version. I highly recommend using NVM
- A Plone installation that includes a
develop.cfg
configuration - Knowledge on how to develop add-ons using Mrbob
Create a sample add-on product
First, create a sample add-on product using mrbob inside the src
directory of the plone instance:
mrbob -O svelte.sample collective.mrbob:addon
Then, add it to the develop.cfg
file:
...
[sources]
svelte.sample = fs svelte.sample
...
eggs +=
...
svelte.sample
Add the initial structure
For now, let's just add a file inside src/svelte/sample/browser/static/sample.js
that will output something to the console so we can tell everything's ok for now. We will replace it with the component later:
console.log("svelte.sample Javascript included");
Add a View
The Svelte sample will be run inside a simple view, so, let's first add it to configure.zcml
:
...
<browser:page
name="svelte-sample"
for="*"
permission="zope2.View"
class=".views.SampleView"
/>
...
The view is inside the views module, so, let's create it inside src/svelte/sample/views.py
:
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class SampleView(BrowserView):
index = ViewPageTemplateFile('templates/sample.pt')
def render(self):
return self.index()
def __call__(self):
return self.render()
We now need the template, so, create the file src/svelte/sample/templates/sample.pt
:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:tal="http://xml.zope.org/namespaces/tal"
metal:use-macro="context/main_template/macros/master">
<metal:block fill-slot="javascript_head_slot">
<script type="text/javascript" src="++plone++svelte.sample/sample.js" />
</metal:block>
<metal:block fill-slot="content-core">
Nothing here yet
</metal:block>
</html>
Now we have a working add-on. If you run the Plone instance, install it and open the @@svelte-sample
view, it will show a page containing "Nothing here yet", and the console will show 'selvte.sample Javascript included'. The add-on itself won't depend on anything else, but we obviously need to create the Svelte components. Thus, we need to create a Svelte project.
Create the Svelte project
Creating the project itself is easy. We will be creating the JS project inside our add-on source for this hands-on. Inside the project's root directory, run the following commands:
npx degit sveltejs/template component
cd component
npm install
Now, modify the file component/src/App.svelte
:
<script>
export let name;
$: uppercaseName = (name || "").toUpperCase();
</script>
<h1>Hello {uppercaseName}!</h1>
Before we build the project, we need to tell the builder to just export your components we built, by modifying component/src/main.js
import App from './App.svelte';
export default {
App
}
Then, let's build the project and copy the resulting bundle.js file:
npm run build
cp public/bundle.js ../src/svelte/sample/browser/static/sample.js
Add the Svelte component to the view
That's it, now the App component is going to be available when we include our sample.js file inside the Plone's view page. Change the src/svelte/component/templates/sample.pt
file to:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:tal="http://xml.zope.org/namespaces/tal"
metal:use-macro="context/main_template/macros/master">
<metal:block fill-slot="javascript_head_slot">
<script type="text/javascript" src="++plone++svelte.sample/sample.js" />
</metal:block>
<metal:block fill-slot="content-core">
<div id="sampleContainer"></div>
<script type="text/javascript">
const sample = new app.App({
target: document.getElementById("sampleContainer"),
props: {
name: "Person"
}
});
</script>
</metal:block>
</html>
Now, if you open the @@svelte-sample
view, it should show a big Hello PERSON!
. No external includes needed.
Bottom Line
I'm not saying you should just use Svelte for every single add-on. If you can target Plone5+, you could probably use React. If you, however, plan to support Plone 4 and/or don't want to rely too much on specific dependencies, it may be a good idea do pick Svelte. Just remember that what Svelte does is not to embed other dependencies inside the resulting JS build. So, my advice is to pick Svelte if you can live without other dependencies and pick React for anything else.
Posted on June 3, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024