Tutorials
Getting started
Chart interface
Web components
Chart internals
Data integration
Customization
Frameworks and bundlers
Mobile development
Plug-ins
Troubleshooting
Glossary
Reference
JSFiddles

Web Component Interface

The ChartIQ sample templates are constructed with W3C-standard web components. The Web Components specification enables you to create your own custom tags with built-in programming logic. The great power of web components is that they are composable; that is, you can assemble web components from other web components, which provides an opportunity for re-use and modularity.

User interfaces built with web components can be relatively transparent when compared with libraries such as React and frameworks such as Angular and Vue. This is primarily because the browser automatically handles initialization of web components, whereas any other framework requires significant scaffolding to allow a set of tags to be tied with business logic. That scaffolding hides operational details, often behind large chunks of "magic" (digest loops, zones, etc.) or preprocessing (jsx). Web component interfaces end up being relatively transparent, requiring less proprietary knowledge, offering fewer pitfalls (for example, getting lost in the digest loop), and doing away with the need for debugging plug-ins.

Unfortunately, the Web Components specification hasn't adequately addressed the crucial need for a standardized approach to data binding. Data binding has been an integral part of web development since Backbone.js and Knockout.js hit the scene. It is the core value provided by a framework. When the Web Components specification was first announced, a native capability called object-observe was touted as the solution for data binding. Unfortunately, it wasn't a fully considered approach and was later eliminated. Currently there is no native interface on the horizon; and so, all frameworks have been forced to come up with their own solution for data binding. See the Integrating the Library with Frameworks tutorial for more details.

While it is not our intention to provide a comprehensive data binding library, some basic capabilities are necessary to take advantage of web components. The library file componentUI.js implements these capabilities and provides a very simple infrastructure for building out interfaces with web components. Of course much of the code in this file is chart-centric, and specific to using chartiq.js, but you should be able to use this technology to build out your application if you so choose.

See the complete list of ChartIQ web components.

Web components 101

1) Web components are custom tags

One of the nice things about web components is that they can be self-contained. A web component starts as a "custom tag". You can name your tag anything you wish but it must contain a hyphen.

Note: All HTML tags, including custom tags, are case iNsEnSiTiVe_. By convention, all ChartIQ tags begin with "cq-".

Example web component :

<cq-dialog></cq-dialog>

Web components behave like other tags. They can be styled with CSS and they can be referenced with the document.querySelector method.

Note: One difference is that, unlike a div, custom tags default to display: inline rather than display:block.

Example: Manipulating web components :

<style>
	cq-dialog {
		background-color: black;
	}
</style>
<script>
	document.querySelector("cq-dialog").innerHTML="Cool!";
</script>

2) Web components are JavaScript objects

A custom tag gets turned into a live web component when you register it:

var Dialog = {
	prototype: Object.create(HTMLElement)
};
document.registerElement("cq-dialog", Dialog);

Web components must implement the HTMLElement interface, so they are typically created using "inheritance". Here we're using Object.create to implement inheritance because it is concise. Since web components are JavaScript objects it is very easy to make them do things! Inside a component, "this" refers to the component itself. So you can combine HTML operations and business logic in one place.

Example: A component comes alive :

Dialog.coolLikeJamesDean = function() {
	this.innerHTML = "I've got the bullets!";
};

One of the tough problems for most frameworks is making components work together. Usually this results in tight coupling which makes most single page applications (SPA) increasingly difficult to manage as they grow in scale. Web components live in the DOM, which means that they are automatically organized and that selectors can be used to link them.

Example: Interact with a web component using a selector :

document.querySelector("cq-dialog").coolLikeJamesDean();

3) Web components "construct" and "destruct", kind of...

Every web component automatically provides three callbacks:

  • createdCallback - Called only once, when the component is created
  • attachedCallback - Called when the component is attached to the DOM. This can get called multiple times if you detach and reattach.
  • detachedCallback - Called when the component is detached from the DOM. This can get called multiple times if you reattach.

attachedCallback is where we end up doing most of our initialization work. This is because a web component that is attached knows where it is in the DOM. For instance, once attached, a web component can find its parents. Because attachedCallback can be called multiple times, one needs to be careful not to create code that isn't "re-entrant" safe (code that breaks if it is called multiple times). In the library, we typically use a flag to protect the code.

Example: Typical pattern for attachedCallback() :

Dialog.attachedCallback = function() {
	if (this.attached) return;
	// do something
	this.attached = true;
};

Generally, createdCallback and attachedCallback are interchangeable, with nuances. One pitfall is to watch out for cloneNode(). When you use cloneNode() you create an entirely new web component but without any of your initializations.

Anyone who has used a framework knows that destruction is a key consideration. With web components, detachedCallback() operates as a sort of destructor, and the logical place to perform cleanup operations. We do get off somewhat easy. Since web components are HTML tags, any attached events are automatically destroyed, and closure references released, when the tag is garbage collected. It is rarely necessary to do much cleanup as a result.

4) Web components can use templates

The template tag provides a clean, easy way to build dynamic content. Content inside a template tag is ignored by the browser, but is still visible when you inspect the HTML which is very nice!.

Example: Let's create a list :

<cq-dialog>
	<template item>
		<li></li>
	</template>
	<ul></ul>
</cq-dialog>

<script>
	Dialog.createListItem = function(str) {
		var ul = this.node.find("ul");
		var template = this.node.find("template[item]");
		var newItem = CIQ.UI.makeFromTemplate(template);
		newItem.innerText=str;
		ul.append(newItem);
	};
</script>

In the example above we use a couple of patterns. First we create a template tag and fill it with the raw html that we want to use to build list items. We've given it an attribute name to make it easy to find with a selector. Note: if you only have a single template in your web component you don't need an attribute to references.

Inside of our component logic, once we've found our template within the DOM, we use the convenience method makeFromTemplate() to convert the template into live HTML. Finally we add the new HTML into the DOM where we want it.

This is an "imperative" approach to dynamic content. It is very simple to follow. The component infrastructure does not provide a "declarative" (data binding) approach. This is beyond the scope of the library and of course many templating libraries already exist if you'd like to use them.

5) Web components are composable

You can compose web components together with other web components. This can be done at two levels. You can do this within a single HTML page.

Example: Compose one component with another:

<cq-dialog>
	<cq-close></cq-close>
</cq-dialog>

In this example, the innards of the web component are visible. Developers can customize the component quite simply by changing the HTML. So long as the component is written "defensively" (without making too many assumptions about its content), this provides a great way to provide ready made components that are still customizable.

Web components can also be designed to be "opaque", the contents are visible but not easily changed. This is done by putting components in a separate file and then importing them. The cq-instant-chart component uses this technique.

Example: Importing a component:

<link rel="import" href="instant-chart.html" />
<html>
	<cq-instant-chart></cq-instant-chart>
</html>

The contents will become visible in the browser inspector after the component is imported, and can be manipulated programmatically, but your initial code is kept very clean. All the details are hidden.

Notes:

  • cq-instant-chart component is not supported on versions 7.x of the library.

Working With The ChartIQ Web Component Framework

Getting Started

Loading the Framework

To use the ChartIQ Web Component Framework you may include the following third party libraries:

  • perfect-scrollbar.esm.js (used to style scrolling components such as menus).

Then include componentUI.js, which is part of the ChartIQ library. sample-template-advanced.html and sample-template-basic.html contain example code. HTML tags that begin with "cq-" are web components that become enabled when you include componentUI.js.

Setting Up the Framework

Before you can use any of the chart library web components, you must set up the cq-context element and cq-ui-manager component.

The cq-context element is a special element used by the chart library web components. It wraps all library web components and holds a reference to a specified ChartEngine instance. This enables the web components to have direct control over the chart. The cq-context can be defined in one of two ways: a custom attribute or a custom element.

Custom Attribute:

<div cq-context>
	<!-- Chart & Chart UI Goes Here -->
</div>

Use this method when placing the chart in an existing html layout.

Custom Element:

<cq-context>
	<!-- Chart & Chart UI Goes Here -->
</cq-context>

Use this method to more explicitly define the container element.

The ChartEngine instance is attached to the cq-context by instantiating a CIQ.UI.Context object:

new CIQ.UI.Context(stxx, document.querySelector("cq-context"));

To host multiple charts on a screen you should set up a cq-context element for each ChartEngine instance.

The cq-ui-manager element is a component that manages all menus and dialogs on the page. It does so by attaching itself to the body and monitoring touch and mouse events, and then instantiating menus and dialogs. For instance, when a user taps on the screen, they expect that any open menus will be closed. This is one of the responsibilities that cq-ui-manager assumes.

One cq-ui-manager is allowed for the entire page, even when multiple charts are instantiated.

Creating Web Components: BaseComponent and ContextTag

Any component that needs to interact with the framework should be derived from the BaseComponent class that is included in componentUI.js. Any component derived from BaseComponent can use the built-in data binding and key capture capabilities.

Components that need to interact with a chart can be derived from ContextTag. This tag automatically locates the cq-context that is parent to the tag. It coordinates to make sure that the context knows about the tag, and that the tag knows about the context. this.context.stx will then reliably provide a reference to the chart object.

A third type of component called a DialogContentTag can be used for components that potentially need to interact with multiple charts. Dialogs are a perfect example, where a single dialog on a page may interact with any chart on the page depending on which button the user pressed. In this case this.context.stx changes depending on the state of the tag.

Helpers

In addition to web components, the UI framework includes a set of classes called "Helpers". The job of a Helper is to bridge functionality between a component and a chart engine (i.e. Layout, StudyEdit). A Helper can also be used to implement data binding on a section of DOM that isn't componentized (i.e. StudyMenu).

Helpers are always associated with a particular chart context. They must be created in your JavaScript:

var context = new CIQ.UI.Context(stxx, document.querySelector("cq-context"));
var layout = new CIQ.UI.Layout(context);

Since Helpers do not live in the DOM, they rely on the context to connect them for data binding. They do this by advertising themselves with the method advertiseAs(). Generally, you don't need to worry about Helpers unless you need to add additional bindings. If so, we recommend extending the CIQ.UI.Layout prototype with additional methods.

Bindings

The UI framework uses attributes for binding. Three types of binding are supported:

  1. stxtap - User click/tap (or hits enter in input tag)

<div stxtap='coolLikeJamesDean()'> will call the method coolLikeJamesDean() when the user clicks/taps on that div tag. The framework will look for the nearest parent object that has that function.

You can also target Helpers with this code <div stxtap='Layout.coolLikeJamesDean()'>. In this case, the nearest ContextTag will activate the binding, and use its context to find an associated Layout helper.

Example, bindings are automatically associated with the nearest parent component :

<script>
	MyTag.prototype.coolLikeJamesDean = function(node) {
		alert("Your clicked " + node.innerHTML);
	};
</script>

<cq-my-tag>
	<div>
		<div stxtap="coolLikeJamesDean()">Be cool</div>
	</div>
</cq-my-tag>
  1. stxbind - Keeps DOM in sync with the contents of a JavaScript variable

Unlike more comprehensive data binding systems, stxbind defers the job of binding to a member call. For instance, <div stxbind='keepMeCool()'> simply calls the method "keepMeCool()" when the component is instantiated. It is the job of keepMeCool() to monitor a JS variable and update the DOM.

Example, establish a binding :

<script>
	MyTag.prototype.keepMeCool = function(node) {
		var self = this;
		myWatchFunction(this.temperature, function() {
			node.text(self.temperature);
		});
	};
</script>
<cq-my-tag>
	<div stxbind="keepMeCool()"></div>
</cq-my-tag>

In this example, myWatchFunction is left to the developer to implement. There are three recommended approaches:

  • Manually track updates - Stash the node and keep it updated by checking for user input. This is a typical imperative approach.

    var watched = [];
    
    MyTag.prototype.keepMeCool = function(node) {
    	watched.push({ variable: "temperature", node: node });
    };
    
    MyMenu.prototype.changeTemperature = function(newTemp) {
    	watched.forEach(function(item) {
    		if (item.variable == "temperature") item.node.text(newTemp);
    	});
    };
    

  • Use a chart injection - Check the value of a chart variable each time it is rendered. In this example, we move the temperature value into stx.layout. The injection will run every time the chart renders, effectively keeping your UI up to date with the value that is associated with the chart.

    MyTag.prototype.keepMeCool = function(node) {
    	this.context.stx.append("draw", function() {
    		node.text(this.layout.temperature);
    	});
    };
    

  • Observe the property - You can set up a property of an object to be observed. When the property value changes, a listener function is executed. This method requires access to a chart engine.

    MyTag.prototype.keepMeCool = function(node) {
    	var self = this;
    	CIQ.UI.observeProperty("temperature", this, function(obj) {
    		node.text(obj.value);
    	});
    };
    
  1. stxsetget - Two-way binding

stxsetget is a convenience method for adding both stxtap and stxbind with a single attribute. The main difference is that "set" and "get" are prepended to the attribute value when called. Two-way binding is used for instance when building the radio button display menu in sample-template-advanced.html.

<script>
	MyTag.prototype.getTemperature = function(node) {
		// ...
	};

	MyTag.prototype.setTemperature = function(node, value) {
		// ...
	};
</script>
<cq-my-tag>
	<div stxsetget="Temperature"></div>
</cq-my-tag>

Of course, there is no restriction on including other data binding or templating engines if you have more demanding data binding needs.

Customizing HTML and CSS

Customizing the provided templates is really as easy as modifying the HTML and CSS. Simply delete a component from the HTML if you don't wish to use it. If you want to make it look different you can reorganize the HTML inside the component and modify the associated CSS.

The CSS used by the templates is actually built using SASS. The SASS is compiled into chartiq.css. You should never modify chartiq.css directly because this will make it very difficult to upgrade in the future. Instead, we recommend adding additional CSS in your own file and then including it after chartiq.css. All CSS values can be overridden in this manner. Following this approach will guarantee easy upgrades!

Example, overriding the height of the cq-toolbar :

cq-toolbar {
	height: 60px;
}
<link rel="stylesheet" type="text/css" href="chartiq/css/chartiq.css" />
<link rel="stylesheet" type="text/css" href="mycss.css" />

An exception to this rule is if you wish to change the colors or fonts for the entire template. In this case, we recommend making a copy of css/chartiq.scss. Change the variables to contain the colors and styles that you prefer. Then use a SASS builder to generate a new chartiq.css file.

Component Specifics

cq-marker - Add this attribute to any component in order to make it a "Marker". Markers are DOM objects that are superimposed on the chart panel. See Markers.

cq-scroll - creates a scrollable area. Scrollable areas can use perfect-scrollbar to create more elegant scrollbars than native. They also accept cursor up, cursor down, and enter keystrokes.

cq-toggle - creates a toggle div that can automatically be bound. See WebComponents.Toggle.

cq-clickable - creates an div that when clicked runs a method on a different component. See WebComponents.Clickable.

cq-dialog - creates a dialog that is centered on the screen. A cq-dialog should typically contain a component that is derived from CIQ.UI.DialogContentTag. Generally, you would call open() or close() on the inner content tag instead of interacting directly with the dialog.

Example, open a dialog :

<cq-dialog>
	<cq-my-dialog-contents> </cq-my-dialog-contents>
</cq-dialog>
<script>
	document.querySelector("cq-my-dialog-contents").open();
</script>

cq-menu - creates a dropdown menu. Menus appear when they are tapped or clicked (not hover). Every cq-menu must contain a cq-menu-dropdown that contains the contents of the menu. The CSS for these two components has been carefully constructed so that menuing is extremely lightweight.

Example, pattern for menus :

<cq-menu class="ciq-menu">
	<span>Menu Label</span>
	<cq-menu-dropdown>
		<cq-item>My menu item</cq-item>
	</cq-menu-dropdown>
</cq-menu>

cq-color-picker - establishes a color picker component for the page. Only a single color picker exists on the page. It is relocated dynamically whenever it is opened. The color picker is triggered by a cq-swatch component which displays a clickable swatch of color. See WebComponents.ColorPicker.

See WebComponents.html for a full list of available components and their documentation.

Data Integration

Driver.Lookup (Symbol Search Engine)

The cq-lookup tags require a Lookup.Driver helper to process "look ahead" (autocomplete) keystrokes and to fetch results. CIQ.ChartEngine.Driver.Lookup provides a default implementation that queries against ChartIQ's symbol server.

To create your own driver, simply derive a new class from CIQ.ChartEngine.Driver.Lookup and implement an acceptText() method. This method will be passed the text entered by the user, and any lookup filters that the user has selected. Results should be returned as an array of objects, each containing a data item (in whatever format you wish) and a display array with displayable values.

Example, sample Lookup.Driver :

CIQ.ChartEngine.Driver.Lookup.MyDriver = function() {};
CIQ.inheritsFrom(CIQ.ChartEngine.Driver.Lookup.MyDriver, CIQ.ChartEngine.Driver.Lookup);

CIQ.ChartEngine.Driver.Lookup.MyDriver.prototype.acceptText = function(
	text,
	filter,
	maxResults,
	cb
) {
	fetchYourResults(function() {
		var results = [];
		results.push({
			data: {}, // your result symbolObject
			display: ["AAPL", "Apple Computer Corp", "NASDAQ"]
		});
		cb(results);
	});
};

NameValueStore

Some supplied web components require a name value store in order to persist their state across sessions. By default, the library comes with a NameValueStore implementation that uses the browser's localStorage. You can however create your own which could use a network service for persistence.

To use an alternative store, you should first create a class that conforms to the NameValueStore interface. This should implement get, set and remove methods.

Example, create a custom NameValueStore :

const MyNVS = {
	get: function(field, cb) {
		cb(null, result);
	},
	set: function(field, value, cb) {
		// set
	},
	remove: function(field, cb) {
		// remove
	}
};

Next, you will need to initialize the desired components with this NameValueStore. cq-themes and cq-views currently use this interface to store values.

Example, initialize components with custom NameValueStore :

document.querySelector("cq-themes").initialize({
	builtInThemes: { "ciq-day": "Day" },
	nameValueStore: new MyNVS()
});

document.querySelector("cq-views").initialize({
	nameValueStore: new MyNVS()
});

Advanced Topics

ModalTag

You can derive your components from ModalTag in order to automatically give them modal capabilities. This means that when you mouse over the tag, stx.modalBegin() and stx.modalEnd() are called, disabling mouse interaction with the associated chart. A ModalTag is typically used when superimposing a component on to a chart, often as a cq-marker. For this reason, ModalTag is derived from ContextTag and must have a cq-context in its ancestor tree.

KeystrokeHub

This is a singleton helper that manages keystrokes. In HTML, you would typically register for key events on an input tag. The KeystrokeHub handles the more complicated problem of handling keystrokes that are independent of input tags. Examples include global hotkeys, passing keystrokes to a chart, or navigating the menuing system with keys.

You must initialize a KeystrokeHub with JavaScript code and associate it with a context. (You can optionally dynamically associate the context by calling setContext()). The KeystrokeHub registers itself with the cq-ui-manager so that it is universally available.

Components receive keystrokes by registering "claims". The KeystrokeHub distributes keystrokes to all components who have staked a claim by calling their keyStroke() method. A component can then decide whether to act on the keystroke, and if so, return true so that no other component acts on the stroke. It is the responsibility of the component to decide whether it should act. For instance, cq-scroll components only act on cursor up, cursor down, and enter keystrokes. Furthermore, they only act on them if the component itself is visible.

If a keystroke is not claimed by any component then the KeystrokeHub will attempt to satisfy any global hotkeys by calling CIQ.UI.KeystrokeHub.defaultHotKeys(). A default implementation is provided but you can override this prototype method if you desire.

For both components as well as hotkeys, the default browser key code will be passed for most keys. For special keys, a string is passed to indicate the key that was pressed. For instance, "up" is passed when the cursor up key is pressed.

Lifts

From time to time, an interactive page runs into formatting constraints. A typical constraint occurs in dialogs, where interactive (pop-up) data is hidden by the edge of the dialog. The most common example of this is when implementing a select-box inside of a dialog. Native select-boxes are ugly, but custom select-boxes get cut off when inside dialogs.

By adding the cq-lift attribute to a tag, the cq-ui-manager will temporarily "lift" the tag outside of its container, freeing it of its bounding box. It does this by using absolute positioning. When the tag is acted upon, the lift is released, and the tag goes back into its dialog.

containerExecute

This provides a convenient pattern for nested composable components. containerExecute will traverse up an ancestor tree looking for a component that contains a given method.

As an example, consider the tag cq-close tag. We would like to nest this tag arbitrarily inside any type of component and have it automatically close the parent component. But we might want to wrap it in some formatting, which decouples the tag in the DOM. In this circumstance, we have coded the cq-close component to emit a containerExecute that will call the "close()" method on the nearest parent that contains that method. So long as a tag implements this interface, cq-close can be nested inside.

An analogy is exception handling. Calling containerExecute is like throwing an exception. It will traverse up the stack until it finds a handler and then passes the message to that handler.

makeFromTemplate

Instantiating templates actually takes a fair amount of boilerplate, namely because they exist as a DocumentFragment and not as an actual DOM element. makeFromTemplate() encapsulates that functionality so that you can easily create a template.

Resizables

Until recently, receiving resize events from DOM elements was not universally supported across browsers (only window resizing is an event in some browsers). In dynamic single page applications though, we frequently encounter interfaces that have DOM elements changing size based on user interactive. For instance, a menu may change in height as elements are added or removed.

Luckily, a workaround exists. The component library makes use of resize listeners that are based on the newly introduced ResizeObserver API. Simply call CIQ.UI.addResizeListener(node, cb) to receive resize events. Note, be sure to call CIQ.UI.removeResizeListener(node) when cleaning up.

Additionally, any component registered this way will also have its resize() method called whenever the window itself is resized. This allows dialogs and menus to resize themselves accordingly.

jQuery

As of version 8.1.0, the web components no longer require jQuery. However, if you do choose to use jQuery, the components remain fully compatible. If you have implemented custom web components that rely on jQuery, you may need to include the deprecated.js module which contains the library's deprecated jQuery methods.

Data binding mechanics

You must call CIQ.UI.begin() in order to start data binding. This is primarily due to the potential for importing web components from other files. Once this has been called, components derived from BaseComponent automatically process their own data binding events.

You can create components dynamically and add them to the DOM. Components are processed for data binding when they are attached, however processing only occurs once. You cannot remove a component and then add it somewhere else and expect the data bindings to change.

One of the complications with any framework is initialization. Many of the components require access to a cq-context, but there is no guarantee that the CIQ.UI.Context exists when those components are created. To address this, we leverage the DOM. Contexts are discovered by traversing the DOM. The actual CIQ.UI.Context implementation is stored in the cq-context tag as document.querySelector("cq-context").CIQ.UI.context. Each component also registers itself in document.querySelector("cq-context").CIQ.UI.components[]. In this way, whoever gets created last (the component or the context) is responsible for linking themselves up. This is done by calling the setContext() method on the component itself.

Finally, this method of "implicit" data binding creates potential ambiguities. As a rule therefore, the framework requires that data bindings occur on their nearest ancestor component. In order to enforce this policy, and to ensure the existence of a full ancestor tree, bindings occur on the "nextTick" after a component is added to the DOM. This ensures that the DOM tree is fully established when all decisions about data binding occur.

Limits to the architecture

While it is not a capability of the software, the architecture technically makes it possible (with additional syntax) for data bindings to target specific ancestors, or even unrelated components. However, since bindings occur at the time a component is added to the DOM, the architecture would not easily support "runtime" bindings - those that must be acted upon at the tick of user click.

CIQ.UI.observeProperty provides a convenient and lightweight observable mechanism however its limits must be understood. CIQ.UI.observeProperty binds directly to objects, not to a representation of an object chain. This means that a binding will not change if a parent object is replaced. This can lead to unintuitive results.

Example, we don't get an alert for frank. Also a memory leak! :

var parent = {
	child: {
		name: "harry"
	}
};

CIQ.UI.observeProperty('name', parent.child, cb)

parent.child = { name: "frank" };

We've created the method CIQ.dataBindSafeAssignment to specifically deal with this scenario.

Example, safe way to deal with the same issue :

var parent = {
	child: {
		name: "harry"
	}
};

CIQ.UI.observeProperty('name', parent.child, cb)

CIQ.dataBindSafeAssignment(parent.child, { name: "frank" });

Even still, you must be careful to target the correct level in an object chain. More comprehensive frameworks such as sightglass take on this challenge by constructing a "chain of observables" to detect if any link in a chain has been modified.

Next steps