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.
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.
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;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.
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);The power of Neo.mjs lies in its interoperability layer. You can seamlessly mix both component models.
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;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);-
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.