Package Exports
- slingjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (slingjs) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme

Sling
Sling is a client-side JavaScript framework for building Single Page Applications (SPAs). Sling is lightweight, 7.3KB minified, and less than 2.8KB gzipped.
Sling creates and uses a virtual 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.
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 3.5.0 (w/Routing) | 1,000 | 45.585ms | 21.936 |
| Angular 10.1.6 (w/Routing) | 1,000 | 331.252ms | 3.018 |
Sling.js creates nodes 7.268 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 3.5.0 (w/Routing) | 1,000 | 131.901ms | 7.581 |
| Angular 10.1.6 (w/Routing) | 1,000 | 565.286ms | 1.769 |
Sling.js changes nodes 4.285 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 / 50;
let str = val.toString(36).substring(4);
this.data.push(str);
}Performance (Network)
Using simulated 3G network speeds, Sling Core 3.4.0 with routing loads 2.16 times faster than an Angular 10.1.6 project and 2.90 times faster than an Angular 10.1.6 project with routing.
| Version | Requests | Async Time (3G network) | Total |
|---|---|---|---|
| Sling Core 3.4.0 | 1 | 2.18s | 4.20KB |
| Sling Core 3.4.0 (w/Routing) | 1 | 2.24s | 5.10KB |
| Angular 9.0.7 | 3 | 4.88s | 144.10KB |
| Angular 10.1.6 | 3 | 4.84s | 143.00KB |
| Angular 10.1.6 (w/Routing) | 3 | 6.51s | 229.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 { addRoute } from './sling/core/sling-router';
import { setState, mount } from './sling/core/sling';
import { setDetectionStrategy } from './sling/core/sling-change';
import { Observable } from '../../sling/reactive/sling-reactive';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!')
]
});
}
}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.)
setTimeoutandsetInterval- XMLHttpRequest and Fetch API requests
Automatic change detection does not perform updates upon the following:
- Websocket events
- IndexedDB callbacks
For versions of setTimeout and setInterval that do not trigger automatic change detection, use the following:
s.DETACHED_SET_TIMEOUT()s.DETACHED_SET_INTERVAL()
For example:
s.DETACHED_SET_TIMEOUT(() => {
console.log('Hello, world!');
}, 0);Lifecycle Hooks
Components may specify up to three lifecycle hooks:
| Lifecycle Hook | Triggers Change Detection | Timing |
|---|---|---|
slOnInit() |
false |
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. |
Directives
Structural directives modify interactions with the DOM layout.
| Directive | Type | Behavior |
|---|---|---|
slUseExisting |
Structural | Create the element or, if it exists, use the existing element. |
slNoChanges |
Structural | Only perform change detection on element's children. |
Example directive usage:
view() {
return markup('div', {
attrs: {
id: 'divSheetContent'
},
children: [
new SelectedPartHeaderComponent().view(),
markup('div', {
attrs: {
id: 'chartDiv',
slUseExisting: 'true',
style: 'width: 90vw;'
}
})
]
})
}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: [] } )
Terse alias for markup() function.
textNode
string textNode( text )
Create a text 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.
version
string version( )
Returns Sling version number represented as a string.
Example:
console.log(s.version); // '3.2.0'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');
});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. |
| authGuard | A function that returns true if route action may be taken, otherwise false. |
| authFail | Object with route property to route to on authGuard fail. Also may specify params. |
Example route definition:
route('all', { component: new TodoListComponent(), root: 'divTodoList' });
route('completed', { component: new TodoListCompletedComponent(), root: 'divTodoList' });
route('user/:userId', { component: new UserProfileComponent(), root: 'divUserProfile' });Example authGuard definition:
route('completed', { component: new TodoListCompletedComponent(), root: 'divTodoList', authGuard: function(proposedRoute) { console.log('This will prevent route to \'completed\'.'); return false; }, authFail: { 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:
s.route('user/5'); // Activates component at root for route '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.
Core Change Detection API
setDetectionStrategy
void setDetectionStrategy ( newDetectionStrategy )
Set the new change detection strategy.
detectChanges
void detectChanges ( )
Trigger automatic change detection immediately.
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.
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.
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.
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.
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.