DEV Community

artydev
artydev

Posted on • Updated on

Components switching with DML

Alt Text

With DML, switching components is very easy, choose a base point, and 'call' your component.

    function Home () {
      let home = div("HOME", cssComp);
      selectBase(div('', 'margin-left:20px'));
        print(`
        <br >
              DML doesn't interrupt your coding flow.... 
        `)
      unselectBase();
    }

    let Todo = () => {
      let todo = selectBase(div("TODOS...", cssComp));
      ul(["Task One", "Task Two"])
      unselectBase();
    }

    function Counter () {
      let counter = div("COUNTER", cssComp);
      selectBase(counter);
        let value = div("0");
        button("inc").onclick = () => 
          value.innerText = Number(value.innerText) + 1;
        button("dec").onclick = () => 
          value.innerText = Number(value.innerText) - 1;
      unselectBase()
    }


    function cleanElt (elt) {
       while(elt.firstChild) {
        elt.removeChild(elt.firstChild);
       }
    }

    function router(component) {
      selectBase(main)
        cleanElt(main);
        component();
      unselectBase();
    }

    function Navbar () {
      selectBase(div('', cssNavbar))
        button("Home",cssButton ).onclick = () => router(Home);
        button("Todo", cssButton).onclick = () => router(Todo);
        button("Counter", cssButton).onclick = () => router(Counter);
      unselectBase();
    }

    Navbar();

    let main = div('', cssContainer);

    router(Home); 

Enter fullscreen mode Exit fullscreen mode

You can test it here : ComponentsSwitcher

<html lang="de">
  <head>
  <meta charset="utf-8">
    <title>title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>
  </head>
  <body> 
  <script> 
    const cssComp = `
      text-align:center;
      font-size:32px;
      color:white;
    `;

    const cssContainer =  `
      background:#344053;
      height: calc(100vh - 70px);
      color:white;
      padding-top: 30px;
    `;

    const  cssButton = `
      color:black;
      line-height: 2rem;
      border: 1px solid transparent
      margin-right: 5px;
      cursor: pointer;
    `

    const cssNavbar = `
      background: rgba(0,0,12,0.4);
      border-bottom: 2px solid white;
    `

    function Home () {
      let home = div("HOME", cssComp);
      selectBase(div('', 'margin-left:20px'));
        print(`
        <br >
              DML doesn't interrupt your coding flow.... 
        `)
      unselectBase();
    }

    let Todo = () => {
      let todo = selectBase(div("TODOS...", cssComp));
      ul(["Task One", "Task Two"])
      unselectBase();
    }

    function Counter () {
      let counter = div("COUNTER", cssComp);
      selectBase(counter);
        let value = div("0");
        button("inc").onclick = () => 
          value.innerText = Number(value.innerText) + 1;
        bdec = button("dec").onclick = () => 
          value.innerText = Number(value.innerText) - 1;
      unselectBase()
    }



   function Tree () {
    print("From the offical site : ")
    br()
    let base = selectBase(div("", "width:350px;margin:0 auto"))
    let canvas = canvas2D()
    let cx = canvas.ctx

    // recursion
    function branch(length, angle, scale, level) {
      cx.fillRect(0, 0, .1 * length, length);
      let r=constrain(level*25,0,255)
      cx.fillStyle = "rgb("+r+ "," + (255-r)+ ",0)";
      if (level > 10) {  // -> exit
        return;
      }
      cx.save();  
      cx.translate(0, length);  // Draw
      cx.rotate(-angle); branch(length * scale, angle, scale, level + 1);
      cx.rotate(2 * angle); branch(length * scale, angle, scale, level + 1);
      cx.restore();
    }

    // draw graphics
    let w = canvas.width = base.clientWidth 
    canvas.height = w;
    canvas.clear(); cx.translate(w / 2, w); 
    cx.rotate(-3.14152)
    let t;
    branch(base.clientWidth / 7, 0.33, 0.82, 0 , 0.01+0.1*Math.sin(0.1*t++))
    unselectBase()     
   }

    function cleanElt (elt) {
       while(elt.firstChild) {
        elt.removeChild(elt.firstChild);
       }
    }

    function router(component) {
      selectBase(main)
        cleanElt(main);
        component();
      unselectBase();
    }

    function Navbar () {
      selectBase(div('', cssNavbar))
        button("Home",cssButton ).onclick = () => router(Home);
        button("Canvas", cssButton).onclick = () => router(Tree);
        button("Counter", cssButton).onclick = () => router(Counter);
      unselectBase();
    }

    Navbar();

    let main = div('', cssContainer);

    router(Tree); 

 </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Discussion (3)

Collapse
efpage profile image
Eckehard • Edited

Very nice example.

For a larger setup it could be helpful to create a Navbar class and put all the functions together there. This will make the Navbar a reusable component.

    // A navbar component class
    function Navbar() { return new _Navbar() }
    class _Navbar {
      constructor() {  // Build an empty div as container
        this.navbar = div('', cssNavbar)
      }
      setTarget(main) {  // attach a content-container
        this.main = main
      }
      addItem(title, route) { // add a new router button
        selectBase(this.navbar)
        button(title, cssButton).onclick = () => this.setRoute(route);
        unselectBase()
      }
      setRoute(component) {  // replace the content
        while (this.main.firstChild)
          this.main.removeChild(this.main.firstChild);
        selectBase(this.main)
        component()  // Create content dynamically
        unselectBase()
      }
    }

    // Create elements
    let nav = Navbar();
    nav.addItem("Home", Home)
    nav.addItem("Canvas", Tree)
    nav.addItem("Counter", Counter)

    nav.setTarget(div('', cssContainer));
    nav.setRoute(Tree)
Enter fullscreen mode Exit fullscreen mode

There is one little disadvantage in the setup: the "component()" content is created any time the user hit´s a button. This may cause delay for larger content (e.g. a larger Tree) and will reset the state of your panel any time your are switching. And - if the components are not deleted - it may cause a memory leak. After removing elements from the DOM you need to manually delete them (el = Null).

But it is far better to keep the "prerendered" components instead, and make them just not visible. You just need to remove the element from the DOM after creation:

function Navbar() { return new _Navbar() }
    class _Navbar {
      constructor() {
        this.navbar = div('', cssNavbar)
        this.components = {}
      }

      // Set target element
      setTarget(main) {
        this.main = main
      }

      // Add new item to the Navbar
      addItem(title, component) {

        // Create button
        selectBase(this.navbar)
        button(title, cssButton).onclick = () => this.setRoute(component);
        unselectBase()

        // Create content
        let myDiv = selectBase(div())       // create div as a container  
        component()                         // Create content in myDiv
        unselectBase()
        myDiv.parentNode.removeChild(myDiv) // Remove container from the DOM
        this.components[component] = myDiv  // Save container "with content" in **components**
      }

      // Change the content of target element
      setRoute(component) {
        if (this.main.firstChild)
          this.main.replaceChild(this.components[component], this.main.firstChild)
        else
          this.main.append(this.components[component])
      }

    }
Enter fullscreen mode Exit fullscreen mode

Now you just switch your working "components" instead of calling component() again. This will preserve the state of the individual components while switching and ist much faster.

Collapse
artydev profile image
artydev Author

Thank you very Eckehard
Using Class is indeed a nice way to create complex compone nt
I must use them a little more
The like very much the idea of prerendering components to preserve states
Thanks again
Regard

Collapse
efpage profile image
Eckehard

Classes are not that hard to use. Making code reusable is always a bit harder, but as you see, there is a natural transistion for procedural code to class based code. In fact, it´s a common practice to start with procedural code and create the classes later. You often will see some global definitions that prevent your code from being reused easily. That´s a good point to start rebuilding your app.