UNPKG

data-context

Version:

Watch data changes in the browser and node.js

486 lines (353 loc) β€’ 17.5 kB
<p> <img src="logo/logo-432x260.png" alt="data-context logo" width="440" style="background-color:#333;border-radius:4em;border-color: #999;border-width: 0.5em;border-style: dashed;"> </p> # data-context Watch data changes in the browser and node.js ## πŸ“š Table of contents - [✨ Features](#-features) - [πŸ“‹ Description](#-description) - [πŸ“¦ Installation](#-installation) - [πŸ§ͺ Testing](#-testing) - [πŸš€ Usage](#-usage) - [πŸ“– API Reference](#-api-reference) - [πŸ“œ License - MIT](#-license) <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## ✨ Features - Create a context from the data. - Listen for change events. - Update the context. - Parse JSON data to the context. - Stringify changes to JSON. - Used extended JSON data format. Metadata and comments are supported. <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## πŸ“‹ Description The 'data-context' module provides a way to create a data context that can watch for changes in the data. 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'](https://www.npmjs.com/package/data-context-binding) module gives very good results. This module is part of the ['conextra'](https://www.npmjs.com/package/conextra) framework, which is a simple and easy-to-use single-page application (SPA) framework. You have to try it! A different solution than MVC (model–view–controller). > Please note, this version is not backward compatible with version 1.x<br> > Please note that JSON string is not 100% compatible.<br> > It has been extended to allow for incremental updates of JSON files.<br> > Added the ability to include metadata and comments.<br> > 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)** - **Description**: Manage the state of an application by creating data contexts for different parts of the state. - **Example**: Use the library to track changes in user data, application settings, or UI state, and automatically update the UI when changes occur. **2. Data Binding** - **Description**: Bind data to the DOM and automatically update the DOM when the data changes. - **Example**: Use the library in conjunction with a data-binding library like data-context-binding to create dynamic and responsive web applications. **3. Form Handling** - **Description**: Track changes to form inputs and validate data in real-time. - **Example**: Create a data context for form data, listen for changes to input fields, and validate the data or provide instant feedback to the user. **4. Real-time Collaboration** - **Description**: Enable real-time collaboration features by synchronizing data changes across multiple clients. - **Example**: Use the library to track changes to a shared document or project, and emit events to notify other clients of the changes. **5. Undo/Redo Functionality** - **Description**: Implement undo and redo functionality by tracking changes to data and allowing users to revert to previous states. - **Example**: Use the library to maintain a history of changes and provide undo/redo capabilities in applications like text editors or drawing tools. **6. Data Synchronization** - **Description**: Synchronize data between different parts of an application or between client and server. - **Example**: Use the library to track changes to local data and synchronize those changes with a remote server or other clients. **7. Configuration Management** - **Description**: Manage application configuration settings and track changes to those settings. - **Example**: Create a data context for configuration settings, listen for changes, and update the application behavior accordingly. **8. Logging and Auditing** - **Description**: Log changes to data for auditing purposes. - **Example**: Use the library to track changes to sensitive data and log those changes for security audits or compliance purposes. **9. Testing and Debugging** - **Description**: Facilitate testing and debugging by tracking changes to data and emitting events. - **Example**: Use the library to create test cases that verify the correct behavior of data changes and event emissions. **10. Incremental JSON Updates** - **Description**: Handle incremental updates to JSON data, including metadata and comments. - **Example**: Use the library to parse, modify, and serialize JSON data with support for incremental updates and metadata. <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## πŸ“¦ Installation [Available on npm](https://www.npmjs.com/package/data-context) ### nodejs: ```bash npm install data-context ``` ### browser: or use CDN (for an HTML page hosted on the server): ```html <script src="./node_modules/data-context/borwser.js" type="text/javascript"></script> ``` or use CDN (for a standalone HTML page): ```html <script src="https://cdn.jsdelivr.net/npm/data-context" type="text/javascript"></script> ``` or use 'tiny-https-server': ```html <script async src="node_modules/data-context@2"></script> ``` <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## πŸ§ͺ Testing 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` <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## πŸš€ Usage ### nodejs example: ```javascript '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); ``` ### browser example: ```html <!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> ``` ## πŸ“– API Reference - [createDataContext(data, propertyName, parent)](#createdatacontextdata-propertyname-parent) - [DataContext](#datacontext) - [EventListener(event)](#eventlistenerevent) - [EventObject](#eventobject) - [PropertyName](#propertyname) - [Reviver](#reviver) <p align="right"><a href="#data-context">Back to top ↑</a></p> ### *createDataContext(data, propertyName, parent)* Create a data context from the data. **Type:** `function` **Parameters:** - `data` {any} - The data object. - `propertyName` {string} - The property name where the data is located. Default is null. - `parent` {[DataContext](#datacontext)} - The parent data context. Default is null. **Returns:** - {[DataContext](#datacontext)} - The data context. **Static Properties:** - `ignoreMetadata` {boolean} - Global flag to ignore metadata and comments. Default is false. - `createDataContext` {(data: any, propertyName?: string, parent?: [DataContext](#datacontext)) => [DataContext](#datacontext)} - Create a data context from the data. - `parse` {(str: string, reviver?: [Reviver](#reviver)) => [DataContext](#datacontext)} - Parse JSON string to the data context. - `stringify` {(data: any, replacer?: Replacer, space?: number) => string} - Stringify the data to JSON string. <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ### *DataContext* The data context Proxy object. **Type:** [`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) **Properties:** - `_isDataContext` {boolean} - The flag that indicates that the object is a data context. Default is true. - `_isModified` {boolean} - The flag that indicates that the object is modified. Default is false. - `_modified` {Array<[PropertyName](#propertyname)>} - The array of modified properties. - `_propertyName` {string} - The property name where the data is located. Default is null. - `_parent` {[DataContext](#datacontext)} - The parent data context. Default is null. - `_events` {Map<string, [EventListener](#eventlistenerevent)>} - The map of event listeners. - `isChanged` {boolean} - 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](#propertyname), listener: [EventListener](#eventlistenerevent)) => void} - Add an event listener that will be called only once. - `on` {(propertyName: [PropertyName](#propertyname), listener: [EventListener](#eventlistenerevent)) => void} - Add an event listener. - `emit` {(eventName: string, event: [EventObject](#eventobject)) => void} - Emit an event. - `emitToParent` {(eventName: string, event: [EventObject](#eventobject)) => void} - Emit an event to the parent. - `toString` {() => string} - Returns the string representation of the data context. - `overwritingData` {(text: string, reviver?: [Reviver](#reviver) ) => void} - Overwrite the data. - `stringifyChanges` {(reviver?: [Reviver](#reviver), space?: number|string, onlyModified?: boolean, setUnmodified?: boolean) => string} - Stringify the changes. <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ### *EventListener(event)* The event listener function. **Type:** `function` **Parameters:** - `event` {EventObject} - The event object. **Returns:** - {boolean} - The flag that indicates that the listener is alive. <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ### *EventObject* The event object. **Type:** `object` **Properties:** - `eventName` {string} - The event name. 'new' | 'set' | 'delete' | 'reposition' | '-change' - 'new' It happens when a new property is added to the data context. - 'set' It happens when an existing property is updated. - 'delete' It happens when a property is removed from the data context. - 'reposition' It happens when the position of an item in an array changes. - 'change' It happens when any change occurs in the data context. - `target` {DataContext} - The target data context. - `propertyPath` {Array<PropertyName>} - The property path in array format. - `parent` {DataContext} - The parent data context. - `oldValue` {any} - The old value. - `newValue` {any} - The new value. <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ### *PropertyName* The property name. **Type:** `string` <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ### *Reviver* The reviver function. **Type:** [`reviver`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#reviver) or [`createDataContext`](#createdatacontextdata-propertyname-parent) or `null` <p align="right"><a href="#-api-reference">Back to API Reference ↑</a></p> <p align="right"><a href="#data-context">Back to top ↑</a></p> --- ## πŸ“œ License This project is licensed under the MIT License.<br> Copyright &copy; Manuel LΓ΅hmus <p align="right"><a href="#data-context">Back to top ↑</a></p> ---