simplycode-dummy-component

Loading a SimplyCode component into a SimplyCode app

SimplyEdit is a Javascript library that makes it trivial to create webpages that can be edited by non-technical users.

In a similar fashion, SimplyCode is an environment to allow users to create web applications.

An application in SimplyCode consists of components. Currently, (2024) these components can only be built in SimplyCode directly.

This project demonstrates how components can be loaded from a different source.

It consists of two parts:

  1. A dummy app
  2. A dummy component

The component is loaded into the app and displayed.

The generated app can be visited at: https://blog.pother.ca/simplycode-dummy-app/generated.html

To make this work, changes were made in both the component and the app.

Component

The component was created in SimplyCode and then exported as a JS module.

The component can be found at: https://blog.pother.ca/simplycode-dummy-component/index.js

The component was created as follows:

  1. A component was created with a command and an action
    • Action:
      function (subject) {
        return new Promise(function (resolve, reject) {
          resolve(`Hello ${subject}`)
        })
      }
      
    • Command:
      function (element) {
        simplyApp.actions.sayHello('World').then(message => alert)
      }
      
    • Template:
      <button data-simply-command="sayHello">Say Hello</button>
      
  2. A page was created with a route and HTML page
    • The hello HTML contains a <simply-render rel="hello"></simply-render>
    • The route for / was set to “hello”
  3. The page-frame was changed to output a JS Module
    export default {
            'componentCss': `
                   
            `,
            'pageCss': `
                   
            `,
            'headHtml': `
                   
            `,
            'bodyHtml': `
                   
            `,
            'footHtml': `
                   
            `,
            'componentTemplates': `
                   
            `,
            'pageTemplates': `
                   
            `,
            'simplyRawApi': {
                   
            },
            'simplyDataApi': {
                   
            },
            'simplyApp': {
                'actions': {
                       
                },
                'commands': {
                       
                },
                'routes': {
                       
                }
            },
            'transformers': {
                   
            },
            'sorters': {
                   
            },
            'dataSources': undefined
                   
        }
    

This was all Simplycode compliant, thus a generated.html file was created.

As the component is to be consumed as ESM, it was copied to index.js.

The final result
export default {
  'componentCss': `
  `,
  'pageCss': `
  `,
  'headHtml': `
  `,
  'bodyHtml': `
  `,
  'footHtml': `
  `,
  'componentTemplates': `
    <template id="hello">
      <button data-simply-command="sayHello">Say Hello</button>
    </template>
  `,
  'pageTemplates': `
    <template data-simply-template="hello">
      <simply-render rel="hello"></simply-render>
    </template>
  `,
  'simplyRawApi': {},
  'simplyDataApi': {},
  'simplyApp': {
    'actions': {
      'sayHello': function (subject) {
        return new Promise(function (resolve, reject) {
          resolve(`Hello ${subject}`)
        })
      },
    },
    'commands': {
      'sayHello': function (element) {
        simplyApp.actions.sayHello('World').then(message => alert)
      },
    },
    'routes': {
      '/': function (params) {
        editor.pageData.page = 'hello'
      },
    },
  },
  'transformers': {},
  'sorters': {},
  'dataSources': undefined,
}

At this point, the repository looks like this:

.
├── components
│   └── hello
│       ├── actions
│       │   └── sayHello.js
│       ├── commands
│       │   └── sayHello.js
│       ├── componentTemplates
│       │   └── hello.html
│       └── meta.json
├── page-frame
│   ├── componentPreview.html
│   ├── fullApp.html          <-- This was changed to output a JS module
│   └── pagePreview.html
├── pages
│   └── hello
│       ├── meta.json
│       ├── pageTemplates
│       │   └── hello.html
│       └── routes
│           ├── home.js
│           └── home.json
├── generated.html
├── index.js                    <-- This is the generated JS module
└── README.md

App

The app was created in SimplyCode.

The app can be found at: https://blog.pother.ca/simplycode-dummy-app/generated.html

The app was created as follows:

  1. A page was created with a route and HTML page
    • The hello HTML contains a <h1>This is an App</h1>
    • The route for / was set to “home”
  2. The page-frame was changed to allow us to prevent SimplyEdit from loading.

    This was done by (ab)using the data-simply-storage attribute. An event listener was added for a custom simply-import-fired event. This event is to be fired once the component is loaded.

    <script>
        window.waitForImport = {
            connect: () => true,
            disconnect: () => true,
            init: () => true,
            load: callback => window.addEventListener('simply-import-fired', () => callback('{}')),
            save: () => true,
        }
    </script>
    <script src="js/simply-edit.js" data-api-key="muze" data-simply-storage="waitForImport"></script>
    
  3. Import logic was added to the foot HTML of the application.

    It imports the component and adds the component’s templates, actions, and commands to the app. It then fires the simply-import-fired event so SimplyEdit can continue loading.

    <script type="module">
      import dc from 'https://blog.pother.ca/simplycode-dummy-component/index.js'
    
      document.body.insertAdjacentHTML('beforeend', dc.componentTemplates)
      document.body.querySelector("[data-simply-field=page]").insertAdjacentHTML('beforeend', dc.pageTemplates);
    
      window.addEventListener("simply-content-loaded", () => {
        Object.entries(dc.simplyApp.actions).forEach(([index,action]) => {
          simplyApp.actions[index] = action
        })
    
        Object.entries(dc.simplyApp.commands).forEach(([index,command]) => {
          simplyApp.commands[index] = command
        })
      })
    
      // @CHECKME: Should we fire at the window or at the document? Why?
      editor.fireEvent('simply-import-fired', window)
    </script>
    

After this, the app route was changed from home to hello to load the component.

The final result
<!DOCTYPE HTML>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script>
      var simplyDataApi = {};
      var simplyApp = {};
      window.addEventListener("simply-content-loaded", function() {
        simply.bind = false;
        var simplyRawApi = {};
        simplyDataApi = {};
        simplyApp = simply.app({
          actions: {},
          commands: {},
          routes: {
            "/" : function (params) {
              editor.pageData.page = 'hello'
            }
          }
        });
      });
    </script>
  </head>
  <body>
    <div class="main" data-simply-field="page" data-simply-content="template">
    <template data-simply-template="home">
      <h1>This is an App</h1>
    </template>
    </div>
    <script src="js/simply.everything.js"></script>
    <script>
      window.waitForImport = {
        init : () => true,
        save : () => true,
        load : function(callback) {
          window.addEventListener("simply-import-fired", () => {
            callback("{}");
          })
        },
        connect: () => true,
        disconnect: () => true
      }
    </script>
    <script src="js/simply-edit.js" data-api-key="muze" data-simply-storage="waitForImport"></script>
    <script type="module">
      import dc from 'https://blog.pother.ca/simplycode-dummy-component/index.js'
    
      document.body.insertAdjacentHTML('beforeend', dc.componentTemplates)
      document.body.querySelector("[data-simply-field=page]").insertAdjacentHTML('beforeend', dc.pageTemplates);
    
      window.addEventListener("simply-content-loaded", () => {
          Object.entries(dc.simplyApp.actions).forEach(([index,action]) => {
              simplyApp.actions[index] = action
          })

          Object.entries(dc.simplyApp.commands).forEach(([index,command]) => {
              simplyApp.commands[index] = command
          })
      })
    
      editor.fireEvent('simply-import-fired', window)
    </script>
  </body>
</html>

At this point, the repository looks like this:

.
├── base-components
│   └── import
│       ├── footHtml
│       │   └── importFoot.html   <-- This was changed to import the component
│       └── meta.json
├── generated.html
├── page-frame
│   ├── componentPreview.html     <-- This was changed to prevent SimplyEdit from loading
│   ├── fullApp.html
│   └── pagePreview.html
├── pages
│   └── home
│       ├── meta.json
│       ├── pageTemplates
│       │   └── home.html
│       └── routes
│           ├── home.js
│           └── home.json
└── README.md