Skip to content

Latest commit

 

History

History
166 lines (129 loc) · 5.86 KB

File metadata and controls

166 lines (129 loc) · 5.86 KB

Describing a View

A Neo.mjs view is comprised of components and containers. A component is a visual widget, like a button, and a container is a visual collection of components.

Neo.mjs is declarative, where your code describes or configures the things it's creating.

There are two primary ways to describe a view: using modern functional components or classic class-based components. Crucially, these two approaches are fully interoperable, allowing you to mix and match them to fit your needs.

The Modern Approach: Functional Components

For most new views, especially those that are primarily presentational (like a button or a hero section), the recommended approach is to use functional components. This method is concise, highly performant, and aligns with modern reactive programming patterns.

Functional components are defined using the defineComponent helper. You provide a configuration object that includes a createVdom method. This method is a reactive function that returns the Virtual DOM (VDOM) for your component.

A simple functional view

Here is a simple view that displays a single button. The createVdom function returns a VDOM object that describes the button.

import {defineComponent} from '../functional/_export.mjs';

const MainView = defineComponent({
    config: {
        className: 'GS.describing.functional.MainView'
    },

    createVdom(config) {
        return {
            cn: [{
                ntype: 'container',
                layout: {ntype: 'vbox', align: 'start'},
                items: [{
                    ntype: 'button',
                    iconCls: 'fa fa-home',
                    text: 'Home'
                }]
            }]
        }
    }
});

export default MainView;

The Classic Approach: Class-Based Components

For more complex, high-order components that require surgical precision and powerful state management (like a buffered grid, a calendar, or an image gallery), the classic class-based approach is more suitable.

This approach involves extending a framework class (like Neo.container.Base) and defining your view within the static config block. It gives you access to a rich set of lifecycle methods (beforeSet..., afterSet..., etc.) for fine-grained control.

A simple class-based view

Here is the same view, but built with the class-based approach.

import Button    from '../button/Base.mjs';
import Container from '../container/Base.mjs';

class MainView extends Container {
    static config = {
        className: 'GS.describing.class.MainView',
        layout   : {ntype:'vbox', align:'start'},
        items    : [{
            module : Button,
            iconCls: 'fa fa-home',
            text   : 'Home'
        }]
    }
}

MainView = Neo.setupClass(MainView);

Interoperability: The Best of Both Worlds

The power of Neo.mjs lies in its interoperability layer. You can seamlessly mix both component models.

Using a Class-Based Component in a Functional View

You can easily instantiate a classic component within the createVdom method of a functional component. This is useful when you need to embed a complex, stateful widget inside a simpler, functional layout.

import {defineComponent} from '../functional/_export.mjs';
import DateSelector      from '../component/DateSelector.mjs';

const MainView = defineComponent({
    config: {
        className: 'GS.describing.interop.MainView1'
    },

    createVdom(config) {
        return {
            cn: [{
                ntype: 'container',
                layout: {ntype: 'vbox', align: 'start'},
                items: [{
                    text: 'My Functional View',
                    tag : 'h1'
                }, {
                    // Drop the class-based DateSelector into our functional view
                    module: DateSelector,
                    height: 300,
                    width: 300
                }]
            }]
        }
    }
});

export default MainView;

Using a Functional Component in a Class-Based View

Conversely, you can drop a functional component into the items array of a classic container. This allows you to compose your complex views from smaller, more manageable functional pieces.

import {defineComponent} from '../functional/_export.mjs';
import Container from '../container/Base.mjs';

// 1. Define a simple functional component
const MyFunctionalButton = defineComponent({
    config: {
        className: 'GS.describing.interop.FuncButton'
    },
    createVdom(config) {
        return {
            cn: [{
                ntype: 'button',
                iconCls: config.iconCls,
                text: config.text
            }]
        }
    }
});

// 2. Create a class-based MainView
class MainView extends Container {
    static config = {
        className: 'GS.describing.interop.MainView2',
        layout   : {ntype:'vbox', align:'start'},
        items    : [{
            // 3. Use the functional component in the items array
            module: MyFunctionalButton,
            iconCls: 'fa fa-rocket',
            text   : 'Launch'
        }]
    }
}

MainView = Neo.setupClass(MainView);

Choosing Your Approach

  • Use Functional Components (defineComponent) for:

    • Simple, reusable components (e.g., buttons, chips, hero sections).
    • Most of your application's views that are primarily presentational.
    • When you want a concise, declarative, and highly performant component.
  • Use Class-Based Components (class ... extends ...) for:

    • Complex, high-order components requiring surgical precision (e.g., buffered grids, calendars, charts, galleries).
    • Creating new reusable components that extend the functionality of existing framework classes.
    • Components with intricate internal logic that benefits from the full range of class lifecycle methods.