Watch data changes in the browser and node.js
This manual is also available in HTML5.
It is a simple and easy-to-use library that can be used in the browser or in node.js.
You can create a context from the data, then listen for change events and stringify changes.
Included event-emitter functions. Automatically detects changes in the data and emits events.
Designed for module 'data-context-binding' for binding data to the DOM and for module 'fs-broker' for working with files.
Using a single-page application (SPA) with the 'data-context-binding' module gives very good results.
Please note, this version is not backward compatible with version 1.x
Please note that JSON string is not 100% compatible.
It has been extended to allow for incremental updates of JSON files.
Added the ability to include metadata and comments.
Parsing of JSON files is enabled.
The code in 'data-context' provides a robust mechanism for creating data contexts that can track changes, emit events, and support serialization/deserialization. It leverages JavaScript proxies to intercept and manage property operations, making it a powerful tool for managing state in complex applications.
The data context library implemented in 'data-context' can be used in various scenarios where tracking changes to data, emitting events, and managing state are essential. Here are some potential use cases:
1. State Management in Single Page Applications (SPAs)
2. Data Binding
3. Form Handling
4. Real-time Collaboration
5. Undo/Redo Functionality
6. Data Synchronization
7. Configuration Management
8. Logging and Auditing
9. Testing and Debugging
10. Incremental JSON Updates
npm install data-context
<script src="https://cdn.jsdelivr.net/npm/data-context@2" ></script>
or use 'tiny-https-server' router:
<script async src="node_modules/data-context@2"></script>
You can test data-context
on your system using this command:
node ./node_modules/data-context/index.test
or in the data-context
project directory:
npm test
or open in your browser:
./node_modules/data-context/index.test.html
'use strict';
//Import the required modules.
const { createDataContext, parse } = require('data-context');
//import { createDataContext, parse } from "data-context";
//Create a JSON string.
var strJSON = `{
"count": 0
}`;
//Interval id.
var intervalId = null;
//Create data context.
const context = parse(
//Parse the JSON string.
strJSON,
//Reviver function. Create data context.
createDataContext
);
//Listen to the count property.
context.on('count', (event) => {
console.log('event:', event);
if (event.newValue > 10) {
console.log('I am dead.');
clearInterval(intervalId);
//I am dead. Remove listener.
return false;
}
//I am live. Continue listening.
return true;
});
context.on('-change', (event) => {
//Stringify the changes.
var str = context.stringifyChanges(
//Reviver function. Default is null.
null,
//Indentation. Default is 0.
4,
//Include only modified data. Default is true.
true,
//Set data to unmodified after stringification. Default is true.
true
);
console.log('changes:', str);
//I am live. Continue listening.
return true;
});
//Start the interval.
intervalId = setInterval(() => {
//Increment the count property.
context.count++;
}, 1000);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>data-context</title>
<!-- STEP 1. Import the module. Import for an HTML page hosted on the server. -->
<script type="text/javascript" src="./index.js"></script>
<!-- STEP 1. Import the module. Import for a standalone HTML page. -->
<!--<script src="https://cdn.jsdelivr.net/npm/data-context"></script>-->
<script>
'use strict';
// STEP 3. Import the module.
importModules(['data-context'], function (DC) {
var { createDataContext, parse } = DC;
//Create a JSON string.
var strJSON = `{
"count": 0
}`;
//Interval id.
var intervalId = null;
//Create data context.
const context = parse(
//Parse the JSON string.
strJSON,
//Reviver function. Create data context.
createDataContext
);
//Listen to the count property.
context.on('count', (event) => {
console.log('event:', event);
if (event.newValue > 10) {
console.log('I am dead.');
clearInterval(intervalId);
//I am dead. Remove listener.
return false;
}
//I am live. Continue listening.
return true;
});
context.on('-change', (event) => {
//Stringify the changes.
var str = context.stringifyChanges(
//Reviver function. Default is null.
null,
//Indentation. Default is 0.
4,
//Include only modified data. Default is true.
true,
//Set data to unmodified after stringification. Default is true.
true
);
console.log('changes:', str);
//I am live. Continue listening.
return true;
});
//Start the interval.
intervalId = setInterval(() => {
//Increment the count property.
context.count++;
}, 1000);
});
// STEP 2. Add module import function.
/**
* Module import function.
* @param {string[]} importIdentifierArray Modules to import.
* @param {(...importModules:any[]) => void} callback Callback function.
*/
function importModules(importIdentifierArray, callback) {
var thisScope = "undefined" != typeof globalThis
? globalThis
: "undefined" != typeof window
? window
: "undefined" != typeof global
? global : "undefined" != typeof self
? self
: {};
if (!thisScope.modules) { thisScope.modules = {}; }
waitModules();
function waitModules() {
if (importIdentifierArray.length) {
for (let i = 0; i < importIdentifierArray.length; i++) {
if (!thisScope.modules[importIdentifierArray[i]]) { return setTimeout(waitModules, 10); }
}
}
callback.call(thisScope, ...importIdentifierArray.map(function (id) { return thisScope.modules[id]; }));
}
}
</script>
</head>
<body>
<h3>Example 'data-context'</h3>
<p>Press F12. Console results.</p>
</body>
</html>
Create a data context from the data.
Type: function
Parameters:
data
- The data object.propertyName
- The property name where the data is located. Default is null.parent
{DataContext} - The parent data context. Default is null.Returns:
Static Properties:
ignoreMetadata
- Global flag to ignore metadata and comments. Default is false.createDataContext
{(data: any, propertyName?: string, parent?: DataContext) => DataContext} - Create a data context from the data.parse
{(str: string, reviver?: Reviver) => DataContext} - Parse JSON string to the data context.stringify
{(data: any, replacer?: Replacer, space?: number) => string} - Stringify the data to JSON string.The data context Proxy object.
Type: Proxy
Properties:
_isDataContext
- The flag that indicates that the object is a data context. Default is true._isModified
- The flag that indicates that the object is modified. Default is false._modified
{Array<PropertyName>} - The array of modified properties._propertyName
- The property name where the data is located. Default is null._parent
{DataContext} - The parent data context. Default is null._events
{Map<string, EventListener>} - The map of event listeners.isChanged
- The flag that indicates that the object is changed.Methods:
resetChanges
{() => void} - Set the current and child objects to status - unmodified. Must be used to start tracking changes again.once
{(propertyName: PropertyName, listener: EventListener) => void} - Add an event listener that will be called only once.on
{(propertyName: PropertyName, listener: EventListener) => void} - Add an event listener.emit
{(eventName: string, event: EventObject) => void} - Emit an event.emitToParent
{(eventName: string, event: EventObject) => void} - Emit an event to the parent.toString
{() => string} - Returns the string representation of the data context.overwritingData
{(text: string, reviver?: Reviver ) => void} - Overwrite the data.stringifyChanges
{(reviver?: Reviver, space?: number|string, onlyModified?: boolean, setUnmodified?: boolean) => string} - Stringify the changes.The event listener function.
Type: function
Parameters:
event
- The event object.Returns:
The event object.
Type: object
Properties:
eventName
- The event name. 'new' | 'set' | 'delete' | 'reposition' | '-change'
target
- The target data context.propertyPath
{Arrayparent
- The parent data context.oldValue
- The old value.newValue
- The new value.The property name.
Type: string
The reviver function.
Type: reviver
or createDataContext
or null
This project is licensed under the MIT License.
Copyright © 2024 Manuel Lõhmus
Donations are welcome and will go towards further development of this project.