Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions dist/firetruss.es2015.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/firetruss.es2015.js.map

Large diffs are not rendered by default.

25 changes: 22 additions & 3 deletions dist/firetruss.umd.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/firetruss.umd.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/firetruss.umd.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/firetruss.umd.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firetruss",
"version": "7.8.4",
"version": "7.8.5",
"description": "Advanced data sync layer for Firebase and Vue.js",
"scripts": {
"update": "yarn up -R '*' && yarn dedupe --strategy highest",
Expand Down
7 changes: 5 additions & 2 deletions src/Modeler.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,12 @@ export default class Modeler {
_wrapProperties(object) {
_.forEach(object, (value, key) => {
const valueKey = '$_' + key;
const descriptor = Object.getOwnPropertyDescriptor(object, key);
const valueDescriptor = descriptor.get && descriptor.set ? {
get: descriptor.get, set: descriptor.set, configurable: true
} : {value, writable: true};
Object.defineProperties(object, {
[valueKey]: {value, writable: true},
[valueKey]: valueDescriptor,
[key]: {
get: () => object[valueKey],
set: arg => {object[valueKey] = arg; angular.digest();},
Expand Down Expand Up @@ -571,7 +575,6 @@ export default class Modeler {
}

isLocal(path, value) {
// eslint-disable-next-line no-shadow
const mount = this._getMount(path, false, mount => mount.local);
if (mount && mount.local) return true;
if (this._hasLocalProperties(mount, value)) {
Expand Down
64 changes: 64 additions & 0 deletions src/Modeler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ class SubrootFoo {
static get $trussMount() {return '/sub/foo';}
}

let earlyObserver;

class EarlyObservedRoot {
static get $trussMount() {return '/';}

constructor() {
earlyObserver = new Vue({data: {scope: this}});
Vue.set(this, 'source', undefined);
}

get derived() {
return this.source;
}
}

test.beforeEach(t => {
t.context = {
rootUrl: 'https://example.firebaseio.com',
Expand All @@ -72,6 +87,10 @@ test.beforeEach(t => {

test.afterEach(t => {
t.context.tree.destroy();
if (earlyObserver) {
earlyObserver.$destroy();
earlyObserver = null;
}
});

test('initialize placeholders', t => {
Expand Down Expand Up @@ -110,3 +129,48 @@ test('computing non-primitive values', t => {
tree.checkVueObject(tree.root, '/');
});
});

test('wrapping observed properties preserves missing child dependencies', t => {
const tree = t.context.tree;
const context = {};
const navigation = {context};
const vue = new Vue({data: {navigation}});

tree._modeler._wrapProperties(navigation);
t.true(Object.hasOwn(navigation, '$_context'));

let review;
const unwatch = vue.$watch(() => navigation.context.review, value => {
review = value;
}, {immediate: true});
t.is(review, undefined);

Vue.set(context, 'review', {ready: true});
return Vue.nextTick().then(() => {
t.deepEqual(review, {ready: true});
unwatch();
vue.$destroy();
});
});

test('computed properties added after observation remain reactive', t => {
const tree = new Tree(
t.context.truss, t.context.rootUrl, t.context.bridge, t.context.dispatcher);
tree.init([EarlyObservedRoot]);
const root = tree.root;

let observed;
const unwatch = tree._vue.$watch(() => {
root.$$touchThis();
return root.derived && root.derived.child;
}, value => {
observed = value;
}, {immediate: true});

Vue.set(root, 'source', {child: 1});
return Vue.nextTick().then(() => {
t.is(observed, 1);
unwatch();
tree.destroy();
});
});
18 changes: 16 additions & 2 deletions src/Tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,25 @@ export default class Tree {

const object = this._modeler.createObject(path, properties);
this._modeler.emitLifecycleHook(object, 'beforeCreate');
Object.defineProperties(object, properties);
this._defineObjectProperties(object, properties);
return object;
}

_defineObjectProperties(object, properties) {
const observer = object.__ob__;
Object.defineProperties(object, properties);
if (!observer) return;

let addedReactiveProperties = false;
for (const key of _.keys(properties)) {
const descriptor = Object.getOwnPropertyDescriptor(object, key);
if (!descriptor.configurable || !descriptor.enumerable) continue;
Vue.util.defineReactive(object, key);
addedReactiveProperties = true;
}
if (addedReactiveProperties) observer.dep.notify();
}

// To be called on the result of _createObject after it's been inserted into the _vue hierarchy
// and Vue has had a chance to initialize it.
_fixObject(object) {
Expand Down Expand Up @@ -728,4 +743,3 @@ export function toFirebaseJson(object) {
}
return result;
}