From Angular to Vue

Porting Autoextra

Wilfred Springer
Apr 5 · 6 min read

Was it six years ago? I don’t remember. But I remember starting to feel quite uneasy about still using Angular 1. However, I also did not feel a lot of love for the alternatives. Angular 2 almost forced you into using TypeScript, React prevented me from using CoffeeScript and Pug and Ember just seemed — I don’t know — dated?

And then Vue came along. It felt like a breath of fresh air. It allowed me to continue to use CoffeeScript and Pug, and its programming model felt much more familiar than React’s.

Soon after that, I got caught up in other business, and stopped doing any front-end work for quite some time. (Okay, I ‘ll admit I did some prototypes of integrating OpenID Connect and Nuxt and headless content management systems, but that mostly sums it up.)

However, nothing helps deepening your understanding of a framework than exploring its limits. For Angular, creating the Autoextra directive was one of the exercises that deepened my understanding of Angular. And last week, when I needed similar behavior in Vue, I realized that building the same component in Vue would force me to consider a lot of the things Vue had to offer.

Meet Autoextra

Suppose that you have a list of things. You want to make sure you can edit each of these individual things. But you also want the ability to add a new thing. Would it not be wonderful if that new things is already there, for you to edit right away? And that the moment you start editing it, a new entry is appearing for the next ‘new thing’?

So, what I needed was a mechanism that would create form entries for all elements in an array, to update the elements of that array, but with one added row to add a new element. The moment you start editing data in that last entry, it should automatically be added to the existing array and a new empty form entry should appear, as outlined in the diagram below.

Before and after entering data in the ‘new’ entry

The reason I care about that so much is because it allows you to do data entry quickly, without being forced to hit buttons to add new entries. You just tab to the next entry and start typing. Boom. Magic happens.

Now, why is this hard? Well, first of all, you don’t want to pollute your existing collection with a new entry that isn’t included in the data yet. However, if you try to avoid that, and just manually add a new entry at the bottom of the list of entries — one that is not bound to an entry of the array —then it gets even harder:

It will become harder because you don’t want to loose focus while typing. With the naive approach, you would use for all elements in the array, and then manually add another entry. The moment you start editing that entry, it should be appended to the existing array. However, if you do that, then it will be added to the elements rendered by the directive, causing it to be rendered there as well. Now you need to change the focus to the right character position of the right input in the element that was just added, and remove the data from the existing row, to make room for a new entry. It’s just too messy.

Autoextra solves it in a very simple and elegant way: by shadowing the original collection and then making sure that the shadowed collection is always the original collection + a new entry. The moment you enter data in the last entry of the shadowed collection, a new element will be added. Edits to the existing elements of the shadowing collection will automatically also affect the elements of the original collection, since the shadowing collection is only a shallow copy of the original collection.

The Vue Implementation

Autoextra in Vue

While implementing it in Vue, I ran into a couple of things:

On rewriting HTML

The Angular version of Autoextra rewrote the HTML at compilation time. That is: if you had an element then it would change that to introduce in the scope and use watchers to keep and in sync.

Vue doesn’t allow you to do that. Okay, to be fair, you can create a directive that rewrites HTML. But by that time, all of the built-in directives have already performed their work. There is no (documented) way to boost the priority of your own directive to give it priority over anything else. (In Angular 1, you can do that.)

It’s also impossible to grab the original HTML, modify it, and then compile it yourself. Well, that is, you can do it if you do it from the Vue app itself. In that case, you could grab all children of the root of your application and modify that. It would however be limited to whatever happened to be inside your root template. In reality, it hardly ever will be. In almost all cases, the HTML that needs to be transformed lives inside one of your components.

On passing data into slots

So with all of that being impossible to do, I started looking into slots. Slots allow you to capture a template enclosed within the start and end tag of your component, and then you can repurpose it at will.

Ideally, I wanted to hide all slot related complexity. If my component would allow you to define the entry itself as an embedded template, and I could interpret it as a slot, introducing a few extra variables, then that would be good enough.

Unfortunately, it’s impossible to do it without exposing some slot API specifics to the end-user of your component. In particular it requires using to get hold of the variables getting introduced by your component, to be able to use it inside your template.

I tried various things, tried manual render methods, dug deeper under the hood, but I just can’t see any way of avoiding it. As a consequence, the component feels a little less self-contained than the Angular version. I hope I’ll get used to it.

Watches

Angular has its various watch methods: , and . In Vue, there is just one: the method on a instance. Autoextra needs two watches: it requires watching the property, to make sure it’s updating the shadowed collection upon change, and it needs to watch the placeholder for new entries. The moment the placeholder changes, it needs to be appended to the collection referenced by the property.

It turns out that I can just use Vue’s method for ‘shallow watching’ the collection referenced by the property. I do however need to pass in the option to have similar behavior as what Angular 1 is offering in its method.

Conclusion

Interestingly, I spent a lot of time trying to build a component that you could configure in exactly the same way as the Angular version. But when I gave up on that, once I started working from the other end and build an implementation first, then it turns out that implementing it in Vue is actually easier than doing the same in Angular. Admittedly, I will have to live with the fact that the slot API is spilling over, but that’s a small price for something that is much easier to grasp.

Check the demo here for the sources:

East Pole

Distributed reflections of the third kind

Wilfred Springer

Written by

Double bass playing father of three, hacker and soul searcher

East Pole

East Pole

Distributed reflections of the third kind

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade