Skip to content

Commit

Permalink
Sub-props for fillables and guard 🐙 [merge]
Browse files Browse the repository at this point in the history
  • Loading branch information
mesqueeb authored Dec 15, 2018
2 parents fe695bb + 833b41d commit 5abd35f
Show file tree
Hide file tree
Showing 20 changed files with 548 additions and 116 deletions.
57 changes: 41 additions & 16 deletions dist/index.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,6 @@ function pluginActions (Firebase$$1) {
return console.error('[vuex-easy-firestore] ids needs to be an array');
if (id)
ids.push(id);
if (doc.id)
delete doc.id;
// 1. Prepare for patching
var syncStackItems = getters.prepareForPatch(ids, doc);
// 2. Push to syncStack
Expand Down Expand Up @@ -1243,6 +1241,46 @@ function flattenToPaths (object) {
return retrievePaths(object, null, result);
}

function recursiveCheck(obj, fillables, guard, pathUntilNow) {
if (pathUntilNow === void 0) { pathUntilNow = ''; }
if (!isWhat.isPlainObject(obj)) {
console.log('obj → ', obj);
return obj;
}
return Object.keys(obj).reduce(function (carry, key) {
var path = pathUntilNow;
if (path)
path += '.';
path += key;
// check guard regardless
if (guard.includes(path)) {
return carry;
}
var value = obj[key];
// check fillables up to this point
if (fillables.length) {
var passed_1 = false;
fillables.forEach(function (fillable) {
var pathDepth = path.split('.').length;
var fillableDepth = fillable.split('.').length;
var fillableUpToNow = fillable.split('.').slice(0, pathDepth).join('.');
var pathUpToFillableDepth = path.split('.').slice(0, fillableDepth).join('.');
if (fillableUpToNow === pathUpToFillableDepth)
passed_1 = true;
});
// there's not one fillable that allows up to now
if (!passed_1)
return carry;
}
// no fillables or fillables up to now allow it
if (!isWhat.isPlainObject(value)) {
carry[key] = value;
return carry;
}
carry[key] = recursiveCheck(obj[key], fillables, guard, path);
return carry;
}, {});
}
/**
* Checks all props of an object and deletes guarded and non-fillables.
*
Expand All @@ -1255,20 +1293,7 @@ function flattenToPaths (object) {
function checkFillables (obj, fillables, guard) {
if (fillables === void 0) { fillables = []; }
if (guard === void 0) { guard = []; }
if (!isWhat.isPlainObject(obj))
return obj;
return Object.keys(obj).reduce(function (carry, key) {
// check fillables
if (fillables.length && !fillables.includes(key)) {
return carry;
}
// check guard
if (guard.includes(key)) {
return carry;
}
carry[key] = obj[key];
return carry;
}, {});
return recursiveCheck(obj, fillables, guard);
}

/**
Expand Down
57 changes: 41 additions & 16 deletions dist/index.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,8 +702,6 @@ function pluginActions (Firebase$$1) {
return console.error('[vuex-easy-firestore] ids needs to be an array');
if (id)
ids.push(id);
if (doc.id)
delete doc.id;
// 1. Prepare for patching
var syncStackItems = getters.prepareForPatch(ids, doc);
// 2. Push to syncStack
Expand Down Expand Up @@ -1238,6 +1236,46 @@ function flattenToPaths (object) {
return retrievePaths(object, null, result);
}

function recursiveCheck(obj, fillables, guard, pathUntilNow) {
if (pathUntilNow === void 0) { pathUntilNow = ''; }
if (!isPlainObject(obj)) {
console.log('obj → ', obj);
return obj;
}
return Object.keys(obj).reduce(function (carry, key) {
var path = pathUntilNow;
if (path)
path += '.';
path += key;
// check guard regardless
if (guard.includes(path)) {
return carry;
}
var value = obj[key];
// check fillables up to this point
if (fillables.length) {
var passed_1 = false;
fillables.forEach(function (fillable) {
var pathDepth = path.split('.').length;
var fillableDepth = fillable.split('.').length;
var fillableUpToNow = fillable.split('.').slice(0, pathDepth).join('.');
var pathUpToFillableDepth = path.split('.').slice(0, fillableDepth).join('.');
if (fillableUpToNow === pathUpToFillableDepth)
passed_1 = true;
});
// there's not one fillable that allows up to now
if (!passed_1)
return carry;
}
// no fillables or fillables up to now allow it
if (!isPlainObject(value)) {
carry[key] = value;
return carry;
}
carry[key] = recursiveCheck(obj[key], fillables, guard, path);
return carry;
}, {});
}
/**
* Checks all props of an object and deletes guarded and non-fillables.
*
Expand All @@ -1250,20 +1288,7 @@ function flattenToPaths (object) {
function checkFillables (obj, fillables, guard) {
if (fillables === void 0) { fillables = []; }
if (guard === void 0) { guard = []; }
if (!isPlainObject(obj))
return obj;
return Object.keys(obj).reduce(function (carry, key) {
// check fillables
if (fillables.length && !fillables.includes(key)) {
return carry;
}
// check guard
if (guard.includes(key)) {
return carry;
}
carry[key] = obj[key];
return carry;
}, {});
return recursiveCheck(obj, fillables, guard);
}

/**
Expand Down
10 changes: 5 additions & 5 deletions docs/config-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ const firestoreModule = {
deleteBatchHook: function (updateStore, ids, store) { return updateStore(ids) },
},

// When items on the server side are changed:
// When docs on the server side are changed:
serverChange: {
defaultValues: {},
// HOOKS for changes on SERVER:
addedHook: function (updateStore, doc, id, store, source, change) { return updateStore(doc) },
modifiedHook: function (updateStore, doc, id, store, source, change) { return updateStore(doc) },
removedHook: function (updateStore, doc, id, store, source, change) { return updateStore(doc) },
addedHook: function (updateStore, doc, id, store) { return updateStore(doc) },
modifiedHook: function (updateStore, doc, id, store) { return updateStore(doc) },
removedHook: function (updateStore, doc, id, store) { return updateStore(doc) },
},

// When items are fetched through `dispatch('module/fetch', filters)`.
// When docs are fetched through `dispatch('module/fetch', filters)`.
fetch: {
// The max amount of documents to be fetched. Defaults to 50.
docLimit: 50,
Expand Down
9 changes: 4 additions & 5 deletions docs/extra-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,14 @@ Exactly the same as above, but for changes that have occured on the server. You

- *id:* the doc id returned in `change.doc.id` (see firestore documentation for more info)
- *doc:* the doc returned in `change.doc.data()` (see firestore documentation for more info)
- *source:* of the change. Can be either `'local'` or `'server'`

```js
{
// your other vuex-easy-fire config...
serverChange: {
addedHook: function (updateStore, doc, id, store, source, change) { updateStore(doc) },
modifiedHook: function (updateStore, doc, id, store, source, change) { updateStore(doc) },
removedHook: function (updateStore, doc, id, store, source, change) { updateStore(doc) },
addedHook: function (updateStore, doc, id, store) { updateStore(doc) },
modifiedHook: function (updateStore, doc, id, store) { updateStore(doc) },
removedHook: function (updateStore, doc, id, store) { updateStore(doc) },
}
}
```
Expand Down Expand Up @@ -278,7 +277,7 @@ If you create a `defaultValues` object, then each document from the server will
Automatically convert Firestore Timestamps into `new Date()` objects! Do this by setting `'%convertTimestamp%'` as the value of a `defaultValues` prop. (see example below).

**Use case 2: Reactivity**<br>
With VueJS, if you need a prop on an item to be fully reactive with your vue templates, it needs to exist from the start. If some docs in your user's firestore doesn't have all props (because you added new functionality to your app at later dates), the *retrieved docs will have reactivity problems!*
With VueJS, if you need a prop on an object to be fully reactive with your vue templates, it needs to exist from the start. If some docs in your user's firestore doesn't have all props (because you added new functionality to your app at later dates), the *retrieved docs will have reactivity problems!*

However, if you add these props to `defaultValues` with some value (or just `'null'`), vuex-easy-firestore will automatically add those props to the doc *before* inserting it into vuex!

Expand Down
2 changes: 1 addition & 1 deletion docs/firestore-fields-and-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Just like Firestore, Vuex Easy Firestore supports the usage of *arrayUnion* and
```js
import { arrayUnion, arrayRemove } from 'vuex-easy-firestore'

store.patch('myModule/patch', {
store.dispatch('myModule/patch', {
id: '001',
array1: arrayUnion('a new val'),
array2: arrayRemove('some val'),
Expand Down
20 changes: 11 additions & 9 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ There are two ways to use vuex-easy-firestore, in 'collection' or 'doc' mode. Yo
- Use when working with a single doc.
- eg. the "settings" or "config" of a user.

Depending on which mode there are some small changes, but the syntax is mostly the same.
Whether a vuex module is set to 'doc' or 'collection' mode, will have small changes in the actions you can do, but the syntax is mostly the same.

The sync is fully robust and **automatically groups any api calls per 1000 ms**. You don't have to worry about optimising/limiting the api calls, it's all done automatically! (Only one api call per 1000ms will be made for a maximum of 500 changes, if there are more changes queued it will automatically be split over 2 api calls).

> If you still are confused how to set up your database structure when it comes to documents vs collections, I highly recommend to check [this guide from Firebase](https://firebase.google.com/docs/firestore/manage-data/structure-data) itself.
## 'collection' mode

Opening [the DB channel](setup.html#open-db-channel) will retrieve all docs in your collection and add them to the vuex-module.
Expand All @@ -41,33 +43,33 @@ dispatch('moduleName/delete', id)

```js
const id = '123'
// Add the `id` as a prop to the item you want to set/update:
// Add the `id` as a prop to the document you want to set/update:
dispatch('moduleName/set', {id, name: 'my new name'})
// OR use the `id` as [key] and the item as its value:
// OR use the `id` as [key] and the document as its value:
dispatch('moduleName/set', {[id]: {name: 'my new name'}})

// Please note that only the `name` will be updated, and other fields are left alone!
```

There are two ways to delete things: the whole item **or just a sub-property!**
There are two ways to delete things: the whole document **or just a field!** (A field is a property of that document)

```js
// Delete the whole item:
// Delete the whole document:
dispatch('moduleName/delete', id)
// Delete a sub-property of an item:
// Delete a field of a document:
dispatch('moduleName/delete', `${id}.tags.water`)

// the items looks like:
// the document looks like:
{
id: '123',
tags: {
fire: true,
water: true, // only `water` will be deleted from the item!
water: true, // only `water` will be deleted in this example!
}
}
```

In the above example you can see that you can delete a sub-property by passing a string and separate sub-props with `.`
In the above example you can see that you can delete a field (or property) by passing a string and separate sub-props with `.` (See [here](firestore-fields-and-functions.html#delete-fields) for more info on deleting fields)

For batch actions see [here](#batch-updates-inserts-deletions).

Expand Down
58 changes: 58 additions & 0 deletions docs/ja/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
home: true
# heroImage: /hero.png
actionText: 始めよう →
actionLink: /setup
features:
- title: 何よりシンプル
details: Minimal setup to get a vuex-module synced with Firestore automatically.
- title: パワフル
details: Easy to use features include filtering, hooks, automatic Firestore Timestamp conversion & much more.
- title: パフォーマンスが第一
details: Automatic 2-way sync is fully optimised through api call batches.
footer: MIT Licensed | Copyright © 2018-present Luca Ban - Mesqueeb
---

# Overview

たった4つの行のコードを追加するだけで、VuexモジュールがFirestoreと自動的に同期される状態にできる。

```js
const userModule = {
firestorePath: 'users/{userId}/data',
firestoreRefType: 'collection', // or 'doc'
moduleName: 'userData',
statePropName: 'docs',
// モジュールのその他 state, actions など
}
// vuex-easy-firestoreでこのuserModuleをvuex pluginとしてstoreに入れるだけ
```

and Alakazam! Now you have a vuex module called `userData` with `state: {docs: {}}`.
All firestore documents in your collection will be added with the doc's id as key inside `docs` in your state.

Now you just update and add docs with `dispatch('userData/set', newItem)` and forget about the rest!

# Features

- Complete 2-way sync between your Vuex module & Firestore
- [Automatic Firestore Timestamp conversion](extra-features.html#defaultvalues-set-after-server-retrieval)
- [Fillables](extra-features.html#fillables-and-guard) (limit props able to sync)
- [Hooks](extra-features.html#hooks-before-insert-patch-delete) (before / after sync)
- [Where / orderBy filters](extra-features.html#filters)

# Motivation

I didn't like writing an entire an API wrapper from scratch for firestore every single project. If only a vuex module could be in perfect sync with firestore without having to code all the boilerplate yourself...

And that's how Vuex Easy Firestore was born.

<div style="text-align: right; margin-bottom: 1rem"><a href="setup.html">Installation and setup</a> →</div>

# Support

If you like what I built, you can say thanks by buying me a coffee! :)

<link href="https://fonts.googleapis.com/css?family=Cookie" rel="stylesheet"><a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/mesqueeb"><img src="https://www.buymeacoffee.com/assets/img/BMC-btn-logo.svg" alt="Buy me a coffee"><span style="margin-left:5px">Buy me a coffee</span></a>

Thank you so much!! Every little bit helps.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vuex-easy-firestore",
"version": "1.20.3",
"version": "1.21.0",
"description": "Easy coupling of firestore and a vuex module. 2-way sync with 0 boilerplate!",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
Expand Down
1 change: 0 additions & 1 deletion src/module/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export default function (Firebase: any): AnyObject {
// 0. payload correction (only arrays)
if (!isArray(ids)) return console.error('[vuex-easy-firestore] ids needs to be an array')
if (id) ids.push(id)
if (doc.id) delete doc.id

// 1. Prepare for patching
const syncStackItems = getters.prepareForPatch(ids, doc)
Expand Down
Loading

0 comments on commit 5abd35f

Please sign in to comment.