{{page-title "How it works"}}

<MdcTopAppBar @fixed="true">
  <MdcTopAppBarRow>
    <MdcTopAppBarSection>
      <MdcTopAppBarNavigateUpTo @route="main.index" />
      <MdcTopAppBarTitle>How it works</MdcTopAppBarTitle>
    </MdcTopAppBarSection>
  </MdcTopAppBarRow>
</MdcTopAppBar>

<div {{mdc-top-app-bar-fixed-adjustment}}>
  <div class="main-content">
    <h6>No more bootstrapping code</h6>

    <p>When you create your frontend using HTML and native Javascript (or a frontend library),
      you have to implement a lot of bootstrapping code as the project scales. As with the native
      Javascript implementation of the HelloWorld example, there are many bootstrapping and functional
      aspects intertwined with one another. This leads to a design that violates the SOLID principles
      of software design and has many code smells.
    </p>

    <CodeSnippet @lang="javascript"
                 @enableCopyButton={{false}}
                 @title="Native Javascript implementation of Hello, World">
      import { demo_backend } from "../../declarations/demo_backend";

      document.querySelector("form").addEventListener("submit", async (e) => {
        e.preventDefault();
        const button = e.target.querySelector("button");

        const name = document.getElementById("name").value.toString();

        button.setAttribute("disabled", true);

        // Interact with demo_backend actor, calling the greet method
        const greeting = await demo_backend.greet(name);

        button.removeAttribute("disabled");
        document.getElementById("greeting").innerText = greeting;

        return false;
      });
    </CodeSnippet>

    <p>When using EmberJS and <a href="https://github.com/onehilltech/ember-cli-dfinity" target="_blank" rel="noopener noreferrer">ember-cli-dfinity</a>,
      much of the bootstrapping and functional code above goes away. Instead, you only need to focus on the main logic of
      what to do when the submit button is clicked. The following code snippet showcases all that you need to write.</p>

    <CodeSnippet @lang="javascript"
                 @enableCopyButton={{false}}
                 @title="EmberJS implementation of Hello, World">
      export default class MainIndexController extends Controller {
        // ...

        @action
        async submit () {
          this.greeting = await this.hello.greet(this.name);
        }

        // ...
      }
    </CodeSnippet>

    <p>Because many of the concerns you manually implement when using native Javascript
      (or a Javascript library) are handled for you out-of-the-box in a clean manner, you
      can focus on making your design more robust. Here is the same example above that includes
      basic exception handling where we display the error as a snackbar message.</p>

    <CodeSnippet @lang="javascript"
                 @enableCopyButton={{false}}
                 @title="EmberJS implementation with some exception handling">
      export default class MainIndexController extends Controller {
        // ...

        @action
        async submit () {
          try {
            this.greeting = await this.hello.greet(this.name);
          }
          catch (err) {
            this.snackbar.showError (err);
          }
        }

        // ...
      }
    </CodeSnippet>

    <h6>Easily bind to actors / canisters</h6>

    <p>The {{ember-cli-dfinity}} addon uses decorators to bind properties to (backend) actors in your frontend
      application. As shown in the code snippet below, we are binding the Hello actor to the
      <InlineCodeSnippet>hello</InlineCodeSnippet> property. This property exposes the interface of the
      <a href="https://github.com/onehilltech/ember-cli-dfinity/blob/main/packages/demo/src/demo_backend/main.mo" target="_blank" rel="noopener noreferrer">hello actor</a>.</p>

    <CodeSnippet @lang="javascript"
                 @enableCopyButton={{false}}
                 @title="EmberJS implementation with some exception handling">
      export default class MainIndexController extends Controller {
        // ...

        // Bind to the hello actor deployed to the demo_backend canister using the default agent.
        @actor ({ canister: 'demo_backend' })
        hello;

        @action
        async submit () {
          try {
            this.greeting = await this.hello.greet(this.name);
          }
          catch (err) {
            this.snackbar.showError (err);
          }
        }

        // ...
      }
    </CodeSnippet>

    <h6>Simplified state management</h6>

    <p>In the native JavaScript implementation, you will see a lot of code for state management.
    There is code of enabling/disabling the submit button. There is code for adding the event listener
    for the button click. There is code for setting the greeting in the HTML element.</p>

    <p>With EmberJS, state management is provided out-of-the-box via
      <a href="https://glimmerjs.com" target="_blank" rel="noopener noreferrer">GlimmerJS</a>. This allows you to focus more on the
      business-logic of your frontend application. Below is the HTML
      (or <a href="https://handlebarsjs.com/" target="_blank" rel="noopener noreferrer">Handlebars</a>) and JavaScript controller code for the
      EmberJS version of the Hello, World example.</p>

    <CodeSnippet @lang="handlebars" @title="app/controllers/demo/index.hbs">
        &lt;div class="demo-hello"&gt;
          &lt;MdcForm @validity=\{{this.validity}} @submit=\{{this.submit}}&gt;
            &lt;MdcTextfield @style="outlined" @label="Enter a name here" required="true" @value=\{{this.name}} /&gt;

            &lt;MdcButton type="submit" @label="Submit" @style="unelevated" disabled=\{{this.isSubmitButtonDisabled}} /&gt;
          &lt;/MdcForm&gt;

          &lt;div class="demo-hello__greeting"&gt;&lt;b&gt;Greeting:&lt;/b&gt; \{{this.greeting}}&lt;/div&gt;
        &lt;/div&gt;
    </CodeSnippet>

    <CodeSnippet @lang="javascript" @title="app/templates/demo/index.js">
      import Controller from '@ember/controller';
      import { action } from '@ember/object';
      import { tracked } from '@glimmer/tracking';

      import { actor } from 'ember-cli-dfinity';

      export default class MainIndexController extends Controller {
        /// This property tracks the name entered in the input element.
        @tracked
        name;

        /// This property tracks the valid state of the form.
        @tracked
        valid;

        /// This property tracks when the frontend submits a request to the backend.
        @tracked
        submitting;

        /// This property tracks the greeting returned from the backend, and displayed to the user.
        @tracked
        greeting;

        // Bind to the hello actor deployed to the hello_backend canister using the default agent.
        @actor ({ canister: 'demo_backend' })
        hello;

        /**
         * This callback received the validity state of the form.
         */
        @action
        validity (valid) {
          this.valid = valid;
        }

        /**
         * Callback that handles clicking the submit button on the form.
         */
        @action
        async submit () {
          try {
            this.submitting = true;
            this.greeting = await this.hello.greet(this.name);
          }
          catch (err) {
            this.snackbar.showError (err);
          }
          finally {
            this.submitting = false;
          }
        }

        /**
         * This accessor determines when the submit button is disabled.
         */
        get isSubmitButtonDisabled () {
          return !this.valid || this.submitting;
        }
      }
    </CodeSnippet>

    <h6>Reusable components</h6>

    <p>EmberJS has a robust component architecture that allows to you create reusable components. This components
      can then be used in other parts of your application. You can even export components from an addon. This makes
      it easy to reuse components on different projects. For this demo, can easily place the frontend code (illustrated above)
      that communicates with the backend actor in a component.</p>

    <h6>Auto- (and better) configuration management</h6>

    <p>Native Javascript implementations of frontend apps on the Internet Computer do not come with
      configuration management out-of-the-box. This can make it <em>hard</em> to design your frontend for
      different execution environments, like production vs. development, or have different agent and canister
      configurations.
    </p>

    <p>With EmberJS and {{ember-cli-dfinity}}, you can easily manage different configurations. For example, you
    can have different configurations for development vs. production. Likewise, you can have different configurations
    for different networking agents. Lastly, you can override default configurations at both build and runtime. This
      offers you great flexibility when designing and testing your frontend application.</p>

    <p>The following code snippet showcases the configuration for the Hello, World demo app. In this example,
    we are not defining any additional configurations because {{ember-cli-dfinity}} is designed to work with minimal
    updates to the configuration. If needed, we can easily update this configuration with domain-specific
    configurations.</p>

    <CodeSnippet @lang="javascript" @title="config/environment.js" @enableCopyButton={{false}}>
      'use strict';

      module.exports = function (environment) {
        let ENV = {
          // ...

          'ember-cli-dfinity': {
            // here you can add additional ember-cli-dfinity configurations to override the configuration
            // generated at build-time.
          }
        };

        if (environment === 'development') {
          // here you can enable a development-specific features
        }

        if (environment === 'test') {
          // here you can enable a test-specific features
        }

        if (environment === 'production') {
          // here you can enable a production-specific features
        }

        return ENV;
      };
    </CodeSnippet>

    <h6>Added bonus: supports all existing EmberJS add-ons</h6>

    <p>When you use EmberJS for your frontend, you are able to use many of the existing EmberJS addons. This can
      help you save time and provide a much richer user experience. For example, this frontend was implemented using
      the following EmberJS add-ons:</p>

    <ul>
      <li>{{ember-cli-dfinity}}. This add-on enables EmberJS for the Internet Computer.</li>
      <li><a href="https://github.com/onehilltech/ember-cli-mdc" target="_blank" rel="noopener noreferrer">ember-cli-mdc</a>. This add-on provides support for Material Design components.</li>
      <li><a href="https://github.com/onehilltech/ember-cli-styled" target="_blank" rel="noopener noreferrer">ember-cli-styled</a>. This add-on adds polymorphic-like behavior to your styles.</li>
      <li><a href="https://github.com/onehilltech/ember-cli-code-snippet" target="_blank" rel="noopener noreferrer">ember-cli-code-snippet</a>. This add-on provides components for displaying code snippets.</li>
    </ul>

    <p>At <a href="https://onehilltech.com" target="_blank" rel="noopener noreferrer">One Hill Technologies (One Hill Tech)</a>, we
      have created a larger number of <a href="https://github.com/onehilltech?q=ember&type=all&language=&sort=" target="_blank" rel="noopener noreferrer">EmberJS addons</a>
      that are available under the Apache 2.0 license. We welcome you to use them in your frontend.
      You can also find a comprehensive list of EmberJS add-ons at <a href="https://emberobserver.com/" target="_blank" rel="noopener noreferrer">Ember Observer</a>.</p>
  </div>
</div>