slingjs
v21.2.1
Published
Client-side JavaScript framework for Single Page Applications
Downloads
522
Maintainers
Readme
Sling
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
Goals
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.
Testing
Run npm run devServer
after a npm install
to start webpack-dev-server
.
Then navigate to localhost:8080/todo.html
.
Performance (Time)
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);
}
Performance (Network)
|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 |
Add Sling
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';
Compatibility
Sling uses ES2015/ES6 syntax. Sling does not have any production dependencies.
Components
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!')
]
});
}
}
Scoped CSS
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')
]
})
]
})
}
}
Change Detection
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')
]
})
]
})
}
}
Lifecycle Hooks
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()
]
});
}
}
Directives
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')
]
})
]
});
}
}
Core API
setState
void setState ( newStateObj )
Set a new state object for SPA.
getState
object getState ( )
Get the state object for SPA.
markup
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: [
]
})
)
]
});
m
object markup ( tagString, { attrs: {}, children: [] } ) or object markup ( tagString, {}, [] )
Terse alias for markup() function.
textNode
string textNode( text )
Append a DOMString to a node.
Example textNode call:
textNode('Click me!');
mount
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
|
update
void update ( rootElementId, component )
Updates the component mounted at element with ID rootElementId
.
renderElementWithoutClass
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')]);
renderElement
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.
version
string version( )
Returns Sling version number represented as a string.
Example:
console.log(version()); // '10.1.1'
resolveAll
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');
});
hydrate
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
|
renderToString
string renderToString( component )
Renders a component into a HTML string.
Example:
const compStr = renderToString(new LoginComponent());
slFor
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.
Node Factory
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) ] }));
}
Node Update Function
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);
}
}
Named slFor
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'
}
})
]
});
}
}
Core Router API
addRoute
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: { } } });
route
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'
removeRoute
void removeRoute ( hashUrlRegEx )
Remove a route from the Sling router.
Example:
removeRoute('user/:userId');
getRoute
void getRoute ( )
Get the current hash-based route.
getRouteSegments
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));
getRouteParams
object getRouteParams ( )
Returns the current route's parameters as an object. Returns { }
if there are none.
getRouteQueryVariables
[] 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.
setRouteStrategy
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 |
Core Change Detection API
setDetectionStrategy
void setDetectionStrategy ( newDetectionStrategy )
Set the new change detection strategy.
detectChanges
void detectChanges ( eleId )
Trigger automatic change detection immediately. If eleId is undefined or null, change detection will be performed on all components.
isDetectorAttached
boolean isDetectorAttached ( eleId )
Returns true if Sling change detector is attached for the given element ID eleId
.
detachDetector
void detachDetector ( eleId )
Detach the Sling change detector for the given element ID eleId
.
wrapWithChangeDetector
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 |
enableDetectOnThen
void enableDetectOnThen ( )
Enable automatic change detection for every successful Promise resolve.
XHR API
slRequest
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: ''
}
slRequestWithBody
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: ''
}
slGet
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: ''
}
slPost
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: ''
}
slPut
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: ''
}
slPatch
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: ''
}
slDelete
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: ''
}
Reactive API
Stream
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);
});
});
Stream Functions
push
object push( value )
Push a value onto a stream. All transformers automatically called. Transformers are only applied on new data. Returns the stream.
transform
object transform ( function(arrayData) { } )
Add a new transformer to stream. Is automatically applied to all existing and new data. Returns the stream.
subscribe
object subscribe( function(arrayData) { } )
Add a function that is automatically called when the underlying stream data changes. Returns the stream.
getHasSubscription
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
clearSubscription
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the stream.
clearSubscriptions
object clearSubscriptions( )
Remove all subscribed functions. Returns the stream.
call
object call ( function(arrayData) { } )
Call a function which operates on the stream's data. Returns the stream.
getData
[ ] getData( )
Returns a copy of stream array data.
clearTransformers
object clearTransformers( )
Clears all transformers acting on the stream. Data will remain in state of last transformation. Returns the stream.
from
object from ( newArray )
Set stream data to newArray
and apply all existing transformers. Returns the stream.
Observable
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;
Observable Functions
subscribe
void subscribe ( listenerFunction )
Listener function will be automatically called whenever the underlying array data changes. Returns the observable.
getHasSubscription
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
clearSubscription
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the observable.
clearSubscriptions
object clearSubscriptions( )
Remove all subscribed functions. Returns the observable.
getData
[ ] getData( )
Get the underlying array data.
BehaviorSubject
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); });
BehaviorSubject Functions
subscribe
void subscribe ( listenerFunction )
Listener function will be automatically called whenever the subject's value changes. Returns the behavior subject.
getHasSubscription
boolean getHasSubscription( functionToCheck )
Check if functionToCheck
is in the list of subscribed functions. Returns true if functionToCheck
is in the list, false otherwise.
clearSubscription
object clearSubscription( functionToClear )
Remove functionToClear
from the list of subscribed functions. Returns the behavior subject.
clearSubscriptions
object clearSubscriptions( )
Remove all subscribed functions. Returns the behavior subject.
next
object next( value )
Set the next value of the subject. All subscribers are automatically called. Returns the behavior subject.
getData
primitive|object getData( )
Get the underlying value.
FormControl
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);
FormControl Functions
getValue
any getValue ( )
Get the current value of the form control.
getValid
boolean getValid ( )
Get the current validity status of the form control.
getDirty
boolean getDirty ( )
Returns true if the form control value has been changed.
getPristine
boolean getPristine ( )
Returns true if the form control value has not been changed.
getErrors
object[] getErrors ( )
Returns the list of error objects.
getError
object getError ( errorKey )
Return the error object identified by the given key, it it exists. Null otherwise.
getValueChanges
BehaviorSubject getValueChanges ( )
Returns the value changes BehaviorSubject which may be subscribed to on form control value change.
setValue
void setValue ( newValue )
Sets the new value of the form control.
setPristine
void setPristine ( )
Sets the form control as pristine.
setValidators
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.