-
Notifications
You must be signed in to change notification settings - Fork 48
Components
A Component is an encapsulation of business logic that is comprised of CSS, HTML, and JavaScript. The end result of a component action is the rendering of markup.
The components directory contains a sub directory for each component. Underneath each component directory there is a
views directory, and optional client and server directories.
|- components
|- component_name
|- views
index.hbs
index.js (optional)
|- client (optional)
|- server (optional)
controller.js (optional)The minimum requirement for a component is an index template file. controller.js and index.js are optional, and
should only used if necessary.
controller.js contains the component’s business logic. This is where actions are defined and values such as models
and collections are added to the component instance context.
Out of the box Lazo supports Handlebars.js and Underscore.js
micro templates. By default Lazo assumes Handlebars. To use a micro template the
view object property templateEngine is set to "micro". Lazo also has an interface for supporting other template
engines that return a string, which can be defined by your application.
Templates are passed a context object that contains a reference to the component context, assets, and any view property that is not a function.
Lazo views are encapsulations of rendering logic and UI behavior. They are extended Backbone views
that run on the client and server with defined life cycles. The hook point for executing DOM is afterRender. This
method executes on first page load after the views have been bound to the DOM and every time a view is rendered.
View instances have a preoprty that contains reference to the controller that instantiated them, ctl.
Lazo automatically loads component CSS after application CSS has been loaded. CSS should be placed in the client
directory or a sub directory of client.
CSS can be scoped to a component by using the component name attribute selector as follows:
[lazo-cmp-name="component_name"] .some-class {
color: #333;
}Other JavaScript can be loaded for component by adding it to a view or controller define statement as a dependency.
The baseUrl is the application root, so paths can be relative to
it or they can be relative to the module which is listing them as a dependency.
// include JavaScript in a view
define(['lazoView', 'l!./client/superWidget'], function (LazoView, SuperWidget) {
'use strict';
return LazoView.extend({
afterRender: function () {
this.widget = new SuperWidget(this.el);
}
});
});
// include JavaScript in a controller
define(['lazoCtl', 'components/cmp_name/util'], function (LazoCtl, util) {
'use strict';
return LazoCtl.extend({
index: function (options) {
this.ctx.params = util.doSomething(this.ctx.params);
options.success('index');
}
});
});The l! is a custom loader that noop's client code of the server and server code on the client. It does this by
examining the path for directory names server, client, and node_modules.
A component action is a method on the controller that can be mapped to a route or executed by calling the controller
navigate method. The default action method is index.
A component action method is passed a set of options, which contain success and error callback functions.
The success callback accepts a single argument, which is the name of the template to be returned. If a view class
with the same name is found it will be bound to the markup that template generates in the DOM.
The error callback accepts a single argument, an error object or a string. The best practice is to pass an error
object. This will trigger a 500 error response and call the 500 error template.
Actions can be mapped to routes. In which case they are automatically executed for you when the route
is matched. Actions can also be executed by calling the controller navigate method.
The component context is an object that persists data for the life of the component instance. It is used as the mechanism to maintain state between the client and server for a given page response or controller navigation.
When the first page load is rendered on the server the context tree for the page is serialized for rehydration on the client. The context tree is comprised of the context objects for each component in the page request. However, only specific properties are serialized for each component context. Otherwise the entire object graph would have to be serialized. The following sections describe the properties that are serialized.
When models and collections are loaded they can optionally be added to the context object to ensure that they are
persisted across environments for a page life cycle. The reserved names in the context object are models and
collections.
define(['lazoCtl'], function (LazoCtl) {
'use strict';
return LazoCtl.extend({
index: function (options) {
var self = this;
this.loadModel('model_name', {
success: function (model) {
// this will be presisted between environments
self.ctx.models.any_name_you_like = model;
options.success('index');
}
});
}
});
});Parameters are persisted.
If you would like to persist data that is accessible by any component this can be done by using the context shared data API.
// inside of a controller method
this.ctx.setSharedData('some_key', 'a_value');
// somewhere else in any controller
this.ctx.getSharedData('some_key'); // returns 'a_value'A controller has the ability to navigate to a different view/template by calling an action. This will render the new output for the component inside of the component container and instantiate the associated view if one exists. This does not change the URL in the browser.
// in a view instance in response to a user action
// callbacks are optional
this.ctl.navigate('action_name', {
success: function () {
console.log('SUCCESS!!!')
}
});A component can have child components. Child components are added using the addChild method. Children are added to
component container. A component container can contain more than one component. Components in a container are rendered
in the order they are added.
define(['lazoCtl'], function (LazoCtl) {
'use strict';
return LazoCtl.extend({
index: function (options) {
var self = this;
this.addChild('container_name', 'component_name', {
// query and path parameter are only passed to the root controller
// or the controller for a layout body
ctx: {
params: this.ctx.params
},
success: function (ctl) {
// you can add as many children as you like
// and use any library you want to make managing
// async code easier; in this case we are just
// adding a single child so we can execue the callback
// for the controller action once the child has been added
options.success('index');
}
});
}
});
});<!-- parent controller template markup -->
<div lazo-cmp-container="container_name"></div>A controller instance has Backbone.Events mixed in. The methods are noop’d on the
server. It is best practice that child components do not have knowledge of their parent. However, a component
instance does have knowledge of its children via the children property in its controller.
// inside of a parent controller method
// get a reference to a child controller
var child = this.children.container_name[0];
// listen for a child event inside of the parent
child.on('some_event', function (message) {
// take some action in the parent controller
});
// inside of a child controller method
var message = { foo: 1 };
this.trigger('some_event', message);Overview
Life Cycles
Getting Started
Development Tools
Application Structure
application.js
Configuration
Configuration Providers
Logging
Module Loader
Assets
Combo Handling
Components
Models and Collections
Views
Templates
Layouts
Imports
Links
Request Filters
Crumb
Server Setup
Error Templates
Page Template
Server Utilities
Node Modules
Contributing