better isRx will determine if any parent to root is reactive

master
Kebler Network System Administrator 2021-05-13 11:53:48 -07:00
parent 543cad8bac
commit 4b76b80f07
1 changed files with 89 additions and 39 deletions

View File

@ -1,3 +1,4 @@
// import methods from './public-methods.js'
// native modules
import { EventEmitter } from 'events'
// reactive modules
@ -5,7 +6,7 @@ import { BehaviorSubject, from } from 'rxjs'
import { distinctUntilChanged, skip } from 'rxjs/operators'
// uci modules
import { check } from '@uci-utils/type'
import { get, set, del, getKeys, pathToString } from '@uci-utils/obj-nested-prop'
import { get, set, del, walkPath, getKeys, pathToString } from '@uci-utils/obj-nested-prop'
import { logger } from '@uci-utils/logger'
// supporting
import equal from 'deep-equal'
@ -17,7 +18,6 @@ const log = logger({ file: '/src/rx-class.js', package: '@uci-utils/rx-class', c
class RxClass extends EventEmitter {
// private fields
#rx
#rxM
constructor (opts = {}) {
super(opts)
@ -32,8 +32,9 @@ class RxClass extends EventEmitter {
hook: rxopts.hook, // function called when setting
namespace: rxopts.namespace || 'rx', // TODO namespace to added to all public rx methods
operators: new Map(), // TODO added to all rx pipes, in order
skip: rxopts.skip == null ? 0 : rxopts.skip
skip: rxopts.skip == null ? 1 : rxopts.skip
}
this.isRx.bind(this)
}
get (path) {
@ -51,25 +52,29 @@ class RxClass extends EventEmitter {
}
set (path, value, opts = {}) {
// console.log(path,value,opts,!this.isRx(path))
if (!opts.noRx && !this.isRx(path)) {
opts = Object.assign(check.isPlainObject(value) ? { values: value, traverse: true } : { value: value }, opts)
// console.log('set opts', opts)
console.log('set:', path, value, opts, !this.isRx(path))
if (!opts.noRx && !this.isRx(path)) { // travsere
opts = Object.assign(
(check.isPlainObject(value) && opts.traverse !== false)
? { values: value, traverse: true }
: { value: value }
, opts)
console.log('adding rx from set', opts)
this.rxAdd(path, opts)
} else {
const curValue = this.#get(path)
if (!equal(curValue, value) && value !== undefined) {
// console.log('in set', path,value)
console.log('setting already reactive value', path, value)
this.#set(path, value)
// console.log('value that was set',this.#get(path))
console.log('value that was set', this.#get(path))
}
return this.#get(path)
}
}
// getRaw () {
// return get(this, [this.#rx.namespace, ...arguments])
// }
getRaw () {
return get(this, [this.#rx.namespace, ...arguments])
}
// if property exists it moves value to #rx.props and creates getter and setter and behavior subject
rxAdd (path, opts = {}) {
@ -78,23 +83,24 @@ class RxClass extends EventEmitter {
const self = this
// console.log('object to traverse', obj)
traverse(obj).map(function () {
// console.log('traversing', this)
if (this.isLeaf) {
const lpath = this.path
lpath.unshift(path)
// console.log(`#{!opts.values ? 'existing':'new'} leaf on path '#{lpath}' with value: #{this.node}`)
// console.log('#{!opts.values ? \'existing\':\'new\'} leaf on path \'#{lpath}\' with value: #{this.node}')
self.rxAdd(lpath, { value: this.node })
}
return true
}
)
return true
} // end traverse
if (this.isRx(path) && !opts.force) return true
const value = this.#get(path) // current value
if (value === undefined) {
// console.log ('no current property or value for',path,'creating temporary null')
// console.log('no current property or value for', path, 'creating temporary null')
this.#set(path, null)
}
if (this.isRx(path) && !opts.force) return true
console.log(path, 'making reactive')
const { parent, name } = this.#rxGetObj(path, '__parent__')
log.debug({
class: 'RxClass',
@ -149,6 +155,7 @@ class RxClass extends EventEmitter {
if (check.isFunction(rx.hook)) rx.hook.call(self, value) // property hook
}
})
// console.log('rxadd done', path, opts, this.#get(['#rx.props', path]))
return true
}
@ -183,7 +190,6 @@ class RxClass extends EventEmitter {
lpath.unshift(path)
if (self.isRx(lpath)) self.rxRemove(lpath, { confirm: true })
}
return true
})
// all done removing the leaves so remove branch
this.#del(['#rx.props', path], true)
@ -244,30 +250,74 @@ class RxClass extends EventEmitter {
return false
}
// // takes several formats for a path to an objects property and return only the . string
// formatObjPath (path) {
// if (path == null) return path
// if (Array.isArray(path)) {
// path = path.filter(Boolean).join('.') // Boolean filter strips empty strings or null/undefined
// }
// path = path.replace(/\/:,/g, '.') // replaces /:, with .
// return path
// }
// checks for getter and the corresponding rx object
isRx (path) {
// console.log(path)
if (this.#get(path) === undefined) return false
const { parent, name, parentPath } = this.#rxGetObj(path, '__parent__')
// console.log(parent, name, parentPath)
const rxparent = this.#get(['#rx.props', parentPath]) || {}
const rx = rxparent[name]
// console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx)
if (!rx) return false
// console.log(Object.getOwnPropertyDescriptor(parent, name).get)
return !!Object.getOwnPropertyDescriptor(parent, name).get
isGetSet (path) {
const keys = getKeys(path)
const prop = keys.pop()
const parent = this.#get(keys)
// console.log('isgetset', path, keys, prop, 'parent?', !!parent)
if (parent && prop) {
return !!(Object.getOwnPropertyDescriptor(parent, prop) || {}).get &&
!!(Object.getOwnPropertyDescriptor(parent, prop) || {}).set
}
return false
}
// is already reactive, checks for setter/getter and the corresponding rx object
isRx (path) {
// console.log('in isRX', path)
const keys = getKeys(path)
const cnt = keys.length
for (let index = 0; index < cnt; index++) {
// console.log('testing rx', index, cnt, keys, !!this.#get(keys), !!this.isGetSet(keys), !!this.#get(['#rx.props', keys]))
if (this.#get(keys) === undefined) return false
if (this.isGetSet(keys) && this.#get(['#rx.props', keys])) return true
keys.pop()
}
return false
// // console.log('in rx every', lpath)
// // if (this.#get(['#rx.props', ...lpath]) === undefined) {
// // return false
// // }
// //
// console.log(parent, name, parentPath)
// const rxparent = this.#get(['#rx.props', parentPath]) || {}
// const rx = rxparent[name]
// console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx)
// if (!rx) return true
// else {
// console.log(Object.getOwnPropertyDescriptor(parent, name).get)
// isRx = !!Object.getOwnPropertyDescriptor(parent, name).get
// return false
// }
// })
// return isRx
}
// isRx(path) {
// // console.log(path)
// let isRx = false
// const lpath = []
// getKeys(path).every(key => {
// lpath.push(key)
// console.log('in rx every', lpath, this.#get(lpath))
// if (this.#get(lpath) === undefined) {
// return false
// }
// // const { parent, name, parentPath } = this.#rxGetObj(lpath, '__parent__')
// // console.log(parent, name, parentPath)
// const rxparent = this.#get(['#rx.props', parentPath]) || {}
// const rx = rxparent[name]
// console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx)
// if (!rx) return true
// else {
// console.log(Object.getOwnPropertyDescriptor(parent, name).get)
// isRx = !!Object.getOwnPropertyDescriptor(parent, name).get
// return false
// }
// })
// return isRx
// }
// PRIVATE METHODS
// pass '__parent__' when getting objects for creating reactive prop