Open Source Adventures: Episode 23: Imba 2 JSON Beautifier
Tags: javascript, imba
Published: 2022-03-25 14:02:08

Let's port another Imba 1 app to Imba 2 - JSON Beautifier, [which you can see in action here](https://taw.github.io/imba-json-beautifier/).

The app is very simple, has just one component, and outsources all logic to `json-stringify-pretty-compact` package.

The most interesting part about it is that it uses Cypress for integration testing, so in the next post I'll also try to setup Cypress integration tests for the Imba 2 version too.

### Imba 1 `app.imba`

```
let stringify = require("json-stringify-pretty-compact")

tag App
  def setup
    @maxlen = 80
    @indent = 2
    @text = JSON.stringify({hello: "world"})
    @error = null

  def prettify
    try
      let indent = parseInt(@indent)
      let json = JSON.parse(@text)
      let spaces = Array.from({length: indent+1}).join(" ")
      @text = stringify(json, {maxLength: @maxlen, indent: spaces})
    catch e
      @error = e

  def clear_error
    @error = null

  def upload(event)
    let file = event.native:target:files[0]
    return unless file
    let reader = FileReader.new

    reader:onload = do |event|
      @text = event:target:result
      @error = nil
      Imba.commit
    reader.read-as-text(file)

  def render
    <self>
      <header>
        "JSON Beautifier"
      <textarea[@text] rows=10 :input.clear_error>
      if @error
        <div.error>
          @error
      <div.controls>
        <input#file type="file" :change.upload>
        <label for="indent">
          "Indent"
        <input#indent[@indent] type="number" min=0>
        <label for="maxlen">
          "Max row length"
        <input#maxlen[@maxlen] type="number" min=0>
        <button :click.prettify>
          "Prettify"

Imba.mount <App>
```

### Imba 1 `app.scss`

```
@import 'normalize-scss';
@include normalize();

.App {
  display: flex;
  flex-direction: column;
  align-items: center;

  header {
    font-size: 64px;
    text-align: center;
  }

  textarea {
    min-width: 50vw;
    margin-bottom: 10px;
  }

  .controls {
    display: grid;
    grid-row-gap: 5px;
    margin: auto;

    label { grid-column: 1; }
    input { grid-column: 2; }
    button { grid-column: 2; }
  }

  .error {
    background-color: #fcc;
    min-width: 50vw;
    padding: 5px;
    border: 1px solid #800;
  }
}
```

### Imba 2 `app.imba`

```
import stringify from "json-stringify-pretty-compact"

tag app
  prop maxlen = 80
  prop indent = 2
  prop text = JSON.stringify({hello: "world"})
  prop error

  def prettify
    try
      let json = JSON.parse(text)
      let spaces = Array.from({length: parseInt(indent)+1}).join(" ")
      text = stringify(json, {maxLength: parseInt(maxlen), indent: spaces})
    catch e
      error = ""+e
    imba.commit()

  def clear_error
    error = null

  def upload(event)
    let file = event.target.files[0]
    return unless file
    let reader = new FileReader

    reader.onload = do |event|
      text = event.target.result
      error = nil
      imba.commit()
    reader.readAsText(file)

  <self>
    <header>
      "JSON Beautifier"
    <textarea bind=text rows=10 :input.clear_error>
    if error
      <div.error>
        error
    <div.controls>
      <input#file type="file" :change.upload>
      <label for="indent">
        "Indent"
      <input#indent bind=indent type="number" min=0>
      <label for="maxlen">
        "Max row length"
      <input#maxlen bind=maxlen type="number" min=0>
      <button :click.prettify>
        "Prettify"

  css
    display: flex
    flex-direction: column
    align-items: center
    ff: sans

    header
      font-size: 64px
      text-align: center

    textarea
      min-width: 50vw
      margin-bottom: 10px

    .controls
      display: grid
      grid-row-gap: 5px
      margin: auto

      label
        grid-column: 1
      input, button
        grid-column: 2

    .error
      background-color: #fcc
      min-width: 50vw
      padding: 5px
      border: 1px solid #800

imba.mount <app>
```

This is a very straightforward translation.

We need to convert `Error` to `String`, as Imba 2 doesn't like putting `Error` objects directly in the output, but that's probably a good idea anyway.

Other than that, it's just minor syntactic changes.

By the way I wonder if any framework will ever offer good solution for the `label for` problem. Right now they inherently use globally scoped ids.

### Source code

Source code is in [imba2-json-beautifier repository](https://github.com/taw/imba2-json-beautifier).

You can also [see the live version here](https://taw.github.io/imba2-json-beautifier/).

### Coming next

In the next episode we'll try to add Cypress tests to this app.