Categories
JavaScript

Update the DOM Easily with the Re:dom Library — Component Updates

We can update the DOM with the Re:dom library in a way easier than plain JavaScript.

In this article, we’ll look at how to use the library to diff the DOM and patch it.

Diffing

Re:dom will do the diffing and patching manually.

For example, we can write:

import { el, mount } from "redom";

class Image {
  constructor() {
    this.el = el("img");
    this.data = {};
  }
  update(data) {
    const { url } = data;

    if (url !== this.data.url) {
      this.el.src = url;
    }

    this.data = data;
  }
}
const image = new Image();
image.update({ url: "https://picsum.photos/id/237/200/300" });
image.update({ url: "https://picsum.photos/id/238/200/300" });

mount(document.body, image);

Then the 2nd image URL will be the final URL that’s set as the src of the img element.

Lists

We can create lists with an li element an array of items:

import { el, list, mount } from "redom";

class Li {
  constructor() {
    this.el = el("li");
  }
  update(data) {
    this.el.textContent = `Item ${data}`;
  }
}

const ul = list("ul", Li);

mount(document.body, ul);

ul.update([1, 2, 3]);
ul.update([2, 3, 4]);

We create an Li component that has the li element.

We just set the text content to an array of items and it’ll automatically be rendered into multiple li elements each with the entry.

Also, we can set the styles and content simultaneously:

import { el, list, mount } from "redom";

class Li {
  constructor() {
    this.el = el("li");
  }
  update(data, index, items, context) {
    this.el.style.color = context.colors.accent;
    this.el.textContent = `[${index}] - ${data}`;
  }
}

const ul = list("ul", Li);

mount(document.body, ul);

ul.update([1, 2, 3], { colors: { accent: "green" } });

We have the context with an object with the styles.

And textContent has the text content.

Component Lifecycle

Components have their own lifecycles.

We can add the following methods to our component to run code at various stages of the lifecycle:

import { el, mount } from "redom";

class Hello {
  constructor() {
    this.el = el("h1", "Hello world");
  }
  onmount() {
    console.log("mounted Hello");
  }
  onremount() {
    console.log("remounted Hello");
  }
  onunmount() {
    console.log("unmounted Hello");
  }
}

class App {
  constructor() {
    this.el = el("app", (this.hello = new Hello()));
  }
  onmount() {
    console.log("mounted App");
  }
  onremount() {
    console.log("remounted App");
  }
  onunmount() {
    console.log("unmounted App");
  }
}

const app = new App();
mount(document.body, app);

onmount is run when the component mounts.

onremount is run when we mount the element again.

onunmount is run when the component unmounts.

Update Keyed Lists

Re:dom can update items by its key.

For example, we can write:

import { el, list, mount } from "redom";

class Li {
  constructor() {
    this.el = el("li");
  }
  update(data) {
    this.el.textContent = data.name;
  }
}

const ul = list("ul", Li, "id");

mount(document.body, ul);

ul.update([
  { id: 1, name: "Item 1" },
  { id: 2, name: "Item 2" },
  { id: 3, name: "Item 3" }
]);

setTimeout(() => {
  ul.update([
    { id: 3, name: "Item 3" },
    { id: 2, name: "Item 2" }
  ]);
}, 1000);

We pass the property name of the ID as the 3rd argument.

Then the list will be updated accordingly.

Conclusion

We can update our components in many ways with Re:dom.

Categories
JavaScript

Update the DOM Easily with the Re:dom Library

We can update the DOM with the Re:dom library in a way easier than plain JavaScript.

In this article, we’ll look at how to use the library to diff the DOM and patch it.

Installation

We can install the Dom Diff library by running:

npm install redom

Adding Elements

We can add elements with the el method.

To use it, we write:

import { el, mount } from "redom";

const hello = el("h1", "Hello world!");

mount(document.body, hello);

The first argument is the tag name.

The 2nd argument is text content.

The mount method mounts the hello DOM object into the body element.

We can also change the element after we created it:

import { text, mount } from "redom";

const hello = text("hello");

mount(document.body, hello);

hello.textContent = "hi!";

Now we see hi! on the screen instead of hello since we changed it by changing the textContent property.

The first argument of el can be a CSS selector.

For example, we can write:

import { el, mount } from "redom";

const hello1 = el("", "hello1");
const hello2 = el("#hello", "hello2");
const hello3 = el(".hello", "hello2");
const hello4 = el("span.hello", "hello4");
mount(document.body, hello1);
mount(document.body, hello2);
mount(document.body, hello3);
mount(document.body, hello4);

If no tag name is specified, then it’ll be a div.

Otherwise, it’ll create the element specified.

So, span.hello will be rendered as a span and the rest are divs.

Also, we can specify the styles with an object:

import { el, mount } from "redom";

const hello1 = el("", { style: "color: red;" }, "hello1");
const hello2 = el("#hello", { style: { color: "green" } }, "hello2");

mount(document.body, hello1);
mount(document.body, hello2);

The 2nd argument has the styles and the 3rd has the text content.

We can also specify attributes in the object in the 2nd argument:

import { el, mount } from "redom";

const input = el("input", { type: "email", autofocus: true, value: "foo" });

mount(document.body, input);

We can also nest child elements with el :

import { el, mount } from "redom";

const element = el("ul", el("li", el("span", "hello")));

mount(document.body, element);

We created a ul with an li inside and a span inside.

Also, we can pass in an array of element objects with el :

import { el, mount } from "redom";

const element = el("ul", [el("li", "foo"), el("li", "bar")]);

mount(document.body, element);

We have a ul with li elements inside.

To display something conditionally, we can add the condition to display the content with plain JavaScript:

import { el, mount } from "redom";
const forgot = false;

const element = el(
  "form",
  el("input", { type: "email", placeholder: "email" }),
  !forgot && el("input", { type: "password", placeholder: "password" })
);

mount(document.body, element);

We only display the password field only if forgot is false .

Middleware

The 2nd argument of el can also make a middleware function which lets us manipulate the created DOM element:

import { el, mount } from "redom";
const element = el("h1", middleware, "Hello!");

function middleware(el) {
  el.style.color = "green";
}

mount(document.body, element);

Now Hello is green.

Conclusion

We can use the Re:dom library to manipulate the DOM more easily.

Categories
React

Render Code Diffs in a React App with the React Diff Viewer Library

To make rendering code diffs easier in a React app, we can use the React Diff Viewer library.

In this article, we’ll look at how to render code diffs with the React Diff Viewer library.

Installation

We can install the package by running:

npm i react-diff-viewer

or:

yarn add react-diff-viewer

Rendering the Diffs

To render the diffs in our React app, we can use the ReactDiffViewer component.

For example, we can write:

import React from "react";
import ReactDiffViewer from "react-diff-viewer";

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

export default function App() {
  return (
    <div className="App">
      <ReactDiffViewer oldValue={oldCode} newValue={newCode} splitView={true} />
    </div>
  );
}

We pass in the old code string into the oldValue prop and the new code into the newValue prop.

Also, we can hide the line numbers with the hideLineNumers prop.

splitView shows the old and new code side by side.

Compare Method

We can change the compare method of for diffing with the compareMethod prop:

import React from "react";
import ReactDiffViewer from "react-diff-viewer";
import Prism from "prismjs";

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

export default function App() {
  return (
    <div className="App">
      <ReactDiffViewer
        oldValue={oldCode}
        newValue={newCode}
        splitView={true}
        compareMethod="diffWords"
      />
    </div>
  );
}

Styling

We can change the styles by passing in the styles prop:

import React from "react";
import ReactDiffViewer from "react-diff-viewer";
import Prism from "prismjs";

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

const newStyles = {
  variables: {
    dark: {
      highlightBackground: "#fefed5",
      highlightGutterBackground: "#ffcd3c"
    }
  },
  line: {
    padding: "10px 2px",
    "&:hover": {
      background: "purple"
    }
  }
};

export default function App() {
  return (
    <div className="App">
      <ReactDiffViewer
        oldValue={oldCode}
        newValue={newCode}
        splitView={true}
        styles={newStyles}
      />
    </div>
  );
}

We set it to an object with specific properties to set the styles.

highlightBackground sets the background color for highlights.

highlightGutterBackground sets the color for the gutter.

line is the styles for each line.

We can change the hover style of a line with the &:hover property.

Conclusion

We can use the React Diff Viewer component to add a code diff view into our React app.

Categories
Ant Design Vue

Getting Started with Ant Design Vue

Ant Design Vue or AntD Vue, is a useful UI framework made for Vue.js.

In this article, we’ll look at how to use it in our Vue apps.

Getting Started

We can install AntD Vue by running:

npm i --save ant-design-vue

Then we can register the plugin by writing:

import Vue from "vue";
import App from "./App.vue";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";
Vue.use(Antd);
Vue.config.productionTip = false;

new Vue({
  render: (h) => h(App)
}).$mount("#app");

This will register the components and load the CSS.

Buttons

We can add buttons with AntD Vue by using the a-button component”

<template>
  <div id="app">
    <a-button type="primary">Primary</a-button>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

The type is the color type.

We can add a button group by writing:

<template>
  <div id="app">
    <a-button-group>
      <a-button>L</a-button>
      <a-button disabled>M</a-button>
      <a-button disabled>R</a-button>
    </a-button-group>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We can add an icon with the icon prop:

<template>
  <div id="app">
    <a-button icon="search">Search</a-button>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

To make a button a block-level element, we can add the block prop:

<template>
  <div id="app">
    <a-button type="primary" block>Primary</a-button>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Also, we can make it show a loading indicator with the loading prop:

<template>
  <div id="app">
    <a-button type="primary" loading>Loading</a-button>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

The size can be changed with the size prop:

<template>
  <div id="app">
    <a-button type="primary" size="large">Primary</a-button>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Icon

AntD Vue comes with various icons.

For example, we can add one by writing:

<template>
  <div id="app">
    <a-icon type="step-backward"/>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We use the a-icon component to add the icon.

There are many more we can add. The list is at https://www.antdv.com/components/icon/.

Grid

Ant Design Vue comes with its own layout grid. We can add it with the a-row and a-col components:

<template>
  <div id="app">
    <a-row>
      <a-col :span="12">col-12</a-col>
      <a-col :span="12">col-12</a-col>
    </a-row>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We set the span prop to a value from 0 to 12 to make the layout.

It supports flexbox, and we can set the options with the justify and align props:

<template>
  <div id="app">
    <a-row type="flex" justify="center" align="top">
      <a-col :span="4">
        <p class="height-100">col-4</p>
      </a-col>
      <a-col :span="4">
        <p class="height-50">col-4</p>
      </a-col>
      <a-col :span="4">
        <p class="height-120">col-4</p>
      </a-col>
      <a-col :span="4">
        <p class="height-80">col-4</p>
      </a-col>
    </a-row>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Now they are all centered.

Conclusion

We can use Ant Design Vue to create a good looking UI in our Vue app.

Categories
Ant Design Vue

Ant Design Vue — Submenus

Ant Design Vue or AntD Vue, is a useful UI framework made for Vue.js.

In this article, we’ll look at how to use it in our Vue apps.

Submenus

We can add a submenu with the a-sub-menu component:

<template>
  <div id="app">
    <a-menu
      style="width: 256px"
      :default-selected-keys="['1']"
      :open-keys.sync="openKeys"
      mode="inline"
      @click="handleClick"
    >
      <a-sub-menu key="sub1" @titleClick="titleClick">
        <span slot="title">
          <a-icon type="mail"/>
          <span>Navigation One</span>
        </span>
        <a-menu-item-group key="g1">
          <template slot="title">
            <a-icon type="qq"/>
            <span>Item 1</span>
          </template>
          <a-menu-item key="1">Option 1</a-menu-item>
          <a-menu-item key="2">Option 2</a-menu-item>
        </a-menu-item-group>
        <a-menu-item-group key="g2" title="Item 2">
          <a-menu-item key="3">Option 3</a-menu-item>
          <a-menu-item key="4">Option 4</a-menu-item>
        </a-menu-item-group>
      </a-sub-menu>
      <a-sub-menu key="sub2" [@titleClick](http://twitter.com/titleClick "Twitter profile for @titleClick")="titleClick">
        <span slot="title">
          <a-icon type="appstore"/>
          <span>Navigation Two</span>
        </span>
        <a-menu-item key="5">Option 5</a-menu-item>
        <a-menu-item key="6">Option 6</a-menu-item>
        <a-sub-menu key="sub3" title="Submenu">
          <a-menu-item key="7">Option 7</a-menu-item>
          <a-menu-item key="8">Option 8</a-menu-item>
        </a-sub-menu>
      </a-sub-menu>
      <a-sub-menu key="sub4">
        <span slot="title">
          <a-icon type="setting"/>
          <span>Navigation Three</span>
        </span>
        <a-menu-item key="9">Option 9</a-menu-item>
        <a-menu-item key="10">Option 10</a-menu-item>
      </a-sub-menu>
    </a-menu>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      current: ["mail"],
      openKeys: ["sub1"]
    };
  },
  watch: {
    openKeys(val) {
      console.log("openKeys", val);
    }
  },
  methods: {
    handleClick(e) {
      console.log("click", e);
    },
    titleClick(e) {
      console.log("titleClick", e);
    }
  }
};
</script>

We can make the menu toggleable by adding a button:

<template>
  <div id="app">
    <a-button type="primary" style="margin-bottom: 16px" @click="toggleCollapsed">
      <a-icon :type="collapsed ? 'menu-unfold' : 'menu-fold'"/>
    </a-button>
    <a-menu
      :default-selected-keys="['1']"
      :default-open-keys="['sub1']"
      mode="inline"
      theme="dark"
      :inline-collapsed="collapsed"
    >
      <a-menu-item key="1">
        <a-icon type="pie-chart"/>
        <span>Option 1</span>
      </a-menu-item>
      <a-menu-item key="2">
        <a-icon type="desktop"/>
        <span>Option 2</span>
      </a-menu-item>
      <a-menu-item key="3">
        <a-icon type="inbox"/>
        <span>Option 3</span>
      </a-menu-item>
      <a-sub-menu key="sub1">
        <span slot="title">
          <a-icon type="mail"/>
          <span>Navigation One</span>
        </span>
        <a-menu-item key="5">Option 5</a-menu-item>
        <a-menu-item key="6">Option 6</a-menu-item>
      </a-sub-menu>
      <a-sub-menu key="sub2">
        <span slot="title">
          <a-icon type="appstore"/>
          <span>Navigation Two</span>
        </span>
        <a-menu-item key="7">Option 7</a-menu-item>
        <a-menu-item key="8">Option 8</a-menu-item>>
        <a-sub-menu key="sub3" title="Submenu">
          <a-menu-item key="9">Option 9</a-menu-item>
          <a-menu-item key="10">Option 10</a-menu-item>
        </a-sub-menu>
      </a-sub-menu>
    </a-menu>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      collapsed: false
    };
  },
  methods: {
    toggleCollapsed() {
      this.collapsed = !this.collapsed;
    }
  }
};
</script>

The inline-collapsed prop is used to set whether the menu is collapsed or not.

We can also make it only open one submenu only bu writing:

<template>
  <div id="app">
    <a-menu mode="inline" :open-keys="openKeys" style="width: 256px" @openChange="onOpenChange">
      <a-sub-menu key="sub1">
        <span slot="title">
          <a-icon type="mail"/>
          <span>Navigation One</span>
        </span>
        <a-menu-item key="1">Option 1</a-menu-item>
        <a-menu-item key="2">Option 2</a-menu-item>
      </a-sub-menu>
      <a-sub-menu key="sub2">
        <span slot="title">
          <a-icon type="appstore"/>
          <span>Navigation Two</span>
        </span>
        <a-menu-item key="3">Option 3</a-menu-item>
        <a-menu-item key="4">Option 4</a-menu-item>
        <a-sub-menu key="sub3" title="Submenu">
          <a-menu-item key="5">Option 5</a-menu-item>
          <a-menu-item key="6">Option 6</a-menu-item>
        </a-sub-menu>
      </a-sub-menu>
      <a-sub-menu key="sub4">
        <span slot="title">
          <a-icon type="setting"/>
          <span>Navigation Three</span>
        </span>
        <a-menu-item key="7">Option 7</a-menu-item>
        <a-menu-item key="8">Option 8</a-menu-item>
      </a-sub-menu>
    </a-menu>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      rootSubmenuKeys: ["sub1", "sub2", "sub4"],
      openKeys: ["sub1"]
    };
  },
  methods: {
    onOpenChange(openKeys) {
      const latestOpenKey = openKeys.find(
        key => this.openKeys.indexOf(key) === -1
      );
      if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
        this.openKeys = openKeys;
      } else {
        this.openKeys = latestOpenKey ? [latestOpenKey] : [];
      }
    }
  }
};
</script>

Set set the openKeys prop to the key prop value of the menu to open.

The openChange is emitted when we click on a submenu, so we can set the menu item to open there by getting the openKeys and setting it to the one that we clicked on.

Conclusion

We can add menus and submenus with Ant Design Vue.