blog

Using a virtual DOM with Backbone views

| categories: virtual-dom, converse, backbone.js

Backbone.js, first released in 2010, provided one-way event binding with unidirectional data-flow and therefore a means to do reactive programming before it was cool.

Backbone views provide a render method, which is supposed to insert HTML into the DOM. How this was actually done was left up to the user as a homework assignment.

Generally, jQuery's html method was used. Like so:

const MyView = Backbone.View.extend({

    initialize () {
        this.model.on('change', this.render, this);
    },

    render () {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

Each view has an associated DOM element, this.$el if you're using jQuery or this.el otherwise. In the above example, the element's contents would be replaced.

What this approach however lacks, is a way to rerender views containing forms without losing the input focus or user-provided form values.

This is because back in 2010, the virtual DOM as popularized by React wasn't yet around.

Additionally, since rendering the HTML and inserting it into the DOM were both done inside the render method, and the implementation details were left up to the user, enough rope was provided for you to hang yourself by putting things inside the render method which shouldn't be there.

Contrast this to a React component's render method, which is a "pure function" that doesn't modify the DOM, and instead simply returns JSX:

class MyComponent extends React.Component {

        render() {
                return <p>Hello World!</p>;
        }
}

The markup is then inserted into the DOM separately with ReactDOM.render

ReactDOM.render(
        <MyComponent />,
        document.getElementById('root')
);

The fact that a component's render method must return JSX, means that they are pure functions without side-effects.

Backbone Views which use a virtual DOM

In Converse we still use Backbone Models and Views, and for the most part, Backbone still works beautifully. It's a small, lightweight library that's fit for purpose and doesn't get in the way.

However, we've been missing out on that sweet sweet virtual DOM.

No more!

To make our Backbone Views more reactive, as is done in React, I wrote Backbone.VDOMView.

This library adds a new view, called Backbone.VDOMView, which uses a Virtual DOM (provided by Snabbdom) to only update those DOM elements (within the view) as necessary.

Instead of writing a render method, for Backbone.VDOMView you need to implement a toHTML method, which is a pure function that simply returns the view's HTML.

Here's what it looks like:

const MyView = Backbone.VDOMView.extend({

    initialize () {
        this.model.on('change', this.render, this);
    },

    toHTML () {
        return this.template(this.model.toJSON());
    }
});

Backbone.VDOMView takes the output of toHTML and converts it into virtual DOM nodes and automatically diffs and patches the DOM.

When the view's associated Backbone.Model (referenced via this.model) changes, we can simply call render again, knowing that only the changed DOM elements will be updated.

We write less to the DOM and we prevent the loss of form input values and loss of element focus.

Include the root element in your HTML

There's one important difference with normal Backbone.Views and that is that toHTML (as opposed to render) must including the view's root element in the returned HTML.

With normal Backbone.View, you don't include the view's root element when you render a template.

React has a similar requirement to this. JSX returned in the render method of a component must have a root node which contains everything else.

jQuery not required

With Backbone, you can use Backbone.NativeView to remove the default dependency on jQuery.

Backbone.VDOMView checks whether you are using Backbone.NativeView and will then subclass it instead of Backbone.View.

Usage in Converse

In release 3.3.0 of Converse I've removed jQuery as dependency and started integrating Backbone.VDOMView.

Various Backbone.View components from Converse have already been migrated over to Backbone.VDOMView.

So far, I'm very happy with the results, and haven't run into any issues.

If you're using Backbone.VDOMView or have any feedback, feel free to leave a comment or contact me here.


Converse.js: XMPP instant messaging with Javascript

| categories: converse, backbone.js, instant messaging, xmpp, javascript, strophe.js, plone

It is now possible to use converse.js, the Javascript from collective.xmpp.chat, in a standalone way, to communicate with any public Jabber account.

Lately I've been spending time refactoring converse.js the Javascript library used by collective.xmpp.chat the instant messaging add-on for the Plone CMS. My goal is to make it usable on it's own, requiring nothing else except an XMPP server to communicate with.

This would enable any website owner to add instant messaging functionality to their website, and due to the federated nature of XMPP, users could chat to any other public XMPP account (once they have been accepted as a contact).

One thing that previously prevented you from using converse.js on its own, was the fact that it made XHR calls to the Plone backend to fetch user data. To fix this, I added vCard support, to converse.js but also to Plone by adding it to collective.xmpp.core.

Last week I reached a significant milestone on the path to this goal, and I'd like to take a moment and share with you.

It is now possible to use converse.js (in a static HTML page) to communicate with Jabber accounts on any public server.

In the demo below, I illustrate this by chatting to a Google user and to a Jabber.org user. In the converse.js page, I'm authenticated with a Jappix.com account and I also use their connection manager to connect to their XMPP server. If you're doing XMPP via HTTP (i.e in the browser), you'll need a connection manager as a bridge to your intended XMPP server. Thanks a lot to the Jappix guys for making their connection manager public!

Note: For detailed fullscreen, make sure that video quality is set to HD, by clicking the gear icon on the video player.

Note

UPDATE: Since this blog post, a production ready version of converse.js has been released. So the following is no longer applicable.

I'm sorry to say that converse.js is not yet 100% ready for primetime as a standalone JS app. There are a few more hurdles (and nice-to-haves) to overcome before we'll achieve this goal:

  • Searching and adding new users still does XHR calls to Plone and should instead query the XMPP server.
  • Service discovery support (e.g so that multi-user chat is only available for servers that support it).
  • Some kind of "Keep me logged in" support when users log in manually.
  • I'm not proud of the CSS and it could probably be improved upon quite a bit.
  • The Jasmine tests are out of date and not passing, also more tests are required.

If you are interested in the project, please contribute by forking the code on github.

Thanks a lot to Alec Ghica and David Ichim from Eau de Web who've made various improvements to both collective.xmpp.chat and converse.js over the past months.


Slides and Video for my talk on XMPP+Plone at Plone Conf 2012

| categories: backbone.js, xmpp, javascript, ploneconf2012, strophe.js, plone

The video of the talk I gave at in Arnhem at the 2012 Plone Conf has been uploaded to Youtube. Check it out if you are interested in integrating XMPP with Plone.

Thanks a lot to the person who uploaded this video! After seeing Cillian de Roiste spending much of his free time uploading the videos of the Munich Plone Konf, I realise how much effort this is!

Click here to view all the Plone Conf 2012 videos uploaded so far.

Follow-up info on XMPP-stuff after the conf

In the talk, I mention collaborative editing and showed demos of it (with TinyMCE). Unfortunately soon afterwards I came upon a significant flaw in the current approach. If you are interested in the collaborative editing possibilities for Plone, please read Collaborative editing of HTML with TinyMCE not going to happen soon.

Also since this talk, I've been quite busy improving collective.xmpp.chat to get it production ready. I've made an initial release to Pypi, which was actually supposed to be a 0.1 alpha release (but which I messed up somehow). Please consider collective.xmpp.chat to still be alpha software and not yet production ready! The most notable issue at the moment is poor support for having multiple tabs open. I'm aware of this and will work on a solution soon. Update: Fixed in the latest release: http://plone.org/products/collective.xmpp.chat/releases/0.2a2

I'm also in the process of extracting the Javascript files from collective.xmpp.chat and putting them in their own repository, called converse.js. The idea behind this is to completely decouple the JS from Plone and in doing so allow one to implement/deploy something like collective.xmpp.chat for other non-Plone backends.

That's it for now. Thanks for the support and encouragement from everyone in the Plone community. You guys rock!

Real-time, collaborative applications in Plone by jcbrand


Instant Messaging for Plone with javascript and XMPP

| categories: backbone.js, xmpp, javascript, screencast, strophe.js, plone

Late last year Yiorgis Gozadinos who at the time was still working for Jarn, released a video showing his work in integrating XMPP (eXtended Messaging and Presence Protocol) into Zope/Plone.

I had some experience in creating a chat client for Plone before and was inspired by the possibility of porting it to XMPP. In the end it wasn't so much of a port but rather a complete rewrite.

Today, I'm relieved and happy that it's finally in a state where I can show it to you. I've been spending most of my free time the last two months writing collective.xmpp.chat.

It's been both frustrating and fun (mostly fun) and a very valuable learning experience. Like that one week where I rewrote the entire codebase to make use of backbone.js. Aaah, fun times.

Some features already implemented:

Single user chat Roster support (adding/removing contacts) (XEP-0144) Setting presence status (online, offline, away, busy) Custom status messages Multi-user chatrooms (XEP-0045) Chat windows stay open upon page reload Offline storage of messages using HTML5 localstorage (still needs to be secured) Message actions (/me) Setting the topic of a chatroom (/topic)

Because almost all the code is written in Javascript (converse.js is about 1500 LOC), you can even stop the Zope server and still continue to chat. Unfortunately I forgot to show this in the screencast.

If anyone has any questions about the code, how to install, configure and use it, please don't hesitate to contact me. The add-on includes a buildout.cfg file that will set up Zope/Plone, Nginx and ejabberd for you. But please beware, the code is still in a pre-alpha state.

And then lastly, I plan to give a talk at the upcoming Plone Conference in Arnhem. This talk will focus on collective.xmpp.chat, the underlying technologies and how to write XMPP-enabled add-ons.

If you consider this an interesting topic, please go and vote for this talk to ensure that it gets accepted.

Screencast

Note: If you have trouble reading the test, click on the gear icon and set the video to High Definition.