Javascript
Introduction
External Links
- The size of the World Wide Web (The Internet)
@[https://www.worldwidewebsize.com/]
- HTML Spec:
@[https://html.spec.whatwg.org/]
- State Of JS:
@[https://stateofjs.com/]
Check out our results to find out which libraries developers want to learn
next, which have the best satisfaction ratings, and much more.
-@[https://developer.mozilla.org/en-US/]
-@[https://eloquentjavascript.net/]
-@[https://www.infoq.com/articles/javascript-web-development-trends]
how well does your browser support HTML5?
@[http://html5test.com/]
- webplatform. Your Web, Documented.
-@[https://webplatform.github.io/]
The latest information on how to use the technology
that runs the web — HTML, CSS, JavaScript and more. WebPlatform.org is a
work in progress. We could use your help in making it better. Join us.
- Great blog about CSS design and HTML5
@[http://www.smashingmagazine.com/]
@[https://jvns.ca/blog/2018/11/01/tailwind--write-css-without-the-css/]
- Browser Built-in objects:
@[http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects]
- @[https://css-tricks.com/learning-to-learn/]
JS 101
•BºIMMUTABLE JS CODEº [qa]
• const doesn't make an object inmutable.
It just avoid re-referencing or adding atributes. Ej:
const a = [1,2,3]
a[0] = 3 ; // Mutable
• Object.freeze(objInstance)
•ºInmutable JSº
@[https://facebook.github.io/immutable-js/] [TODO]
•BºArrays/lists (Array.prototype)º [data_structure]
const list01 = [1,2,3,4,1]
const list02 = list01.map(n =˃ n * 2); [functional_code]
list01.length // 5
1 in l // true
list01.indexOf(1) // ← 0 (-1 if not found)
list01.lastIndexOf(1) // ← 4
list01.join() // ← "1,2,3,4,1" string
list01.pop(2/*index*/)// ← returns 3. list01 after:[1,2,4,1]
list01.pop() // ← returns 1. list01 after:[1,2,4]
list01.push(5) // ← add to current a.list01 after:[1,2,4,5]
list03 =
list01.concat(5) // ← add object to new array [immutable_code]
do NOT modify original one
list01.reverse() // l = [5,4,2,1]
list01. shift() // ← 5 (1st element) l = [4,2,1]
list01.unshift(5) // ← 4 (new length) l = [5,4,2,1]
list01.slice(start[,end]) // ← return new "slice" of original array
// (Works also for strings)
list01.sort() // l = [1,2,4,5]
//ºA sort function can be providedº
list01.splice() // Adds/removes elements from array.
splice accepts two lists of elements: the
elements to remove and the ones to be added.
list01.toString()
list01.valueOf() // TODO: Returns the primitive value of the array.
•BºWalking over arraysº
const list01 = [1,2,3,...]
for (var idx = 0; idx ˂ list01.length; idx++) {
const elementI = list01[idx]
...
}
for (const elementI of list01) { ... } // Doesn't look to work properly
list01.forEach((elementI, idx) =˃ console.log(elementI))
•BºMerging arrays. Remove duplicatesº
const mergeList = (a, b) =˃ [...new Set([...a, ...b]) ]
•BºLet vs Varº
let var1 = ...; // ← Preferred. Variable scope limited to block/statement/expression
var var2 = ...; // ← Discouraged. Variable global to function (even if defined inside block)
• BºDATE AND TIMEº
@[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date]
const date1 = new Date('December 17, 1995 03:24:00') ← ºGMTº
const date2 = new Date('1995-12-17T03:24:00') ← ºGMTº
console.log(date1 === date2) ← false
console.log(date1 - date2) ← 0
const date3 = Date.now()
BºStringsº
• String object are immutable.
• methods do NOT alter the original String, but return new ones.
s = "1234"
s.length // 4
s.charAt(1) // 2
s.charCodeAt(index) // 49 (Unicode Code for "1")
String.concat(s1,..)// Joins Strings, returns copy of result.
s.indexOf(2) // 2 (1st occurrence) , -1 if doesn't exists
s.lastIndexOf(2) // 2 "
s.match(/^1/) // ["1"] (returns matching array)
s.replace(/^./,"A") // "A234"
s.search(/2.4/) // 1 position of 1st match.
slice(start[, end]) // New string from original string
(similar to to Array.slice,...)
split(token) Splits String into string[], using the
specified token as the delimiter. TODO
substr (startPos, len) : Extracts subString
subString(startPos, endPos): Extracts subString
toLowerCase(): Returns new String
toUpperCase(): Returns new String
trim() : Returns String without any whitespace at either end.
valueOf(): Returns the primitive value of the String.
String.fromCharCode() // Converts Unicode → characters.
cost t = `a: ${a}` ← ECMAScript 6.0+
console.log(t) ºTemplate string interpolationº.
• ES6 in depth [TODO]
@[https://hacks.mozilla.org/category/es6-in-depth/]
• es6 design Patterns [TODO]
@[https://github.com/loredanacirstea/js-design-patterns] !!!
@[http://loredanacirstea.github.io/es6-design-patterns/]
JS "OOP"
function Message (subject, recipient, content){
this.subject = subject;
this.recipient = recipient;
this.content = content;
}
Now that we have created a Message function we define its ".prototype" to complete the "Class":
Message.prototype = {
constructor: Message, ← !!
sendMessage: function(){
console.log('Sending message to ' + this.recipient);
},
show : function(){
console.log('To:' + this.recipient + 'Subject: ' + this.subject + 'Message:' + this.content);
}
};
The complete definition of the Message object is now in one place, and the same methods are available to all
instances of Message we create. Each message created can have its own instance properties that are passed through to
the constructor.
var workMessage = new Message('Work complete'', 'boss@mycorp.com', 'My work is done here');
var socialMessage = new Message('Time to go out', 'friend@gmail.com', 'Finished work now.');
workMessage.send();
socialMessage.send();
________________________
var F = function() {};// This is a function object.
var p = F.prototype; // prototype object associated.
var c = p.constructor; // function associated with pro.
c === F
true: F.prototype.constructor==F for any function
________________________
Method overide:
Dog.prototype.talk = function(){
console.log('The dog says');
Animal.prototype.talk.call(this);
}
When calling the parent object method earlier, we needed to know that the
parent was Animal.prototype. We can make the code more elegant by keeping a
record of what the parent object is.
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
//define the parent property
Dog.prototype.parent = Animal.prototype;
Dog.prototype.talk = function(){
console.log('The dog says'); //use the parent property to invoke the method
this.parent.talk.call(this);
}
__________________
Namespaces:
var com = com || {};
com.apress = com.apress || {};
com.apress.chapterone = com.apress.chapterone || {};
com.apress.chapterone.Message = function Message(...) {...}
The usage of a Message object will now need to respect the namespace as follows:
var myMessage = new com.apress.chapterone.Message(...);
WebPack Summary
• Depends on Node.JS 10.13.0+ (2021-11)
$º$ npm init -y º ← Init new NodeJS proyect (package.json)
$º$ npm --development webpack@v... º ← add webpack dev.dependency
$º$ npm --development webpack-dev-server@v....º ← web server enabling page reload on changes.
$º$ npm css-loader style-loader --save-dev º ← css bundler.
$º$ npm --save-dev typescript ts-loader º ← Typescript support. (requires extra tsconfig.json)
┌ Bº webpack.config.js º──────────────────┐ ← NOTE: JS, not JSON file
│ 'use strict' │
│ const debug = process.env.NODE_ENV !== │
│ "production" │
│ var ExtractText = │
│ require('extract-text-webpack-plugin') │
│ const path = require('path'), │
│ webpack = require('webpack') │
│ module.exports = { │
│ mode: 'production' ←···················· := development, ºproductionº, none
│ context: __dirname, │
│ devtool: debug ? 'inline-sourcemap' : │
│ devtool: debug : null │
│ │
│ entry: { ←···························· Where to start when resolving dependencies.
│ myApp : './src/app.js', ←·········· App we are working on (frequent changes) (other examples index.ts , ...)
│ vendor: './src/vendor.js', ←·········· (jQuery, angular, images,..) modules NOT CHANGING over re-builds, with
│ │ Oºoutput cached after 1st build.º
│ entry3: { ←·········· Example entry showing all options allowed.
│ dependOn: [...] ←··················· Must be loaded before this.
│ filename: ... ←··················· Specifies name of output file. e.g.:
│ ···················'[name].[contentHash].bundle.js'
│ import: ... ←···················ºruntimeºmods loaded upon startup
│ ··················· (afte build and packaging)
│ library: ... ←··················· Lib.options to bundle a lib. More info at:
│ │@[https://webpack.js.org/configuration/output/#outputlibrary]
│ runtime: ... ←··················· If set, a new runtime chunk will be
│ │ created. (imcompatible with dependOn)
│ publicPath: ... ←··················· Public external URL address for output files
│ } │
│ } │
│ output: { │
│ path : "./" , filename:"bundle.js" ←··· must match ˂script src="bundle.js" /˃ @ index.html
│ }, │ Or filename: '[name].build.js' where name will be replaced in multi-
│ │ output packages by myApp, vendor, entry3, ....
│ │
│ module: { ←························ Extension point. Only JS and JSON input by def.
│ rules: [ ←························ºOrdered listºof rules for loaders
│ { │
│ test: /\.txt$/ ←·············· Apply to files matching regex
│ ,use : 'raw-loader' ←··············ºLoader (transformer)ºto use for
│ }, │ matching file
│ │
│ { test: /\.css$/, ←·············· embed output css in bundle.js (discouraged)
│ loader: 'style!css' │ alternatively, to output css to styles.css:
│ │ loader: ExtractText.extract('style', 'css')
│ } │
│ { test: /\.tsx?$/, ←············· Add rules for type-script for .ts and .tsx files
│ use: 'ts-loader', │ (or 'babel-loader', ...)
│ exclude: /node_modules/, │
│ } │
│ ] │
│ }, │
│ │
│ resolve: { ←·············· type-script support.
│ extensions: ['.tsx', '.ts', '.js'], │
│ }, │
│ │
│ plugins: debug ? [] : [ ←············· ºPluginsº allow wider range of tasks than loaders,
│ │ (e.g.: bundle optimization, ENV.VARs injection,...)
│ new HtmlWebpackPlugin( │
│ { template: │
│ '.../tpl.html' } │┌ inspectpack de─duplicate plugin: *1
│ ) ││ STEP 1) meta─level tips on prioritization and focus:
│ , ││ - Look first to identical code sources.
│ new webpack.optimize.DedupePlugin(),←┘ - Change dependencies in root package.json:
│ new webpack.optimize │ (protect yourself by commiting package.json/package-json.lock first)
│ .OccurenceOrderPlugin(), │ - Critically examine and scrutinize your dependencies:
│ new webpack.optimize │ Do you really need all of the bundled packages?
│ .UglifyJsPlugin({ │
│ mangle: false,sourcemap: false │ STEP 2) potentially force packages and code sources to collapse
│ }) │ to single entities, even if the normal rules of npm/yarn/webpack
│ ], │ would prevent it.
│ , new XXX...Plugin(...) │ -ºSet resolve.alias in webpack.config.jsº
│ ], │ Direct webpack to use a single package when resolving dependencies
│ } │ that would follow normal (multi-package) resolution.
└─────────────────────────────────────────┘ -ºSet the resolutions field with yarn:º Direct yarn (not available
on npm) to resolve packages to a single version, overriding what is
normally considered an allowed resolution and node_modules
installation. This work better with type-script modules than
js modules, due to natural tendency of JS to break even for minor
version upgrades. TIP: Don't use JS for module/lib development.
More info at:
@[https://formidable.com/blog/2018/finding-webpack-duplicates-with-inspectpack-plugin/]
Bº############################º
Bº# USSAGE (once configured) #º
Bº############################º
$º$ webpack ... \ º ← Alt use. package app
$º --watch º ← monitor changes and rebuild automatically
$º --progress --colors \ º http://localhost:8080/webpack-dev-server/
$º -p º ← production optimizations (slower during dev)
$º --display-modules º ← show intermediate modules using during packing.
Use --config ..../webpack.config.js to overload default path
$º$ webpack-dev-server \ º ← Alt 2. web server that autoreload web page on changes!!!.
$º --hot \ º OºHot Module Replacement (HMR) "magically" keeps the internal state º
$º --inline º Oºof the Web App (and potentially the UI if tied to the state) º
W.pack vs ...
• Package bundler "==" assets bundle + runtime module "loader"
REF: https://x-team.com/blog/rollup-webpack-parcel-comparison/
•ºWebpackº: Most user/project base
No config needed (since Webpack v4), but still very customizable.
"Non official standard" for packaging apps, adopted by most projects.
•ºRollupº: Best option and similar to webpack
out-of-the-box features likes source maps,
not config-dependencies on .babelrc
•ºParcelº: zero-conf option.
• Gulp, Grunt, and Browserify: Old options, still in use in some projects.
• SPEED COMPARATIVE: (Time in seconds)
DEV BUILD | PRO BUILD
─────────────────────────┼─────────────────────────
1st run 2nd run │ 1st run 2nd run
───────── ───────── │ ───────── ─────────
RºParcel 14.92 5.22 º │ 738.50 35.36 º
BºRollup 0.57 0.48 º │ 0.71 0.66 º
Webpack 3.60 3.32 │ 3.63 3.80
PRE-SETUP:
1) Adding Babel (ES6 to ES7) to package.json (using presets, polyfills and shims).
"babel-loader": "=7.1.4",
"babel-plugin-external-helpers": "=6.22.0",
"babel-preset-env": "=1.6.1",
"babel-preset-react": "=6.24.1",
"babel-preset-stage-0": "=6.24.1"
1) add $prj_root/.babelrc:
{ "presets": ["env", "react", "stage-0"] }
Bº##################º
Bº# PARCEL SUMMARY #º
Bº##################º
• PRE-SETUP) Add next dependency to package.json:
$º$ npm install --save-dev parcel-bundler:1.7.1º ← local project install
$º$ npm install -g --save-prod parcel-bundler:1.7.1º ← global install
• modify 'package.json' like:
- "main": "index.js"
+ "source": "src/index.html".
• main diff with webpack:
parcel sets index.js file into │ webpack sets index.js file into
script.scr @ index.html │ compiled bundle
˂script │ ˂script src="built/vendor.min.js"˃˂/script˃
src="../reactAppSrc/index.js"˃ │ ˂script src="built/built.min.js"˃˂/script˃
˂/script˃
• Ussage: BºNo human (zero-conf) configuration neededº
$º$ parcel º ← Start web development server
$º$ parcel index.html º ← transpile dependencies
$º$ parcel build public/parcel.index.htmlº ← production builds
1) builds dependency tree + script
2) transpile, minify, and bundle
(built cached. very Fast re-builds)
Optionally add next flags to tune build:
-d dist/
--public-url '.'
•ºParcel 2, rewritten in Rust, claims to have a 10x Faster Javascript Compiler,º
modular configuration (while zero-confs remains possible), differential bundling
and improved JSX and web worker support.
@[https://www.infoq.com/news/2021/09/parcel-2-rust-faster-javascript/]
Bº##################º
Bº# ROLLUP SUMMARY #º
Bº##################º
• PRE-SETUP: Add next dependencis to package.json:
"rollup": "=0.58.2", [TODO]: Update (2019) version
"rollup-plugin-babel": "=3.0.4",
"rollup-plugin-uglify": "=3.0.0"
• PROJECT CONFIG:
DEVELOPMENT PRODUCTION
$º$ rollup \ º $º$ rollup \ º
$º -c rollup/rollup.dev.config.js \ º $º -c rollup/rollup.prod.config.js \ º
$º --external all º $º --external all º
import babel from 'rollup-plugin-babel' import babel from 'rollup-plugin-babel';
import uglify from 'rollup-plugin-uglify'
export default { export default {
entry: 'reactAppSrc/rollup.index.js', entry: 'reactAppSrc/rollup.index.js',
dest: 'public/built/main.min.js', dest: 'public/built/main.min.js',
format: 'iife', format: 'iife',
sourceMap: 'inline',
plugins: [ plugins: [
babel({ babel({
babelrc: false, babelrc: false,
exclude: 'node_modules/**', exclude: 'node_modules/**',
presets: [ presets: [
"react", "react",
[ "es2015", { "modules": false } ] [ "es2015", { "modules": false } ]
], ],
"plugins": [ "external-helpers" ] "plugins": [ "external-helpers" ]
}) }),
uglify()
], ],
} }
Bº#################º
Bº# Vite SUMMARY #º
Bº#################º
• BºModern alterantive to WebPack, Rollup and Parcel,ºdesigned with ES modules
in mind from the scratch, completely removing the need for "old bundling".
• Vite divides modules into:
·ºDependencies:ºwith little/no-change during development, shipped in various
module formats (e.g. ESM or CommonJS).
·ºSource code:ºoften non-plain standard (JSX/Vue/Svelte components) that needs
transforming, and edited very often.
• Depends onºesbuildºfor fast packaging:
@[https://www.infoq.com/news/2020/06/esbuild-faster-go-js-bundler/]
•ºwritten in go and multi-coreº
• seeks to bring order-of-magnitude speed improvements in
JS bundling+minification. (10x-100x)
Bº0.37 secs vs 63 secs pf webpack 5!!!!º
Major features:
✓ Extreme speed without caching ✓ TypeScript and JSX syntax
✓ ES6 and CommonJS modules ✓ Source maps
✓ Tree shaking of ES6 modules ✓ Minification
✓ An API for JavaScript and Go ✓ Plugins
WebPack What's new
@[https://www.infoq.com/news/2020/10/webpack-5/]
• Webpack 5.0:
rethinks some core architectural decisions focusing on:
✓ Improved performance (persistent and long-term) caching
✓ Improved algorithm defaults
✓ Improved bundle sizes (better Tree Shaking and Code generation)
✓ Improved compatibility with web platforms
✓ clean up internal structures inRº"weird state" while implementing features in v4º
✓ Preparing for LTS (no breaking changes after 5)
• two key changes have caught the attention of JS community:
· Module Federation
· change in default behavior for the Node.js API polyfills.
Async Programming
Promise 101
Bº#######################
Bº# Waiting N secs. #
Bº#######################
await new Promise(callbackFun =˃ setTimeout(callbackFun, 20000));
Bº#######################
Bº# callback to Promise #
Bº#######################
function setPromiseTimeout() { ← Returns promise:
return new Promise( modern JS can invoke it with await
function(callbackOK, invoking async code with sync syntax
callbackError) {
setTimeout(callbackOK, 500)
});
}
async function start() {
for (idx=0; idx˂3; idx++) {
await setPromiseTimeout() ← call sequentially async funct. inside loop
console.log(idx) very-difficult/impossible with Callbacks
}
}
start();
RºWARN!!!º: Promise can not throw since that would force
client code calling the promise to use both
try-catch and ".catch(...).
async/await explained
@[https://2ality.com/2016/10/asynchronous-iteration.html]
by Domenic Denicola and Kevin Smith.
• async/await serve for "Asynchronous Iteration"
• ECMAScript 6 ECMAScript 2018
✓ support for sync ✓ support for async
iteration over data iteration over incomming data
(in RAM memory) (from remote HTTP/AJAX/... response)
const iterable const asyncIterable
= ['1', '2']; = createAsyncIterable(['1', '2']);
const iterator = const asyncIterator =
iterable[Symbol.iterator](); asyncIterable[Symbol.asyncIterator]();
iterator.next() asyncIterator.next()
└──────┬──────┘ .then(iterResult1 =˃ {
return {value:..., done:...} /* ↖ { value: '1', ... } */
^{ value: '1', done: false } return asyncIterator.next();
})
iterator.next() asyncIterator.next()
└──────┬──────┘ .then(iterResult1 =˃ {
^{ value: '2', done: false } /* ↖ { value: '2', ... } */
return asyncIterator.next();
})
iterator.next() asyncIterator.next()
└──────┬──────┘ .then(iterResult1 =˃ {
{ value: undefined,ºdone: trueº} /* ↖ { ...,ºdone: trueº } */
return asyncIterator.next();
^ next() doesn't work async })
└─────────────┬──────────────────┘
┌─────────────┴──────────────────┐
Within async functions syntax
for managing Promise results
can be simplified as:
async function f() {
const asyncIterable
= createAsyncIterable(['1', '2']);
const asyncIterator =
asyncIterable[Symbol.asyncIterator]();
console.log(await asyncIterator.next());
// { value: '1', done: false }
console.log(await asyncIterator.next());
// { value: '2', done: false }
console.log(await asyncIterator.next());
// { value: undefined,ºdone: trueº}
}
Bº#################################º
Bº# ECMAScript 2018 for-await-of #º
Bº#################################º
• Syntax converts each iterated value via
Promise.resolve() to a Promise, which it then awaits.
• Can be used with sync/async iterables. e.g:
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) { ← long explicit syntax
// for await (const x of ['a', 'b'] ) { ← short syntax
console.log(x);
}
}
• for-await-of and rejections
Similarly to how await works in async functions, the loop throws an exception if next() returns a rejection:
MOCKED asyncIterator Mocked code capturing
returning an exception Promise error
============================== ======================
function createIterableMock() { Qº(ºasync function () { // (A)
returnº{º ºtry {º
[Symbol.asyncIterator]() { for await (const x of
return this; createIterableMock()) {
}, console.log(x);
next() { }
return Promise.reject( ··········→ º} catch (e) {º
new Error('Problem!')) console.error(e);// ← Error: Problem!
},// ^ º}º
º}º // async code can not throw }Qº)º()
} // exceptions. Promise.reject Qº^º: wrap code: async doesn't work
// used as replacement. at script|module top level
Bº####################º
Bº# Async generators #º
Bº####################º
async function* // ← '*' turns fun. into (a)sync generator
createAsyncIterable(syncIterable) { // ← Converts
for (const elem of syncIterable) { // ← sync iterable to
yield elem; // ← async " . next(...) will
} return Promise for {value:,done:}
}
Calling the generator does NOT return a value but a generator object
that can be invoqued with .next(...). Then .next(...) returns
{value:...,done:}
In Each invocation genObj.next() returns a Promise for
an object {value,done} that wraps a yielded value.
In the async version JS automatically will queue invocations to next()
(think of next(...) are async request to a remote serve). JS will
call the generator once it becomes ready. There is no need to wait
for the Promise before invoquing next(..) again (in the same way that
there is no need to wait for a server TCP-ACK to send another TCP-connection).
In many/most occasion code needs to wait for the Promise and the Promise
resolution, and then the for-wait-of takes care of it transparently.
Still Promise.all() can be use to "send" next in parallel, then wait
for "all" completion like:
const [{value:v1},{value:v2}] = await
Promise.all( [ asyncGenObj.next(), asyncGenObj.next(),..] );
(See original source @[https://2ality.com/2016/10/asynchronous-iteration.html]
for more details and complex examples)
Bºcaolan/asyncº [qa][TODO]
@[https://github.com/caolan/async]
@[http://caolan.github.io/async/v3/]
• Async provides around 70 functions that include the usual
'functional' suspects (map, reduce, filter, each…) as well as some
common patterns for asynchronous control flow (parallel, series,
waterfall…).
• It works on browsers and (NodeJS) server code.
Concurrency
Service Workers
• Not to be confuse with web workers: [comparative]
·ºService-Workersº handle net.requests/cache/push notifications,...
·ºWeb-Workerº: Mimics multithreading, allowing intensive scripts
to be run in the background.
• See also Service Workers in Angular #[angular_service_worker]
• JS module providing web apps reliability and performance on par
with natively-installed code.
• Service workers function as a network proxy managing cache.
• intercept outgoing HTTP requests (data, HTML, even index.html),
and choose how to respond to them:
• If properly designed, it can completely satisfy the app load
without the need for the network.
• S.Workersºdo NOT rely on server-specified caching headersº.
• preserved after closing the tab/window.
• When a browser loads the web page/app, service workers loads first.
•@[https://serviceworke.rs/]
Service Worker Cookbook:collection of working, practical examples [qa]
of using service workers in modern web apps.
- Network or cache: try fetch from network, fallback to cache.
- Cache only:
- Cache and update: fetch from cache, update cache then.
- Cache, update and refresh UI.
- server embedded content on missing resources.
- Push notification, Retrieve Payload on received notification
- Push notification with Payload (string, ArrayBuffer, Blob, JSON).
- Push Rich, defining notification language, vibration pattern, image
- Push Simple: (even when page is not open).
- Push Tag: (replace old notifications).
- Push Quota (experiment with browser limits)
- Push control-Clients: focus re-open tab
- Push Subscription:
- Immediate Claim: take control of page without waiting for avigation event
- Cache from ZIP recipe: cache contents from zipfile
- Message Relay, Fetch Remote Resources, Live Flowchart,
Offline Fallback, Offline Status, JSON Cache, Local Download,
Virtual Server, API Analytics, Load balancer, Dependency Injection,
Request Deferrer, Cache then Network, Render Store
• @[https://www.infoq.com/news/2019/08/postMessage-performance-study/]
Surma, Web Advocate at Google, recently published a study on the
performance of postMessage, the method used by web workers to
communicate. Surma concludes that while postMessage comes with some
overhead, provided the payload is below a given budget, moving non-UI
tasks off the main thread may result in increased overall
user-perceived performance.
BºSUMMARYº [TODO]
• probably the most powerful API added to the web platform recently.
• allows to do anything with each request/response received by a browser.
(cancel requests, re-route, modify/cache responses, ...)
BºWorkbox libraryº [low_code]
@[https://developers.google.com/web/tools/workbox/]
• rapidly integrate common Service Worker functionality like
precaching, runtime caching, request routing, background sync, and more.
• Fully async, compatible with XHR and localStorage.
• External resources:
• @[https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API] [TODO]
• @[https://developers.google.com/web/fundamentals/primers/service-workers/]TODO]
Web Workers
• See also #[service_workers]
Debugging
Bº#################º
Bº# DEBUGGING 101 #º
Bº#################º
• Play with ajax requests (Firefox only?)º
→ Go to inspector → network tab → RightClick on request
→ choose Edit and Resend: Now you can change anything
you want: header, params, ... and resend.
• "debugger" keyword: pause in code just when console is open
• dump object to string: ºJSON.stringify(myJS_object)º
To avoid cicular dependencies problems redefine stringify like:
REF: @[https://stackoverflow.com/questions/11616630/how-can-i-print-a-circular-structure-in-a-json-like-format]
JSON.stringifySec = function (circ) {
cache = []
result = JSON.stringify(circ, function(key, value) {
if (typeof value === 'object' ⅋⅋ value !== null) {
if (cache.indexOf(value) !== -1) {
return // Duplicate reference found, discard key
}
cache.push(value) // Store value in our collection
}
return value
})
cache = null
return result
}
var circ = {};
circ.circ = circ;
JSON.stringifySec (circ) // ← Ussage
• Print the calling function in the called function:
var f1 = function(...){
console.warn( "Called from:" +
ºarguments.callee.caller.toString()º);
...
}
• Quickly access elements in the console:
$('css-selector’) ← return first match of CSS selector
$$(‘css-selector’) ← return all matches
Bº########################º
Bº# debugging async code #º
Bº########################º
OºNodeJS Dump async function chain ("async stack-trace")º [async]
@[https://stackoverflow.com/questions/40300996/nodejs-is-is-possible-to-show-the-stack-trace-of-a-calling-async-function]
@[https://gist.github.com/lll000111/3d6a53da4d53beb909189793c7631ba6]
• Context: fnThrowingError is call asynchronously
const RºfnThrowingErrorº= async () =˃ {
await ...
ºthrow new Errorº( ← Some deeply nested (in the actual call hierarchy)
'Houston we have a problem!'); asynchronous function that throws an error.
}
• Alternative 1: Node.js 12+. (2019-08-04+)
Async Stack Traces come out of the box using flag '--async-stack-traces'
@[https://medium.com/@nodejs/introducing-node-js-12-76c41a1b3f3f]
• Alternative 2:
const fn2 = async () =˃ {
try {
return awaitRºfnThrowingErrorº()
} catch(err) { // ← STEP 1: try-catch all errors.
(DON'T return promise directly)
err.stack += // ← STEP 2: Add a locally generated stack trace
º(new Error()).stackº to the one already existing on
throw err the error object
}
...
return result
}
• Alternative 3: (Syntax alternative)
const result =RºfnThrowingErrorº()
º.catchº(err =˃ { // ← STEP 1: replace try-catch → promiseº.catch(..)º
main advantage: allows assignment to "const"
err.stack += // ← STEP 2: Same as Alt. 2
º(new Error()).stackº
throw err
}
)
BºDumping "async" stack-trace fn0 → fn1 (async call) → fn2: [async]
constºfn1º= async () =˃ { ← STACK "LEVEL 1"
try {
return awaitºfn2º() ← RºWARNº:if something more complex than just
retuning is executed then err can be different
to the fn1 async err and the async stack trace
will be "false"
} catch(err) {
err.stack +=
(new Error())º.stackº ← Add locally generated stack trace to existing one
ºthrow errº
}
}
constºfn0º= async () =˃ { ← STACK "LEVEL 0"
try {
return await fn1()
} catch(err) {
err.stack +=
(new Error())º.stackº ← Add locally generated stack trace to existing one
throw err
}
};
fn0().catch(console.error) ←ºDump stack here !!!º
• TODO:
https://stackoverflow.com/questions/7697038/more-than-10-lines-in-a-node-js-stack-error
https://github.com/mattinsler/longjohn
Bº###################º
Bº# advanced tricks #º
Bº###################º
• REF:@[https://raygun.io/blog/2015/06/useful-javascript-debugging-tips-you-didnt-know/]
console.table(animals); ← Display object as tables in the console: (Chrome only?)
this.funcZ = function(){
console.trace('trace car') ← Dump stack trace for function funcZ
}
debug(car.functY) ← Quick find a function to debug: (Chrome only?)
(not the same that console.debug)
monitor(funct1) ← Every time the function is called values pased in will be logged
debug(car.functY) ← Quick find a function to debug.
(not to be confused with console.debug)
• Ignore (black list) Script while debugging
CHROME: FIREFOX:
· Alt 1. inspector → source → Right click Inspector → Debugger → Mark scriptto black list →
the file to black list and choose black box script. Click on the eye in the very bottom left corner.
· Alt 2. Open up settings view → Click https://hacks.mozilla.org/2013/08/
"manage framework black boxing" → write a regexp in new-features-of-firefox-developer-tools-episode-25/
popup to blackbox, for example, the entire vendor folder.
https://developer.chrome.com/devtools/docs/blackboxing
• SOURCE MAPPING
REF: @[http://blog.getsentry.com/2015/10/29/debuggable-javascript-with-source-maps.html]
• source maps : JS's version of debug symbols:
• Include with:
//# sourceMappingURL=/path/to/app.min.js.map
• Generated by modern JS transpilers
• Ex source map:
source map example:
{
version : 3, // version of source map spec (should be 3)
file: "app.min.js", // generated filename associated to
sourceRoot : "", // all sources are relative to it (opt)
sources: ["foo.js", "bar.js"], // URLs to original files
names: ["src", "maps", "are"], // variable/method names found in your code
mappings: "AAgBC,SAAQ,CAAEA" // base64Enc VLQ values source code mappings
}
Fiddler Proxy
@[www.telerik.com/fiddler] [tool]
• Allows to replace remote html/css/AJAX responses with local/mocked ones.[troubleshooting]
Very useful when deploying to remote server can be "slow" due
to CI constrains.
• Allows to see the "total page weight" HTTP caching and compression
at a glance
• Tutorial: http://www.youtube.com/watch?v=TP_5JYatlNQ [Video]
Prog.WApps Summary
@[https://developer.mozilla.org/en-US/Apps]
• apps built using standard Web technologies, working in any modern Web browser.
• Compared to standard web apps/sites:
· Apps are installed by a user, they are self-contained and don't always
require the chrome of a browser window.
· can be built to run offline.
• Example of Progressive web apps include Gmail, Twitter or Etherpad.
• Integrate within desktop and mobiles OSes, by allowing to access them through
"start" or desktop icons and run in background pooling/pushing data from/to
remote servers while the browser is "closed".
Bº######################º
Bº# "Hello World!" PWA #º
Bº######################º
SUMMARY FROM
@[https://medium.com/james-johnson/a-simple-progressive-web-app-tutorial-f9708e5f2605]
by James Johnson
• PWA layout:
Hello-PWA/
├─ index.html ← ✓ Hard code initial content to avoid empty-page when offline
│ or using old / non-PWA compliant browsers.
│ ← ✓ PWA UI must display properly on mobile/desktop:
│ Add viewport meta tag to index.html like:
│ ˂meta name="viewport"
│ content="width=device-width, initial-scale=1.0"˃
│
├─ sw.js ← ✓ Service Worker running in background in charge of
│ non-user-interactive tasks (cache dynamic data, ...)
│
├─ manifest.json ← ✓ json file specifying look and behavior on
│ different devices. (orientation, theme color,...)
├─ css/
│ └─ style.css
│
├─ js/
│ └─ main.js ← ✓ Register service worker here.
│
│ favicon.ico ← 16x16 px
└─ images/
└─ ... ← ✓ At least image resolution of 128x128px, 144x144 px,
152x152 px, 192x192 px, 256x256 px and 512x512px needed
for compliance with Windows, Mac, iPhone and Android.
• ┌─ e.g. Hellow-PWA/index.html:───────────────────
│ ˂!doctype html˃
│ ˂html lang="en"˃
│ ˂head˃
│ ˂meta charset="utf-8"˃
│ ˂title˃Hello World˂/title˃
│ ˂link rel="stylesheet" href="css/style.css" ˃
│ ˂link rel="manifest" href="/manifest.json"˃
│ ˂meta name="viewport"
│ content=
│ "width=device-width, initial-scale=1.0"˃
│
│ ˂!-- app icon related stuff {{{ ˃
│ ˂link rel="icon" href="favicon.ico" type="image/x-icon" /˃
│ ˂meta name="theme-color" content="white"/˃
│ ˂link rel="apple-touch-icon" href="images/app-icon-152.png"˃
│ ˂meta name="msapplication-TileImage" content="images/app-icon-144.png"˃
│ ˂!-- }}} ˃
│
│ ˂!-- apple mobile custom config {{{ ˃
│ ˂meta name="apple-mobile-web-app-capable" content="yes"˃
│ ˂meta name="apple-mobile-web-app-status-bar-style" content="black"˃
│ ˂meta name="apple-mobile-web-app-title" content="Hello World"˃
│ ˂!-- }}} ˃
│
│ ˂body class="fullscreen"˃
│ ˂div class="container"˃
│ ˂h1 class="title"˃Hello World!˂/h1˃
│ ˂/div˃
│ ˂script src="js/main.js"˃˂/script˃ ← Load at the bottom of page.
│ ˂/body˃ Really needed?
│ ˂/html˃
└────────────────────────────────────────────────
• ┌─ e.g. Hellow-PWA/css/style.css:────────────────
│ body { font-family: sans-serif; }
│
│ html,
│ .fullscreen {
│ display: flex;
│ height: 100%; ┐ Make content area fill
│ width: 100%; ├ entire browser window
│ margin: 0; │
│ padding: 0; ┘
│ }
│
│ .container {
│ margin: auto; ┐ Center the content
│ text-align: center; ┘ in browser window
│ }
│
│ .title { font-size: 3rem; }
└────────────────────────────────────────────────
• TIP: Periodically audit tests over the app using some tool
like (OOSS) Google’s Lighthouse.
• ┌─ e.g. Hellow-PWA/sw.js: ────────────────
│ const cacheName = 'hello-pwa';
│ const filesToCache = [
│ '/' ,
│ '/index.html' ,
│ '/css/style.css',
│ '/js/main.js'
│ ];
│
│ self.addEventListener(
│ 'install', function(e) { ← PWA standard life-cycle callback
│ e.waitUntil( called automatically when installing
│ caches for the first time.
│ .open(cacheName)
│ .then(function(cache) {
│ return
│ cache.addAll(filesToCache)
│ })
│ );
│ });
│
│ self.addEventListener(
│ 'fetch', function(e) { ← bind "fetch" event to serve
│ e.respondWith( cached content when possible
│ caches
│ .match(e.request)
│ .then(function(response) {
│ return response ← Return cached content if available
│ || fetch(e.request); ← try online fetch.
│ }) WARN: demo code will fail if just
│ ); one of the file fetches fails.
│ }); retry strategies needed.
└────────────────────────────────────────────
• ┌─ e.g. Hellow-PWA/js/main.js ────────────────
│ window.onload = () =˃ {
│ 'use strict';
│ if (! 'serviceWorker' in navigator) {
│ alert("Browser not supported. PWA needed")
│ throw new Error("PWA not available")
│ }
│ navigator
│ .serviceWorker ← Register service worker
│ .register('./sw.js');
│ }
└─────────────────────────────────────────────
• ┌─ e.g. Hellow-PWA/manifest.json ─────────────
│ {
│ "name": "Hello World", ← Used at install time
│ "short_name": "Hello", ← Used in app icon
│ "lang": "en-US",
│ "start_url": "/index.html",
│ "display": "standalone", ← Treat as standalone without
│ "background_color": "white", browser chrome
│ "theme_color": "white" ← Must match
│ } ˂meta name="theme-color" content="white"/˃
└─────────────────────────────────────────────
Functional Testing
Mocha
@[https://mochajs.org/]
• Easy testing of async services / REST APIs / ...
better than Jasmine [comparative]
• JS based, runs in browser and NodeJS.
• tests run async service serially, allowing for
accurate reporting, mapping uncaught exceptions
to the correct test
• install:
$ npm install -g mocha
• Commonly used withº"Chai" library as assertion libraryº
(assert, expect, should), installed per project like:
$ npm install --save-dev chai
• Commonly used withº"Supertest" SuperAgent extension library asº
ºHTTP AJAX test library.º, installed per project like:
$ npm install --save-dev supertest
• Ussage summary:
$ mocha ← execute tests with default conf. (defaults + mocha.opts)
$ mocha directory_with_tests ← Run tests at location
$ mocha --grep {{^regex$}} ← Run tests matching pattern
$ mocha --watch ← Run tests once, the on JS file change
$ mocha --reporter $reporter ← Run tests with specific reporter
• Test how-to summary:
· mkdir test
· vim test/test01.js
iconst request = require('supertest');
const app = require('../server/app.js');
describe('GET /users', function() {
it('return list of users', function() {
return request(app)
.get('/users')
.expect(200)
.expect('Content-Type',/json/)
.expect('[{"name":"ej","age":26},{"name":"jh","age":28}]')
})
})
• See official doc for info about hooks (before, after, afterEach)
Retry Tests, Timeouts, Diffs, ...
• Official examples at:
@[https://github.com/mochajs/mocha-examples/tree/master/packages]
selenium, rxjs, react-webpack,programmatic-usage
Selenium (Web test Automation)
• External Resources:
·@[http://docs.seleniumhq.org/]
·@[http://docs.seleniumhq.org/projects/ide/]
·@[http://code.tutsplus.com/tutorials/headless-functional-testing-with-selenium-and-phantomjs--net-30545]
·@[http://stackoverflow.com/search?q=selenium]
• Automate browser execution applying test on expected elements,
URL, events ....
• Oldest and most widely adopted End-to-End automation framework.
• Can orchestrate tests in any major browser.
• Example test:
const {Builder, By, Key, until} = require('selenium-webdriver'); [code_snippet]
(async function example() {
let driver = await new
Builder().forBrowser('firefox').build();
try {
await driver.get('https://www.google.com'); // ← Navigate to Url
await driver
.findElement(By.name('q') // *1
).sendKeys('cheese', Key.ENTER); // ← Enter "cheese", then "Enter"
let firstResult = await driver.wait(
until.elementLocated(
By.css('h3')), // *1
10000);
console.log(await firstResult.getAttribute('textContent'));
} finally{
driver.quit();
}
})();
*1 The crux of Selenium is the element locator.
✓ id ✓ tag name
✓ name ✓ class name
✓ link text ✓ css
✓ partial link text ✓ xpath
• Selenium IDE: Firefox/Chrome extension to automatically record/save/replay tests
@[https://examples.javacodegeeks.com/enterprise-java/selenium/selenium-ide-commands-example/]
@[https://www.youtube.com/watch?v=ZG3VFDMaAlk]
•ºAlternatives to Seleniumºinclude:
✓ºCypressº: JS centric framework, probably more friendly to web developers. [TODO]
· No need for "wait" commands in test scripts.
Cypress waits automatically for commands and assertions.
· Tests can make use of Spies, Stubs, and Clocks to verify
and control the behavior of responses, functions and timers.
· Automatic scrolling ensures that elements are visible
before performing (testing) any action.
✓ºPlaywrightº: JS centric
· Focused on web-scraping (headless browser)
· Ex:
const playwright = require('playwright'); [code_snippet]
async function main() {
const browser = await
playwright.chromium.launch({ ← Use choromium
headless: false ← Disable headless mode (enable view)
});
const page = await browser.newPage();
await page.goto('https://.../testPage');
const scraped_data_list = await page.
$eval('#someID', headerElm =˃ {
const data_list = [];
const li_list = headerElm.getElementsByTagName('li');
li_list.forEach(domObject =˃ {
data_list.push(domObject.innerText.split('\n'));
});
return data_list;
});
console.log('scraped_data_list:', scraped_data_list);
await page.waitForTimeout(5000); ← wait for 5 seconds
await browser.close();
}
main();
Mock Service Worker MSW (REST/GraphQL Testing)
@[https://www.infoq.com/news/2020/11/msw-mocking-graphql-rest-api/]
• test UI components webapps without actual back-end, setting up mock server,
or stubbing native http/https/fetch implementations.
• test as much as possible in the same conditions and in the same way
as a real user would.
• User actions are exercised on an actual rendered component,
they may trigger real effects (e.g., remote data fetching, API calls)
that need to be mocked or stubbed.
• having tests passing against an altered version of a native fetch
browser method does not completely guarantee that the same tests
will pass against the unaltered version of fetch.
• MSW allows developers to mock Rest and GraphQL APIs without having to
modify the component code or stubbing native http/https/fetch implementations.
Testing
Google Lighthouse
@[https://developers.google.com/web/tools/lighthouse/]
• OOSS Automated tool, searching to improve quality of web pages.
• Can test any site (public or requiring authentication).
• Audits performance, accesibility, PWA, SEO, ...
• Can be roon on Chrome Devtools, command line, or Node module.
E.g. ussage extracted from
https://medium.com/james-johnson/a-simple-progressive-web-app-tutorial-f9708e5f2605
"""... Make sure "Progressive Web App" option is checked. You can
uncheck the others for now. Then click on the “run tests” button.
After a minute or two Lighthouse should give you a score and list of
audits that the app passed or failed. The app should score around 45
at this point. If everything was coded properly, you’ll notice that
most of the tests it passes are related to the requirements we
outlined at the beginning..."
StoryBook.js
https://storybook.js.org/ !!!!
robust UIs: sandbox to build UI components in isolation so you can
develop hard-to-reach states and edge cases.
for React, Angular, Vue, ...
Profiling
YSlow Profiler
• home page: @[http://yslow.org]
• YSlow analyzes web pages and why they're slow
based on Yahoo!'s rules for high performance web sites
• Feature highlights:
✓ Grades web page based on one of three predefined ruleset
or a user-defined ruleset
✓ It offers suggestions for improving the page's performance
✓ Summarizes the page's components
✓ Displays statistics about the page;
✓ Provides tools for performance analysis, including Smush.it™
and JSLint.
• Check for nextºbest-patterns/rulesº:
✓ Minimize HTTP Requests
✓ Use a Content Delivery Network
✓ Add an Expires or a Cache-Control Header
✓ Gzip Components
✓ Put StyleSheets at the Top
✓ Put Scripts at the Bottom
✓ Make JavaScript and CSS External
✓ Reduce DNS Lookups
✓ Minify JavaScript and CSS
✓ Remove Duplicate Scripts
✓ Configure ETags
✓ Make AJAX Cacheable
✓ Use GET for AJAX Requests
✓ Reduce the Number of DOM Elements
✓ Reduce Cookie Size
✓ Use Cookie-Free Domains for Components
✓ Make favicon.ico Small and Cacheable
❌ Avoid empty src or href
❌ Avoid CSS Expressions
❌ Avoid Redirects
❌ No 404s
❌ Avoid Filters
❌ Do Not Scale Images in HTML
Mozilla Perf.Tool
• REF: @[https://developer.mozilla.org/en-US/docs/Tools/Performance]
• Performance tool gives insights into a site's general responsiveness,
JS and layout performance.
• It allows to create a recording(profile), of a site over a period of time.
The tool then shows you an overview of the things the browser was doing
to render the page/s and a graph of the frame rate over the profile.
JS Alternatives
Typescript
• JS with types. @[https://www.typescriptlang.org/]
• Quick tutorial:
@[https://learnxinyminutes.com/docs/typescript/]
• Debugging TS in Firefox DevTools: [TODO]
@[https://hacks.mozilla.org/2019/09/debugging-typescript-in-firefox-devtools/]
• BºSyntax summaryº
@[https://learnxinyminutes.com/docs/typescript/]
let isDone: boolean = false ← 3 basic types
let lines : number = 42
let name : string = "Anders"
let var1 = false ← Type inference if type ommited
let name : any = jsObject ← any: Compatibility with non-typed JS
const CON1 = false ← Make constant/immutable (prefered) [qa][billion_dolar_mistake]
let typedArray01: number[] = [1, 2, 3]
let genericArray02: Array˂number˃ = [1, 2, 3]
enum Color { Red, Green, Blue }
let c: Color = Color.Green
BºFunctionsº
let f1 = function (i: number): number {
return i * i; └─┬──┘
} Return type (or void)
(It can also be inferred)
let f2 = (i: number): number =˃ ← Arrow syntax (used also in anonoymous
{ return i * i; } lamdas)
let f5 = (i: number) =˃ i * i; ← braceless means void/none return
BºInterfacesº
interface Person { ← Interface declaration
name: string;
age?: number; ← Optional type [qa][billion_dolar_mistake]
move(): void;
}
let p: Person = { ← Interface implementation
name: "Bobby",
move: () =˃ { }
};
interface SearchFn { ← Interface describing a function type
(source: string, Only param's types are important, names are not
subString: string): boolean;
}
let fnSearch: SearchFn = function ← Function implementing the interface
(src: string, sub: string) {
return src.search(sub) != -1
}
BºClassesº
class Point { ← class definition
x: number; ← Properties public by default
static origin = new Point(0, 0); ← Static member
readonly name: string; ← read-only, only allowed to be [qa]
writen in constructor.(Typescript 3.1+)
constructor(
_x: number,
private y: number = 0 ← syntax shortcut 'y' defined as class property as 'x'
_name : string ) {
this.x = _x;
this.name )
}
fn01(): number { ... }
}
class PointPerson
implements IPerson { ... } ← Class implementing interface
class Child
extends ParentClass { ... } ← Class inheriting from parent class .
RºWARN:º Discouraged. Better use composition
BºModulesº
module Geometry { ← Modules, "." can be used as separator for sub modules
export class Square { ... }
}
import G = Geometry; ← Local alias for referencing a module
let s1 = new Geometry.Square(5); ← Or just new G.Square(5);
BºGenericsº
class Tuple˂T1, T2˃ { | interface Pair˂T˃ { | function ˂T˃(p: Pair˂T˃) {
constructor( | item1: T; | return
public item1: T1, | item2: T; | new
public item2: T2) { ... } | } | Tuple(p.item1, p.item2);
... | | };
} | |
BºTemplate Stringsº
let greeting = `Hi ${name}, how are you?`
let multiline = `This is an example
of a multiline string`;
BºInmutable Collectionsº
const number_array: Array˂number˃ = [0, 1, 2, 3, 4]; ← number_array can not be reasigned to other
array (const), but current array can be modified
const inmutable_number_array: ReadonlyArray˂number˃ = numbers; ← inmutable_number_array can not be reasigned to other
array (const), and no modifications allowed either.
BºTagged Union Typesº (model state-machine states easely) [qa]
DECLARATION USSAGE
type State = declare const state: State;
| { type: "loading" } if (state.type === "success") {
| { type: "success", value: number } ... state.value ...
| { type: "error", message: string }; } else if (state.type === "error") {
... state.message ...
}
BºIterators and Generatorsº
let arrayOfAnyType = [...]; ← Array or list
for (const val of arrayOfAnyType) { .... } ← Iterate overºvalues in listº
let object = { ... }
for (const key in list) { ... } Iterate overºkeys in objectº
KotlinJS
• Kotlin modern language features and first-class support for Javascript compilation
makes of it an ideal alternative to Javascript.
• Kotlin support integration with:
(@[https://kotlinlang.org/docs/js-modules.html])
• Unified Module Definitions (UMD): compatible with both AMD and CommonJS.
UMD modules are also able to be executed without being imported
or when no module system is present. This is the default option for
the browser and nodejs targets.
• Asynchronous Module Definitions (AMD), which is in particular
used by the RequireJS library.
• CommonJS, widely used by Node.js/npm (require function and module.exports object)
• Plain. Don't compile for any module system. You can access a module by its
name in the global scope.
J2CL
• Java to Javascript transpiler by Goole.
• Production ready and actively use in Google's products.
@[https://github.com/google/j2cl]
@[https://www.infoq.com/news/2019/05/j2cl-java-javascript-transpiler/]
Elm (Haskell like)
• See also:
@[https://www.infoq.com/articles/functional-UI-introduction-no-framework/]
@[https://www.infoq.com/news/2021/10/elm-lessons-learnt-production/]
The Elm architecture is the core of Functional UI techniques that
have crossed over to non-functional languages. The Elm architecture
has been transposed to Rust/Wasm (e.g., yew), JavaScript (e.g., 1KB
ferp, AppRun), TypeScript (e.g., elm-ts) and has inspired many other
languages and libraries (e.g., Redux, SwitfUI, Dark). The Rakuten
article provides the following illustration of the Elm architecture.
@[https://elm-lang.org/news/small-assets-without-the-headache]
If you use a package with hundreds of functions, like elm/html, it
automatically gets trimmed down to the 10 you actually use. The
compiler can just figure it out. This works across the entire
ecosystem. Using one function from a massive package with tons of
dependencies? No problem. You just get that one function in the
generated code.
• Why not use Elm:
@[https://lukeplant.me.uk/blog/posts/why-im-leaving-elm/#technical-limitations]
· Post recomends alternatives like:
- https://rescript-lang.org/
- https://github.com/darklang/philip2
•BºNo Runtime Exceptionsº
Elm uses type inference to detect corner cases and give friendly
hints.
NoRedInk switched to Elm about two years ago, and 250k+ lines
later, they still have not had to scramble to fix a confusing runtime
exception in production.
Ex:
-- TYPE MISMATCH ---------------------------- Main.elm
The 1st argument to `drop` is not what I expect:
8| List.drop (String.toInt userInput) [1,2,3,4,5,6]
^^^^^^^^^^^^^^^^^^^^^^
This `toInt` call produces:
Maybe Int
But `drop` needs the 1st argument to be:
Int
Hint: Use Maybe.BºwithDefaultº to handle possible errors.
•BºGreat Performanceº
•BºEnforced Semantic Versioningº
✓ Breaking APIs force new major version automatically.
thanks to its "ML" type system:
BºNo surprises in PATCH releasesº
$ Bºelm diffº Microsoft/elm-json-tree-view 1.0.0 2.0.0
This is a MAJOR change.
---- JsonTree - MAJOR ----
Changed:
- parseString : String → Result String Node
+ parseString : String → Result Error Node
- parseValue : Value → Result String Node
+ parseValue : Value → Result Error Node
•BºSmall Assetsº (faster page loads)
Use --optimize flag, compiler do the rest. (no config needed)
•BºJavaScript Interoperabilityº
Reason (OCalm)
@[https://reasonml.github.io/]
• By Facebook.
• " Reason lets you write simple, fast and quality type safe code
while leveraging both the JavaScript and OCaml ecosystems."
• See (excellent) comparative Elm vs Reason
@[https://stackoverflow.com/questions/51015850/reasonml-vs-elm]
• See also: ReasonReact
PureScript (Haskell like)
@[https://www.purescript.org/]
• strongly-typed functional programming language transpiling to JS.
• Benefits
transpile to readable JS
reuse existing JS code easily
extensive collection of libraries for web applications/servers/...
Excellent tooling and editor support with instant rebuilds
Active community
Build real-world applications using functional and expressive types:
· Algebraic data types and pattern matching
· Row polymorphism and extensible records
· Higher kinded types
· Type classes with functional dependencies
· Higher-rank polymorphism
┌─ Hello, PureScript! ──────────────────┐
│ │
│ import Prelude │
│ import Effect.Console (log) │
│ │
│ greet :: String -˃ String │
│ greet name = "Hello, " ˂˃ name ˂˃ "!" │
│ │
│ main = log (greet "World") │
└───────────────────────────────────────┘
Erlscripten (Erlang)
@[https://blog.aeternity.com/erlscripten-92c815786987]
• Erlang:
· language+framework most reliable choice to build highly
stable applications
· A normal Erlang app is built out of hundreds of small Erlang
processes.
· Originally proprietary software within Ericsson, released as
free and open-source software in 1998.
· Erlang/OTP is supported and maintained by the
Open Telecom Platform (OTP) product unit at Ericsson.
·ºWhatsapp is developped on Erlang!!!º
Erlang → (Erlscripten) → PureScript → (transpiler) → Javascript
Security
Trusted Web Code
@[https://www.infoq.com/articles/encryption-defense-native-browser-apps/]
• Trusted code: first-line-of-defense for browser security.
Reusing libraries makes web development faster at the cost
of increasing the risk of introducing malicious code.
• There are also under-used code-delivery settings that instruct the
browser to take extra precautions since they reduce the flexibility
of the development and integration process.
Bºthe security they provide is worth the work, whether your applicationº
Bºdoes encryption or not.º
BºHTTP Strict Transport Security (HSTS):º
- Instructs the browser to always load this page over HTTPS. This
prevents downgrade attacks, for instance, if the attacker can
redirect your DNS to a malicious service they can't downgrade the
connection to unencrypted HTTP.
BºStrict Content Security Policies (CSP):º
Whitelist safe sources for loading code, preventing apps to
dynamically load code from unknown remote resource.
BºSubresource Integrity (SRI):º
Only load trusted scripts, using cryptographic hashes to mark
trusted scripts.
BºWebCrypto API:º
"relatively" new browser API helping efficient and secure delivery
of cryptographic primitives.
- low-level ciphers.
- hashes
- "other" encryption components.
• Being directly implemented into (trusted) browser and taking
advantage of local native execution (and even hardware acceleration)
Gºthey are much safer than js code.º
RºWARN:º They do NOT prevent certain attacks, like
just sending an unencrypted copy of the data to the "bad guys".
WebScript
• "WARN": Low maintenance (v0.2) (Maybe not really needed).
HTML
=====================================================
˂div class="card-image"˃
˂img src="images/sample-1.jpg" alt="Sample Image"˃
˂span class="card-title"˃Card Title˂/span˃
˂/div˃
WEBSCRIPT
=================================================
div.class`card-image`(
img.src`images/sample-1.jpg`.alt`Sample Image`, ← JS standard Tagged template syntax for string arguments.
span.class`card-title`("Card Title") ) == img.src("images/sample-1.jpg").alt("Sample Image")
• Webscript is vanilla Javascript (vs HTML templating language)
with the full power of Javascript without thtemplate language limitations.
Webscript is not an HTML templating language. Webscript uses Javascript, not a templating language. Therefore Webscript gives you the full power of Javascript and not the limitations of a template language.
AAA
External Links
• @[/Architecture/architecture_map.html?topics=oauth]
• @[/Architecture/architecture_map.html?topics=aaa]
Web Authentication API
• This API enables users toºavoid text-based passwords for websitesº
and insteadºuses a local device with a biometric check or private PINº
to generate a secure cryptographic identifier.
• Firefox adopted in in 2018
@[https://www.infoq.com/news/2018/05/firefox-web-authentication-api]
W3C DIDs
• Part of the W3C technologies for Self Souveiring Identity (SSI)
• SPEC: @[https://www.w3.org/TR/did-core/]
• DID stands for Decentralized IDentifier representing any type of subject
(person, organization, thing, data model, abstract entity, etc.)
decoupled from any centralized registries (Active Directory, ..),
identity providers (Facebook, Google, ...) or certificate authorities. Eg:
did:ether:21tDAKCERh95uGgKbJNHYp;foo:bar=high
───┬────
┌───────────────────────────────────────┘
generic DID parameter names.
· hl resource hash of DID document (integrity protection)
· service (id) Identifies a service from the DID document
· version-id Identifies a specific version of DID document
(sequential number, UUID, ...).
(optional in implementations)
· version-time Identifies a certain version timestamp of DID document
(optional in implementations)
• DIDs are URIs linking the DID subject to a DID document. The DID
document in practice will be:
✓ cryptographic material (public keys, revoked pub.keys,...)
✓ verification methods/services (signature schemas, ...)
? other public credentials as
desired by the owner/controller
· Non-normative (self-managed) minimal DID-document example:
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:id_of_DID_method:DIDI_method_specific_id",
"authentication": [{
"id": "did:id_of_DID_method:DIDI_method_specific_id#keys-1",
"type": "RsaVerificationKey2018",
"controller": "did:id_of_DID_method:DIDI_method_specific_id",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}],
"service": [{
"id":"did:id_of_DID_method:123456789abcdefghi#vcs",
"type": "VerifiableCredentialService",
"serviceEndpoint": "https://example.com/vc/"
}]
}
BºNOTE:º A single DID document could be related to N DIDs in 1-to-N
relationship, allowing the same "identity" be represented by different
implementations/methods (e.g., one DID on top Ethereum , and another
one on top of github+gpg).
• DID controller refers to the subject that can prove "ownership" or
"control" of the DID (in practice through private/pub crypto signature)
whithout the intervention of permissions of third parties.
More formaly, it represents the entity/ies authorized to make changes
to the associated DID Document.
· Authorization and Delegation of DID Document's controller is contemplated.
• Designed to provide Global unique identifier world-wide.
• DID registries are in charge of storing or recording the DID documents.
Such registries can be and many times are virtual ones:
- from the DID the DID Document can be generated using an algorithm.
E.g.: a DID based on a public key contains all the information needed
to recreate the DID Document.
On the other side, a DID can require some sort of existing
database to recreate the DID Document. In such case this database
will be the DID registry.
• Technology neutral but most implementations works on top of public/
permissioned blockchains.
•ºPart of a bigger echosystem.º W3C models identity as a graph, where nodes
are globally unique IDs and directed edges are claims that neighbors nodes
make towards it:
• DID BºMethod Registryº:
more than 80 implementations (methods) currently available
@[https://w3c-ccg.github.io/did-method-registry/]
• Public Keys in DID documents are expressed in either
· JSON Web Key (JWK) format using the publicKeyJwk property
· JWK or Privacy Enhanced Mail (PEM) encoding Base58
· Ed25519 public key encoded as JWK or raw 32─byte Base58 Bitcoin format
· Secp256k1 Koblitz " " " " " " " 33 " " " "
· Secp256r1 " " " " " " " 32 " " " "
· Curve25519 " " " " " " " 32 " " " "
("X25519")
• RºWARN:º Caching and expiration of the keys in a DID document is entirely the
responsibility of DID resolvers and other clients.
more info about JWT @[../General/cryptography_map.html?query=jwt&showSearchMenu=false]
did-jwt
@[https://github.com/decentralized-identity/did-jwt]
• The did-JWT library allows you to sign and verify JSON Web Tokens
(JWT) using ES256K, ES256K-R and Ed25519 algorithms.
• Public keys are resolved using the Decentralized ID (DID) of the
signing identity of the claim, which is passed as the iss attribute
of the encoded JWT.
• Supported methods: ethr | uport | https | nacl | muport
W3C Credentials API
@[https://w3c-ccg.github.io/credential-handler-api/]
• Credential Management Level 1 describes an imperative API enabling
a website to request a user's credentials from a user agent, and to
help the user agent correctly store user credentials for future use.
• User agents implementing that API prompt the user to select a way to
handle a credential request, after which the user agent returns a
credential to the originating site. This specification defines
capabilities that enable third-party Web applications to handle
credential requests and storage.
Solid (decentrilized) Protocol
@[https://solidproject.org/for-developers/]
• Standard proposed by Teem Berners Lee (Web inventor).
• Solid is an extension (vs replacement) of current Webs,
working on any modern browser.
• The objective is to centralize all "scattered data" around
on the Internet like Social Networks, Online-Shops (Amazon,...),
digital services (online banks, ...) and keep that data under
the control on final users (vs Facebook, Google, Amazon,...).
User's "Pods" are in charge of storing all our personal data:
Pods can be self-hosted or provided by Pod's service provider.
• Compares to W3C DID/VC. WebID is the equivalent to W3C DID. [comparative]
As with DIDs, different WebID's can be owned by the same person,
e.g., one WebID issued by governments, another self-issued WebID for
personal uses, ...
• "Vocabularies" help to interpret Pod's data to any independent
service/application/company that has been granted access.
• Helps to buildºapplication decoupled from dataº, avoiding to [low-code]
maintain a separate back-end.
• Data is requested directly to users (vs provider's custom API).
Styling
CSS
CSS Selectors
•ºBY COORDINATE:º
selector Example Example description
-------- ------- ------- -----------
.class .intro match 1+ element/s by class="intro"
#id #firstname match element by id
º º match all elements
html_tag p match all ˂p˃ elements
sel1,sel2,... div, p match ˂div˃ + ˂p˃ elements
element element div p match ˂p˃ elements inside ˂div˃ elements
element˃element div ˃ p match ˂p˃ elements with parent ˂div˃
element+element div + p match ˂p˃ elements placed immediately
after ˂div˃ elements
element1~element2 p ~ ul match every ˂ul˃ element when preceded by ˂p˃s
[attribute] [target] match elements having a "target" attribute
[attribute=value] [title=flower] match all elements when title is flower
[attribute~=value] [title~=flower] match all elements when title is not flower
[attribute*=value] [title*=flower] match all elements when title val. contains "flower"
[attribute|=value] [lang|=en] match all elements when lang val. starts with en
[attribute^=value] a[href^="http"] match ˂a˃ elements when href val. starts with http
[attribute$=value] a[href$="pdf"] match ˂a˃ elements when href val. ends with .pdf
:active a:active Selects the active link
•ºBY RELATIVE COORDINATE:º
selector Example Example description
-------- ------- ------- -----------
:first-of-type p:first-of-type match ˂p˃ element when ˂p˃ is 1st element of parent
:last-of-type p:last-of-type match ˂p˃ element when ˂p˃ is last element of parent
:not(selector) :not(p) match all elements except ˂p˃s
:first-child p:first-child match ˂p˃ element when ˂p˃ is 1st child of its parent
:last-child p:last-child match ˂p˃ element when ˂p˃ is last child of its parent
:nth-child(n) p:nth-child(2) match ˂p˃ element when ˂p˃ is 2nd child of its parent
:nth-last-child(n) match ˂p˃ element when ˂p˃ is "-2" child of its parent
p:nth-last-child(2)
:nth-last-of-type(n) match ˂p˃ element when ˂p˃ is last-but-2 of ˂p˃'s parent
p:nth-last-of-type(2)
:nth-of-type(n) p:nth-of-type(2) match ˂p˃ element when ˂p˃ is nth of ˂p˃'s parent
º:only-of-type p:only-of-type match ˂p˃ element when it's the only ˂p˃ of its parentº
º:only-child p:only-child match ˂p˃ element when it's the only child of its parentº
:root :root match document's root element
::after p::after Insert content after every ˂p˃ element
::before p::before Insert content before every ˂p˃ element
º:empty p:empty match ˂p˃ elements when it has no children and no textº
::first-letter p::first-letter match first-letter of ˂p˃ elements
::first-line p::first-line match first-line of ˂p˃ elements
•ºBY STATUS:º (INPUT ELEMENTS)
selector Example Example description
-------- ------- ------- -----------
:checked input:checked match checked ˂input˃ element
:disabled input:disabled match disabled ˂input˃ element
:enabled input:enabled match enabled ˂input˃ element
:valid input:valid match input elements with a valid value
:invalid input:invalid match input elements with an invalid value
:read-only input:read-only match input elements with "readonly" attribute specified
:read-write input:read-write match input elements with "readonly" attribute NOT specified
:required input:required match input elements with "required" attribute specified
:optional input:optional match input elements with no "required" attribute
:in-range input:in-range match input elements with a value within a specified range
:out-of-range input:out-of-range match input elements with a value outside a specified range
:focus input:focus match the input element which has focus
•ºLINKS:º
::selection ::selection match portion of an element selected by a user
:target #news:target match current active #news element
(clicked on a URL containing that anchor name)
:visited a:visited match all visited links
:link a:link match all unvisited links
•ºOTHERS:º
:hover a:hover match links on mouse over
:lang(language) p:lang(it) match every ˂p˃ element with lang attribute "it"
CSS variables
• CSS variables (also called CSS custom properties) let express dynamic
relations among CSS properties.
· Declared by prefixing their names with --. e.g. --background
· Its value can be reused in other CSS declarations using the var() function.
·ºThe variable can be updated either via CSS or JavaScriptº,
ºautomatically updating all dependent variables reactivelyº
and allowing developers to express custom algorithms
ºcloser to a Turing-complete languageºlike JavaScript.
· They are scoped to the element(s) they are declared on.
• CSS becomesºmore maintainable,ºeasier to understand, paremeterize and change.
• Can also be used with 'calc'. E.g.:
width: calc(var(--variable-width) + 20px);
• Ex:
:root {
--first-color: #16f;
--second-color: #ff7;
}
#firstParagraph {
background-color: var(--first-color);
color: var(--second-color);
}
#secondParagraph {
background-color: var(--second-color);
color: var(--first-color);
}
#container {
--first-color: #290;
}
#thirdParagraph {
background-color: var(--first-color);
color: var(--second-color);
}
• Extracted from
REF:@[https://www.infoq.com/news/2020/06/css-variables-design-systems/]
""" Tolinski concluded with the following advice:
RºFrameworks require overhead of both kb and onboarding.º
... Write only what you need, use variables as the backbone. """
calc(ulated)
• RºWARNº: css calc(ulated) can slow down rendering according to some sources.
• E.g. extracted from @[https://developer.mozilla.org/en-US/docs/Web/CSS/calc()]
width: calc(10px + 100px);
width: calc(2em * 5);
width: calc(var(--variable-width) + 20px);
• box-sizing asºpotential alternative to css calcº:
@[http://stackoverflow.com/questions/16034397/css-calc-alternative]
box-sizing Description
related Prop.
------------- -----------------------------------------------------
• content-box · Default width and height (and min/max) includes
only the content.
· Border/padding/margin not included
• border-box · width and height properties (and min/max properties)
includes content, padding and border, but not the margin
• initial · Set property to default value.
(Read about initial)
• inherit · Inherits property from parent element.
CSS Layout
• Layout mode:
algorithm that determines position+size of boxes based on
how they interact with sibling and ancestor boxes:
@[https://developer.mozilla.org/en-US/docs/Web/CSS/Layout_mode]
· Normal flow : (default) includes:
· block layout: lay out boxes (paragraphs,...)
· inline layout: lay out inline items (text,...).
· Table layout:
· Float layout: cause item to position left|right with
the rest of the content.
· Positioned layout: position elements without much interaction
with other elements.
· Multi-column layout: lay out content in columns.
· Flexible box layout: lay out complex pages that can be
resized smoothly.
· Grid layout: lay out elements relative to a fixed grid.
→ºStep 1:ºDefine "display" property
· @[https://developer.mozilla.org/en-US/docs/Web/CSS/display]
· sets
· - outer layout respect siblings (block or inline)
· - inner layout of children (flow-root,flow,flex,grid,table)
·
· none: Turns off display of an element
· RºWARNº: use visibility:hidden to replace element with transparent box
· contents: don't produce a specific box by themselves.
· Place inner content on parent container.
·
· - CSS3 value syntax Legacy CSS2
· ------------------ -----------
· block flow-root
· block flow
· block flex
· block grid
· block table
· inline flow-root inline-block
· inline flow
· inline flex inline-flex
· inline grid inline-grid
· inline table inline-table
·
└·→ºStep 2:ºDefine float:= left | right | none | inline-start | inline-end
· @[https://developer.mozilla.org/en-US/docs/Web/CSS/float]
· It implies theºuse of a block layoutº, modifying if needed the
· computed value of display values if needed.
· It defines element's placementºrelative to parent containerº.
·
└·→ºStep 3:ºDefine clear:= left | right | both | none
@[https://developer.mozilla.org/en-US/docs/Web/CSS/clear]
It allows to control vertical placement relative to ºsibling floating elementsº.
It defines whether current element must be moved below (cleared)
sibling floating elements to its right/left.
(the element itself can be or not floating).
Responsive 101
There are two main aspects to consider when adapting a web "page"
to be used on mobiles and desktops (and TVs, ...)
•º#################################º
º# ASPECT 1) USER INPUT HARDWARE #º
º#################################º
· This is the main difference between mobiles and desktops.
· Desktop users can profit from a mouse with accurate movement
over the screen, while mobile users must use coarse finger "touchs".
· ✓ CSS 4 (º2018+º, support by all major browsers) @media allows to
show/hide/modify (input) elements according to the accuracy of
system's pointer ("mouse").
It also allows to show/hide/modify elements according to the
possibility to detect mouse hovering (flying above) above them.
PRIMARY INPUT POINTER TYPE DEVICE TYPE
========================== ================= ===============
@media (pointer : coarse) { ... } limited accuracy mobiles
@media (pointer : fine ) { ... } high accuracy laptops,desktops
@media (pointer : none ) { ... } no pointer embedded info-pannels,...
PRIMARY INPUT PRI/SECONDARY INPUT/S HOVER TYPE
========================== ========================== ===========================
@media (hover : hover ) @media (any-hover : hover ) easy hover over elements
@media (hover : none ) @media (any-hover : none ) limited/none hover
└───────────┬────────────┘
Complementary to those @media filters we can
dynamically use Javascript to detect the
mouse/hover properties and react accordingly
ºJavascript Media queryº:
if ( window.matchMedia("(any-hover: none)".matches ) { [code-snippet]
...
}
· Ussually input elements requiring high accuracy, tooltips "on Hover", ...
will need to be replaced by ("simpler") alternatives on mobile phones.
· mobile "multi-touch" (zoom-in/out) effects probably will require input sliders
(˂input type="range" ...˃) on desktops.
•º#########################º
º# ASPECT 2) SCREEN SIZE #º
º#########################º
The styling to apply depending on the screen size is more subject to interpretations.
• meta-header viewport (mobile only):
˂meta name="viewport"
content="user-scalable=no,
width=device-width, º*1º
initial-scale=1" /˃
º*1º (emulated) width. If physical mobile screen is 6"-diagonal and has a native 1920px
screen resolution, then an 192px-diagonal image will display in
just ~0.6" by default.
By setting viewport width to 192px virtual pixels, it will display
Initial-scale:1
Minimum-scale:
Maximum-scale:
User-scalable: yes / no
• Vision angles (vs screen size and resolution) is the key point to consider.
There is always an important Rºmissing parameterºto choose the right layout:
"Distance-from eye-to-screen".
This implies that we must make assumptions covering standard scenarios:
· For mobiles eye-to-screen is about 20- 40 cms.
· For laptops eye-to-screen is about 70-100 cms.
· For TV eye-to-screen is about 300-500 cms.
with that arbirary assumption different @media can be applied:
@media only screen and
(pointer : coarse) ← probably mobile at 20-40 cms distance
and (min-device-width : 320px) ← low (mobile) resolution
and (max-device-width : 800px) {
(max-width: 800px) {
body { font-size: 1.3em } ← Increase font-size
... 1 column layout ... ← e.g.: Render just 1 column of "payload" info
}
@media only screen and
(pointer : coarse) ← probably mobile at 20-40 cms distance
and (min-device-width : 801px) ← medium (mobile) screen resolution
and (max-device-width :1024px) {
body { font-size: 1.2em } ← Increase font-size
... 1 column layout ... ← e.g.: Render just 1 column of "payload" info
}
@media only screen and
(pointer : coarse) ← probably mobile at 20-40 cms distance
and (min-device-width :1025px) ← high (mobile) screen resolution
{
body { font-size: 1.2em } ← Increase font-size
... 2 column layout ... ← e.g.: Render 2 columns of "payload" info
}
@media only screen and
(pointer : fine ) and ← probably desktop at 70-100 cms distance
probably high-screen resolution
(max-width: 960px) { ← probably browser windows right/left of
desktop with a 1920+px screen resolution
body { font-size: 10px } ← Increase font-size
... 1 column layout ... ← e.g.: Render just 1 column of "payload" info
}
@media only screen and
(pointer : fine ) and ← probably desktop at 70-100 cms distance
probably high-screen resolution
(max-width: 960px) { ← probably maximized windows filling
desktop with a 1920+px screen resolution
body { font-size: 10px } ← Increase font-size
... 2 column layout ... ← e.g.: Render 2 columns of "payload" info
}
Tailwindcss
@[https://tailwindcss.com/]
• “Best practices” don’t actually work.
""" I’ve written a few thousand words on why traditional
“semantic class names” are the reason CSS is hard to maintain,
but the truth is you’re never going to believe me until you
actually try it. If you can suppress the urge to retch long enough to
give it a chance, I really think you'll wonder how you ever worked
with CSS any other way."""
RunCSS
@[https://github.com/mudgen/runcss] by Nick Mudge (@[http://www.perfectabstractions.com/])
• RunCSS is a runtime version of TailwindCSS. It has no build. RunCSS
provides all the same CSS utility class names that we know and love
from TailwindCSS.
• RunCSS is batteries included. It has feature parity with TailwindCSS
and beyond. RunCSS defaults are the same as TailwindCSS defaults plus
TailwindCSS's additional variants, plus more. By default all variants
such as hover, active, visited, group-hover etc. and responsive
variants such as sm, lg etc work with all class names.
• RunCSS is possible because it is a Javascript file that generates CSS
at runtime.
• The primary difference between TailwindCSS and RunCSS is that
TailwindCSS generates CSS at build time and RunCSS generates CSS at
runtime.
• RunCSS has no build. Just use it. Off to the races!
• The tradeoff to using RunCSS is a small amount of Javascript
execution to generate CSS at runtime. The necessary CSS for each
class name is generated one time as it is encountered. CSS is only
generated for class names that are actually used.
CSS Flex/Grid
REF: @[http://flexbox.malven.co/] by @[https://malven.co/]
BºELEMENT FLEX PROPERTIES RELATIVE TO ***PARENT CONTAINER***º
• display:= how current element behaves with ºsiblingsº
· flex : As box (paragraph,...)
· inline-flex: As line(text ,...)
• flex-direction:= establishes the main axis for children
row row-reverse column column-reverse:
|||⇒ ⇐||| - ⇑
⇓ -
• flex-wrap:= What to do when items don't fit in a line/column
nowrap wrap wrap-reverse
┌───────┐ ┌───────┐ ┌───────┐
│|||||||│ │|||||||│ │||| │
│ │ │||| │ │|||||||│
└───────┘ └───────┘ └───────┘
• justify-content: who to (attempt to) distribute extra space on main axis
flex-start flex-end center
┌───────┐ ┌───────┐ ┌───────┐
│||| │ │ |||│ │ ||| │
space-between space-around space-evenly
┌───────┐ ┌───────┐ ┌───────┐
│| | |│ │ | | | │ │ | | | │
• align-items: Determines how items are laid out on cross (vs main) axis
flex-start flex-end center baseline stretch
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
│·······│ │ │ │ │ │·······│ │1º2º...│
│ │ │ │ │·······│ │ │ │1º2º...│
│ │ │·······│ │ │ │ │ │1º2º...│
└───────┘ └───────┘ └───────┘ └───────┘ └───────┘
• align-content: Only aplies with 2+ content lines (Ex using flex-wrap)
flex-start flex-end center space- space- stretch
between around
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
│1 2 3 4│ │ │ │ │ │1 2 3 4│ │ │ │1 2 3 4│
│5 6 ...│ │ │ │1 2 3 4│ │ │ │1 2 3 4│ │1 2 3 4│
│ │ │1 2 3 4│ │5 6 ...│ │ │ │5 6 ...│ │5 6 ...│
│ │ │5 6 ···│ │ │ │5 6 ...│ │ │ │5 6 ...│
└───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘
BºFLEX PROPERTIES OF ELEMENT's CHILDRENº
• order: Allows to explictly set children's order:
:= integer
• flex-grow: determine how each child is allowed to grow as a part of a whole
flex-grow: 1 flex-grow
┌────────────┐ ┌────────────┐
│┌──┐┌──┐┌──┐│ │┌┐┌──┐┌────┐│
││ ││ ││ ││ ││││ ││ ││
││ ││ ││ ││ ││││ ││ ││
(applied to all) (1, 2, and 3)
• flex-basis: Defines size of children before remaining space is distributed
┌────────────┐ ← first item 20%,
│┌┐┌──┐┌────┐│ second item 40%
││││ ││ ││
││││ ││ ││
• flex-shrink: shrink childrens if necessary. Only really useful with
a set size or flex-basis
┌──────────┐
│┌──┐┌────┐│← Ex: both want to be 100% wide
││ ││ ││ 2nd item has flex-shrink: 2
││ ││ ││
• align-self: Sets alignment for individual item.
┌───────┐
│·· ····│← Ex: 3rd item has align-self:flex-end
│ │
│ · │
└───────┘
- http://grid.malven.co/ [TODO]
CSS Grid
REF: @[http://grid.malven.co/] by @[https://malven.co/]
• CSSgrid generator: online visual Grid design tool.
@[https://cssgrid-generator.netlify.app/]
[TODO] beatify with ascii-art visual hints (like original page) [TODO]
BºELEMENT GRID PROPERTIES RELATIVE TO ***PARENT CONTAINER***º
• display := grid | inline-grid | subgrid
• grid-template: Defines the rows and columns of the grid.
Ex. 3x3 matrix:
grid-template-columns: 12px 12px 12px;
grid-template-rows: 12px 12px 12px;
Ex. 3x3 matrix:
grid-template-columns: repeat(3, 12px);
grid-template-rows: repeat(3, auto);
Ex. 3x3 matrix:
grid-template-columns: 8px auto 8px;
grid-template-rows: 8px auto 12px;
Ex. 3x3 matrix:
grid-template-columns: 22% 22% auto;
grid-template-rows: 22% auto 22%;
• grid-gap: set size of column and row gutters.
Ex. 1: Ex.2: Ex.3:
grid-row-gap : 1px; grid-gap: 1px 9px; grid-gap: 6px;
grid-column-gap: 9px;
• justify-items: Aligns content in grid along row axis.
:= start | end | center | stretch (default)
• align-items: Aligns content in grid along column axis.
start | end | center | stretch (default)
• justify-content: Justifies ALL grid content on row axis
when total grid size is smaller than container.
align-content : Justifies ALL grid content on column axis
when total grid size is smaller than container.
:= start | end | center | stretch | space-around |
space-between | space-evenly
• grid-auto-flow: Algorithm used to automatically place grid items
not explictly placed.
:= row | column | dense
BºFLEX PROPERTIES OF ELEMENT's CHILDRENº
• grid-column: Determines an items column-based location within the grid.
grid-column-start: 1;
grid-column-end : 3;
grid-column-start: span 3;
grid-column-start: 2;
grid-column-end : 4;
grid-column : 2 / 3
grid-column : 2 / span 2
• grid-row: Determines an items row-based location within the grid.
grid-row-start: 1;
grid-row-end: 3;
grid-row-start: span 3;
grid-row-start: 2;
grid-row-end: 4;
grid-row: 1 / 3;
grid-row: 1 / span 3;
• grid-row + grid-column: Combining grid rows with grid columns.
grid-row: 1 / span 2;
grid-column: 1 / span 2;
grid-row: 2 / span 2;
grid-column: 2 / span 2;
• justify-self: Aligns content for a specific grid item along the row axis.
justify-self: start;
justify-self: end;
justify-self: center;
justify-self: stretch; (default)
• align-self: Aligns content for a specific grid item along the column axis.
align-self: start;
align-self: end;
align-self: center;
align-self: stretch; (default)
CSS Cont.
CSS3 Animations
• Animations allows to gradually change an element's CSS properties from
initial style/value to final one.
@keyframesºanimation01º{ ← STEP 1) Define key frames describing
element/s styles at certain "times"
from {color: red; left: 0; } ← from == "0%"
10%,90%{color: #AAA; left: 10%; }
30%,70%{color: blue; left: 20%; }
to {color: green;left: 30%; } ← to == "100%"
}
div { ← Animation will apply to all div's
...
color : red;
ºanimation-name : animation01;º← STEP 2) Animation ussage
animation-duration : 4s; ← (non-Opt) Set transition time
animation-delay : 2s; ← Opt. If negative anim. starts as
if running for "-N" secs.
animation-direction: normal ← Opt. normal* | reverse
| alternate | alternate-reverse
animation-iteration-count: 3; ← Opt. Repeat N times (vs infinite)
animation-timing-function: linear; ← Opt. := linear | ease | ease-in
| ease-out | ease-in-out
animation-fill-mode: forwards; ← Opt. Effect CSS before-1st/after-lst.frame
none* : Keep original CSS
forwards : retain last keyframe style
backwards: retain first keyframe style
both : retain 1st keyframe before start
lst.keyframe after end
}
• External references:
· Advanced CCS3 animations using SVG can be
@[http://css3.bradshawenterprises.com/cfimg/]
Profiling
•ºProblem Contextº:
Performance issue when adding CSS-based animation (dropped frames)
ºQº: Are there any tools to debug/profile the issue?
ºAº:
• Chrome : Timeline tab
Display stats about restyles, reflows, paint, compositing
operations as time goes.
Will be available in Firefox in a future (2014-08)
• Firefox: FirefoxDevTools → "performance" tab → profiler
STEP 1) Create JS calls record.
STEP 2) Inspect ºframerate graphº along with a
bar graph showing the types of activities occurred
on each recorded sample
(network, JIT, GC, events, styles, graphics, storage).
└──────┬───────┘
CSS animation related ones
Optional: Enable "Gecko platform data" for extra information
It works overºremote devtools-protocolº allowing for mobile
profile / debug.
CSS Logical Props.
@[https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties]
• CSS Logical Properties and Values CSS modules introduces
logical properties and values that provide the ability to
control layout through logical, rather than physical,
direction and dimension mappings.
• The module also defines logical properties and values for properties
previously defined in CSS 2.1. Logical properties define
direction‐relative equivalents of their corresponding physical
properties.
CSS Background
• background-color: red;
• Image background attributes:
background-image : url(image);
background-repeat : repeat | no-repeat | repeat-x | repeat-y
background-position: top | center | bottom
background-size : 100px | 90% | cover | contains
• Gradient:
p1: coordinates gradient start
TODO: Firefox(top|left|right|down)
Webkit: % horizontal/vertical
p2: coodinates gradinet end
1: color start
c2: color end
(-webkit|moz)-gradient(linear, p1, p2, from(c1), to(c2));
linear-gradient(p1, c1, c2) no-repeat;
• Gradient Vertical:
background: -moz-linear-gradient(top, red, blue);
background: -webkit-gradient(linear, 0%, 0%, 0%, 100%, from(red), to(blue));
• Gradient Horizontal
background: -moz-linear-gradient(left, red, blue);
background: -webkit-gradient(linear, 0%, 0%, 100%, 0%, from(red), to(blue));
• Gradient Radial
background: -webkit-gradient(radial, p1, r1, p2, r2, from(c1), to(c2));
Fonts/Art Gallery
fontsquirrel
Declaring external font:
@font-face
{
font-family: MyFontFamilyName; /* "id" to be used in CSS for this font*/
src: url('my_font.ttf');
src: url('my_font.eot');
src: url('my_font.svg');
src: url('my_font.woff');
}
use:
h1 { font-family: 'MyFontFamilyName'; }
- https://www.fontsquirrel.com/
Free fonts have met their match. We know how hard it is to find quality
freeware that is licensed for commercial work. We've done the hard work, hand-
selecting these typefaces and presenting them in an easy-to-use format. Here
are some of our favorites:
http://christ-offer.blogspot.com.es/2011/05/html5css3-fonts-with-font-face.html
- Embedding specific fonts on a web page, traditionally, has been
accomplished using images. Though IE 5.0+ allowed you to embed fonts on your
web page, they needed to be in EOT (Embedded OpenType) format. Other
browsers supported TTF (TrueType) and OTF (OpenType) fonts. IE9 now supports
TTF, OTF and also WOFF (Web Open Font). To accomplish the task of embedded
real fonts into your web pages, use the CSS3 @font-face rule.
- Where to get quality FREE fonts for use in web pages? FontSquirrel is a
great place with hundreds of free fonts, including kits that demonstrate how
to embed and use them into a web page, plus a generator to create your own.
With the kit, you get the font in all the formats so that you can craft your
web pages to support all the major
"fonts for pros"
https://type-scale.com/
CSS :
From: @[https://www.infoq.com/news/2020/06/css-variables-design-systems/]
Typography-wise, Tolinski recommended leveraging existing tools to
visualize fonts and their scale ratio. An example of code
auto-generated for copy-pasting purposes by type-scale.com is as
follows:
@import url('https://fonts.googleapis.com/css?family=Poppins:400|Poppins:400');
html {font-size: 100%;} /*16px*/
body {
font-family: 'Poppins', sans-serif;
...
}kkkkkkkkk
h1, h2, h3, h4, h5 {
font-family: 'Poppins', sans-serif;
...
}
Extra typography resources include
• modularscale
https://www.modularscale.com/
• Figma typography recommendations
https://www.figma.com/best-practices/typography-systems-in-figma/
• vertical-rhythm-reset
http://jhildenbiddle.github.io/vertical-rhythm-reset/sassdoc/
Art Gallery
Icon Gallery:
@[https://github.com/NitruxSA/nitrux-icons]
@[http://tiheum.deviantart.com/art/Faience-icon-theme-255099649]
@[http://tiheum.deviantart.com/art/Faenza-Icons-173323228]
@[http://0rax0.deviantart.com/art/Uniform-Icon-Theme-453054609]
@[http://gnome-look.org/content/show.php/Dalisha?content=166286]
Others
Colors-for-all
@[https://github.com/AmadeusITGroup/Colors-for-All]
• Tool enabling to easily check color contrasts and WCAG
compliance (AA or AAA levels as defined by W3C) between specific
colors in one shot !
• online application available for UX/UI designers or any other
people involved in digital accessibility.
Resize_Observer_API
See also: @[https://en.wikipedia.org/wiki/Responsive_web_design]
@[https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API]
• The Resize Observer API provides a performant mechanism by which code
can monitor an element for changes to its size, with notifications
being delivered to the observer each time the size changes.
Apply Masks to images
@[http://www.html5rocks.com/en/tutorials/masking/adobe/]
• Clipping an image:
˂svg˃ ← STEP 1: Define SVG clip
˂defs˃
˂/clipPathº id="clipping01"º˃
˂circle
cx=284 cy=213 r=213 /˃
˂/clipPath˃
˂/defs˃
˂/svg˃
img#img01 { ← STEP 2: Add css to img dom
clip-path: url(#clipping01º); ← with a clip-path property
}
• Animated Clipping:
@keyframes animation01 { ← STEP 1: Define animation
0% {
clip-path: polygon(...);
},
100% {
clip-path: polygon(...);
}
}
img#img01:hover { ← STEP 2: Apply clip to image.
clip-path:
polygon(0px 208px, 146.5px 207px,... ...);
animate: animation01 3s; ← STEP 3: Apply animation to CSS
}
gRPC
Summary
@[https://grpc.io/]
@[https://en.wikipedia.org/wiki/GRPC]
- Cloud Native Foundation project (initial develop:Google)
- High performance, cross-platform/language RPC framework.
-ºHTTP/2 for transport and Protocol Buffers for interface º
ºdescription languageº.
- pluggable support for load balancing, tracing, health
checking and authentication.
- bidirectional streaming and flow control [async]
- blocking or nonblocking bindings, and cancellation and timeouts.[async]
- binary encoding on the wire (protocol buffers, or "protobufs" for short).
- Cross-language Proxy objects auto-generated with
safe type checking (when supported language) [qa]
BºPROTOC + gRPC INSTALLATION STEPSº
STEP 1) install protobuf + protoc commands
STEP 2) Install Node/Python/Go/... tools:
Bºgrpcurlº:
@[https://github.com/fullstorydev/grpcurl]
• cURL for gRPC!!!!. CLI tool for interacting with gRPC servers.
• accept JSON encoding as input, much more friendly (than binary)
for humans/scripts.
• bi-directional streaming methods supported
• TLS supported, including mutual TLS.
• Docker support:
$º$ docker run fullstorydev/grpcurl api.grpc.me:443 list º
• Go quick install:
$º$ go get github.com/fullstorydev/grpcurl/... º
$º$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl º
$º$ grpcurl {{grpc.server.com:443}} list \º ← List all services exposed
$º {{my.custom.server.Service}} º ← Optional. Filter by service
using server supporting reflection or
reading proto source files or
by loading in compiled "protoset"
files (containiing encoded file
descriptor protos).
$º$ grpcurl {{grpc.server.com:443}} \ º ← Send an empty request
$º {{my.custom.server.Service/Method}} º
$º$ grpcurl -H \ º ← Send request with
$º "{{Authorization: Bearer $token}}" \ º ← header and
$º -d {{'{"foo": "bar"}'}} \ º ← body
$º {{grpc.server.com:443}} \ º
$º {{my.custom.server.Service/Method}} º
BºCOMPILING NODEJS SCRIPT EXAMPLEº
#!/bin/sh
set -e # ← Abort on first error.
PROTOC=/opt/protoc_v3/bin/protoc # ← PATH to protoc compiler executable
OUT_DIR="./src/lib/protobuf"
if [ ! -d ${OUT_DIR} ]; then mkdir -p ${OUT_DIR} ; fi
# protoc fails if relative path is provided. (2017-01)
GRPC_NODE_PLUGIN=/usr/local/lib/node_modules/grpc-tools/bin/grpc_node_plugin
${PROTOC} \
--proto_path=../project_proto_definition/ # ← Path to search *proto for
--js_out=import_style=commonjs,\
binary:${OUT_DIR} \# ← JS protobuf output path
\
--grpc_out=${OUT_DIR} \ # ← gRPC(services) output path
--plugin=protoc-gen-grpc=\ # ← gRPC service related
${GRPC_NODE_PLUGIN} \
file1.proto
BºCOMPILING JAVA SCRIPT EXAMPLEº
@[https://github.com/grpc/grpc-java/blob/master/README.md]
- Gradle protobuf conf:
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
buildscript {
repositories { mavenCentral() }
dependencies {
classpath
'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
}
}
- Gradle+grpc conf:
compile 'io.grpc:grpc-netty:1.0.1' ┐
compile 'io.grpc:grpc-protobuf:1.0.1' ├non-Android
compile 'io.grpc:grpc-stub:1.0.1' ┘
compile 'io.grpc:grpc-okhttp:1.0.1' ┐
compile 'io.grpc:grpc-protobuf-lite:1.0.1'├Android
compile 'io.grpc:grpc-stub:1.0.1' ┘
...
protobuf {
protoc { artifact = "com.google.protobuf:protoc:3.1.0" }
plugins {
grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.0.1' }
}
generateProtoTasks { all()*.plugins { grpc {} } }
}
BºgRPC PROTOBUF EXCEPTION HANDLINGº [qa]
C⅋P FROM @[http://stackoverflow.com/questions/38810657/exception-handling-in-grpc]
Context: server written in Java, client written in PHP.
Q: How can client catch exceptions from server?
A: For handled exceptions, call responseObserver.onError().
If you pass in a StatusRuntimeException or StatusException
(generally created via status.asRuntimeException()) the status
code and description will be communicated to the client.
RºUnhandled exceptionsº within a callback will cancel
the RPC and will continue propagating the exception
(generally leading in an UncaughtExceptionHandler being
called for the executor).
grpc ecosystem
@[https://github.com/grpc-ecosystem]
RPC Ecosystem that complements gRPC
· grpc-gateway : gRPC to JSON proxy generator following the gRPC HTTP spec
· go-grpc-prometheus: Prometheus monitoring for your gRPC Go servers.
· grpc-health-probe : cli tool to perform health-checks for gRPC apps
· ...
gRPC JAVA Summary
GºJAVA SERVER IMPEMENTATION SUMMARYº
service RouteGuide {
rpc GetFeature(Point) returns ( Feature) {} ← ºgRPC DEFINITIONSº
rpc ListFeatures(Rectangle) returns (stream Feature) {}
rpc RecordRoute(stream Point) returns ( RouteSummary) {}
rpc RouteChat(stream RouteNote) returns (stream RouteNote ) {}
}
public class RouteGuideServer ← Server implementation
... initialization code ...
private static class RouteGuideService
ºextends RouteGuideGrpc.RouteGuideImplBaseº
...
┌──────────────────────────────────────────────┬────────────────────────────────────────────┐
│ºSINGLE RESPONSEº │ºSTREAM RESPONSEº │
┌─────────┼──────────────────────────────────────────────┼────────────────────────────────────────────┤
│ºSINGLE º│ @Override │ @Override │
│ºREQUESTº│ public void getFeature( │ public void listFeatures( │
│ │ Point request, │ Rectangle request, │
│ │ StreamObserver responseObserver) │ StreamObserver responseObserver) { │
│ │ { │ for (Feature feature : features) │
│ │ // Single onNext iteration │ { │
│ │ responseObserver.onNext( │ ... │
│ │ _checkFeature(request)); │ responseObserver.onNext(feature); │
│ │ responseObserver.onCompleted(); │ Oº ^^^^^^^º │
│ │ //^ ends inmediatelly ^ │ Oºfor each feature foundº │
│ │ } │ } │
│ │ │ //*streaming response* │
│ │ │ responseObserver.onCompleted(); │
│ │ │ } │
├─────────┼──────────────────────────────────────────────┼────────────────────────────────────────────┼
│ºSTREAM º│ @Override │ @Override │
│ºREQUESTº│ public StreamObserver recordRoute( │ public StreamObserver routeChat( │
│ │ final StreamObserver responseObserver) │ final StreamObserver responseObserver)│
│ │ { │ { │
│ │ return new StreamObserver() { │ return new StreamObserver() { │
│ │ @Override │ @Override │
│ │ public void onNext(Point point) { │ public void onNext(RouteNote note) │
│ │ ... │ { │
│ │ } │ for (RouteNote prevNote : │
│ │ │ notes.toArray(new RouteNote[0]))│
│ │ @Override │ { │
│ │ public void onError(Throwable t) { │ responseObserver.onNext(prevNote);│
│ │ logger.... "recordRoute cancelled");│ } │
│ │ } │ ... │
│ │ │ } │
│ │ @Override │ │
│ │ public void onCompleted() { │ @Override │
│ │ long seconds = │ public void onError(Throwable t) { │
│ │ NANOSECONDS.toSeconds( │ logger.... "routeChat cancelled");│
│ │ System.nanoTime() - startTime); │ } │
│ │ responseObserver │ │
│ │ Oº.onNext(º │ //*Do not complete until receivedº │
│ │ RouteSummary.newBuilder() │ //*onCompleted for peerº │
│ │ .setPointCount(pointCount) │ @Override │
│ │ .setFeatureCount(featureCount) │ public voidºonCompleted()º{ │
│ │ .setDistance(distance) │ responseObserver.onCompleted(); │
│ │ .setElapsedTime((int) seconds) │ } │
│ │ .build() │ }; │
│ │ Oº)º; │ } │
│ │ responseObserver.onCompleted(); │ │
│ │ ^^^^^^^^^^^^^^ │ │
│ │ end stream │ │
│ │ } │ │
│ │ }; │ │
│ │ } │ │
└─────────┴──────────────────────────────────────────────┴────────────────────────────────────────────┴
GºJAVA CLIENT IMPEMENTATION SUMMARYº
ºInitialization codeº
function getServer() { │ if (require.main === module) {
var server = new grpc.Server(); │ var routeServer = getServer();
server.addService(services.RouteGuideService, { │ routeServer.bind('0.0.0.0:50051',
getFeature: getFeature, │ grpc.ServerCredentials.createInsecure());
listFeatures: listFeatures, │ ...
recordRoute: recordRoute, │ routeServer.start();
routeChat: routeChat │ });
}); │ exports.getServer = getServer;
return server; │
}
┌───────────────────────────────────────────────────┬──────────────────────────────────────────
│ºSINGLE RESPONSEº │ºSTREAM RESPONSEº
┌─────────┼───────────────────────────────────────────────────┼──────────────────────────────────────────
│ºSINGLE º│ feature = blockingStub. │ Rectangle request =
│ºREQUESTº│ getFeature( │ Rectangle.newBuilder()
│ │ Point.newBuilder() │ .setXXX
│ │ .setXXX() │ ...
│ │ .... │ .build();
│ │ .build()); │ Iterator˂Feature˃ features
│ │ │ = blockingStub
│ │ │ .listFeatures(request);
├─────────┼───────────────────────────────────────────────────┼───────────────────────────────────────────
│ºSTREAM º│ public void recordRoute( │ public void routeChat()
│ºREQUESTº│ List˂Feature˃ features, int numPoints) │ throws InterruptedException
│ │ throws InterruptedException │ {
│ │ // NOTE: java.util.concurrent.CountDownLatch │ //
│ │ details removed │ // rpc RouteChat(stream RouteNote)
│ │ │ // returns (stream RouteNote) {}
│ │ // STEP 1. SETUP RESPONSE OBSERVER │ //
│ │ StreamObserver˂RouteSummary˃ responseObserver = │
│ │ new StreamObserver˂RouteSummary˃() { │ //*STEP 1. SETUP RESPONSE OBSERVER*
│ │ @Override │ final CountDownLatch finishLatch
│ │ public void onNext(RouteSummary summary) { │ = new CountDownLatch(1);
│ │ info... summary.get*()); │ StreamObserver˂RouteNote˃
│ │ } │ requestObserver =
│ │ @Override │ asyncStub.routeChatBº(º
│ │ public void onError(Throwable t) { │ new StreamObserver˂RouteNote˃()Oº{º
│ │ // Rº^^^^^^^ º │ @Override
│ │ // RºRPC Failedº │ public void onNext(RouteNote note)
│ │ log ... Status.fromThrowable(t);); │ {
│ │ } │ info"Got message ... ";
│ │ @Override │ }
│ │ public void onCompleted(){ │ @Override
│ │ finishLatch.countDown(); │ public void onError(Throwable t)
│ │ } │ {
│ │ }; │ warn ... Status.fromThrowable(t);
│ │ │ }
│ │ // STEP 2. SETUP REQUEST OBSERVER │ @Override
│ │ StreamObserver˂Point˃ requestObserver = │ public void onCompleted()
│ │ asyncStub.recordRoute(responseObserver); │ {
│ │ │ finishLatch.countDown();
│ │ try { │ }
│ │ for (int i = 0; i ˂ numPoints; ++i) { │ º}*
│ │ // push point to req.stream │ Bº)*;
│ │ requestObserver.onNext(point); │
│ │ if (finishLatch.getCount() == 0 ) { │ //*STEP 2: SETUP REQUEST OBSERVER*
│ │ /* RPC completed|failed before end of │ try {
│ │ * request stream │ RouteNote[] requests =
│ │ * Sending further requests won't error, │ {newNote(...), newNote .. };
│ │ * but they will just be thrown away. │ for (RouteNote request : requests)
│ │ */ │ {
│ │ // │ requestObserver.onNext(request);
│ │ return; │ // ^^^^^^^
│ │ } │ // Send Message
│ │ } │ }
│ │ } catch (RuntimeException e) { │ } catch (RuntimeException e) {
│ │ requestObserver.onError(e) / Cancel RPC /;│ requestObserver.onError(e); throw e;
│ │ throw e; │ Rº^^^^^^^^^^^º
│ │ } │ RºCancel RPC º
│ │ requestObserver.onCompleted(); │ requestObserver.onCompleted();
│ │ º^^^^^^^^^^^^^ º │ ^^^^^^^^^^^^^^
│ │ ºMark the end of requestsº │ // Mark the end of requests
│ │ │ finishLatch.await(1, TimeUnit.MINUTES);
│ │ // Receiving happens asynchronously │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ finishLatch.await(1, TimeUnit.MINUTES); │ // Receiving happens asynchronously
│ │ ^^^^^^^^^^^^^^^^ │ }
│ │ java.util.concurrent. │
└─────────┴───────────────────────────────────────────────────┴────────────────────────────────────────────
BºgRPC ecosystemº: [TODO]
-@[https://github.com/grpc-ecosystem]
GºNODEJS SERVER IMPLEMENTATION SUMMARYº
- ºSERVER INITIALIZATIONº
@[https://github.com/grpc/grpc/blob/master/examples/node/static_codegen/route_guide/]
var routeServer = new grpc.Server();
routeServer.addService(services.RouteGuideService, {
getFeature: getFeature,
listFeatures: listFeatures,
recordRoute: recordRoute,
routeChat: routeChat
});
routeServer.bind('0.0.0.0:50051',
grpc.ServerCredentials.createInsecure());
... "any other initialization"...
routeServer.start();
┌────────────────────────────────────┬──────────────────────────────────────────
│ ºSINGLE RESPONSEº │ºSTREAM RESPONSEº
┌─────────┼────────────────────────────────────┼──────────────────────────────────────────
│ºSINGLE º│ function getFeature( │ function listFeatures(
│ºREQUESTº│ call, responseCallback ) │ call) { // ← where call.request is
│ │ { │ Rectangle(lo, hi)
│ │ responseCallback(null, feature); │ _.eachBº(ºfeature_list, feature =˃ Oº{º
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ if ("feature OK") {
│ │ write response │ call.write(feature)
│ │ } │ ^^^^^^^^^^^^^^^^^^^
│ │ │ write to response stream
│ │ │ }
│ │ │ Oº}º
│ │ │ Bº)º
│ │ │ call.end();
│ │ │ ^^^^^^^^^^^
│ │ │ end stream
│ │ │ }
├─────────┼────────────────────────────────────┼──────────────────────────────────────────
│ºSTREAM º│ function recordRoute ( │ function routeChat (
│ºREQUESTº│ call // ← stream, callback) │ call // ← stream. NOTE: no callback )
│ │ { │ {
│ │ call.on('data', │ call.on('data',
│ │ (point) =˃ { ... } │ (note) =˃ {
│ │ ) │ _.each(internal_data,
│ │ call.on('end', │ (note) =˃ {
│ │ () =˃ { │ call.write(note)
│ │ var summary = new │ ^^^^^^^^^^^
│ │ messages │ write to resp.stream
│ │ .RouteSummary().set... │ })
│ │ callback(null, summary); │ })
│ │ }) │ call.on('end',
│ │ } │ () =˃ { call.end(); }
│ │ │ )
│ │ │ }
└─────────┴────────────────────────────────────┴──────────────────────────────────────────
GºNODE.JS CLIENT IMPLEMENTATION SUMMARYº
messages = require('...._pb')
services = require('...._grpc_pb')
grpc = require('grpc')
client = new services
.RouteGuideClient(
'localhost:50051',
grpc
.credentials
.createInsecure())
┌────────────────────────────────┬───────────────────────────────────
│ºSINGLE RESPONSEº │ºSTREAM RESPONSEº
┌──────────┼────────────────────────────────┼───────────────────────────────────
│ºSINGLE º │ function featureCallback( │ function runListFeatures(callback)
│ºREQUESTº │ error, feature) │ {
│ │ { │ call = client
│ │ if (error) { │ .listFeatures(rect);
│ │ .... │ call.on(
│ │ return │ 'data',
│ │ } │ (feature) =˃ {
│ │ var latitude = feature.get.. │ // process feature 'event'
│ │ ... │ })
│ │ next(); │ call.on('end', callback);
│ │ } │ }
│ │ client │
│ │ .getFeature( │
│ │ point1, │
│ │ featureCallback); │
├──────────┼────────────────────────────────┼───────────────────────────────────
│ºSTREAM º │ //* a) Create call.write(able)*│ function runRouteChat(callback)
│ºREQUESTº │ //* object* │ {
│ │ var call = client.recordRoute( │ //*1) create call.write(able)*
│ │ function( │ //* object with onData notifier*
│ │ error, │ var call = client.routeChat()
│ │ stats // ← server response │ call.on(
│ │ ) │ 'data',
│ │ { │ (note)=˃ { // ← response from stream /
│ │ if (error) { │ //*2) callback for stream response*
│ │ ... │ //* object received*
│ │ return │ .. do whatever with note response
│ │ } │ }
│ │ // process stats response │ )
│ │ } │ //*3) Setup onEnd*
│ │ ); │ call.on('end', callback)
│ │ //* b) Write to stream* │
│ │ for ("location in ddbb") { │ //*4) Write to request stream*
│ │ call.write(location); │ for ("note in note_ddbb") {
│ │ } │ var noteMsg = new messages
│ │ │ .RouteNote().setXXX..;
│ │ │ call.write(noteMsg);
│ │ │ }
│ │ │ call.end();
│ │ │ }
└──────────┴────────────────────────────────┴───────────────────────────────────
Rsocket (gRPC alt)
Rsocket authors claim that it's better than gRPC:
- @[https://github.com/rsocket/rsocket/blob/master/Motivations.md]
- @[https://medium.com/netifi/rpc-thunder-dome-3103e2449957]
Data Visualization
SVG Summary
˂svg xmlns="http://www.w3.org/2000/svg" ← xmlns not necesary inside html
viewBox="0 0 256 256" ← left_x up_y width height
preserveAspectRatio="xMidYMid" ← Optional uniform scaling:
xMidYMid aligning to the middle.
xMinYMin aligning to the top-left.
xMaxYMax aligning to the bottom-right.
(think of viewBox having 9 different
anchor points)
transform="scale(1,-1)" ˃ ← Make y coordinate work bottom-to-top.
┌ ˂g stroke="orange" class="..." ← group: Apply default attribute to children
│ fill="orange" ← fill: shape color (name|#hex|rgb()|rgba())
│ opacity=".5" ˃
│ ˂circle cx="100" cy="100" r="100" /˃
│ ˂ellipse cx="100" cy="100" id=".." ← id can also be used as in html
│ rx="100" ry="50" /˃
│ ˂rect x="0" y="0"
│ width="10" height="4"
│ rx="5" ry="5" ← Optional. rx/ry: Radio x/y
│ stroke="orange" ← Adds line around outside-shape/along-path.
│ stroke-width="10"
│ stroke-opacity=".5"
│ stroke-linecap="round" ← control stroke-end shape: butt, round, square.
│ stroke-linejoin="bevel" ← control corner-style: miter, round, bevel.
│ stroke-miterlimit="4" ← control two-different-stroke
│ corners "meeting" at a point
│ /˃ (avoid pointy triangle)
│
│ ˂polygon points="0,0 10,0 0,10" /˃ ← Polygon (triagle in this case)
│ ˂line x1="0" y1="0" x2="3" y2="5" ← x1/y1: start, x2/y2: end
│ stroke-dasharray="4,6" /˃ ← create dashed lines for strokes
│ 4,6: "4-pixel-dash then 6-pixel-space"
│ ˂polyline points="0,0 1,2 2,1 3,3"/˃ ← multipoint x1/y1 x2/y2 x3/y3 ... line.
│ ˂path d="M1,1 Q2,2 3,1" /˃ ← multiple anchors with handles.
└ ˂/g˃ Generally written by a program (vs by hand)
˂text x="10" y="10" ← x,y: anchor point
text-anchor="middle" ← alignment: start | middle | end
textLength="900" ← Space out letters to fill width.
Lorem Ipsum...
˂tspan fill="limegreen" ← tspan used to surround words inside ˂text˃
dx="5" dy="-3"˃
... some text ...
˂/tspan˃
˂/text˃
˂a xlink:href="https://..."˃ ← Add hyperlink
˂text˃Dinosaurs!˂/text˃
˂/a˃
˂image xlink:href="..." /˃
˂symbol id="icon01" viewBox="..."˃ ← defining Reusable symbol
˂rect .../˃
˂/symbol˃
˂use xlink:href="#icon01" /˃ ← Using it later on.
┌ ˂defs˃ ← section to create shapes that won't be
│ visible until used in other places like
│ gradients, paths for text, masks, etc.
│
│ ˂linearGradient ºid="grad01"º˃ ←ºGRADIENTSº
│ ˂stop offset="0%" stop-color=" .." /˃
│ ˂stop offset="100%" stop-color=" .." /˃
│ ˂/linearGradient˃
│ ˂mask ºid="mask01"º ˃ ºMASKINGº
│ ˂image width=... height=... ← Mask Image SHOULD be black-and-white
│ xlink:href="..." /˃ (black:transparent, white: show)
│ ˂/mask˃
│
│ ˂pattern id="texture01" ºTEXTURING ⅋ PATTERNSº
│ width=... height=...
│ patternUnits="userSpaceOnUse"˃
│ ˂image xlink:href="..." ... /˃
│ ˂/pattern˃
│ ˂filter id="blur01"˃ ºFILTERº
│ ˂feGaussianBlur
│ in="SourceGraphic"
│ stdDeviation="5" /˃
│ ˂/filter˃
└ ˂/defs˃
˂rect fill="url(#texture01)" ... ← Apply texture to shape / text
Alt. fill="url(#grad01)" (Apply gradient)
filter="url(#blur01)" ← Apply filter to shape / text
mask="url(#mask01) /˃ ← Apply mask to shape / text
˂/svg˃
Perspective
@[https://github.com/jpmorganchase/perspective]
@[https://perspective.finos.org/]
• interactive visualization forºlarge, real-time datasetsº
and customizable analytics.
• JS engine, runs on any modern browser.
• Integrates withºPython and JupyterLabº.
• fast, memory efficient streaming query engine,
ºwritten in C++ and compiled for both WebAssembly and Python,º
ºwith read/write/stream/virtual support for Apache Arrow.º
D3.js
• External resouces:
@[https://d3js.org/]
@[https://github.com/d3/d3]
@[https://www.youtube.com/watch?v=_8V5o2UHG0E&t=894s]
• White-paper summary:
@[http://idl.cs.washington.edu/files/2011-D3-InfoVis.pdf]
· ...based on CSS these libraries (jQuery) share the concept
of a selection:
identify a set ofe lements using simple predicates, then
apply a series of operators that mutate the selected elements.
· For data visualization, document transformers must handle the
cre-ation and deletion of elements, not just the styling of existing
nodes.This is impossible with CSS, and tedious with jQuery.
· D3 alternatives, like Processing and Raphaël are tedious for [comparative]
complex tasks (no convenient abstractions).
•ºselection º: D3's atomic operand: filter over document's elements.
•ºOperators º: Act on selections, modifying content.
•ºData joinsº: bind input data to elements, enabling functional
operators that depend on data, and producing
enter and exit subselections for the creation and
destruction of elements in correspondence with data.
•ºevent º: Special operators responding to user input and
ºhandlers º enable interaction.
•ºhelper º: simplify common tasks:
ºmodules º ✓ data-processing utilities such as next/cross operators,
CSV parser, date/number formating, ...
✓ layouts:
✓ ...
const x = (d, i) =˃ { return i*25 }, ← scale fun. for position encoding
y = (d, i) =˃ { return 10 -d }
const svg = d3
.select("body") ← Add SVG container to body
.append("svg:svg")
.data([[1, 2, 3, 2, 1, 0.5]]) ← bind data (array of arbitrary values)
Data is then passed to functional operators
ºas first argument by conventionºalong with
numeric index (i). By default Nth element
is binded to Nth data (ordered by index).
Aº"key" functionºcan override the def.behaviour.
º*1º
data can also be used to reorder(ºsortº)and/or
cull (ºfilterº) DOM elements.
svg.append("svg:path") ← Add path element for the area
.attr("class", "area")
.attr("d",
d3.svg
.area().x(x).y0(14).y1(y))
svg.append("svg:path") ← Add path element to emphasize
.attr("class", "line") the top line.
.attr("d" ,
d3.svg.line().x(x).y(y))
const g = svg.selectAll("g") ← Add containers for reference values.
.data(d3.range(0,2, .5)) select || selectAll accept CSS selector
.enter() syntax?.
selects can be chained,
ex: selectAll("p").select("b")
.append("svg:g") ← insert(),append() ops. add new elements to DOM
remove ops. del. elements from DOM
transition(..) same syntax than style/attr but
interpolating from current to final value
gradually over time.
g.append("svg:line") ← Add reference lines.
.attr("class", (d) =˃ {"rule"}) ← operator acting on selection and
.attr("x2",160) wrapping W3C DOM API,
.attr("y1", y) := .attr(...) | .style(...) | .property(...)
.attr("y2", y) .html(...) | .text(...) |
.each(...) (general extension point operator)
and expressed either as constants or functions.
g.append("svg:text") ← (g) Add reference labels.
.attr("x",164)
.attr("y",y)
.attr("dy", ".3em")
.text(d3.format(",.1"))
º*1º ┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐ NOTE: Data is sticky: Once bound
e e e e e e │u││u││u││u││u││ ││ ││ ││ ││ │ to nodes, it is available
└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘ in next re-selection with
└───────┬──────┘ └──────┬──────┘└──────┬──────┘ no need for .data(...) operator
entering data updated data exit nodes with
data with no bounded to no matching data
matching nodes. matching nodes allowed to be
insert will bind removed.
new nodes to the
data.
• used as "kernel" library for other high level ones
e.g.: @[#vega_lite_summary]
• Extracted from @[https://observablehq.com/@d3/learn-d3]
• D3 stands for "Data Driven Documents".
• D3 allows to bind arbitrary data to a Document Object Model (DOM),
and then apply data-driven transformations to the document.
e.g:
INPUT BIND TO
======== =======
number[] --→ D3.js ─┬→ Interactive SVG chart
├→ HTML table
├·····→
•RºStep learning pathº: 30+ modules , 1000s of methods.
Vega Lite!!!
@[https://vega.github.io/vega-lite/]
@[https://vega.github.io/vega-lite/examples/]
•ºHIGH-LEVEL GRAMMAR OF INTERACTIVE GRAPHICSºwith declarative JSON syntax.
by University of Washington Interactive Data Lab
• visualizations for data analysis and presentation.
• Used by:
Tableau , Google, Microsoft, Airbnb, Los Angeles Times, CERN,
Carnegie Mellon University, Berkeley University, ...
• Extracted from: https://vega.github.io/vega/about/vega-and-d3/
... D3 is intentionally a lower-level library (than Vega) ...
a "visualization kernel" rather than a "toolkit" or "framework"
... intended as a supporting layer for higher-level visualization
tools. Vega is one such tool, and Vega uses D3 heavily within its
implementation: vega lite → vega → d3.js
•ºHOWTO: STEP 1) HTML SETUPº
@[https://vega.github.io/vega-lite/tutorials/getting_started.html]
┌──────────────────────────────────────────────────────────────────────────┐
│ ˂!DOCTYPE html˃ │
│ ˂html˃ │
│ ˂head˃ │
│ ... │
│ ˂script src="https://cdn.jsdelivr.net/npm/vega@5.20.2" ˃˂/script˃ │
│ ˂script src="https://cdn.jsdelivr.net/npm/vega-lite@5.1.0" ˃˂/script˃ │
│ ˂script src="https://cdn.jsdelivr.net/npm/vega-embed@6.17.0"˃˂/script˃ │
│ │
│ ˂style media="screen"˃ │
│ .vega-actions a { /* Add space between Vega-Embed links */ │
│ margin-right: 5px; │
│ } │
│ ˂/style˃ │
│ ˂/head˃ │
│ ˂body˃ │
│ ... │
│ ˂divºid="vis"º˃˂/div˃ ˂!-- visualization container --˃ │
│ │
│ ˂script˃ │
│ var vlSpec = { │
│ $schema: 'https://vega.github.io/schema/vega-lite/v5.json', │
│ ºdata º: { values: [ ... ] }, │
│ ºmark º: 'bar', │
│ ºencodingº: { ... } │
│ } │
│ │
│ vegaEmbed(º'#vis'º, vlSpec); // Embed visualization in "div" │
│ ˂/script˃ │
│ ˂/body˃ │
│ ˂/html˃ │
└──────────────────────────────────────────────────────────────────────────┘
•ºHOWTO: STEP 2) DATA and Visualization SETUPº
@[https://vega.github.io/vega-lite/tutorials/getting_started.html]
TABULAR DATA SET │ EQUIVALENT VEGA-LITE
│ JSON ARRAY + RENDER PROP
================ │ ========================
var. a var. b │ {
cate- vs nume- │ º"data"º: { ← "data" prop defines data source for chart
gorical rical │ "values" : [ Other data sources type supported
C ←→ 2 · · ·│· · · {"a": "C", "b": 2}, ┐ e.g: "data": {"url": ".../data/myData.csv"}
C ←→ 7 · · ·│· · · {"a": "C", "b": 7}, ┤ ← each value (row) will be rendered
C ←→ 4 · · ·│· · · {"a": "C", "b": 4}, ┤ as a1visual markº
D ←→ 1 · · ·│· · · {"a": "D", "b": 1}, ┤
...
E ←→ 7 · · ·│· · · {"a": "E", "b": 7} ┘
│ ]
│ },
vega-lite ┌ → º"mark":º"point", ← mark key indicates how to render data
chart │ │ mark properties := position, size, color
visualization ─┤ │ vega-lite will render one point per "data" object
properties │ │
└ → º"encoding":º{ ← Without encoding output marks will overlap.
│ "x": { ← encode "x-channel" to ("row") data fields.
vega-lite │ "field": "a", ("a" in this case)
chart │ "type" : "nominal" := "quantitative" | "temporal" | "ordinal" | "nominal" º*1º
visualization │ },
│ "y": { ← "x" alone will already render "a" distribution.
│ "field": "b", "y" to compare "var a" vs "var b"
│ "type" : "quantitative"
│ "title": "b's average" ← optional.
│ "aggregate": "average" ← optional := "aggregate" | "average"
│ } ^
│ } "mark" : "bar"
│ }
º*1º@[https://vega.github.io/vega-lite/docs/encoding.html#type]
TIP: Vega-Lite will auto. add axis-with-labels for different-categories + axis title.
•ºEXPLORING DATA EXAMPLESº
@[https://vega.github.io/vega-lite/tutorials/explore.html]
QUANTITATIVE CONTINUOUS SAME QUANTITATIVE VAR DISCRETIZED
VAR DISTRIBUTION ALONG X AXIS AS BAR HISTOGRAM ALONG X/Y AXES
============================= ===============================
{ {
"data": ... "data": ...
"mark": "tick", "mark": "bar",
"encoding": { "encoding": {
"x": { "x": {
"field": "precipitation", "field": "precipitation"
"type" : "quantitative" º"bin" : true,º ← bin == discretize cont.var.
} }, (bin quantitative by default)
"y": {
º"aggregate": "count"º
}
} }
} }
MEAN PRECIPITATION VS (month) DATE
==================================
{
"data": ...
"mark": "line",
"encoding": {
"x": {
"field" : "date"
º"timeUnit" : "month",º ← : yearmonth will compare same month
}, in different years
"y": {
"field" : "precipitation"
º"aggregate": "mean",º
}
}
}
WEATHER TYPE OVER MONTH STACKED (aggregate) BAR
==============================================
{
"data": ...
º"mark": "bar"º,
"encoding": {
"x": {
"timeUnit" : "month",
"field" : "date",
"type" : "ordinal"
},
"y": {
º"aggregate": "count", ←···· we need to add "color" prop.
"type" : "quantitative" to indicate stack visualization
}, ·
"color": { ·
"field": "weather", ←······─┘
"type": "nominal",
"scale": { ← Optional. Customize default category colors
"domain": ["sun", "fog" , "drizzle", "rain", "snow"],
"range": ["#ea2","#c77", "#aec7e8", "#1b4", "#946"]
}
}
}
}
•ºCALCULATED FIELD HOW-TOº
=======================
{
"data": ...
"transform": [
{
"calculate" : "datum.temp_max - datum.temp_min",
"as" :º"temp_range"º
}
],
"mark": ...
"encoding": {
...
"y": {
"field":º"temp_range"º
...
}
}
}
Visx
• @[https://www.infoq.com/news/2020/12/airbnb-visx-data-visualization/]
Airbnb Releases Visx, a Set of Low-Level Primitives for Interactive
Visualizations with React components that can be composed into interactive
visualizations.
• Builds on D3 primitives, React component model, DOM handling.
• data visualization solution Bºeasy to learnºwithout sacrificing
expressiveness.
" ... After 3 years of development, 2.5 years of production use
at Airbnb, and a rewrite in TypeScript we are excited to announce
the official 1.0 release of visx (formerly vx). ..."
JointJS
@[http://www.jointjs.com/tutorial]
@[http://www.jointjs.com/demos/umlcd]
• JointJS
INPUT PROCESSING OUTPUT
===== ========== ======
GRAPH MODEL: ···→ attach graph to ···→ rendered view
· N joint.dia.Element's joint.dia.Paper
(or descendants)
· M joint.dia.Link's
^^^^^^^^^^^^^^^^
cell := Element or Link
- Joint.JS "Hello World!:
˂script src="joint.js"˃˂/script˃
˂script˃
const graph01 = new joint.dia.Graph() ← Represent a diagram.
const paper = new joint.dia.Paper({
el: $('#myholder'), ← paper container
width: 600, height: 200, gridSize: 1 ← CSS data
model: graph01, ← attached graph
})
paper.scale(.5);
paper.$el.css('pointer-events', 'none');
graph01.on('all', ← .on(...) D3 operator reacting to input events
On D3, callback receives data and index as
1st arg.RºTODO:Q:ºIs the same for vega-lite?
function(eventName, cell) ← Event handling based on the Backbone MVC
{ ... }) See all events triggered by any model in the
graph.
const rect01 = ← Basic shapes in JointJS:
new joint.shapes.basic.Rect({ ✓ joint.shapes.basic.Rect
position: { x: 100, y: 30 }) ✓ joint.shapes.basic.Circle
✓ joint.shapes.basic.Text
✓ joint.shapes.basic.Image
rect01.attr({ ← Shape styling
rect: { rx: 5, ry: 5,
fill : '#2C3E50',
'stroke-width': 2,
stroke : 'black' },
text: { text : 'my label',
fill : ...,
'font-size': 18, ... }
})
rect01.on('change:position', ← bind event to shape
(element) =˃ {
... element.id
... element.get('position')
})
link.set('vertices', ← Set vertices on a link
[ { x: 300, y: 60 },
{ x: 400, y: 60 },
{ x: 400, y: 20 }
])
link.set('smooth', true) ← Avoid sharp break at vertice
(Use interpolated curve)
JS Pivot Tables
See also Vega-Lite pivot charts @[#vega_lite_summary]
@[https://github.com/search?q=pivot+table]
@[https://pivottable.js.org/examples/]
@[https://github.com/nicolaskruchten/pivottable]
@[https://www.flexmonster.com/demos/pivot-table-js/]
Complex Model Real Time Render
@[https://www.infoq.com/presentations/autodesk-forge-viewer/]
Shwetha Nagaraja & Federico Rocha explore in detail some of the most
interesting heavily-optimized techniques and strategies that Autodesk
Forge Viewer introduces for viewing extremely large 2D and 3D models
in the browser in real-time. These include progressive rendering to
reduce time to first pixel, geometry consolidation to reduce draw
overhead.
Multimedia
- http://www.w3.org/2010/05/video/mediaevents.html
- http://stackoverflow.com/questions/14317179/display-a-video-from-a-blob-javascript
var reader = new FileReader();
reader.readAsDataURL(vid);
function display(vid){
var video = document.getElementById("video");
video.src = window.URL.createObjectURL(vid);
}
- http://html5doctor.com/video-canvas-magic/
http://html5doctor.com/demos/video-canvas-magic/demo2.html
https://developer.mozilla.org/en-US/docs/Web/HTML/Manipulating_video_using_canvas
- Video & MediaSource:
https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html
During playback, the media element pulls segment data out of the source
buffers, demultiplexes it if necessary, and enqueues it into track buffers so
it will get decoded and displayed. buffered describes the time ranges that
are covered by media segments in the source buffer.
Once a new SourceBuffer has been created, it expects an initialization
segment to be appended first. This first segment indicates the number and
type of streams contained in the media segments that follow. This allows the
media element to configure the necessary decoders and output devices
A MediaSource object can be attached to a media element by assigning a
MediaSource object URL to the media element src attribute or the src
attribute of a ˂source˃ inside a media element. MediaSource object URLs are
created by passing a MediaSource object to window.URL.createObjectURL().
Initialization segments are an optimization. They allow a byte stream format
to avoid duplication of information in Media Segments that is the same for
many Media Segments. Byte stream format specifications need not specify
Initialization Segment formats, however. They may instead require that such
information is duplicated in every Media Segment.
| http://updates.html5rocks.com/2011/11/Stream-video-using-the-MediaSource-API
|*//SUMMARY:
| video.src = video.webkitMediaSourceURL;
| ...
| video.webkitSourceAppend(new Uint8Array(...));
| if (endOfStreamDetected) {
| video.webkitSourceEndOfStream(HTMLMediaElement.EOS_NO_ERROR);
| }
|
|*//FULL CODE:*
| const chunkSize = Math.ceil(file.size / 5 /*number of Chunks */); // file is a video file
| var video = document.querySelector('video');
| video.src = video.webkitMediaSourceURL;
| video.addEventListener('webkitsourceopen', function(e) {
| for (var i = 0; i ˂ NUM_CHUNKS; ++i) {
| var startByte = chunkSize * i;
| var chunk = file.slice(startByte, startByte + chunkSize); // get slice
| var reader = new FileReader();
| reader.onload = (function(idx) {
| return function(e) {
| video.webkitSourceAppend(new Uint8Array(e.target.result)); // appending chunk:
| if (idx == NUM_CHUNKS - 1) {
| video.webkitSourceEndOfStream(HTMLMediaElement.EOS_NO_ERROR);
| // appending chunk:
| }
| };
| })(i);
| reader.readAsArrayBuffer(chunk);
| }
| }, false);
|
| splits video into chunks, then "stream" to a ˂video˃ tag by
| appending each chunk to the element using the MediaSource API.
|
| If you're interested in learning more about the API, see the specification.
|
| Support: Currently, the MediaSource API is only available in Chrome Dev Channel 17+ with the
| --enable-media-source flag set or enabled via about:flags.
|
| CONST chunkSize = Math.ceil(file.size / 5 /*number of Chunks */); // file is a video file
Web-Audio Components [TODO]
@[https://github.com/web-audio-components]
• External Resources:
· JS Oscillator:
@[https://tonejs.github.io/examples/oscillator]
WebRTC: Peer-to-peer media streaming
@[http://www.html5rocks.com/en/tutorials/getusermedia/intro/]
@[http://gingertech.net/2014/01/08/use-deck-js-as-a-remote-presentation-tool/]
Kurento Media Server (KMS)
@[https://doc-kurento.readthedocs.io/en/latest/]
• multimedia server to develop advanced video applications
for WebRTC platforms.
• FaceOverlayFilter is a simple Computer Vision example that
detects people's faces on the video streams, to add an overlay
image on top of them.
shaka-player
@[https://github.com/google/shaka-player]
• JS media player library with support for adaptative video streams
(video resolution and bandwith adapted on "real-time" to the available
network and client/resources) supporting DASH and HLS protocols.
and MSE-EME player.
• 5.5K stars on Github
Binary Data
@[https://github.com/jDataView]
- jBinary High-level API for working with binary data.
- jDataView. DataView. Extended. Anywhere.
@[https://github.com/jDataView/jBinary/wiki/jBinary-Constructor]
@[https://github.com/jDataView/jDataView]
@[http://www.khronos.org/registry/typedarray/specs/latest/]
| "* BufferArray: Array of Bytes. Constructor BufferArray(byte length)
| * TypedArray: Type view of the Buffer Array (Ui|i)nt(8|16|32)Array , Float(32|64)Array
| Constructors:
| +- TypedArray(byte length) ← Creates new BufferArray
| +- TypedArray(TypedArray (existing)array) ← Creates new BufferArray duplicated of array
| +- TypedArray(sequence array) ← Creates new BufferArray from Javascript array
| +- TypedArray((existing)buffer[, byteOffset, length]) ← Creates new view from existing buffer
| Methods:
| +- void set( TypedArray array[, offset]) ← copy values from TypedArray
| +- void set( type[] array[, offset]) ← copy values from JS array
| +- TypedArray subarray(offsetFirstElement [, offsetLastElement=""last one""]) ← Creates a view, not a new buffer!!
|
| * DataView: Low level endianess aware view of the Buffer (use for file/hardware/network access with a defined endianess defined ""outside"" our computer)
| Contructor:
| +- DataView(ArrayBuffer buffer[, byteOffset, byteLength])
| Methods (Read):
| +- get(Ui|I)nt(8|16|32)(byteOffset[, littleEndian])
| +- getFloat(32|64)(byteOffset[, littleEndian])
| Methods (Write):
| +- set(Ui|I)nt(8|16|32)(byteOffset, value[, littleEndian] )
| +- setFloat(32|64)(byteOffset, value[, littleEndian] )
| Note: Detecting endianness:
| var littleEndian = function() {
| var buffer = new ArrayBuffer(2);
| new DataView(buffer).setInt16(0, 256, true);
| return new Int16Array(buffer)[0] === 256; /* true | false */
| };
- Creating a simple array of 128 32-bit floats and filling with 8 consecutive floats:
var f32s = new Float32Array(128);
var groupLength = 8;
for (var i = 0; i ˂ 128/groupLength; ++i) {
var sub_f32s = f32s.subarray(i, i+groupLength);
for (var j = 0; j ˂ 8; ++j) {
sub_f32s[j] = j;
}
}
- Representing a point with 3xFloat32 coordinates values plus 4xUint8 color data values:
var elementSize = 3 º Float32Array.BYTES_PER_ELEMENT + 4 º Uint8Array.BYTES_PER_ELEMENT;
var buffer = new ArrayBuffer(4 * elementSize);
var coords = new Float32Array(buffer, 0);
var colors = new Uint8Array(buffer, 3 * Float32Array.BYTES_PER_ELEMENT);
- Slicing a large array into multiple regions:
var buffer = new ArrayBuffer(1024);
var floats = new Float32Array(buffer, 0 /* 0 offset*/, 128); (128*4 = 512 bytes)
var shorts = new Uint16Array (buffer, 512 /*floats offset*/, 128); (12*2 = 256 bytes)
var bytes = new Uint8Array (buffer, shorts.byteOffset + shorts.byteLength);
- JBinary
ref: https://github.com/jDataView/jBinary
- Typical scenario:
- Create your custom types using jBinary.Type (if needed).
- Describe type set with JavaScript-compatible declarative syntax.
- Create jBinary instance from jDataView (or any underlying type) and your type set.
- Constructors:
+- new jBinary(jDataView data[, typeSet])
+- new jBinary(javascript Byte array[, typeSet])
Read Methods:
+- read(type[, offset = binary.tell()]) ← If offset is provided internal pointer doesn't moves
Pointer Methods:
+- tell(): Return the current position. Ex: var currentPos = binary.tell();
+- seek(position[, callback]): Go to position; if callback → execute it and reset position.
+- skip(count[, callback]): Advance by count bytes; same callback behavior as seek
Instance helpers
+- slice(start, end[, forceCopy = false]): Returns sliced version of current binary with same type set.
+- as(typeSet[, modifyOriginal = false]): Casts instance to typeSet
Loading Data
+- jBinary.loadData(source, callback(error, data)) (static method)
| source := Blob/File | HTTP(S)_URL | Data-URI (simple|base64-encoded) | Node.js (local file path|stream)
var b1 = new jBinary([0x05, 0x03, 0x7F, 0x1E]); // with default typeset
var b2 = new jBinary(new jDataView(data, 5, 10, true), { MetaName: ['string', 30]}); // typeset with custom type MetaName.
var firstInt = b1.read('uint32'); // uint32 value at offset 0 → Pointer moves 4 bytes
var byteAtOffset100 = b2.read('uint8', 100);// uint8 value at custom position → Pointer unchanged(offset provided)
var intAt200 = binary.seek(200, function () { return this.binary.read('int32') });
var intAfter8 = binary.skip(8, function () { return this.binary.read('int32') });
var binary = someExternalBinary.as(TAR); // casting
fileInput.addEventListener('change', function () {
jBinary.loadData(fileInput.files[0], function (error, data) {
if (error) { return console.log(error); }
...
});
});
jBinary.load('sample.tar', function (error, binary) {
if (error) { return console.log(error); }
var tar = binary.read('File'); // TAR format auto-detected (using jBinary.Repo)
});
- Typesets normally are object dictionaries
{ typeName1 : type1 , ← types that can refer to each other but they may also contain special config
typeName2 : type2 , values that set some global options or modes for entire typeset. Options are:
typeName3 : type3 ,
typeName4 : type4 ,
typeName5 : type5 ,
typeName6 : type6 ,
...
}
- jBinary.all - reference to general type that represents entire data; required for enabling user to read/write entire file at once.
- jBinary.mimeType - sets mime-type which should be used for saving data from this typeset (i.e., when calling toURI without argument).
- jBinary.littleEndian - sets endianness for this format.
- __________ Integers ______________
uint8 (byte) / int8 - one-byte integer.
uint16 / int16 - word.
uint32 / int32 - dword (double-word).
uint64 / int64 - qword - please see warning about precision loss in jDataView documentation.
___________ Floats ______________
float32 (float) - classic 32-bit float used in languages like C.
float64 (double) - 64-bit float with double precision (double in C), default for JavaScript number representation.
___________ Strings ______________
char - one-byte binary character.
string(@length, encoding = 'binary') - string of given length in binary or 'utf8' encoding, reads/writes to the end of binary if length is not given.
string0(@length, encoding = 'binary') - null-terminated string stored in given number of bytes; treated as dynamic null-terminated string if length is not given.
_________ Complex types __________
const(baseType, value, strict = false) - treats type as constant; if read value does not match expected and strict mode is enabled, calls strict(readValue) if it is function or simply throws TypeError if not.
array(baseType, @length) - array of given type and length, reads/writes to the end of binary if length is not given.
object(structure, proto = Object.prototype) - complex object of given structure (name → type), creates new context while processing inner properties; object may also contain functions instead of types for calculating some values during read/write for internal purposes.
extend(...object structures...) - extends one structure with others; merges data into one object when reading and passing entire object when writing.
enum(baseType, matches) - enumeration type with given key <=> value map (if value not found in the map, it's used "as-is").
_________ Binary types __________
bitfield(length) - unsigned integer of given bit length (supports up to 32 bits, wraps around 2^32).
blob(@length) - byte array represented in most native type for current engine; reads/writes to the end of binary if length is not given.
binary(@length, typeSet = {}) - jBinary instance on part of original one with given length and optional custom typeset (useful for container formats); accepts also raw binary data when writing.
_____ Control statements ______
if(@condition, trueType, falseType) - conditional statement.
if_not(@condition, falseType, trueType) - same but inverted.
skip(@length) - simply skips given length on read/write.
______About @ __________________
All the arguments marked with @(references) can be passed not only as direct values, but also as getter functions callback(context) or string property names inside current context chain.
- Usage (in Browser): Include scripts for jDataView and jBinary like that:
˂script src="//jdataview.github.io/dist/jbinary.js"˃˂/script˃
˂script˃
var typeSet = { magic: ['array', 'uint8', 4] };
jBinary.load('file.bin', typeSet, function (err, binary) { console.log(binary.read('magic')); } );
˂/script˃
- HLS player using JBinary
http://rreverser.github.io/mpegts/
- Ex1: Audio Data Transport Stream (ADTS):
(function (exports) {
var ADTS = {
/* :
* Structure: AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
* ^bit (not byte)
* Header consists of 7 or 9 bytes (without or with CRC).
* Usage in MPEG-TS
* - ADTS packet must be a content of PES packet. Pack AAC data inside ADTS frame, than pack inside PES packet, then mux by TS packetizer.
* - ADTS frames goes one by one in TCP stream. Look for syncword, parse header and look for next syncword after.
*/
ADTSPacket: {
/*bits Letter*/ _start : function () { return this.binary.tell() },
/*12 A */ _syncWord : ['const', 12, 0xfff, true], // syncword 0xFFF, all bits must be 1
/* 1 B */ version : ['enum', 1, ['mpeg-4', 'mpeg-2']], // MPEG Version: 0 for MPEG-4, 1 for MPEG-2
/* 2 C */ layer : ['const', 2, 0], // Layer: always 0
/* 1 D */ isProtectionAbsent: 1, // protection absent, Warning, 1 → no CRC, 0 → CRC
/* 2 E */ profileMinusOne : 2, // Audio_Object_Types
/* 4 F */ samplingFreq : ['enum', 4, [96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350]],// MPEG4 Sampl.FrequencyIndex
/* 1 G */ _privateStream : 1, // private stream, set to 0 when encoding, ignore when decoding
/* 3 H */ channelConfig : 3, // MPEG4 Audio Channel_Configurations(if 0, configuration is sent via an inband PCE)
/* 4 IJKL */ _reserved : 4, // originality, home, copyrighted, copyright start bits
/*13 M */ frameLength : 13, // frame length value. Must include 7 or 9 bytes of header length: = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)
/*11 O */ bufferFullness : 11, // Buffer fullness
/* 2 P */ aacFramesCountMinusOne: 2, // # of AAC frames(RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame
/*16 Q */ data : ['blob', function (context) { return context.frameLength - (this.binary.tell() - context._start) }] // CRC if protection absent is 0
}
};
if (typeof module !== 'undefined' && exports === module.exports) {
module.exports = ADTS;
} else {
exports.ADTS = ADTS;
}
})(this);
- Ex2: MPEG-TS Packet:
MPEG-TS Packet:
Name |#bits|Description
_______________________________________________________________________________________________
sync byte |8 |Bit pattern from bit 7 to 0 as 0x47 or ASCII char 'G"
Transport Error Indicator(TEI)|1 |Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
Payload Unit Start Indicator |1 |Boolean flag with a value of true meaning the start of PES data or PSI otherwise zero only.
Transport Priority |1 |Boolean flag with a value of true meaning the current packet has a higher priority than other packets with the same PID.
PID |13 |Packet Identifier
Scrambling control |2 |'00' = Not scrambled. For DVB-CSA only→'01':Reserved,'10':Scrambled with even key,'11':Scrambled with odd key
Adaptation field exist |1 |Boolean flag
Contains payload |1 |Boolean flag
Continuity counter |4 |Sequence number of payload packets. Incremented only when a payload is present (i.e., payload value is true)
Note: the total number of bits above is 32 and is called the transport stream 4-byte prefix or Transport Stream Header.
Adaptation field |0+ |If adaption field exist value is true
Payload Data |0+ |If contains payload value is true
jBinary Packet Representation: {
_startof: function () { return this.binary.tell() },
_syncByte: ['const', 'uint8', 0x47, true],
transportError: 1,
payloadStart: 1,
transportPriority: 1,
pid: 13,
scramblingControl: 2,
_hasAdaptationField: ['Flag', 'adaptationField'],
_hasPayload: ['Flag', 'payload'],
contCounter: 4,
adaptationField: ['FlagDependent', '_hasAdaptationField', 'AdaptationField'],
payload: ['FlagDependent', '_hasPayload', jBinary.Template({
getBaseType: function (context) {
var pid = context.pid, file = this.binary.getContext(1);
if (pid ˂ 2 || pid in file.pat) {
ANGULAR [Official Doc]
Ext.Links
- Angular API:
@[https://angular.io/api?type=package]
- Github Repo:
@[https://github.com/angular/angular]
What's new
•BºAngular 11º
@[https://www.infoq.com/news/2020/11/angular-11/]
@[https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7]
- Hot Module Replacement implementation:
$º$ ng serve --hmr º ← It can be initialized with --hmr flag.
NG app will not need full page refresh on code changes:
Instead, latest changes to code/style/templates will be
updated directly while preserving the current state of
the application.
- Build process speed boost by upgrading to TypeScript 4.0
and through improving the ngcc update process.
- developers can now opt-in to the experimental Webpack 5 support
(faster builds with persistent disk caching and smaller bundle sizes
using improved tree-shaking). To enable it:
"resolutions": { "webpack": "5.4.0" } ← Add to package.json
- NG language service (popular add-on offers autocomplete, type checking, etc...)
is being transitioned to the new Ivy engine, offering improved support for
generic types.
- For simple upgrade of projects:
$º$ ng update @angular/cli @angular/coreº
RºWARNº: for more complex updates, use the interactive update guide
provided by the Angular team.
•BºAngular 9º
@[https://www.infoq.com/news/2020/02/angular-9-ivy-rendering-engine/]
• Ivy Compiler:
Smaller bundle sizes, improved build times, and better debugging
thanks to the release of the much anticipated Ivy rendering engine
that has been in development by Google since 2018.
Ivy uses tree shaking, allowing it to include only the parts of
Angular needed for the current application.
•BºAngular 5º
Service worker implementation shipping by default.
Architecture
@[https://angular.io/guide/architecture]
ºVIEWº: "screen element" on browser, mobile, ...
ºCOMPOMENTº: $º $ ng generate component "compName" º
• Typescript Class with ng-decorator.
• Defines N views, arranged hierarchically.
The (optional) ºRouter serviceº defines navigation paths
among them.
• At least one ºroot componentº is needed by App, connecting a
component hierarchy with the page document object model (DOM).
• ºServiceº (providers) are injected (@Injectable() decorator) to provide
needed non-visual functionality.
• When a component is instantiated, it's associated directly with
the (root) host view.
Bº DATA º
ºTEMPLATEº == HTML + BºBINDINGº + DIRECTIVES
BºMARKUP º @[https://angular.io/guide/built-in-directives]
└─┬──┘ └───┬────┘
┌────────────┘ └ There can be:
FRONTEND │ ANGULAR ├─ Component directives (the most common)
DOM │ COMPONENT │
└┬┘ ┌──────┴─────────────────┐ └───┬───┘ ├─ Attribute Directives:
│ │ │ - [ngClass]="JS expression"
←················Bº{{value}}º···· ←┤ │ - [ngStyle]="JS expression"
│ │ │ - NgModel two way data binding:
│ │ │
←·······Bº[property]="value"º···· ←┤ │ PRE-SETUP: add FormsModule to @"X".module.ts
│ │ │ imports:[...,FormsModule]
│ │ │
├─··→·Bº(click)="handler"º·········→ │ ˂input [(ngModel)]="model.field01" id="field01"˃
│ │ │
│ │ │
←····Bº[(ng-model)]=comp.varNameº··→ └─ Structural directives: add/remove elements
│ │
----------------------- NgIf, NgFor, NgSwitch:
Two way binding (forms mainly). Ex: ˂app-item *ngIf="isActive" [item]="item"˃...
˂input [(ngModel)]="hero.name"˃
˂div *ngFor="let I of items"˃{{I.name}}˂/div˃
☞GºAll data bindings are processed once º
Gºfor each JS event cycle from appº ˂div [ngSwitch]="e.index"˃
Gºroot component through tree children.º ˂comp1 *ngSwitchCase="'1'" [item]="ddbb[1]"˃˂/comp1˃
...
˂comp1 *ngSwitchDefault [item]="ddbb[0]"˃˂/comp1˃
˂/div˃
e.g.: ˂input type="text" [(ngModel)]="inputIdentity" (change)="userInputFromUICallback()"˃
ºTEMPLATEº : template → evaluate → resolve → modify
ºEVALUATIONº input directives binding HTML DOM
syntax
ºTEMPLATE PIPESº (data transformation)
• Ex: date data → pipe → locale-date
• Predefined Pipes (@[https://angular.io/api?type=pipe]):
Async , Currency , Date , Decimal, I18n(Plural|Select), JSON,
KeyValue, (Lower|Upper)Case , Percent , Slice , TitleCase
User click → (intercepted by) → Router → (reacts by) → showing/hiding view hierarchies.
(ºlazy loadº └───────┬──────┘
of related modules) │
┌····································Bºhierarchical structureºis key ···┘
│ to how Angular detects/reacts
│ to changes in the DOM|app data.
┌───┴─────┐
┌→ view root ←→ NgModule1.rootComponent ┐ ←ºboostrapping moduleº (ex. app.module.ts)
· └- view1 ←→ NgModule1.component1 ├ A view hierarchy can include
· └- view2 ←→ NgModule2.rootComponent │ views from components in the
· └- view3 ←→ NgModule2.component3 ┘ same or different NgModule
· └───┬───┘ └───┬───┘
· | @[https://angular.io/guide/architecture-components]
· | - (typescript) app logic controling a view
· | through an properties+methods API.
· | -
· @[https://angular.io/guide/architecture-modules]
· - container|scope for a set of (component list, Service list, ...)
· -Bºcompilation contextº for components:
· - orthogonal app-domain|workflow|capabilities set.
· - can depend on other (imported) NgModules
·
└→ Ex Module: app.module.ts:
import {OºNgModuleº } from '@angular/core'; ← JS input
import {BºBrowserModuleº } from '@angular/platform-browser'; ← JS input
º@NgModuleº({ ← Decorator function.
ºimportsº: [ BºBrowserModuleº ],
ºprovidersº: [ Logger ], ← Creators of services that this NgModule
contributes to. (providers at component
level can also be specified , which is
often preferred).
A service declared here also ensures that it
will be available as a singleton instance to
any other component/service.
declarations: [ AppComponent ], ← components|directives|pipes declared
exports: [ AppComponent ], ← (opt) Subset of declarations accesible
by templates in other NgModules
bootstrap: [ AppComponent ] ← (Only root module): main app view
})
export class AppModule { } ← JS export (vs NgModule export)
→ Ex Component: src/app/hero-list.component.ts
º@Componentº({
selector : 'app-hero-list', ← associated html tag name
┌→ templateUrl: './hero-list.component.html', ← seteable property programatically
·┌ providers: Bº[HeroService]º ← required Service prov. array
·· }) ºSize must match constructor arg. sizeº
··
·· export class HeroListComponent implements ºOnInitº {
·· Oºheroesº : Hero[]; ← data app property. Note: Many components wrap all
·· selectedHero: Hero; ← data app property data in a model dict to make code
·· more readable.
·└ constructor Bº(private service:HeroService)º ← service will be injected
· { ...} ºMust match @Component.providers
·
· Qº@Input()ºhero : Hero; // ← @Input() marks field as input, bounding it
· Qº@Input()ºpower: string; // to template DOM prop. Used to comunicate data
· // among parent←→children components.
· @[https://angular.io/guide/inputs-outputs]
· ...
· QºngOnChanges(change_map: SimpleChanges)º{ // @[https://angular.io/api/core/OnChanges]
· └───────────┬───────────┘
· /* lifecycle hook called when data-bound properties change.
· * Implemented by NgModel, FormControl*, Max/MinLenghtValidator, ...
· * maps each changed property to a SimpleChange
· * instance with (current,previous) values. */
· for (let key in change_map) {
· let chng = change_map[key];
· let cur = JSON.stringify(chng. currentValue);
· let prev = JSON.stringify(chng.previousValue);
· .log('PropName: ${key}: Val: ${cur}, previous ${prev}');
· }
· }
·
·
· ºngOnInit()º { ←·lifeCycle hook
· this.Oºheroesº = this.service.getHeroes(); See also detailed examples at:
· } @[https://angular.io/guide/lifecycle-hooks]
· selectHero(hero: Hero) { this.selectedHero = hero; } │
· } └─────────┬──────────┘ │
· └─────────────────────────────────────────┐ │
· │ │
└→ Ex template: src/app/hero─list.component.html │ │
˂h2˃Hero List˂/h2˃ │ │
Pick a hero from list: │ │
˂ul˃ ┌───────┴──────┐ │
˂li º*ngForº="let hero of Oºheroesº" º(click)º="selectHero(hero)"˃│
Oº{{hero.name | UpperCase}}º │
˂/li˃ │
˂/ul˃ │
│
˂app-hero-detail ← another component │
º*ngIfº="selectedHero" │
º[hero]º="selectedHero"˃ │
˂/app-hero-detail˃ │
Qº˂on-changes [hero]="hero" [power]="power"˃˂/on-changes˃º │
│
┌───────────────────────────────────────────────────────────┘
┌───────┴───────────┐
ºMain Life-cycle Hooksº for Component(or Directive in general):
HOOK PURPOSE AND TIMING
──── ──────────────────
ngOnChanges(changes: SimpleChanges) (re)sets data-bound input properties.
ngOnInit() Initialize component after first display
with data-bound properties and sets the
directive/component's input properties.
ngDoCheck() Detect and act upon changes that Angular
can't or won't detect on its own.
ngAfterContentInit() Respond after Angular projects external content
into the component's view
ngAfterContentChecked() ...
ngAfterViewInit() ...
ngAfterViewChecked() ...
ngOnDestroy() tap into Cleanup just before Angular destroy
Use it to Unsubscribe Observables and detach
Other hooks: event handlers to avoid memory leaks.
- Angular sub-systems
- 3rd party libraries may add new hooks.
└→ Ex Service: src/app/logger.service.ts
$º$ ng generate service ...º
BºService: "Do one thing and do it right" (root/module/component)singletonº.
- Injected in components through constructors.
@Injectable()
export class Logger {
log (msg: any) { console.log (msg); }
error(msg: any) { console.error(msg); }
warn (msg: any) { console.warn (msg); }
}
Note on Services injection procedure:
Angular will create 1+providers for each service(or function|value).
- The provider can be part of the service's own metadata, to make
it available everywhere, or it can be registered with an specific
module|component using the respective decorator
(@Injectable(), @NgModule() or @Component())
NOTE: If the providers is in @Injectable() decorator, Angular
will optimize the app by removing it, if it's not used.
-ºService SCOPE:º
┌──────────┬─────────────────────────────────┬──────────────────────────────
│ LEVEL │ Scope │ Declaration
├──────────┼─────────────────────────────────┼──────────────────────────────
│ ROOT │ single instance injected in all │ @Injectable(
│ (app) │ app─components (Default) │ {providedIn: 'root'})
│ │ │ (@ service01.service.ts)
├──────────┼─────────────────────────────────┼──────────────────────────────
│ NgModule │ single instance injected in all │ @NgModule( ...
│ │ module─components │ { providers:
│ │ │ [ service01, service02 ]
│ │ │ })
├──────────┼─────────────────────────────────┼──────────────────────────────
│ Component│ new service instance created for│ @Component({
│ │ each new component │ selector : '...'
│ │ │ ,templateUrl: '...component.html'
│ │ │ ,providers : [ service01 ]
│ │ │ })
└──────────┴─────────────────────────────────┴──────────────────────────────
Responsive App
BºObservables and event processingº
@[https://angular.io/guide/observables]
- similar (but not identical) to the publish/subscribe
design pattern. In the observer pattern:
"subject" object maintains a list o its dependents (observers)
"subject" automatically notifies dependent observers of state
changes.
Component01 → +Observable: Observable is a function that
function executes only when a consumer subscribes
Component02 → Component02 : create Component02.Observer01
Component02 → Observable : subscribe (Component02.Observer01)
function
Observable function
execution "loop":
Observable → Observer01: event (keystroke, HTTP response,
function timer, ...)
-BºSetup and teardown logic is handled by the observable functionº.
Bºapplication code in Component02 only needs to worry about º
Bºsubscribing to consume values, and when done, unsubscribingº
BºIt doesn't change whether the code was sync or async. º
- Unicast example:
const Oºobservable01º =
new Observable(
/*
* This code creates a new independent execution for each
* subscribed observer creating a relation
* Execution 1 ←→ 1 Subscriber
* (vs) Execution 1 ←→ N Subscriber ("Multicast")
*/
Bº(observer) {º ← function to execute at ".subscribe(...)
const {nextCallback, errorCallback} = observer;
let watchId = navigator.geolocation
.watchPosition(nextCallback, errorCallback);
return {
unsubscribe() { // ← ☞ Freing resources is
navigator.geolocation. responsability of Observable
clearWatch(watchId);
}
};
Bº}º);
const Bºsubscription01º = Oºobservable01º.subscribe(
{
next(position) { console.log(position); },
error(msg) { console.log('Error: ', msg); },
});
...
setTimeout(() =˃ { observable01.unsubscribe(); }, 10000);
- Multicast example:
☞TIP: multicasting tools simplify next code by allowing
to convert any existing observable to "multicast".
// Emit one value/second until end-of-array.
┌·→ function executor(observer, arr, idx) { ←┐
| return setTimeout(() =˃ { |
| observer.next(arr[idx]); |
| if (idx === arr.length - 1) { |
| observer.complete(); |
| } else { |
| executor(observer, arr, ++idx); ···┘
| }
| }, 1000);
| }
|
| /*
| * multicastSequenceSubscriber wraps the executor
| * keeping state of subcribers.
| */
| function multicastSequenceSubscriber() {
| const seq = [1, 2, 3];
| const Bºobserversº = []; // ← Keep track o subscribers
| // Still a single timeoutId because there will only ever be one
| // set of values being generated, multicasted to each subscriber
| let timeoutId;
|
| // Return the subscriber function (runs when subscribe()
| // function is invoked)
| return (observer) =˃ {
| Bºobserversº.push(observer);
| if (Bºobserversº.length === 1) {
| // ^^^^^^^^^^^^^^^^^^^^^^
| // start executor at 1st subcription only
└·· timeoutId = executor({
next(val) {
Bºobserversº.forEach(obs =˃ obs.next(val));
},
complete() {
Bºobserversº.forEach(obs =˃ obs.complete());
}
}, seq, 0);
}
return {
unsubscribe() {
observers.splice(observers.indexOf(observer), 1);
if (observers.length === 0) {
clearTimeout(timeoutId);
}
}
};
};
}
const mcastObservble01 = new Observable(multicastSequenceSubscriber());
setTimeout(() =˃ {
multicastSequenceº.subscribeº({
next (num) { console.log('1st subscribe: ' + num); },
complete() { console.log('1st sequence finished.'); }
});
},º 0 /*← Subscribe "now"*/º);
setTimeout(() =˃ {
multicastSequenceº.subscribeº({
next (num) { console.log('2nd subscribe: ' + num); },
complete() { console.log('2nd sequence finished.'); }
});
},º1500 /*← Subscribe after 1st event */º);
Console will show:
(second 1) → 1st subscribe: 1
(second 2) → 1st subscribe: 2
" → 2nd subscribe: 2
(second 3) → 1st subscribe: 3
" → 1st sequence finished
" → 2nd subscribe: 3
" → 2nd sequence finished
- EX USING REACTIVE RX.JS:
┌─ Observable Service (EVENT EMITTER) ──── ┌─ Observer Component (EVENT CONSUMER) ─────
│ import {Subject} from 'rxjs'; │ import { Component } from '@angular/core';
│ // npm i -s rxjs@v6.5.3 │ import { ServiceX} from ...
│ │
│ @Injectable() │ @Component({
│ export class ServiceX { │ selector: ...
│ ... │ templateUrl: ...
│ publicºinfoObservable =º │ providers: ...
│ ºnew Subject˂string[]˃();º │ styleUrls: ...
│ │ })
│ private async refreshInfo() { │ export class Comp01 implements OnInit {
│ ... │
│ const info = await getRemoteInfo() │ constructor(
│ this.infoObservableº.next(info);º │ private myService : ServiceX ) {
│ } │ ...
│ } │ }
└───────────────────────────────────────── │
│ ngOnInit() {
│ this.myService.
│ .infoObservable.subscribe(
│ ( info ) =˃ { /* "consume info */ }
│ )
│ }
└───────────────────────────────────────────
PrimeNG Library
@[https://www.primefaces.org/primeng/#/]
• See also other "top" Frameworks:
@[https://www.ngdevelop.tech/best-angular-ui-component-libraries/]
•BºPRE-SETUPº
$º$ npm install primeng --save º
$º$ npm install primeicons --save º
$º$ npm install @angular/animations --save º (opt)
^
Or manually add dependencies to
package.json add like :
...
"dependencies": {
...
+ "primeng": "^10.0.0",
+ "primeicons": "^4.0.0"
...
}
•º95% of code is native with no 3rd party dependenciesº
•ºUI component library configured as ng-modules. e.g:
import {AccordionModule} from 'primeng/accordion'; //accordion and accordion tab
import {MenuItem} from 'primeng/api'; //api
• Lot of free themes out-of-the-box.
• Ex. config. for required styles:
"styles": [
"node_modules/primeng/resources/themes/saga-blue/theme.css",
"node_modules/primeng/resources/primeng.min.css",
"node_modules/primeicons/primeicons.css",
...
],
...
• See also: PrimeNG Video Tutorial:
@[https://www.youtube.com/watch?v=4Wk4RgYN9ZQ&list=PLEXcOZ7ShIgAnxrnPgiOpIz1uPKZNQ9ZJ]
Service Workers
@[https://angular.io/guide/service-worker-intro]
• See general introduction to Service workers at #[service_workers_summary]
• The Angular service worker's (v5.0+) design goal:
• Caching app as one unit. The running app will use the [devops]
same version of all files (vs randomnly receiving cached
files from a newer incompatible version).
• Refreshing will show latest fully cached version. [devops]
with updates executing in background.
• NG service will load the PWA manifest file from the server
describing the resource to cache. When an update to the
PWA is deployed, manifest data changes triggering the
background update.
• TheBºmanifest is generated from "ngsw-config.json"º. [devops]
• NG Service how-to:
• Include NgModule ???? [TODO]
• Registering NG Service Worker with the browser:
•RºWARN:º PWApp must be accessed over HTTPS, not HTTP.
Otherwise, registration will fail to avoid
tampering. (security meassure since Service Workers
are "quite powerful").
Exception: localhost is allowed for developers.
• See Service Worker config at: [TODO]
@[https://angular.io/guide/service-worker-config]
• See Service Worker production ready at: [TODO][devops]
@[https://angular.io/guide/service-worker-devops]
Interactive Forms
@[https://angular.io/guide/forms]
Support complex data entry scenarios with HTML-based validation and dirty checking.
- template-driven interactive form how-to
- Requested features:
- from with control elements bound to data properties
- input data integrity validation with two-way data binding
- design approaches:
-GºAlt 1: write templates using:º
- NG template syntax
- form-specific directives
- "best-pattern".
- suitable for small or simple forms
-GºAll 2: Use reactive(==model-driven) approachº
- lay out the controls creatively, binding to the data in your object model.
- Set input validation rules and output display validation errors.
- conditionally enable controls.
- trigger built-in visual feedback,
- ...
_________________________________________________________
GºAlt 1: template-driven formº
- Next directives defined in ºFormsModuleº are used:.
-ºNgModelº: reconcilites "form element" ←→ data model value changes
- used also to respond to user input (input validation, error handling).
-ºNgFormº: - creates top-level ºFormGroupº instance bined to ˂form˃ DOM.
- track aggregated form value and validation status.
- active by default on all ˂form˃ tags at "FormsModule" import
(it has a selector matching the ˂form˃ element).
-ºNgModelGroupº: create+bind ºFormGroupº instance ←→ DOM element.
- STEPS:
- Define a sample data model.
- Include FormsModule,... "stuff".
- Bind form-controls ←→ data properties with ºngModelº
- Examine how ngModel reports control states using CSS classes.
- Name controls to make them accessible to ngModel.
- Track input validity/control status with ºngModelº
- Add custom CSS to provide visual feedback on the status.
- Show and hide validation-error messages.
- Respond to a native HTML button-click event by adding to the model data.
- Handle form submission using form ºngSubmitº output property.
- Disable the Submit button until the form is valid.
- After submit, swap out the finished form for different content on the page.
Ex: two required fields (css:left border when OK, red otherwise)
with initial default values.
- src/app/hero.ts: (Model)
export class Hero {
constructor(
public id: number,
public name: string,
public power: string,
public alterEgo?: string
) { }
}
- src/app/hero-form/hero-form.component.ts: (v1) (layout, details)
import { Component } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-hero-form', ·············· ← drop form in parent template using ˂app-hero-form˃
templateUrl: './hero-form.component.html',
styleUrls: ['./hero-form.component.css']
})
export class HeroFormComponent {
powers = ['Really Smart', 'Super Flexible',
'Super Hot', 'Weather Changer'];
model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');
submitted = false;
onSubmit() { this.submitted = true; }
// TODO: Remove this when we're done
get diagnostic() { return JSON.stringify(this.model); }
}
- src/app/app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; ← enable Forms feature
import { AppComponent } from './app.component';
import { HeroFormComponent } from './hero-form/hero-form.component';
@NgModule({
imports : [ BrowserModule, FormsModule ],
declarations: [ AppComponent, HeroFormComponent ],
providers : [],
bootstrap : [ AppComponent ]
})
export class AppModule { }
- src/app/app.component.html:
˂app-hero-form˃˂/app-hero-form˃ ← display at root component's template.
- src/styles.css:
(Twitter Bootstrap style classes: container/form-group/form-control/btn)
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
- src/app/hero-form/hero-form.component.html
˂style˃ .no-style .ng-valid { border-left: 1px solid #CCC } ; ... ˂/style˃
˂div class="container"˃
˂div [hidden]="submitted"˃ ← main form visible by default since submitted init to false
˂form (ngSubmit)="onSubmit()" #heroForm="ngForm"˃
└───┬────────────┘
└──── Access overall form status
#heroForm : tpl ref.var is now a ref. to
NgForm directive inst.governing form
as a whole.
˂div class="form-group"˃
˂label for="name"˃Name˂/label˃
˂input type="text" class="form-control" id="name"
└───┬──┘
└──────────── (standard HTML). used by ˂label˃ to match
required
[(ngModel)]="model.name"
└──────────┬───────────┘
└──────────── two-way data binding. model.name server to display and debug that
model is really being updated when user changes input.
NG sets special CSS classes on (NgModel) control elements to
reflect the state.
┌─────────────────────────────────┬────────────┬─────────────┐
│State │ Class │ Class │
│ │ if true │ if false │
├─────────────────────────────────┼────────────┼─────────────┤
│The control has been visited. │ ng─touched │ ng─untouched│
├─────────────────────────────────┼────────────┼─────────────┤
│The control's value has changed │ ng─dirty │ ng─pristine │
├─────────────────────────────────┼────────────┼─────────────┤
│The control's value is valid. │ ng─valid │ ng─invalid │
└─────────────────────────────────┴────────────┴─────────────┘
name="name"
└───┬─────┘
└──────────── [(ngModel)] on an element requires also the name attribute for that element, used by NG
to register such element with the NgForm directive attached to parent form element.
#name="ngModel"˃
└───┬─────┘
└──────────── tpl reference variable that you can use to access the input
box's Angular control from within the template.
It is set to "ngModel" because that is the value of the NgModel.exportAs property.
This property tells Angular how to link a reference variable to a directive.
┌─────────── Show or hide the error message by binding properties
│ of the name control to the message ˂div˃ element's hidden property
┌──────┴─────────────────────────────┐
˂div [hidden]="name.valid || name.pristine" class="alert alert-danger"˃Name is required˂/div˃
˂/div˃ └──┬───┘
└── pristine == user hasn't changed the value since it was displayed.
˂div class="form-group"˃
˂label for="alterEgo"˃Alter Ego˂/label˃
˂input type="text" class="form-control" id="alterEgo"
[(ngModel)]="model.alterEgo" name="alterEgo"˃
˂/div˃
˂div class="form-group"˃
˂label for="power"˃Hero Power˂/label˃
˂select class="form-control" id="power"
required
[(ngModel)]="model.power" name="power"
#power="ngModel"˃
˂option *ngFor="let pow of powers" [value]="pow"˃{{pow}}˂/option˃
˂/select˃
˂div [hidden]="power.valid || power.pristine" class="alert alert-danger"˃
Power is required
˂/div˃
˂/div˃
˂button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid"˃Submit˂/button˃
└─ automatically hiddes if ─────┘
form is invalid
˂button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()"˃New Hero˂/button˃
˂i˃with˂/i˃ reset
˂button type="button" class="btn btn-default" (click)="newHero()"˃New Hero˂/button˃
˂i˃without˂/i˃ reset
˂!-- NOT SHOWN IN DOCS --˃
˂div˃
˂hr˃
Name via form.controls = {{showFormControls(heroForm)}}
˂/div˃
˂!-- - --˃
˂/form˃
˂/div˃
˂div [hidden]="!submitted"˃
˂div class="col-xs-3"˃Name ˂/div˃ ˂div class="col-xs-9"˃{{ model.name }}˂/div˃
˂div class="col-xs-3"˃Alter Ego ˂/div˃ ˂div class="col-xs-9"˃{{ model.alterEgo }}˂/div˃
˂div class="col-xs-3"˃Power ˂/div˃ ˂div class="col-xs-9"˃{{ model.power }}˂/div˃
˂button class="btn btn-primary" (click)="submitted=false"˃Edit˂/button˃
˂/div˃
˂/div˃
- customizing CSS :
└ src/assets/forms.css:
· .ng-valid[required], .ng-valid.required {
· border-left: 5px solid #42A948; /* green */
· }
·
· .ng-invalid:not(form) {
· border-left: 5px solid #a94442; /* red */
· }
·
·
└ src/index.html (styles)
˂head˃
...
˂link rel='stylesheet' type='text/css' href='...' /˃
but it does trigger a form-submit event because of its type (type="submit").
Dev.cycle
JIT Compilation
@[https://angular.io/guide/aot-compiler]
- Angular provides just-in-time (JIT) compilation for the
development environment, and ahead-of-time (AOT) compilation
for the production environment.
Testing platform
@[https://angular.io/guide/testing]
- Run unit tests on your application parts as they interact with the Angular framework.
Internationalization
@[https://angular.io/guide/i18n]
- Make your app available in multiple languages
with Angular's internationalization (i18n) tools.
Security guidelines
@[https://angular.io/guide/security]
- Learn about Angular's built-in protections against common
web-app vulnerabilities and attacks such as cross-site scripting attacks.
Setup, build, deploy config
ng CLI reference
@[https://angular.io/cli]
- Used to:
- create projects,
- generate app. and library code.
- testing, bundling, deployment.
- ...
BºPRE-SETUP:º
$º$ npm install -g @angular/cliº
BºBASIC WORKFLOWº
$º$ ng new my-first-project\º ← create, build, and serve (Wait ~ 1 minute to complete)
$º --strict º ← Opt. @[https://angular.io/guide/strict-mode] BºForbids "any" typeº
$ºCREATE ... º ← initial app created goes to root project folder
$ºCREATE ... º ← Additional apps/library go to ./projects/
$º º
$º$ cd my-first-project º new, basic project
$º$ ng serve º ←BºAutomatic rebuilds on edit!!!
Test server listening at:
http://localhost:4200/
BºApp Layoutº:
- New app contains:
- source files for root module
- root component + root template.
- /src folder: contains
- logic
- data
- assets.
within a project folder, use $º$ ng generate ...º to add new
components,services, pipe code, directives, ... (or do it manually)
See more about the Workspace file structure.
BºWorkspace and project configurationº
NOTE: ☞ WORKSPACE 1 ←→ 1+ Project
^^^^^^^
application or
shareable lib
${WORKSPACE}
└ ./angular.json ← workspace per-project defaults for "ng" command
like custorm target defaults, ...
Edit it manually or through $º$ng config ...º
See more about Workspace Configuration.
See the complete schema for angular.json.
ng syntax:
$º$ ng commandNameOrAlias requiredArg --opt1 --boolOpt2 --noBoolOpt3º
└─────────────┬──────────────┘
NOTE: opt.names in angular.json are "camelCase".
opt.names in ng command are "camelCase" or
"dash-case".
$º$ ng generate "artifact or library to be generated"º
$º$ ng add "artifact or library to be added" º
└──┬───┘ └──────────────────────────────────┘
│ each artifact/library defines its
│ own options in a schematic.
┌────┴─────────────────────────────────────────────────────────┐
- add
- analytics https://angular.io/cli/usage-analytics-gathering.
- build output to dist/
- config
- deploy
- doc Opens Official Angular documentation in browser,
and searches for a given keyword.
- e2e Builds and serves an Angular app, then runs
Bºend-to-end tests using Protractorº
- generate Generates/modifies files based on a schematic.
- help
- new
- run Runs an Architect target with an optional custom builder
configuration defined in your project.
- serve
- test
- update Updates app and dependencies.
BºSee @[https://update.angular.io/]º
- version
- xi18n Extracts i18n messages from source code.
Ex:
$º$ ng build my-app -c productionº
^^^^^^
generated artifact can also be
indicated with --name "my-app"
BºWorkspace, Project Layout
@[https://angular.io/guide/file-structure]
${WORKSPACE}/
└ .editorconfig
└ .gitignore
└ README.md
└ angular.json
└ package.json (npm package dependencies)
└ package-lock.json (version information for all npm packages installed)
(yarn.lock in case o using yarn)
┌ └ src/ src for root-level ºmain application projectº
· └ node_modules/
· └ tsconfig.json "Solution Style" TypeScript config file. Used by
· code editors and TS's language server to inprove dev. experience.
· ºNOT used by compilersº. See detailied info about "noImplicitAny",
· "suppressImplicitAnyIndexErrors", ... at
· @[https://angular.io/guide/typescript-configuration]
· └ tsconfig.base.json base TypeScript config for all (sub)projects in W.S.
· └ tslint.json Default TSLint config for all (sub)projects in W.S.
· └ º./projects/º ← Only for (git SCM) ºmonorepoº. Recommended for:
├ └ my-lib01/src - shareable libraries
· └ src/lib library project's logic and data(components,services,
· · modules, directives and pipes)
· └ src/test.ts main unit-test entry point (normally defaults are OK)
· └ src/public-api.ts Specifies all files exported from library.
· └ karma.conf.js
· └ ng-package.json Config file used by ºng-packagrº at build time.
· └ package.json npm package dependencies required by library.
· └ tsconfig.lib.json Lib-custom TypeScript configuration.
· └ tsconfig.spec.json TypeScript configuration for the tests.
· └ tslint.json
·
├ └ py-app02/src - enterprises using "monorepo" approach.
· └ ... In monorepos all developers in all dev.groups
· └ e2e/ can see all changes from other devs./dev.groups
· ... ºGit becomes the "source-of-true"º.
· All projects will share similar parent config.
· Add new one as:
· $º$ ng generate application projectXX (--strict)
· └ e2e/ BºEnd-to-end test filesº corresponding to root-level
· · app, along with test-specific configuration files.
· └ protractor.conf.js (test-tool config)
· └ tsconfig.json (extends tsconfig.json from workspace)
· └ src/
· └ app.e2e-spec.ts (end-to-end tests for my-app)
· └ app.po.ts
·
·
└ ºSOURCE DIRECTORY LAYOUTº
app/ Angular components, templates, styles, logic and data
└ app.component.ts : app root component logic ("AppComponent").
Its view becomes root of view-hierarchy
└──┬───┘
└ app.component.html : ←──┘
└ app.component.css :
└ app.component.spec.ts: unit test for root AppComponent.
└ app.module.ts : Defines the root module named "AppModule",
Bºtelling Angular how to assemble the whole app.º
BºAs you add more components to the app, they mustº
Bºbe declared here.º
assets/ images, ... to be copied as-is
environments/ build config options for a given target
By default:
- unnamed standard development
- production ("prod")
- (custom ones can be defined, like acceptance, ...)
favicon.ico
index.html CLI automatically adds all JavaScript and CSS files
when building your app, so you typically there is no
need to add any script/link tags manually.
main.ts BºApp entry pointº
Compiles the application with the JIT compiler and
bootstraps the application's root module (AppModule) to
run in the browser.
- AOT compiler can also be used by appending by using
$º$ ng build|serve .... --aotº
polyfills.ts Polyfill scripts for browser support.
styles.sass Lists CSS files supplying styles for a project.
(sass extension can vary depending on defaults for project)
RºWARN:!!!ºsass is in deprecated state. It requires npm packages
with lot of dependencies (including gcc/python2/...) when
"npm" installing.
Don't use it unless you need to maintain a old software.
test.ts Bºmain entry point for unit testsº.
package.json (strict mode only)
Not used by package managers, it tell tools⅋bundlers whether
the code under this directory is free of non-local side-effects.
BºAPPLICATION-SPECIFIC CONFIG FILESº
.browserslistrc : Config sharing of target browsers and Node.js versions
among various front-end tools. More info at:
@[https://github.com/browserslist/browserslist]
karma.conf.js : App-specific Karma configuration.
tsconfig.app.json : App-specific TypeScript configuration,
(TypeScript, ng template compiler options, ...)
tsconfig.spec.json: TypeScript config. for the app. tests.
tslint.json : App-specific TSLint config.
NG+Keycloak AAA
@[https://medium.com/keycloak/secure-angular-app-with-keycloak-63ec934e5093]
• Core principle behind OpenID-Connect:
Delegation of Authentication/Authorization
┌─────┐ ┌───────┐ ┌────────┐
│Alice│ │NG-App │ │Keycloak│
└──┬──┘ └───┬───┘ └───┬────┘
│ Can I see │ │
│ this page? │ Can Alice see │
│────────────────˃│ that page? │
│ │─────────────────˃│
│ Please, enter (login) credentials? │
│˂───────────────────────────────────│
│ crendentials (password, token, ...)│
│───────────────────────────────────˃│
│ │ OK, Alice │
│ │ can see it │
│ page content │˂─────────────────│
│˂────────────────│ │
┌──┴──┐ ┌───┴───┐ ┌───┴────┐
│Alice│ │NG-App │ │Keycloak│
└─────┘ └───────┘ └────────┘
•BºPRESETUPº
→ Keycloak
→ Client Config
→ Add Client :
Client ID : angular-test-app
Client protocol: openid-connect
Root URL : https://myapp.mydomain.com ← or dev URL
(http://localhost:4200)
→ Create new angular app if needed:
$º$ ng new angular-keycloak º
→ Adding official keycloak js adapter
$º$ npm i keycloak-js --save º
• Modify Angular app and next code to main.ts
let initOptions = { //keycloak init options
url : 'https://0.0.0.0:8445/auth',
realm : 'keycloak-demo',
clientId: 'angular-test-app'
}
let keycloak = Keycloak(initOptions);
ºkeycloakº
.init({ onLoad: "login-required" }) //← page load will redirect to
.success((auth) =˃ { // keycloak Auth. page
if (!auth) {
window.location.reload();
return
}
console.log("successful authentication. Bootstrapping now");
platformBrowserDynamic()
.bootstrapModule(AppModule) // ← boostrap module
.catch(err =˃ console.error(err));
localStorage.setItem
( "ang-token" , ºkeycloakº.token); // Access token
localStorage.setItem
( "ang-refresh-token", ºkeycloakº.refreshToken) // Refresh token
setTimeout( refreshToken, 60000 /* 1 min*/ ) // May just each hour/day
})
.error(() =˃ { console.error("Authenticated Failed"); });
function refreshToken = () =˃ {
ºkeycloakº.updateToken(70)
.success( (bRefreshed) =˃ {
if (bRefreshed) { /* OK */ return ; }
console.warn('Token not refreshed, valid for '
+ Math.round(
ºkeycloakº.tokenParsed.exp
+ºkeycloakº.timeSkew
- new Date().getTime() / 1000) + ' seconds');
})
.error(() =˃ { console.error('Failed token refresh'); });
}
NativeScript (Native Apps Angular)
NativeScript
@[https://docs.nativescript.org/]
- Open source framework for building truly native mobile apps with
Angular, Vue.js, TypeScript, or JavaScript.
- NativeScript features deep integration with modern Angular with
full-stack features like integration with the Angular CLI, router
support, code generation, webpack and more.
BºExternal Resourcesº
@[https://github.com/alexziskind1/nativescript-oauth2] [AAA]
@[https://github.com/jbristowe/awesome-nativescript] !!!
@[https://nativescript.org/blog/adding-analytics-to-your-nativescript-app/]
Recipes
BºINIT SERVICE ON PAGE LOADº
let window: any;
@Injectable()
export class MyService01 {
constructor() {
Bºwindow.addEventListener('load'º, function (event) { ... });
}
}
BºRX Observables:º (TODO:Explain)
import {Subject} from 'rxjs';
@Injectable()
export class MyService01 {
public accountsObservable = newºSubject˂string[]˃º();
...
this.accountsObservable.next(accs); ← Inject new events
(Will be delivered to observers)
@Component({ ... })
export class MyComponent01 {
watchAccount() {
this.myService01.accountsObservable
.subscribe(function(account_l) { ← Subscribe
... ← Update component / GUI
});
}
}
single-spa: NG+React+Vue integration
@[https://single-spa.js.org]
- Framework freedom:
Use multiple frameworks in a single-page application, allowing you to split code
by functionality and have Angular, React, Vue.js, etc. apps all living in harmony.
- Lazy load applications
Stand up new apps next to the old one. You write the applications, single-spa makes
them work together and won't load them until they're needed.
- Front-end microservices
Combine many small apps, empowering teams to choose their technology. Stay nimble as
your team, product, and tech stack grows and changes over time.
React
Summary
• React as declarative "source-of-trust":
Summary from: @[https://css-tricks.com/why-javascript-is-eating-html/]
• In recent years, JS devs. have realized that by defining
a page's structure in JS (vs HTML) using framworks like React,
development and maintenance of user interaction code is
simplified:
┌───────────────────────────────┬─────────────────┐
│ Designed for │ Type │
────────┼───────────────────────────────┼─────────────────┤
· HTML │ document structure │ Declarative │
────────┼───────────────────────────────┼─────────────────┤
· CSS │ appearance. │ Declarative │
────────┼───────────────────────────────┼─────────────────┤
· JS │ behaviour. │ Imperative │
────────┼───────────────────────────────┼─────────────────┤
· React │ model/behaviour/doc structure │ Declarative JS │
└────────────┬──────────────┘
It allows to define document structure based on
data stored in a single place/source-of-true.
BºBootstrap new React Appº
@[https://github.com/facebook/create-react-app]
$ npx create-react-app my-app ← Alt 1 (preinstalled with npm 5.2+)
$ npm init react-app my-app ← Alt 2
$ yarn create react-app my-app ← Alt 3
bootstrap file output:
my-app
├─ README.md
├─ node_modules
├─ package.json
├─ .gitignore
├─ public
│ ├─ favicon.ico
│ ├─ index.html
│ └─ manifest.json
├─ App.css
├─ App.js
├─ App.test.js
├─ index.css
├─ index.js
├─ logo.svg
├─ serviceWorker.js
└─ setupTests.js
$ cd my-app
$ npm start
Bº┌───────────────────────────────────────────┐º
Bº│React Summary(@[https://reactjs.org/docs/])│º
Bº└───────────────────────────────────────────┘º
BºJSX summaryº
- Optional but recommened: allows react to report better error/warnings.
- Prevent Injection Attacks from external input (XSS cross-site-scripting,...).
By default, any values embedded are scaped before rendering
const user = {name: "...", ...}
const element =º( º ← JSX expression:
º˂h1 id="1"˃ º ← Use quotes to insert string literals as attr.
º Hello, {user.name}!º - Closer to JS than to HTML.
º˂/h1˃; º class → className, tabindex → tabIndex,...
º); º - compiles to:
const element = React.createElement( 'h1', ...);
^^^^^^^^^^^^^^^^^^^
after few checks ends up creating and object like
{ type: 'h1', props: { ... } }
ReactDOM.render( ← Add to ReactDOM
element,
document.getElementById('root')
);
BºSimplest React Appº:
- index.html
˂div id="root"˃˂/div˃ ← react "root" DOM
function Welcome(ºpropsº) { ← BºComponentsº: loosely coupled reusable UI object
return ˂h1˃Hello, {props.name}˂/h1˃; props (input) → Component → (output) element and components
} └─────┬────┘ (See section State management for alternative using classes)
│ convention: Always start name with capital letter
│ lower case are treated as DOM tags
│ (Ex: ˂div /˃ means HTML div tag)
│
DON'T FORGET: All components must act like pure
functions with respect to their props.
props must always be treated as read-only
by components. Use state to represent
data updates from user/network/...
const element = ( ← Element build from component
˂Welcome name="Sara" /˃ ); ← name="Sara" translated to input props
- smallest "renderable" Bºimmutableº
building block for components
- Translates to plain JS (vs DOM)
function App() { ← App component formed by Welcome comps.
return ( Use like:
˂div˃ ReactDOM.render(
˂Welcome name="Sara" /˃ ˂App /˃,
˂Welcome name="Cahal" /˃ document.getElementById('root')
˂/div˃ );
); name convention: component at root == "App"
ReactDOM.render( ˂App/˃,
document.getElementById('root')
);
-BºState Managementº
class Clockºextends React.Componentº{ ← Alt.2: class declaration for component.
Preferred when managing component internal state
constructor(props) {
super(props); ← props: immutable internal data of component
this.state = { date: new Date()}; } ← state: mutable internal data of component
render() {
return (
˂div˃
{props.title} ˂br/˃
{this.state.getSeconds()}
˂/div˃
);
}
ºcomponentDidMount()º{ ← Lifecycle component hook.
this.timerID = setInterval(
() =˃ { this.ºsetStateº({ ← By calling setState reacts will know that
date: new Date() } ) internal state changed.
} , 1000) RºWARNº: Never change this.state directly
} after initial setup in constructor
BºNOTEº: State is merged with old one (vs
replaced with new one)
ºcomponentWillUnmount()º{ ← Lifecycle component hook.
clearInterval(this.timerID);
}
}
BºNOTE:º[performance] React may batch multiple setState() calls into a single update
RºWARNº: updating new state from props and state:
RºWrongº: BºCorrectº
(props/state change asynch) (Use setState( (state,prop) ) )
this.setState({ this.setState((state, props) =˃ ({
counter: this.state.counter counter: state.counter
+ this.props.increment, + props.increment
}); }));
- TODO: Read also advanced guides:
https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized
BºHandling Eventsº
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = ← Don't forget (not needed with funct.
this.handleClick.bind(this); style syntax)
} Alt. use () =˃ in JSX
onClick={() =˃ this.handleClick()}
(Not recommended, a new callback
created on each new render)
handleClick() {
this.setState(
state =˃ ({ isToggleOn: !state.isToggleOn })
);
}
render() {
return (
˂button onClick={this.handleClick} ˃ ← event always cammelCase(onClick)
{this.state.isToggleOn ? 'ON' : 'OFF'}
˂/button˃
˂button
onClick=
{this.deleteRow.bind(this, id)} ˃ ← Pass data to handler. Also
delete {this.id} {(e) =˃ this.deleteRow(id, e)}
˂/button˃ (lambda syntax)
);
}
BºConditional Renderingº
function Greeting(props) {
...
if (condition1) { return null; } ← Return null to skip rendering
return (condition1)
? ˂Component1 /˃
: ˂Component2 /˃
}
BºLists and Keysº
function NumberList(props) {
const number_l = props.number_l;
const listItems = number_l.map( ← Use standard Ecmascript "map"
( (n) =˃ ˂hi key={n.toString()} ← key: optional, helps React idenity added/changed/deleted items
˂li˃{n}˂/li˃ ); Use {index} (not recomended) as a last resort for key value
return ( ˂ul˃{listItems}˂/ul˃ ); Ussage:
} const number_l = [1, 2, ...];
...
˂NumberList number_l={number_l} /˃
BºFormsº (Controlled Components)
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value1: '',value2: '',value3: ''}; ← make React component state be the
"single source of truth" for form inpunts
value can also refresh some other UI elements.
this.handleChange = this.handleChange.bind(this); ← Don't forget to bind
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { ← similar to 2/3
const T = event.target;
const value = (T.type === 'checkbox') ← Example logic.
? T.checked : T.value;
const name = target.name; ← use target.name to known which input was affected
this.setState({ (or a different funct. handler is logic is different).
[name]: value }); ← {[...]:...} ES6 computed prop.name syntax.Equivalent to ES5
d = {}; d[name]=value; this.setState(d);
this.setState( {value1: event.target.value} );
}
handleSubmit(event) {
...
event.preventDefault(); ← Don't forget.
}
render() {
return (
˂form onSubmit={this.handleSubmit}˃
˂label˃Name:
˂input type="text" value={this.state.value1}
name="value1"
onChange={this.handleChange} /˃
˂/label˃
˂label˃Essay:
˂textarea value={this.state.value2} ← Notice: textarea content defined by value attribute
name="value2" (vs inner text in standard HTML)
onChange={this.handleChange} /˃
˂/label˃
˂label˃ Pick one:
˂select value={this.state.value3} ← Notice: Selected value defined by value attribute
name="value3" (vs 'selected' attribute in option in standard HTML)
onChange={this.handleChange3}˃ For multiselect:
˂option value="value1"˃text1˂/option˃ ˂select multiple={true} value={['B', 'C']}˃
˂option value="value2"˃text2˂/option˃
˂option value="value3"˃text3˂/option˃
˂/select˃
˂/label˃
˂label˃Upload file:
˂input type="file" /˃ ← read only, uncontrolled by react
˂/label˃
˂input type="submit" value="Submit" /˃
˂/form˃
);
}
}
BºBest patternsº
- If two components need to reflect the same changing data
put such data as prop (immutable in both) and move the state
to a container "parent".
BºComposition vs Inheritanceº
- Component composition recommended vs inheritance in order to reuse components.
(pass them as props.children to other components).
Sometimes we think about components as being “special cases” of
other components. For example, we might say that a WelcomeDialog is a
special case of Dialog.
In React, this is also achieved by composition, where a more
“specific” component renders a more “generic” one and
configures it with props:
BºAt Facebook, we use React in thousands of components, and we º
Bºhaven’t found any use cases where we would recommend creatingº
Bºcomponent inheritance hierarchies. º
BºNon-UI functionalityº
To reuse non-UI functionality between components, we
suggest extracting it into a separate JavaScript module.
The components may import it and use that function, object,
or a class, without extending it.
Preact
@[https://github.com/preactjs/preact]
Fast 3kB alternative to React with the same modern API.
Most UI frameworks are large enough to be the majority of an app's
JavaScript size. Preact is different: it's small enough that your
code is the largest part of your application.
That means less JavaScript to download, parse and execute - leaving
more time for your code, so you can build an experience you define
without fighting to keep a framework under control.
Formik Forms
@[https://formik.org/]
- orchestrate validation and submission: values/errors/visited fields
- focus on business logic.
- Intuitive: plain React state and props.
-ºeasy to debug, test and reason about.º
- No use of external state management libraries like Redux or MobX.
- Keeps bundle size to a minimum.
React Router
- collection of navigational components that compose declaratively
with your application for bookmarkable URLs , composable way to
navigate in React Native, ...
React Suspense
(alt. to Redux)
@[https://www.infoq.com/news/2018/12/react-suspense-redux-alternative]
"If your primary use-case for Redux is using it as a client-side cache of
server-side data, then Suspense could replace your redux usage. I’d consider
it because you’d have simpler code and the ability to tame all those spinners."
Hooks
Hooks == Reusable+Composable Logic
React Native
@[https://reactnative.dev/]
- Replaces web components by native Android/iOS/... ones.
import React from 'react'; // ← needed fro JSX
import { View, Text, Image, // ← native components
ScrollView, TextInput }
from 'react-native';
const App = () =˃ {
const [text, setText] = useState('');
return (
˂ScrollView˃ // Horiz. or Vert. container
˂Text˃Some text˂/Text˃
˂View˃ // ← View: UI building block
˂Text˃Some more text˂/Text˃
˂Image
source={{
uri: 'https://.../image.png',
}}
style={ { width: 200,
height: 200 }}
/˃
˂/View˃
˂TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1
}}
placeholder="..."
onChangeText={text =˃ setText(text)}
defaultValue={text}
/˃
˂FlatList ←BºWorks very well with long listsº. Unlike
data={[ ScrollView, it only renders elements on the
{key: 'val1'}, {key: 'val2'}, screen.
{key: 'val3'}, {key: 'val4'}, SectionList preferred to split into logical
... sections.
]}
renderItem={ ← Take item as input, outpus formatted output
({item}) =˃
˂Text style={styles.item}˃
{item.key}
˂/Text˃}
/˃
˂/ScrollView˃
);
}
export default App;
------------------------------------------------------------
React Android View Web
------------------------------------------------------------
View ViewGroup UIView div
------------------------------------------------------------
Text TextView UITextView p
------------------------------------------------------------
Image ImageView UIImageView img
------------------------------------------------------------
ScrollView ScrollView UIScrollView div
------------------------------------------------------------
TextInput EditText UITextField input type="text"
------------------------------------------------------------
See also:
@[https://github.com/tldr-pages/tldr/blob/master/pages/common/ignite.md]
- A CLI for React Native boilerplates, plugins, generators, and more.
More information: https://infinite.red/ignite.
Redux summary
BºOfficial redux templateº:
@[https://github.com/reduxjs/cra-template-redux]
- already configured with a standard Redux application structure,
using Redux Toolkit to create the Redux store and logic, and
React-Redux to connect together the Redux store and the React components.
$º$ npx create-react-app \ º ← Quick install
$º redux-essentials-example \ º
$º --template redux º
RºWARNº:... if you’re just learning React, don’t make Redux your
first choice.
@[https://redux.js.org/tutorials/essentials/part-1-overview-concepts]
- Redux: pattern + library for managing and updating application state,
using events called "actions".
- centralized store for (global) state that needs to be used across your entire
application, with rules ensuring that the state can only be updated
in a predictable fashion.
- Redux Toolkit: recommended approach for writing Redux logic.
- Redux DevTools Extension: allows for things like "time-travel debugging".
BºCONCEPTSº
-ºExtracting state Management from components:º
function Counter() { // ← initial React component.
const [counter, setCounter] = useState(0) // ← non-redux state management.
redux-way: extract (shared/global) state
from the components to "Store".
...
return ( ˂div˃... ˂/div˃)
}
BºBEST PATTERNº: Keep local (non-global/shared) component
state inside component (vs Redux state)
- Redux expects that all state updates are done immutably.
-ºActionsº: plain JS object representing an application event.
{
"type" : "domain/eventName", ← only required field in action
"payload" : ... ← Optional. payload key name is a convention.
}
-ºReducer functionsº, sort of event listener
(the name comes from its similarity with Array.reduce() )
- input : (current state, action object)
- logic : decides how to update the state based ONLY on input state and action
(or ignore given an input action of no interest)
- next rules must be respected:
- not allowed to modify the existing state only clone-and-edit new version.
- side efffects (asynchronous logic, random values,...) NOT allowed.
- output: returns new state
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
if (action.type !== 'counter/increment') { return state; }
return { value: state.value + 1 }
}
-ºStoreº: Global app state (state "moved" from component states)
import { configureStore } from '@reduxjs/toolkit'
const getIncrementAction = () =˃ { ← Utilityºaction creatorº.
return { type: 'counter/increment' } (avoid typing/mistakes)
}
const store = configureStore( ← Store builder
{ reducer: counterReducer } ← 1+ reducer/s linked to new store
) key names will define keys in final state value.
This code means that we want to have a state.reducder
section in the Redux state object, and that we want the
counterReducer function to be in charge of deciding if
and how to update the state.counter
A real app using state slides could look like:
import reducer1 from '../features/users/usersSlice'
...
export default configureStore({
reducer: {
users : reducer1,
posts : reducer2,
comments: reducer3
}
})
...
store.getState() ← get initial state ({value: 0})
store.dispatch( getIncrementAction() ) ← -º.dispatch()º method is the only way allowed
to update the state
store.getState() ← get new state ({value: 1})
-ºSelectors functionsº:
- "utility" functions to extract specific information from a store state. Ex:
const selectCounterValue = state =˃ state.obj1.obj2.value
-ºSlicesº:
- collection of reducer logic and actions for a single app feature
(typically defined in a single file).
- name comes from splitting up root-state into multiple "slices".
- Example from @[https://github.com/reduxjs/cra-template-redux]
(features/counter/counterSlice.js)
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: { ←·················· UI will dispatch matching action types:
increment: state =˃ { {type: "counter/increment"}, {type: "counter/increment"}, ...
state.value += 1 ←········· RºWARNº: There is some magic here. Code looks to be "mutating" input state.
}, "immer" library detects changes and produces new immutable state
decrement: state =˃ { (middleware automatically added in configureStore)
state.value -= 1 ←·················· - Writing immutable update logic by hand is hard, and
incrementByAmount: (state, action) =˃ { Rºaccidentally mutating state in reducers is the single º
state.value += action.payload Rºmost common mistake Redux users make.º
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
- Example app: counter allowing to add or subtract from a number as we click buttons.
@[https://redux.js.org/tutorials/essentials/part-2-app-structure]
ºFile layout:º
/src
index.js ← the starting point for the app
App.js ← the top-level React component
/app
store.js ← creates the Redux store instance
- store setup can be customized with different
kinds of plugins ("middleware" and "enhancers").
/features
/counter
Counter.js ← a React component that shows the UI for the counter feature
counterSlice.js ← exports reducer function with the the Redux logic for "counter" feature
-BºOur application might be made up of many different features, and eachº
Bºof those features might have its own reducer function.º
- createSlice takes care of non-logic centric stuff like
generation of action type strings/creator functions/objects)
by just defining an slice name, writing an object with reducer functions.
(initial state is also needed to init properly).
- slice_name +"." reducer_key_name → {type: "slice_name/reducer_key_name"}
Ex: "counter" + "increment" → {type: "counter/increment"}
- reducer_function_name → action_creator_name
Ex:
createSlice({
reducers: {
increment: state =˃ ... → counterSlice.actions.increment()
}
...
})
-ºThunksº
- specific kind of Redux function tuple that can contain asynchronous logic.
- PRE-SETUP: add redux-thunk middleware (a type of Redux plugin) at store creation time.
(automatically added by configureStore).
- inside thunk function: takes dispatch and getState as arguments
- outside creator function: creates and returns the thunk function
Ex 1: (features/counter/counterSlice.js)
export const incrementAsync // thunk can be dispatched like regular action:
= amount =˃ dispatch =˃ { `dispatch(incrementAsync(10))`.
// └───────────┴················ inside function
// └───────┴······························ outside function
setTimeout(() =˃ {
dispatch(
incrementByAmount(amount) ← Utility action creator
) returning { type: 'XXX/YYY', payload :... }
}, 1000 /* millisecs */)
}
Ex 2: AJAX call: (features/counter/counterSlice.js)
const fetchUserById = (userId) =˃ { // ← outside "thunk creator" function
return async (dispatch, getState) =˃ { // ← inside "thunk function"
try {
const user = await userAPI.fetchById(userId) // ← make an async call in the thunk
dispatch(userLoaded(user)) // ← dispatch an action when we get the response back
} catch (err) {
...
}
}
}
The React Counter Component#
features/counter/Counter.js
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment, ... } from './counterSlice'
import styles from './Counter.module.css'
export function Counter() {
const count = useSelector(selectCount) ← RºWARN: components are NOT allowed to import/access the store,º
└────┬────┘ use the useSelector(...) injected Redux hook. º*1º
const dispatch = usedispatch() └─················selectCount is defined in features/counter/counterSlice.js:
const [incrementAmount, setIncrementAmount] const selectCount = (state) =˃ { state.counter.value }
= useState('2') (It could also be defined inline)
return ( ... {count} BºAt every dispatched action (store updated), º
Bºthe useSelector hook will re-run the selector º
˂button Bºand if value has changed, trigger re-rendering.º
onClick={() =˃ dispatch(increment())} ˃... ← useDispatch (const dispatch = useDispatch()) is just
˂button another Redux hook (º*1º) allowing to dispatch to the
onClick={() =˃ dispatch(decrement())} ˃... ← actual (otherwise hidden to component) Redux store
) BºNOTE:º components don't care whether we're dispatching a
} sync or async (Thunk) methods.
They only react to input events by dispatching events.
º*1º: Q: how do hooks know what Redux store instance to talk to?
A: At ºindex.js:º
...
import App from './App'
import store from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
˂Providerºstore={store}º˃ ←RºDON'T FORGET:º It allows to pass down/link/inject
˂App /˃ the Redux store to redux hooks in components.
˂/Provider˃,
document.getElementById('root')
)
BºAdvanced Redux use-cases:º
@[https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367]
- Persist state to a local storage and then boot up from it, out of the box.
- Pre-fill state on the server, send it to the client in HTML, and
boot up from it, out of the box.
- Serialize user actions and attach them, together with a state
snapshot, to automated bug reports, so that the product developers
can replay them to reproduce the errors.
- Pass action objects over the network to implement collaborative
environments without dramatic changes to how the code is written.
- Maintain an undo history or implement optimistic mutations
without dramatic changes to how the code is written.
- Travel between the state history in development, and re-evaluate
the current state from the action history when the code changes, a la
TDD.
- Provide full inspection and control capabilities to the
development tooling so that product developers can build custom tools
for their apps.
Provide alternative UIs while reusing most of the business logic.
DevOps
• React+Flux Docker Dev Workflow [TODO]
@[https://medium.com/@tribou/react-and-flux-a-docker-development-workflow-469957f3bbf0]
Server side Tech
API Management
OpenAPI Spec 3.x
@[https://swagger.io/specification/]
• language-agnostic RESTful APIs interface definitionºfor computers and humansº.
• can also be used as documentation.
• External Resources:
· Collection of OpenAPI Tools: @[https://github.com/OpenAPITools]
· OpenAPI-diff:
@[https://github.com/quen2404/openapi-diff]
· OpenAPI topics @ github:
@[https://github.com/topics/openapi]
· ...
Ex OpenAPi Definition:
| openapi: '3.0.3'
|
| info:
| title: 'MyRESTService'
| description: MyRESTService API Document
| version: '0.0.1'
|
| servers: [ { url: https://www.mycompany.com/ } ]
|
| paths:
| '/api/v1/module1/service1':
| put:
| summary: ...
| description: ˃
| This is a multi-line markup description
| * bullet1
| * bullet2
| tags: [ MyModule1, Service1, ... ] ← Orthogonal topics
|
| requestBody:
| $ref: '#/components/requestBodies/entity01'
| parameters:
| - name: SecuritySchemes
| in : header ← := header ← HEAD / GET / PUT / POST
| | query ← GET / PUT / POST
| | body ← PUT / POST
| description: |
| Set Access Token
| Format: "Bearer"+[space]+[jwt]
| schema:
| Oº$ref: "#/components/schemas/entity01"º
| required: true
|
| - name: queryParam1
| in : query
| required: true
| schema:
| type: string ← boolean | number | ...
| { type: string, enum: [asc, desc] }
| - name: ...
|
| responses:
| '200': { description: 'OK' }
| '400': { description: 'KO' }
|
| '/api/v1/module1/service1':
| get:
| summary: ...
| ...
| responses:
| '200':
| description: 'OK'
| content:
| application/json:
| schema:
| Oº$ref: '#/components/schemas/entity01'º
| ...
|
| components:
| schemas:
| entity01:
| type: object
| properties:
| blNo : { type: string }
| senderCompanyId: { type: string }
| version : { type: number }
|
| requestBodies:
| entity01:
| content:
| "application/json":
| schema:
| $ref: '#/components/schemas/entity01'
TOP Open APIs
REF: @[https://projects.tmforum.org/wiki/display/API/Open+API+Table]
•ºAccount Management APIº
standardized mechanism for management of billing and settlement
accounts, as well as for financial accounting (account receivable)
either in B2B or B2B2C contexts.
•ºAppointment APIº
standardized APIs to book appointment, including:
✓ parameterized searching for free slots
✓ nature and place of appointment.
✓ ...
•ºCustomer Management APIº
standardized API for customer and customer account management
(creation, update, retrieval, deletion and notification of events).
•ºPartnership Type Management APIº
Standardized API for CRUD operations on partnership types.
It is one of the APIs involved in anºonboarding processº.
✓ Identifies type of partnership among parties
(allowed role types list, e.g: [ Buyer, Seller, Developer].
•ºParty Management APIº:
Standardized API for CRUD party management.
Party := individual | organization
•ºParty Role Management APIº
standardized API for general CRUD party roles and notification of events.
•ºProduct Catalog Management APIº
standardized API for adding partners' products to existing Catalog.
It brings the capability for Service Providers to directly
feed partners systems with the technical description of the products they
propose to them.
•ºProduct Inventory Management APIº
standardized API for CRUD (representation of)product inventory management
and product lifecycle event notification.
•ºProduct Offering Qualification APIº:
One of Pre-Ordering Management API Family.
•ºProduct Ordering APIº:
standardized API for placing a product order.
simple set of operations to interact with CRM/Order negotiation systems
in a consistent manner.
•ºQuote Management APIº:
one of the Pre-Ordering Management APIs.
•ºShopping Cart APIº:
standardized API for shopping-carts CRUD and events management.
used for the temporary selection and reservation of product
offerings in e-commerce and retail purchase.
•ºTrouble Ticket APIº:
standardized API to Trouble Ticket Management Systems CRUD among
partners as result of an issue or problem identified by a customer
or another system.
Examples of Trouble Ticket API clients include CRM applications,
network management or fault management systems, or other trouble
ticket management systems (e.g. B2B).
•ºAgreement Management APIº:
standardized API for managing agreements, especially in the context
of partnerships between partners.
•ºCommunication APIº:
create+send communications, notifications, and instructions to
Parties, Individuals, Organizations or Users.
•ºEntity Catalog Management APIº:
The entity catalog is intended to provide any SID entity to consumers via a
catalog, with its specification and policy providing governance over its
content. The API provides management of the entity specifications and their
associations thru CRUD operations.
•ºFederated Identity APIº: [aaa]
management of principals of any kind (persons, objects, …) and their
access to resources in an open environment which can span across different
enterprise boundaries. It relies on authentication, authorization and consent
mechanisms to protect privacy with a simple and easy user experience.
Different parties can provide identity services
(operators, social networks, GSMA, …).
•ºGeographic Address Management APIº:
Standardized client interface to an (worldwide) Address management system,
validating and searching for geographic address data:
1) searching an area as a start (city, town ...)
2) zooming on the streets of this area
3) listing all the street segments (numbers) in a street.
•ºGeographic Location Management APIº:
Provides the information of a geographic region of the entity (customer,
equipment, address).
•ºGeographic Site Management APIº:
CRUD ops to manage sites that can be associated with customers, accounts,
service delivery or other entities.
•ºLoyalty Management APIº: [monetization]
Supports the management of loyalty program specifications, loyalty program
members, their associated products and loyalty accounts with loyalty balances
https://www.tmforum.org/resources/specification/tmf658-loyalty-management-api-rest-specification-r17-0-1/
•ºPartnership Type Management APIº:
Standardized mechanisms for creating partnership types. It is one of the APIs
involved in an onboarding process.
•ºParty Interaction Management APIº:
A User Interaction captures information about past interactions in order to
reuse it in future ones. This allows agents to serve users better by knowing
the steps they went through. It also allows customers to see better the
actions they have performed and how they interacted with us.
•ºParty Management APIº:
Provides a standardized API for party CRUD management.
•ºParty Role Management APIº
Provides a standardized API for general party roles CRUD management.
•ºPayment Management APIº: [payments]
standardized client interface to Payment Systems for notifying about
performed payments or refunds.
Examples of Payment API originators (clients) include:
Web servers, mobile app servers, Contact center dashboards or retail
store systems.
•ºPayment Methods APIº: [payments]
This API supports the frequently-used payment methods for the customer to
choose and pay the usage, including voucher card, coupon, and money transfer.
•ºPrepay Balance Management APIº [payments]
REST API for Balance Management. It includes the model definition as well as
all available operations for prepay balance management. Prepaid subscribers
pay fees before using services. Therefore, the subscribers must have
sufficient balances. Operators can provide multiple recharge channels for
subscribers. Subscribers can pass credit between different subscriptions,
therefore transferring the balance from one account to another.
•ºPrivacy Management APIº: [privacy]
The Privacy management API provides standardized mechanisms for privacy
profile types, privacy profiles and privacy agreements such as creation,
update, retrieval, deletion and notification of events..
•ºProduct Catalog Management APIº:
standardized API for rapidly adding partners' products to an existing
Catalog. It brings the capability for Service Providers to directly
feed partners systems with the technical description of the products they
propose to them.
•ºProduct Inventory Management APIº:
standardized API for CRUD of representation-of-product in inventory.
•ºProduct Offering Qualification APIº:
One of Pre-Ordering Management API Family, providing Product Offering
commercial eligibility.
•ºProduct Ordering APIº:
standardized API for placing a product order interacting with CRM/
Order negotiation systems in a consistent manner.
•ºQuote Management APIº: [payments]
One of the Pre-Ordering Management APIs allowing to place a customer
quote with all of the necessary quote parameters.
•ºResource Catalog Management APIº:
API spec allowing the management of the entire lifecycle of the
Resource Catalog elements and the consultation of resource catalog
elements during several processes such as ordering process.
•ºResource Function Activation and Configuration APIº:
This API introduces Resource Function which is used to represent a Network
Service as well as a Network Function. The Network Service and Network
Function class definitions and associations in TR244 (which, in turn, builds
on concepts from the SID addenda on Logical Resource and Service) are
utilized to define the Resource Function
•ºTrouble Ticket APIº:
standardized client interface to Trouble Ticket Management Systems
for creating, tracking and managing trouble tickets among partners as a
result of an issue or problem identified by a customer or another system.
API clients include CRM applications, network management or fault
management systems, or other trouble ticket management systems (e.g. B2B).
•ºUsage Consumption Management APIº: [monetization]
API allowing real-time view of the balance of the various buckets
(SMS, Voice, Data for example) that a user consumes with each of his devices
and the usages done out of the bucket. Usage report retrieves the data
related to these balances.
This API should also allow performing a direct top-up on the balance of
a prepaid bucket with voucher references or with a given credit value after
a payment done by a credit card or with a credit value transfer.
•ºUsage Management APIº:
Provides standardized mechanism for collection-of-usages CRUD management.
E.g: It allows a service provider to
1) retrieve usage generated by a partner service platform in order to rate it
2) provide rated usage to a partner for consumption follow up purposes.
•ºUser Roles⅋Permissions APIº:
user role : entity that defines a set of privileges covering various functions
and/or manageable assets.
(session) User are assigned a given role then it is actually allocated
all the privileges defined for that role type and the corresponding
permissions are created for that user.
Open Data Protocol
@[https://www.odata.org/]
• ISO/IEC approved, OASIS standard
• It defines aºset of best practices for building and consumingº
ºRESTful APIs to focus on business logic without worryingº
ºabout different approaches to define request/response headers,º
ºstatus codes, HTTP methods, URL conventions, media types,º
ºpayload formats, query options, etc.º
• OData provides guidance for:
· tracking changes
· defining functions/actions for reusable procedures
· sending asynchronous/batch requests. [async]
• OData RESTful APIs are easy to consume.
- OData metadata, a machine-readable description of the
data model of the APIs, enables the creation of powerful
generic client proxies and tools.
• OData Tools include:
- Visual Studio Code for OData Extension:
- adds rich support for theºOData query languageº
- XOData
- generic online OData API/Service visualizer and explorer.
It assists in rapid prototyping, verification, testing,
and documentation of OData APIs. With XOData Chrome App
it's also possible to explore OData Services deployed
locally or on private networks.
asyncapi.org/
@[https://www.asyncapi.org/]
• Low level standard (vs OpenAPI high-level/business oriented)
to architect future event-driven architectures.
• Open source tools to easily build and maintain your event-driven
architecture. All powered by the AsyncAPI specification, the industry
standard for defining asynchronous APIs.
• Compatible with OpenAPI Spec, allowing to re-use schema definitions.
See comparative with OpenAPI and CloudEvents at:
@[https://www.asyncapi.com/blog/async_standards_compare]
• Extracted from ebay anouncement to adopt AsyncAPI:
@[https://www.infoq.com/news/2021/05/ebay-adopts-asyncapi/]
One of the critical AsyncAPI features that eBay's team has found
particularly useful is aºclean separation between channels, º
ºoperations, and servers.º
· A channel represents an event stream
· An operation describes a publish or subscribe process.
· A server facilitates the message transfer (i.e., the message bus).
This separation allows a complete representation of producers, consumers,
and message schemas, resulting in a standardized model of a
message-driven ecosystem.
Mirage: Fast Prototyping
@[https://miragejs.com]
• Mirage JS is an API mocking library that lets you build, test and
share a complete working JavaScript application without having to
rely on any backend services.
WireMock
• HTTP-based simulator to mock APIs for fast, robust and comprehensive testing.
• Fix the problem of testing APIs that do NOT yet exists or is imcomplete.
• test edge cases/failure modes that real API won't reliably produce.
• It can reduce build time from hours to minutes.
APIgee: x-Cloud API Mng
- The Cross-Cloud API Management Platform
https://cloud.google.com/apigee/
NodeJS
nvm Install
@[https://github.com/nvm-sh/nvm]
• nvm: Node Version Manager allows to install different versions of node
in parallel.
$º$ nvm install 14 º
$º$ nvm use 14 º
$º$ node -v º
v14.18.0
$º$ nvm uninstall 12 º
NodeJS summary
@[https://nodejs.org]
• Server-side JavaScript platform (Node.js).
built against modern versions of V8.
• Server API extensions for subprocesses, TTY, TCP servers, ...
Bº##########º
Bº# nodenv #º
Bº##########º
@[https://github.com/nodenv/nodenv]
• Utility to manage multiple Node.js versions installed in parallel.
• Preferred to OS install (apt, dnf, chocolate, ...)
$º$ nodenv install $version º ← Install specific version of Node.js
$º$ nodenv install --list º ← list of available versions
$º$ nodenv global $version º ← Use specific version across whole system
$º$ nodenv local $version º ← Use specific version within directory
$º$ nodenv version º ← Display Node.js version for current dir.
$º$ nodenv which $command º ← Display location of installed command
(e.g. npm,yarn,...)
$º$ node º ← Start interactive shell (REPL loop)
$º$ node path_to_file.js º ← Run JS file
$º$ node -e "{{js_code}}" º ← Evaluate JS code
$º$ node -p "{{js_code}}" º ← Evaluate and print result, useful to see
node's dependencies versions
e.g: $º$ node -p "{{process.versions}}" º
{ http_parser: '2.8.0',
node: '10.16.3',
v8: '6.8.275.32-node.54',
...
unicode: '12.1',
... }
$º$ node --no-lazy \ º ← Activate inspector, pausing execution until [debug]
$º --inspect-brk file.js º a debugger is connected once source code is
fully parsed
Bº#######º
Bº# npm #º
Bº#######º
@[https://www.npmjs.com/]
• npm : Node Package Manager manage projects and module dependencies. [devops]
• npm project layout
./package.json ← JSON file with dev/pro. package dependendies
./node_modules ← Installed packages (Add to Rº.gitignoreº, do not commit).
./package-lock.json ← npm v5+: It describes the exact tree that was generated,
ºsuch that subsequent installs are able to generate º
ºidentical treesº, regardless of intermediate dependency updates.
Must be kept in git (version control) as any other source file.
$º$ npm init º ← create "package.json" interactively
$º$ npm install º ← Download dependencies listed in package.json
$º$ npm install $mod@$ver º ← Download specific package ver. and add it
dependencies@package.json
· Use '--save-dev' to add to
dev-dependencies@package.json
· Use '--global' to install OS system wide
$º$ npm uninstall $mod º ← Uninstall +remove dependencies@package.json
$º$ npm list º ← Print tree of locally-installed dependencies
· Use --global --depth={{0}} to list
top-level globally installed modules
$º$ npm-check º ← report outdated/incorrect/unused dependencies
· Use --skip-unused to ignore un-used packages
$º$ npm-check --update º ← Interactively update out-of-date packages
$º$ npm-check --update-allº ← Update all no prompting
$º$ npm-why $mod_name º ← Identifies why an package is installed.
Bº#################º
Bº# Windows setup #º [windows][troubleshooting]
Bº#################º
@[https://www.npmjs.com/package/windows-build-tools]
$º$ npm install --global windows-build-tools º
└─────────────────┘
NodeJS What's new
• Node 14+
·BºOptional chaining operator '?.'º [billion_dolar_mistake][qa]
skip manual null validation when fetching property located deep
within a chain of connected objects. e.g.:
const adventurer = {
name: 'Alice',
cat: { name: 'Dinah' }
}
console.log(adventurer.ºdog?.ºname); ←º?.º Result: undefined (no error)
console.log(adventurer.dog ⅋⅋ ← manual (non-compact) check (false)
adventurer.dog.name);
RºWARN:º This operator doesn't fix the billion dolar mistake,
since check for nulls/undefined is still needed, but
it makes syntax more compact and clear to read.
·BºNullish coalescing logical operator '??'º [billion_dolar_mistake][qa]
logical op. returns the operand on its right when
the operand on its left is null or undefined.
Otherwise, it returns its left-hand-side operand. e.g:
let safe_name = nullableEntry ?? "n/a" ← Recomended fallback op. (vs || )
• Node 12: 2019-10 BºLTS version!!!º
@[https://medium.com/@nodejs/introducing-node-js-12-76c41a1b3f3f]
• 30% faster startup generating code cache for built-in libraries
in advance at build time, and embed it in the binary.
• better default heap limits based on available mem vs using defaults.
Particularly useful when processing large data-sets. [performance]
• new features:
✓ diagnostic report [troubleshooting]
generate report on demand on certain events
(production crashes, slow performance, memory leaks,
high CPU usage, unexpected errors ...). More info at:
https://medium.com/the-node-js-collection/⏎
easily-identify-problems-in-node-js-applications⏎
-with-diagnostic-report-dc82370d8029
✓ bundled heap dump capability
Useful to debug memory problems. More info at:
https://github.com/nodejs/node/pull/27133
https://github.com/nodejs/node/pull/26501
• updates to V8(v 7.4) brings:
· Async stack traces:
https://v8.dev/blog/v8-release-72#async-stack-traces
· Faster calls with arguments mismatch
https://v8.dev/blog/v8-release-74#faster-calls-with-arguments-mismatch
· Faster await:
https://v8.dev/blog/v8-release-73#faster-await
· Faster javascript parsing:
https://v8.dev/blog/v8-release-72#javascript-parsing
• Hello TLS 1.3 introduces and defaults to TLS 1.3 [TLS]
• Native Modules Easier to implement include better support
in Worker threads, and N-API .
https://nodejs.org/api/n-api.html#n_api_n_api
• Workers Threads no longer experimental. More info at:
@[https://medium.com/@Trott/using-worker-threads-in-node-js-80494136dbb6]
• Experimental ES6 Module Support [es6]
oclif
@[https://oclif.io/]
• Open (console) command line interface Framework to create
friendly cli tools.
Hapi Web Framework!!!
• NodeJS web framework.
• See also complementarion Caddy HTTPs server automating
certificate renewals:
@[../General/cryptography_map.html#caddy_https_server]
• Simple, Secure Framework that Developers Trust
• Originally developed to handle Walmart’s Black Friday scale.
• Recommended by Brendan Eich
Creator of JavaScript and CEO, Brave
"...hapi provides the right set of core APIs and extensible
plugins to support the requirements of a modern service
- session management, security, connectivity, and testing"
•BºWhen you npm install @hapi/hapi, every single line of code you getº
Bºhas been verified. (No deep dependency being poorly maintained). º
Bºhapi is the only leading node framework without any external code º
Bºdependencies. None. º
• End-to-end Code Hygiene — hapi requires the most secure settings
to manage, control, and distribute code, including 2FA for all
contributors.
• Secure Defaults, Updated Regularly — every hapi component comes [security]
with the most secure defaults out-of-the-box. Along with protecting
server load with payload limits and request timeouts, hapi blocks
error messages that could leak information or echo back exploits.
• Integrated Authorization and Authentication Architecture — the [aaa]
most comprehensive authorization and authentication API available in
a Node framework.
• Advanced Features — with encrypted and signed cookies, secret or
key rotation, and HTTP security headers, there are no excuses for
building insecure applications.
• Reliable, Predictable Ownership – when something goes wrong, you
know who to contact. Security updates are handled under a strict,
well-defined protocol.
• Rich ecosystem – hapi’s extensive set of official plugins means
no more blindly trusting some middleware you found for critical
functionality just because it has a high count on npm.
• In-house Security Expertise – created by Eran Hammer, the author
of the OAuth specifications and other identity protocols.
Deno (NodeJS +++)
• extracted from
@[https://www.infoq.com/news/2020/06/deno-1-ready-production/]
Deno Is Ready for Production @ma
• Deno: secure runtime for JS, TS and WebAssembly on V8 engine.
• created by the original developer of Node.js, Ryan Dahl, to
address what he called "10 things I regret about Node.js".
• Notable differences for application developers:
• single binary application (vs Node.js modular binaries).
• Avoid complex dependency management solutions like the NPM.
Developers declare dependencies in source code using direct URLs.
• Already 500+ third-party Deno modules available.
(Rº not compatible with NPM packages written for Node.jsº, but
porting must be easy).
• Deno runtime is secure by default. If application needs to [security]
access the file system or network, the developer needs to
declare it explicitly.
Monetization
Web Monetization
@[https://webmonetization.org/]
• JS browser API to allows the creation of a payment
stream from the user agent to the website.
• Proposed W3C standard at the Web Platform Incubator
Community Group.
• SETUP HOW-TO:
1) Set up a web monetized receiver for receiving payments.
Most of the times this include signing with some payment
services, but a crypto-wallet owned by the user could be
an alternative solution.
The wallet must support the Interledger protocol ILP. [protocol]
account.
2) Fetch the ºpayment-pointerº from the ILP wallet. e.g.:
$wallet.example.com/alice
3) Create ˂meta˃ tag telling Monetization providers how to pay. e.g.:
˂meta name="monetization" ← name is always monetization
content="$wallet.example.com/alice"˃
4) Add ˂meta˃ tag from step 3 to the ˂head˃ of each page to be monetized.
Coil
@[https://medium.com/coil/coil-building-a-new-business-model-for-the-web-d33124358b6?utm_source=singlepagebookproject]
Innovating on Web Monetization: Coil and Firefox Reality
https://hacks.mozilla.org/2020/03/web-monetization-coil-and-firefox-reality/
Ilp: coilhq/coil-wordpress-plugin: Coil's Wordpress Web Monetization Plugin
@[https://github.com/coilhq/coil-wordpress-plugin]
OpenRTB
Real-time Bidding (RTB) is a way of transacting media that allows an
individual ad impression to be put up for bid in real-time. This is
done through a programmatic on-the-spot auction, which is similar to
how financial markets operate. RTB allows for Addressable
Advertising; the ability to serve ads to consumers directly based on
their demographic, psychographic, or behavioral attributes.
The Real-Time Bidding (RTB) Project, formerly known as the OpenRTB
Consortium, assembled technology leaders from both the Supply and
Demand sides in November 2010 to develop a new API specification for
companies interested in an open protocol for the automated trading of
digital media across a broader range of platforms, devices, and
advertising solutions.
Advanced Libs
Stork: Wasm in-browser Full-text Search!!!
@[https://www.infoq.com/news/2020/03/stork-wasm-rust-text-search/]
Stork, a Rust/Wasm-Based Fast Full-Text Search for the JAMStack
JiSON: context-free
@[http://zaach.github.io/jison/docs/]
Jison takes a context-free grammar as input and outputs a JavaScript
file capable of parsing the language described by that grammar.
You can then use the generated script to parse inputs and accept,
reject, or perform actions based on the input. If you’re familiar
with Bison or Yacc, or other clones, you’re almost ready to roll.
HTML Editors (in HTML)
• Monaco Editor(used by VSCode,Che...)
• Blazeling fast web-editor (Used by VSCode, Che, ...)
@[https://code.visualstudio.com/docs/editor/editingevolved]
• Quill.js WySiWyG Editor
@[https://www.scalablepath.com/blog/using-quill-js-build-wysiwyg-editor-website/]
IA Tools
trackingjs Face detection
@[https://trackingjs.com]
• computer vision algorithms into the browser environment.
• Enables real-time color tracking, face detection and much more.
• lightweight core (~7 KB)
• intuitive interface.
Tensorflow.org
Tensorflow using JS + WebGL
@[https://js.tensorflow.org/]
Face⅋Speech Recog.
@[https://dzone.com/articles/tutorial-how-to-build-a-pwa-with-face-and-speechrecognition?edition=636291]
•ºFace detection API:º
face recognition in the browser:
@[https://justadudewhohacks.github.io/face-api.js/docs/index.html]
•ºWeb speech API:º
enables "Speech to text" in the app.
@[https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API]
(above APIs can only work when enabling
"Experimental Web Platform features" in the
browser via the url: chrome://flags
$º$ git clone https://github.com/petereijgermans11/progressive-web-appº
SEO
SEO Summary
• SEO stands for ºSearch Engine Optimizationº.
Bº############################º
Bº# SEO rules for WHOLE site #º
Bº############################º
1) Create 1 or more *sitemap.xml
2) Create 1 robot.txt
3) Use HTTPS (vs HTTP)
Bº############################º
Bº# SEO rules for html pages #º
Bº###########################º
✓ Use Rich Structured Data to provide indexing metadata.
For special content (charts, images, media, ...) it is
specially important. e.g, use schema.org/VideoObject
to index videos inside pages.
✓ Add primary keywords to:
• Title Tag / Meta Description
• URL
• H1, H2 Headings.
•ºanchor text of internal linksº
✓ Add secondary keywords to:
• Subheadings
• Alternative text
✓ Make page/site mobile friendly
✓ Remove broken links
NOTE: Page load speed does NOT affect SEO indexing, but it can affect
final users, that will "skip over" slow sites.
Bº#############################º
Bº# Sitemap Protocol standard #º
Bº#############################º
@[https://www.sitemaps.org/protocol.html]
• Sitemaps standard, proposed by Google, helps search engines
(crawlers and indexers) to inform about URLs metadata
for a given Web Resource (URL).
˂?xml version="1.0" encoding="UTF-8"?˃ ← Max. size: 50MB
˂urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"˃
˂url˃ ← one entry per URL. 50.000 urls per XML
˂loc˃http://www.foo.com/˂/loc˃ ← non-optional. All loc in
XML must match same host
˂lastmod˃2005-01-01˂/lastmod˃ ← Complements If-Modified-Since (304)
HTTP response header (from servers)
˂changefreq˃monthly˂/changefreq˃ ← Aproximate hint to search engine
:= always | hourly | daily | weekly
| monthly | yearly | never
always : dynamic content
never : archived content
˂priority˃0.8˂/priority˃ ← 0.0-to-1.0: relative to other site's URLs
(defaults to 0.5)
˂/url˃
˂/urlset˃
NOTE: additional metadata about alternate language versions, video, image,
or news-specific pages can be included [TODO]
• sitemaps can be "pushed" directly to different search engines [devops]
to trigger fast-indexation (vs waiting for hours/days/weeks for passive
indexing by web crawlers). e.g,ºGoogle Search Consoleº allows to
manually submit one or more XML sitemaps for a given domain an see
indexation results. Also via "PUSH" HTTP request like:
$º$ curl https://${searchengine}/ping?sitemap=http://www.example.com/sitemap.gzº
(via curl, wget, ..., returning HTTP 200 if every works "OK")
• Asking Google to re-crawl a site:
@[https://developers.google.com/search/docs/advanced/crawling/ask-google-to-recrawl]
· There is a quota for submitting individual URLs:
Alt 1) only for 'owners' Search Console property
→ URL Inspection tool
→ Inspect the URL
→ Select "Request indexing".
Alt 2) Submit a sitemap ("many" URLs)
• About URL encodings:
all URLs (including Sitemap hosting URL) must be URL-escaped
by the web server on which they are located.
https.../üm.php⅋q=name
https.../%FCm.php⅋q=name ← Server using ISO-8859-1
https.../%C3%BCm.php⅋q=name ← Server using UTF-8
https.../%C3%BCm.php⅋amp;q=name ← entity escaped URL
• Sitemapºindexº needed when using more than one Sitemap file.
˂?xml version="1.0" encoding="UTF-8"?˃ ← Obligatory when using 2+
˂sitemapindex sitemaps
xmlns=
"http://www.sitemaps.org/schemas/sitemap/0.9"˃
˂sitemap˃
˂loc˃http://foo.com/sitemap1.xml.gz˂/loc˃ ← foo.com must match loc. of
˂lastmod˃2004-10-01T18:23:17+00:00˂/lastmod˃ sitemapindex.
˂/sitemap˃
˂sitemap˃
˂loc˃http://foo.com/sitemap2.xml.gz˂/loc˃
˂lastmod˃2005-01-01˂/lastmod˃
˂/sitemap˃
˂/sitemapindex˃
Alternatively or in parallel sitemaps can be specified in robots.txt like:
Sitemap: http://www.example.com/sitemap-host1.xml
Sitemap: http://www.example.com/sitemap-host2.xml.gz
Rº###########º
Rº# DONT's: #º
Rº###########º
• Don't forget a "robots.txt" to skip content, define Sitemaps.
• Don't block a page in robots.txt and include it in an XML sitemap.
Rº########################################º
Rº# robots.txt Robots Exclusion Protocol #º
Rº########################################º
TIP: Must be all lower-case,
placed at URL "root"
┌(/var/www/html)/robots.txt ─┐
│User-agent: * ← "*" is an acronym for "any robot".
│Disallow: /cgi-bin/ │ Any other globing or regular expression in
│Disallow: /tmp/ │ User-agent or Dissallow: is NOT allowed.
│Disallow: /~joe/ │ e.g.: Disallow: /tmp/*.gif is incorrect
*1→│ │
│User-agent: BadBot ← To entiry server from BadBot robot:
│Disallow: / │
*1→│ │
│User-agent: Google ← allow full access to Google robot
│Disallow: │
*1→│ │
*2→│Sitemap: sitemap01.xml ← e.g: URL category sitemap
│Sitemap: sitemap02.xml ← e.g: URL subcategory sitemap
│Sitemap: sitemap.cgi ← Dinamically generated XML sitemap
└────────────────────────────┘
*1: white-lines used to separate different registries.
(one for "*", another one for BadBot, and a 3rd one
for Google in our example)
*2: Different search engines "control pannels" allows to push
the sitemap directly (vs waiting for random crawling)
• NOTE: difference between html ˂meta name=robots˃ and /robots.txt :
•º/robots.txtº: ignore page, do not even read html headers.
· /robots.txt file is controlled by web-server/site administrator.
•º˂meta name="robots" content="noindex,follow"˃º
· ignore links inside this page.
· Can be controlled by page-author, overriding
defaults in robots.txt.
• External Resources:
• A curated list of SEO (Search Engine Optimization) links.
@[https://github.com/teles/awesome-seo]
Bº########################
Bº# Schema.org: #
Bº# Rich Structured Data #
Bº# for Search Engines #
Bº########################
Ref: @[https://www.infoq.com/news/2021/02/seo-structured-data-rich-snippet/]
• Add semantic support to html.
• Used by º10+ million sitesº to markup web pages and email messages.
• activity community maintaining and promoting schemasºfor structured dataº.
• While OpenAPI standard @[#openapi_summary] allows to [comparative]
design APIs (requests from client to servers) to simplify
implementation of client mobile/web applications,
schema.org allows toºsimplify the translation, by search engines/indexers,º
ofºhtml-markup to structure-data , relationships and actionsº.
• Extensible vocabulary.
•QºGoogle Search Gallery lists 30 rich snippet categoriesº [TODO]
with their use cases and specific user interface
(articles, books, how-to, recipes, products, job postings,
events, ...):
· Each category has a dedicated user interface designed
to facilitate the search user’s next steps.
e.g: A news article may be featured in "Top stories"
˂head˃
˂title˃...˂/title˃
...
˂script type="application/ld+json"˃ ← Google's recommended
{ format (vs microdata|rdf)
"@context": "https://schema.org/", @[#json-ld_summary]
º"@type": "Recipe",º
"name": "Party Coffee Cake",
"author": {
"@type": "Person",
"name": "Mary Stone"
},
"datePublished": "2018-03-10",
"description": "...",
"prepTime": "PT20M"
}
˂/script˃
...
˂/head˃
• SCHEMA.ORG TOOLS:
• @[https://search.google.com/test/rich-results]
• Google/Bing/Yandex/... Search Consoles:
site-wide monitoring and testing for rich results.
JSON-LD
@[https://json-ld.org/] [TODO]
• JSON-based format used to serialize Linked Data primarily targeting
Web environments in order to build interoperable Web services, and
to store Linked Data in JSON-based storage engines.
• Provides smooth upgrade path from JSON to JSON-LD.
Non-classified / Radar
HTTP/3
@[https://www.infoq.com/news/2020/01/http-3-status/]
• HTTP/2 introduced the concept of first-class streams embedded
in the same TCP connection enabling multiplexing simultaneous
request-response exchanges.
• HTTP/2 major flaw: when packet loss increases, performance
degrades due to dependency on TCP packet retransmission
(HOL blocking):
RºWhen packet loss surpasses a given threshold it's slower than º
RºHTTP/1 multiple parallel TCP connections.º
• QUIC (HTTP/3 or HTTP over UPD) has first-class streams. solving
HTTP/2 issues.
axios
@[https://www.npmjs.com/package/axios]
Promise based HTTP client for the browser and node.js
Features
• Make XMLHttpRequests from the browser and
• http requests from node.js
• Supports the Promise API
• Intercept request and response
• Transform request and response data
(See service workers @[#service_workers_summary]
for advanced/complementary alternative)
• Cancel requests.
• Automatic transforms for JSON data.
• Client side support for protecting against XSRF [security]
PouchDB
https://pouchdb.com/ (46KB gzipped)
"...PouchDB is an open-source JavaScript database inspired by
Apache CouchDB that is designed to run well within the browser.
PouchDB was created to help web developers build applications that
work as well offline as they do online.
It enables applications to store data locally while offline, then
synchronize it with CouchDB and compatible servers when the
application is back online, keeping the user's data in sync no matter
where they next login..."
var db = new PouchDB('dbname');
db.put({ _id: 'dave@gmail.com', name: 'David', age: 69 });
db.changes().on('change', function() {
console.log('Ch-Ch-Changes');
});
db.replicate.to('http://example.com/mydb');
PassportJS.org
@[http://www.passportjs.org/packages/]
Simple, unobtrusive authentication middleware for Node.js and Express.
passport.authenticate('twitter'); // 'facebook' 'google' 'linkedin' 'github'
• A comprehensive set of (500+) strategies support authentication using
username/password, Facebook, Twitter, JWT, ...
Pino JSON logger
@[https://github.com/pinojs/pino]
• Compatible with Fastify , Express , Hapi , Restify , Koa , Node core http , Nest
• code. example:
const logParent = require('pino')()
const logChild = logParent.child({ childProp: 'property' })
logParent.info('hello world!') // {"level":30,"time":15...,"msg":"hello ...","pid":XXX,"hostname":"Davids-MBP-3.fritz.box"}
logChild .info('hello child!') // {"level":30,"time":15...,"msg":"hello ...","pid":XXX,"hostname":"Davids-MBP-3.fritz.box","childProp":"property"}
matomo.org
@[https://matomo.org/]
• Google Analytics that preserves customer's privacy.