DEV Community

Cover image for Cascade-esq Notation in JavaScript?
keogami
keogami

Posted on

Cascade-esq Notation in JavaScript?

A little while ago I was going through the Dart's Language Tour and found this cool notation they got. They call it the Cascade Notation.

Here's a code example from the tour:

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

Which translates to:

var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

Now, ain't that pretty? 😆

JavaScript's influence on Dart's syntax is quite evident. Its almost as if Dart is Javascript with nutella smeared all over it.
And as such, Dart has some feature over JavaScript which have got me drooling! 🤤

My Problem

Its pretty common in JavaScript to programmatically build elements and populate them in some parent element. Like so:

let anchor = document.createElement('a');
anchor.href = data.link;
anchor.innerText = data.caption
anchor.classList.add(data.type)
parent.appendChild(anchor)

It has always bugged me to have to type that anchor.someProp. I wish we had something like that Cascade Notation, but alas, we don't. And that's my problem.

My Solution - Chain 'em all!

I will simply create a class that chains and have a .build() method that returns the element. Something like:

parent.appendChild(
  new Link()            // Returns a chainable builder
    .to(data.link)
    .text(data.caption)
    .type(data.type)
    .build()            // Finally, returns anchor element
)

Now, how to implement?

Chainable objects have been around for years and are pretty well known. And here's how I like to construct them.

First, I create a helping curry-ed function(check Currying):

let chainer = (ctx) => (mutator) => (...args) => {
  mutator.apply(ctx, args)
  return ctx
}

Here,

  1. ctx is the object that is chained upon
  2. mutator is a function that is used to actually make changes to the ctx
  3. ...args are the arguments provided to the mutator

Then, I create the builder:

let Link = function () {
  let linker = chainer(this)

  let config = {
    to: "",
    text: "",
    type: "",
  }

  this.config = config
  this.to = linker((link) => {
    config.to = link
  })
  this.text = linker((text) => {
    config.text = text
  })
  this.type = linker((type) => {
    config.type = type
  })

  this.build = () => {
    let a = document.createElement("a")
    a.href = config.to
    a.innerText = config.text
    !!config.type && a.classList.add(config.type)
    return a
  }
}

Voila! we are done (︶^︶)

Usage

We can use them ubiquitously, like literally smearing nutella. 😋

document.body.appendChild(
  new Link()
    .to("http://localhost")
    .text("home sweet home")
    .build()
)

Wanna try?

End Note

Yo reader! This was my way of trying to recreate the cascading syntax with JavaScript, if you got a better way... Share it with everyone!

Cheers~

Top comments (0)