Sling is a client-side JavaScript framework for building Single Page Applications (SPAs). Sling is lightweight, 32KB minified, and less than 10KB gzipped.
Sling creates and uses an Incremental DOM to perform differential updates for fast rendering.
Sling has an automatic change detection mechanism which updates your components for you.
Sling is structured using ECMAScript modules so that Sling code is tree shakeable to ultimately reduce bundle sizes.
See: slingjs.org
Next Billion Users (NBUs) Empower developers to create SPAs for the NBUs of the web. The NBUs tend to use more affordable and less powerful devices. These devices struggle to achieve a two second Time to Interactive (TTI) with larger component libraries and frameworks.
Practical Familiarity with other JavaScript component libraries. Components are instantiated objects which may be used to control their own state. Components have a simple markup language with a gradual learning curve.
Generalized API as unopinionated as possible. Developers choose the right design patterns for their SPAs—not the library.
Fast High performance. Sling aims to get your SPA to interactive as quickly as possible and aims to keep your SPA as responsive as possible by staying within small code production budgets. With Sling, it should be easier for your SPA to run at 60 frames per second for a native application experience.
Minimal Setup Simply import the Sling functions required by your SPA and Sling is ready to use. No configuration files and no hidden requirements.
Run npm run devServer
after a npm install
to start webpack-dev-server
.
Then navigate to localhost:8080/todo.html
.
Because Sling is so lightweight, it can render thousands more nodes than Angular can in the same time period. Both test applications were served using the local-web-server
NPM package.
Version | Number of nodes created | Average time | Nodes per ms |
---|---|---|---|
Sling Core 10.1.0 | 1,000 | 37.195ms | 26.885 |
Mithril.js 2.0.4 | 1,000 | 39.312ms | 25.437 |
Angular 10.1.6 | 1,000 | 165.810ms | 6.030 |
Sling.js creates nodes 8.173 times faster than Angular.
The above nodes were <p>
tags containing strings generated by the following simple function:
for (let i = 0; i < 1000; ++i) {
const value = i / 100;
let str = val.toString(36).substring(7);
this.data.push(str);
}
Version | Number of nodes changed | Average time | Nodes per ms |
---|---|---|---|
Sling Core 10.1.0 | 1,000 | 17.197ms | 58.149 |
Mithril.js 2.0.4 | 1,000 | 23.600ms | 42.372 |
Angular 10.1.6 | 1,000 | 362.536ms | 2.758 |
Sling.js changes nodes 21.263 times faster than Angular in automatic change detection mode.
The above nodes were <p>
tags containing strings generated by the following simple function:
for (let i = 0; i < 1000; ++i) {
const value = i / 50;
let str = val.toString(36).substring(4);
this.data.push(str);
}
Version | Requests | Total |
---|---|---|
Sling Core 10.1.1 | 1 | 7.9KB |
Mithril.js 2.0.4 | 1 | 26.7KB |
Angular 10.1.6 | 3 | 143.00KB |
To add Sling to your project, simply import the Sling function required by your application.
Below is an example of Sling import statements:
import { setState, mount, setDetectionStrategy, addRoute } from './sling';
import { Observable } from './sling-reactive';
import { slGet } from './sling-xhr';
Sling uses ES2015/ES6 syntax. Sling does not have any production dependencies.
A component is a JavaScript class with a view()
function that returns markup to render.
Components may be nested, but lifecycle hooks for nested components will not be automatically called. This is done for performance reasons and to stay within production code budgets.
Example component:
class HelloWorldComponent {
constructor() {
}
view() {
return markup('h1', {
children: [
textNode('Hello, world!')
]
});
}
}
Sling.js version 15.0.0 supports scoped CSS for components.
Scoped CSS may be applied to a class by specifying a slStyle
function which returns CSS. Note that CSS imports are currently not supported.
class ExampleScopedStyleComponent {
constructor() {
}
slStyle() {
return 'div a[target="_blank"], nav { background-color: #cacaca; } kbd { background-color: #cacaca; }';
}
view() {
return markup('div', {
attrs: {
'id': 'divslstyle4'
},
children: [
markup('a', {
attrs: {
target: "_blank"
},
children: [
textNode('Styled a')
]
}),
markup('nav', {
children: [
textNode('Unstyled nav')
]
}),
markup('kbd', {
children: [
textNode('Tab')
]
})
]
})
}
}
Sling supports two change detection strategies: automatic and manual. The default mode is automatic.
Strategy | Description |
---|---|
s.CHANGE_STRATEGY_AUTOMATIC |
Automatically update components after browser events and requests. This is the default setting. |
s.CHANGE_STRATEGY_MANUAL |
Manually update components after browser events and requests. |
Automatic change detection performs updates upon the following:
- All browser events (click, mouseover, keyup, etc.)
- XMLHttpRequest and Fetch API requests
Automatic change detection does not perform updates upon the following:
- Websocket events
- IndexedDB callbacks
For functions bound in model views that do not trigger change detection, but are run in automatic change detection mode, start the function name with case-sensitive slDetached
. Below is an example of a detached slDetachedIncrementCount
function that does not trigger change detection when run.
export class TestDetachedFunctionComponent {
constructor() {
this.count = 0;
}
slDetachedIncrementCount() {
this.count++;
}
view() {
return markup('div', {
attrs: {
'id': 'divDetachedExample'
},
children: [
markup('button', {
attrs: {
onclick: this.slDetachedIncrementCount.bind(this),
},
children: [
textNode('Detached Button')
]
})
]
})
}
}
Components may specify up to three lifecycle hooks:
Lifecycle Hook | Triggers Change Detection | Timing |
---|---|---|
slOnInit() |
false |
Before the component's view function is called and before the component is mounted to the DOM. |
slOnDestroy() |
false |
Before the component is removed from the DOM. |
slAfterInit() |
true |
After the component is mounted to the DOM. |
Lifecycle hooks are executed for nested components returned by a component's view function.
class NestedHookComponent {
slOnInit() {
console.log('Will be called after root component slOnInit hook.');
}
view() {
return markup('div', {
children: [
textNode('Child component.')
]
});
}
}
class RootComponent {
slOnInit() {
console.log('Will be called before view function of component is called.');
}
view() {
return markup('div', {
children: [
textNode('Root component.'),
new NestedHookComponent()
]
});
}
}
Structural directives modify interactions with the DOM layout.
Directive | Type | Behavior |
---|---|---|
useexisting |
Structural | Create the element or, if it exists, use the existing element. |
onlychildren |
Structural | Only perform change detection on element's children. |
onlyself |
Structural | Only perform change detection on the element and not children. |
trustchildren |
Structural | Render HTML string children. |
slfor |
Structural | Render a named list using a node factory and an update function. |
slfornamed |
Structural | Render a named list using a node factory and an update function. This directive may be used instead of slfor where function names are minified in builds. |
slref |
Structural | Store the DOM node reference in the component. Takes in variable name. |
Attribute directives change the appearance or behavior of a DOM element.
Directive | Type | Behavior |
---|---|---|
slanimatedestroy |
Attribute | Wait for CSS class animation to finish before removal from the DOM. |
slanimatedestroytarget |
Attribute | Used together with slanimatedestroy . Should be a function which returns a DOM node to animate. The proposed node to animate is supplied as an argument to the function. |
slpreventdefault |
Attribute | Prevent default behavior on Event object. |
Namespace directives apply namespace information to an element or attribute.
Directive | Type | Behavior |
---|---|---|
slns |
Namespace | Specify the element's namespace. |
slnsfor |
Namespace | Specify attributes with different namespaces in the format: { [key: string]: { namespace: string; value: string; } } . |
Example directive usage:
view() {
return markup('div', {
attrs: {
id: 'divSheetContent'
},
children: [
new SelectedPartHeaderComponent().view(),
markup('div', {
attrs: {
id: 'chartDiv',
sldirective: 'useexisting',
style: 'width: 90vw;'
}
})
]
})
}
Another example of directive usage:
view() {
return markup('div', {
attrs: {
id: 'divnav',
},
children: [
textNode('Select a route to navigate to.'),
markup('button', {
attrs: {
onclick: this.navigateToHydrate.bind(this)
},
children: [
textNode('Hydrate Route')
]
}),
markup('button', {
attrs: {
onclick: this.navigateToRoot.bind(this)
},
children: [
textNode('Root Route')
]
}),
markup('div', {
attrs: {
sldirective: 'trustchildren'
},
children: [
textNode(this.ssrContent)
]
})
]
})
}
Another example of directive usage:
view() {
return markup('div', {
attrs: {
...this.showhide !== true && { class: 'visible' }
},
children: [
...(this.hide === false ? [
markup('h1', {
attrs: {
slanimatedestroy: 'hide'
},
children: [
textNode('Hello, world!'),
markup('button', {
attrs: {
onclick: this.hideTemplate.bind(this)
},
children: [
textNode('Hide')
]
})
]
})
] : [
])
]
});
}
Example of slfor
directive usage:
export class TestRenderElement3 {
constructor() {
this.data = function () { return Store3.data; };
this.selected = function () { return Store3.selected; };
this.run = function () {
Store3.run();
};
this.add = function () {
Store3.add();
};
this.update = function () {
Store3.update();
};
this.select = function (id) {
Store3.select(id);
};
this.delete = function (id) {
Store3.remove(id);
};
this.runLots = function () {
Store3.runLots();
};
this.clear = function () {
Store3.clear();
};
this.swapRows = function () {
Store3.swapRows();
};
this.add();
}
updateRow(ctx, v) {
if (this.$id === undefined) {
this.$fid = this.childNodes[1];
this.$label = this.children[2].childNodes[0];
}
this.children[2].children[0].onclick = wrapWithChangeDetector(ctx.delete.bind(this, v.id));
if (this.$label.childNodes[0].data !== v.label) {
this.$label.removeChild(this.$label.childNodes[0]);
this.$label.append(v.label);
}
const idStr = String(v.id);
if (this.$id.childNodes[0].data !== idStr) {
this.$id.removeChild(this.$foo.childNodes[0]);
this.$id.append(v.id);
}
var className = (v.id === ctx.selected()) ? 'danger' : ''
if (this.className !== className) this.className = className
}
makeRow(d) {
return markup('tr', {
attrs: {
...d.id === this.selected() && { class: 'danger' },
onclick: this.select.bind(this, d.id),
onremove: this.delete.bind(this, d.id)
},
children: [
new TestRenderElement4(),
markup('td', {
attrs: {
'class': 'col-md-1'
},
children: [
textNode(d.id)
]
}),
markup('td', {
attrs: {
'class': 'col-md-4',
},
children: [
markup('a', {
attrs: {
'href': '#',
onclick: this.select.bind(this, d.id)
},
children: [
textNode(d.label)
]
})
]
}),
markup('td', {
attrs: {
'class': 'col-md-1',
},
children: [
markup('a', {
attrs: {
'href': '#',
onclick: this.delete.bind(this, d.id)
},
children: [
markup('span', {
attrs: {
'class': 'glyphicon glyphicon-remove',
'aria-hidden': 'true'
}
})
]
})
]
}),
markup('td', {
attrs: {
'class': 'col-md-6'
}
})
]
});
}
view() {
return markup('div', {
attrs: {
'class': 'container',
'id': 'rendertoelement3'
},
children: [
markup('table', {
attrs: {
'class': 'table table-hover table-striped test-data'
},
children: [
markup('tbody', {
attrs: {
'slfor': 'myfor:data:makeRow:updateRow'
}
})
]
})
]
});
}
}
Below is an example of slanimatedestroytarget
directive usage.
Note that for slanimatedestroytarget
, unlike regular slanimatedestroy
, change detection is paused for the entire duration of all animated elements. Change detection is resumed and called immediately after that last animation ends.
export class TestKeyedHideAnimation1 {
constructor() {
this.list = ['a', 'b', 'c'];
this.toRemoveIndex = 1;
}
slDetachedOnNodeDestroy(proposedNode) {
const parent = proposedNode.parentNode;
return parent.childNodes[this.toRemoveIndex];
}
onHide() {
this.list.splice(1, 1);
}
view() {
return markup('div', {
attrs: {
id: 'divkeyedanimation1'
},
children: [
...Array.from(this.list, (note) =>
markup('div', {
attrs: {
slanimatedestroy: 'animExit',
slanimatedestroytarget: this.slDetachedOnNodeDestroy.bind(this)
},
children: [
markup('p', {
children: [
textNode(note)
]
})
]
})
),
markup('button', {
attrs: {
id: 'keyedhidebtn1',
onclick: this.onHide.bind(this)
},
children: [
textNode('Keyed Hide Button')
]
})
]
});
}
}
void setState ( newStateObj )
Set a new state object for SPA.
object getState ( )
Get the state object for SPA.
object markup ( tagString, { attrs: {}, children: [] } )
Returns markup object to render. May be mounted to DOM.
Example markup call:
markup('div', {
attrs: {
style: "width:50%;margin:auto;padding:1rem;"
},
children: [
...Array.from(getState().getNotes(), (note) =>
markup('div', {
attrs: {
class: 'input-group mb-3 animEnter',
style: 'width:100%;'
},
children: [
]
})
)
]
});
object markup ( tagString, { attrs: {}, children: [] } ) or object markup ( tagString, {}, [] )
Terse alias for markup() function.
string textNode( text )
Append a DOMString to a node.
Example textNode call:
textNode('Click me!');
element mount ( rootElementId, component, attachDetector = true )
Mounts component
on element with ID rootElementId
in DOM.
Returns root element in DOM where component
was added.
Mounted components replace the element with rootElementId
to avoid an excessive DOM size. Mounted components must have the same root element ID as the element in the DOM they are attached to.
By default, the Sling change detector is attached for the mounted component. Setting attachDetector
to false
prevents the change detector from being attached to this component. There are two convenience constants for change detection which are as follows:
Constant | Value |
---|---|
s.CHANGE_DETECTOR_DETACHED |
false |
s.CHANGE_DETECTOR_ATTACHED |
true |
void update ( rootElementId, component )
Updates the component mounted at element with ID rootElementId
.
HTMLElement renderElementWithoutClass ( tagName, attrs, children )
Render a DOM node from markup. All parameters must be defined. Class may not be specified as children to be consumed, only DOM nodes or DOMStrings are valid children. Useful for use with the slfor
structural directive.
const node1 = renderElementWithoutClass('kbd', {}, []);
const node2 = renderElementWithoutClass('span', { style: 'color: #cacaca' }, []);
const node3 = renderElementWithoutClass('div', {}, [ textNode('a') ]);
const node4 = renderElementWithoutClass('p', {}, [ renderElementWithoutClass('header', {}, ['header'])]);
const node5 = renderElementWithoutClass('p', {}, [ renderElementWithoutClass('header', {}, ['header']), 'b', textNode('c')]);
HTMLElement renderElement ( { tagName, attrs, children }, isDetached = false )
Render a DOM node from markup.
const node = renderElement(markup('p', { children: [ textNode('Hello, world!') ] }));
Set isDetached
to true
if the created DOM node will not be attached to the DOM and managed by Sling.js.
string version( )
Returns Sling version number represented as a string.
Example:
console.log(version()); // '10.1.1'
object resolveAll( promiseArray )
Returns an object with data about settled Promises in the format:
{ result: Promise Result | null, error: Error | null, status: 'fulfilled' | 'rejected' }
Example:
const requestPromises = [
fetch('todo.html'),
fetch('http://does-not-exist')
];
resolveAll(requestPromises).then((results) => {
const successfulPromises = results.filter(p => p.status === 'fulfilled');
});
element hydrate ( rootElementId, attachDetector = true )
Attach event handlers to component on element with ID rootElementId
in DOM.
Returns root element in DOM.
Sling will take over the static HTML sent by the server and manage change detection.
In order to correctly hydrate static HTML sent by the server, the static HTML of the root element must contain two attributes. First, an ID must be specified so Sling can locate the component for hydration. Second, the attribute slssrclass
must be specified. The value of slssrclass
should be the class name of the view which manages the component. The class identified by slssrclass
should be defined on the window
object, or defined on the value of this
.
Example:
class TestSsrHydrateComponent1 {
hydratedFunction() {
const state = getState();
state.ishydrated = true;
setState(state);
}
view() {
const state = getState();
const isFuncCalled = state.ishydrated;
return markup('div', {
attrs: {
id: 'testssrhydrate',
slssrclass: 'TestSsrHydrateComponent1'
},
children: [
markup('button', {
attrs: {
id: 'ssrTest2',
onclick: this.hydratedFunction.bind(this)
},
children: [
textNode('Test Hydrate')
]
}),
markup('div', {
attrs: {
id: 'ssrTest1'
},
children: [
...(isFuncCalled === true ? [
textNode('Hydrated function called.')
] : [
textNode('SSR placeholder.')
])
]
})
]
})
}
}
window.TestSsrHydrateComponent1 = TestSsrHydrateComponent1;
hydrate('testssrhydrate');
By default, the Sling change detector is attached for the mounted component. Setting attachDetector
to false
prevents the change detector from being attached to this component. There are two convenience constants for change detection which are as follows:
Constant | Value |
---|---|
s.CHANGE_DETECTOR_DETACHED |
false |
s.CHANGE_DETECTOR_ATTACHED |
true |
string renderToString( component )
Renders a component into a HTML string.
Example:
const compStr = renderToString(new LoginComponent());
Below is an example of slFor
structural directive usage.
attrs: {
'slfor': 'myfor:data:makeRow:updateRow'
}
Arguments:
- Name of list (must be unique)
- The data list or function to retrieve data list. Must be a property of the enclosing class.
- The node factory. May return DOM node or Sling markup.
- The node update function.
The node factory receives a list item as the first argument.
Below is an example node factory function:
makeRow(listItem) {
return renderElement(markup('p', { children: [ textNode(listItem.id) ] }));
}
The node update function receives a reference to the object which constructed the row as the first argument and a list item as the second argument. The current DOM node is referenced by this
.
Below is an example of a node update function:
updateRow(context, listItem) {
if (this.$label === undefined) {
this.$label = this.children[0];
}
this.children[1].onclick = wrapWithChangeDetector(context.deleteRow.bind(this, listItem.id));
if (this.$label.childNodes[0].data !== listItem.id) {
this.$label.removeChild(this.$label.childNodes[0]);
this.$label.append(listItem.id);
}
}
For builds where function names are minified, the structural directive slfornamed
may be used instead of slfor
.
The structural directive slfornamed
takes arguments which are slfor
property values of functions belonging to an object. The slfor
property is typically set on functions in the slOnInit
lifecycle hook.
One difference between slfor
and slfornamed
is that for slfornamed
every argument must reference a function identified by slfor
. The data list can not be a simple property.
Below is an example of slfornamed
usage.
class NamedSlForComponent1 {
constructor() {
this.list = ['a', 'b', 'c'];
}
slOnInit() {
this.makeRow.slfor = 'make';
this.updateRow.slfor = 'update';
this.getData.slfor = 'data';
}
getData() {
return this.list;
}
makeRow(data) {
return markup('p', {
children: [
textNode(data)
]
});
}
updateRow(context, data) {
if (this.childNodes[0].data !== data) {
this.removeChild(this.childNodes[0]);
this.append(data);
}
}
view() {
return markup('div', {
attrs: {
id: 'divnamedslfor1'
},
children: [
markup('div', {
attrs: {
'slfornamed': 'namedfor:data:make:update'
}
})
]
});
}
}
void addRoute ( hashUrlRegEx, { root: elementId, routeObj: object })
Define a hash-based route that will replace element with ID elementId
's content with the specified component on route action.
Below is a list of possible routeObj
properties:
Property | Description |
---|---|
root | The id of the element to replace on route. |
component | The component to replace root . |
onCanDeactivate | A function that returns true if the current route may be navigated away from. Called before onActivationCheck. |
onActivationCheck | A function that returns true if route action may be taken, otherwise false. |
onActivationFail | Object with route property to route to on onActivationCheck fail. Also may specify params and attachDetector . |
onBeforeRoute | Function to execute before taking route action. Called after onActivationCheck and before the route action is taken. |
animateDestroy | Used with animation structural directives to animate router transitions. |
Example route definition:
addRoute('all', { component: new TodoListComponent(), root: 'divTodoList' });
addRoute('completed', { component: new TodoListCompletedComponent(), root: 'divTodoList' });
addRoute('user/:userId', { component: new UserProfileComponent(), root: 'divUserProfile' });
addRoute('.*', { component: new DefaultRouteComponent(), root: 'divRouterOutlet' });
Note: Use '.*' for the default route. Routes are checked in the order they are registered with addRoute.
Example onActivationCheck
definition:
route('completed', { component: new TodoListCompletedComponent(), root: 'divTodoList', onActivationCheck: function(proposedRoute) { console.log('This will prevent route to \'completed\'.'); return false; }, onActivationFail: { route: 'all', params: { } } });
object route ( hashUrl, params = { }, attachDetector = true )
Navigate to the hash-based route according to a previously defined route. May specify route parameters as an object. Returns the component that was routed to.
By default, the Sling change detector is attached for the mounted component. Setting attachDetector
to false
prevents the change detector from being attached to this component.
Example route call:
route('user/5'); // Activates component at root for route 'user/:userId'
void removeRoute ( hashUrlRegEx )
Remove a route from the Sling router.
Example:
removeRoute('user/:userId');
void getRoute ( )
Get the current hash-based route.
string[] getRouteSegments ( )
Returns the current hash-based route's segments or an empty array if there are none.
Example:
console.log(getRouteSegments()); // [ 'user', '5' ]
Using Sling Reactive, route changes may be listened to by using a Sling Observable. Every time the route changes, the subscribed function below will be called.
let routeObservable = Observable(getRouteSegments());
routeObservable.subscribe(function(routeArr) {
if (routeArr.length > 0) {
this.primaryRoute = routeArr[0];
}
else {
this.primaryRoute = '';
}
}.bind(this));
object getRouteParams ( )
Returns the current route's parameters as an object. Returns { }
if there are none.
[] getRouteQueryVariables ( )
Returns a list of objects containing parsed query string variables in the following format:
[
{ var: 'param1', value: '123' },
{ var: 'param2', value: 'some+text' }
]
Note value
will always be a string.
void setRouteStrategy ( newStrategy )
Sets the router strategy. Valid options for newStrategy
are listed in the table below.
Strategy | Description | Example |
---|---|---|
'#' | A hash based strategy. | http://localhost:8080/todo.html#/docs |
'?' | A query string route strategy. | http://localhost:8080/?/query=1 |
'' | A path name strategy. | http://localhost:8080/docs/1 |
void setDetectionStrategy ( newDetectionStrategy )
Set the new change detection strategy.
void detectChanges ( eleId )
Trigger automatic change detection immediately. If eleId is undefined or null, change detection will be performed on all components.
boolean isDetectorAttached ( eleId )
Returns true if Sling change detector is attached for the given element ID eleId
.
void detachDetector ( eleId )
Detach the Sling change detector for the given element ID eleId
.
function wrapWithChangeDetector ( funcToWrap, optionsObject )
Wrap a function funcToWrap
with a change detector call, so every time the function is called change detection is also run.
Valid options for optionsObject
are listed in the table below.
Key | Purpose | Usage |
---|---|---|
slpreventdefault | Prevent default on Event object. | slpreventdefault: true |
void enableDetectOnThen ( )
Enable automatic change detection for every successful Promise resolve.
Promise slRequest ( url, methodString, optionsObject = { } )
Create a XML HTTP Request (XHR) for the specified URL using the specified method, such as GET
. Returns a Promise.
Request Option | Default | Detail |
---|---|---|
contentType | application/json |
Set Content-Type request header. |
body | '' |
Body of the request. |
withCredentials | false |
Send cookies to 3rd party domains. |
timeout | 0 |
0 is no timeout. Specified in milliseconds. |
headers | {} |
Key/value request headers to set. |
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slRequestWithBody ( url, methodString, bodyObject = { } )
Create a XML HTTP Request (XHR) for the specified URL using the specified method, such as GET
, with the specified body object. Returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slGet ( url, data = { } )
Create a GET
XHR request with the specified data
which returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slPost ( url, data = { } )
Create a POST
XHR request with the specified data
which returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slPut ( url, data = { } )
Create a PUT
XHR request with the specified data
which returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slPatch ( url, data = { } )
Create a PATCH
XHR request with the specified data
which returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
Promise slDelete ( url, data = { } )
Create a DELETE
XHR request with the specified data
which returns a Promise.
On success, returns XMLHttpRequest which has data in response
property like so:
XMLHttpRequest
{
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: Ć’ ()
ontimeout: null
readyState: 4
response: "[↵ {↵ "userId": 1,↵ "id": 1,↵ "title": ""
...
}
On request fail, returns an object in the following format:
{
status: 404,
statusText: ''
}
object Stream( )
Returns a Sling stream. A stream is a sequence of values over time and the associated operations which are automatically applied as those values change.
Example stream usage using Sling XHR API:
slGet('https://jsonplaceholder.typicode.com/posts').then(xhrResp => {
let postArr = JSON.parse(xhrResp.response);
let postStream = Stream().from(postArr).transform(function(arr) {
return arr.filter(v => v.userId === 1);
}).transform(function(arr) {
return arr.filter(v => v.body.includes('quo'));
});
});
Equivalent stream usage using preexisting stream object and Sling XHR API:
let postStream2 = Stream();
postStream2.transform(function(arr) {
return arr.filter(v => v.userId === 1);
}).transform(function(arr) {
return arr.filter(v => v.body.includes('quo'));
});
slGet('https://jsonplaceholder.typicode.com/posts').then(xhrResp => {
let postArr = JSON.parse(xhrResp.response);
postArr.forEach(post => {
postStream2.push(post);
});
});
object push( value )
Push a value onto a stream. All transformers automatically called. Transformers are only applied on new data. Returns the stream.
object transform ( function(arrayData) { } )
Add a new transformer to stream. Is automatically applied to all existing and new data. Returns the stream.
object subscribe( function(arrayData) { } )
Add a function that is automatically called when the underlying stream data changes. Returns the stream.
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the stream.
object clearSubscriptions( )
Remove all subscribed functions. Returns the stream.
object call ( function(arrayData) { } )
Call a function which operates on the stream's data. Returns the stream.
[ ] getData( )
Returns a copy of stream array data.
object clearTransformers( )
Clears all transformers acting on the stream. Data will remain in state of last transformation. Returns the stream.
object from ( newArray )
Set stream data to newArray
and apply all existing transformers. Returns the stream.
object observable( array )
Returns a Sling observable. An observable is an array which may be listened to.
Example observable usage:
let myArray = [1, 2, 3];
let myObservable = Observable(myArray);
myObservable.subscribe(function(arr) {
console.log('New length: ' + arr.length);
});
myObservable.getData().push(4);
obs.getData()[myObservable.getData().length] = 5;
void subscribe ( listenerFunction )
Listener function will be automatically called whenever the underlying array data changes. Returns the observable.
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the observable.
object clearSubscriptions( )
Remove all subscribed functions. Returns the observable.
[ ] getData( )
Get the underlying array data.
object BehaviorSubject( value )
Returns a Sling behavior subject. A behavior subject is a value that emits changes to subscribers.
Example behavior subject usage:
let subject = BehaviorSubject(5);
subject.next(subject.getData() + 1);
let value = subject.getData(); // 6
subject.subscribe(function (value) { console.log('Value: ' + value); });
void subscribe ( listenerFunction )
Listener function will be automatically called whenever the subject's value changes. Returns the behavior subject.
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the behavior subject.
object clearSubscriptions( )
Remove all subscribed functions. Returns the behavior subject.
object next( value )
Set the next value of the subject. All subscribers are automatically called. Returns the behavior subject.
primitive|object getData( )
Get the underlying value.
object FormControl( initialValue )
Returns a Sling form control. A form control is a value checked by attached validation functions that emits changes to subscribers.
Example form control usage:
const formControl = FormControl(200);
const validatorFn1 = (val) => {
if (!isNaN(val) && isFinite(val)) {
return null;
} else {
return { nonNumeric: true };
}
}
formControl.setValidators([validatorFn1, validatorFn2]);
formControl.getValueChanges().subscribe((value) => {
console.log(value);
});
const valid = formControl.getValid() === true;
const pristine = formControl.getPristine() === true;
const errors = formControl.getErrors();
formControl.setValue(2);
any getValue ( )
Get the current value of the form control.
boolean getValid ( )
Get the current validity status of the form control.
boolean getDirty ( )
Returns true if the form control value has been changed.
boolean getPristine ( )
Returns true if the form control value has not been changed.
object[] getErrors ( )
Returns the list of error objects.
object getError ( errorKey )
Return the error object identified by the given key, it it exists. Null otherwise.
BehaviorSubject getValueChanges ( )
Returns the value changes BehaviorSubject which may be subscribed to on form control value change.
void setValue ( newValue )
Sets the new value of the form control.
void setPristine ( )
Sets the form control as pristine.
void setValidators ( validatorFnList )
Sets the validator functions of the form control. Each function accepts a value and returns null if valid or an object with a key.