Unicore Singleton

Tapping into the power of the Unicore

When you create components using the cli without a template for the controller, you might notice the major difference between a route-component and a api-component starts at what the class extends:

Default route-component:


        <?php

        namespace Neoan3\Component\MyComponent;

        use Neoan3\Core\Unicore;

        class MyComponentController extends Unicore
    

Default API-component:


        <?php

        namespace Neoan3\Component\MyComponent;

        use Neoan3\Frame\MyFrame;

        class MyComponentController extends MyFrame
    

However, it's important to understand that is is only the case because the use of the Unicore singleton is implied. If you do not want to use the Unicore singleton, you can directly extend your frame (or Serve) instead.


The disadvantage of not using Unicore is that associated files (like component/MyComponent/myComponent.ctrl.js or component/MyComponent/myComponent.style.css) are not automatically loaded when present. However, this can sometimes be wanted. Additionally, the used frame cannot be dynamic but is hard-coded into the class. This binds the component to a certain frame which might be of importance for reusability considerations.
The Unicore singleton generates a wrapper around the business logic and "forces" the developer to write logic in what we call "stream-of-consciousness":


        <?php

        namespace Neoan3\Component\MyComponent;

        use Neoan3\Core\Unicore;

        class MyComponentController extends Unicore
        {
            function init()
            {
                // initiate a particular frame (this can be dynamic e.g. via SESSION, GET, SERVER, sub() etc)
                $this->uni('myFrame')

                // we now have a singleton including methods inherited from the frame as well as from the core
                // and files in the same folder ending in 'ctrl.js' and 'view.html' are included (NOTE: file extensions can be set differently)
                // let's load a template for the body's main part:
                     ->hook('main', 'myComponent')

                // and output the page
                     ->output();
            }
        }
    

By adding public functions into your frame, you can add functionality to the singleton or overwrite the following methods:

Important methods

There are multiple methods of interest that can be chained to the singleton.

hook

The hook helps to indicate what part of the frame to target and what view to use. Optionally you can even take in an array of substitutions declared in the view by double-curly braces.
Standard hooks are

  • header
  • main
  • footer

The underlying structure is:


        <!doctype html><html>
        <head>{{head}}</head>
        <body>
        <style>{{importedStyles}}{{style}}</style>

        <header><!-- header hook --></header>
        <neoan-root><!-- entry-point for SPA use --></neoan-root>

        <!-- main hook -->

        <footer><!-- footer hook --></footer>

        <!-- additional scripts & module-loading -->
        </body></html>
    

The reason you won't see anything other than "main" used in all examples is due to the fact that default header & footer are addressed on the frame level rather than on the component level. Example in init():


        // src/test/test.view.html will hook into the frame's main-hook and substitute "{{title}}" with the value of $title
        $this->uni->hook('main', 'test', ['title' => $title]);
    

Typically, the view passed as a parameter will be the view.html that is local to the ctrl.php.

addRenderParameter

This method offers the most direct option to provide variables to the template. It is therefore ideal to be used with model interactions.
Be aware of the scope implication of this method: these variables will be available to all hooks.


        function init()
        {
            $this->uni()
                 ->addRenderParameter('greeting', function(){
                    // the render-variable "greeting" is now available to all hooks
                    return "Hello world";
                })
                 ->hook('main')
                 ->output();
        }
    

callback

To include additional functionality, the callback-method is a useful way to indicate when functionality outside the stream-of-consciousness is executed. Alternatively, you can directly place a function into the callback method.


        public $greeting;

        function init()
        {
            $this->uni()
                 ->callback($this, 'getGreeting')
                 ->hook('main', ['greeting' => $this->greeting])
                 ->output();
        }

        function getGreeting($context)
        {
            $hour = date('H');
            switch($hour){
                case $hour < 12: $context->greeting = 'good morning'; break;
                case $hour > 11 && $hour < 18: $context->greeting = 'good afternoon'; break;
                case $hour > 17: $context->greeting = 'good evening'; break;
            }
        }
    

includeElement, includeJs, includeJsModule

neoan3 has the unique ability to serve js-modules with dynamic content. Please read custom component for more information on how to use this capability in order to work with web components, custom elements or front-end frameworks like vueJS, react or polymer. In general, includeElement is used for neoan3 custom components, includeJs for any javascript file you want to include into the DOM and includeJsModule for components located in your node_modules folder.


        ...
        $this
            ->uni()
            ->callback(function($uni){
                // load component/MyElement/myElement.ce.js and (if exists) component/MyElement/myElement.ce.html in a template tag with the id "myElement".
                 $uni->renderer->includeElement('myElement',['userId' => $userId])

                // load external js
                 $uni->renderer->includeJs('//some-awesome-jslibrary.com/file.min.js')

                // load local js
                // note how the first parameter is the location (required),
                // the second one an array with substitutions (as always, with curly brackets) (optional),
                // and the last one a type for th script-tag (optional, defaults to "text/javascript"),
                 $uni->renderer->includeJs(base. 'component/sibling/sibling.ctrl.js', ['context' => 'test'], 'text/javascript')

                // include node-modules (note: the path-parameter is already based on the node_module directory)
                 $uni->renderer->includeJsModule('@coolPackage/index.js')
            })

        ...
    

output

Output simply compiles the files & responds (echoes) to the client. The parameter isn't necessary but you can add an array of pre-compiling function(s). Typically, this is also at the end of a singleton.


        ...
        ->output();