vuefire icon indicating copy to clipboard operation
vuefire copied to clipboard

Make it easy to access the per item firebase refs.

Open anachirino opened this issue 7 years ago • 13 comments

If you add res['.ref'] = _getRef(snapshot) to the createRecord function. Like:

function createRecord (snapshot) {
  var value = snapshot.val()
  var res = isObject(value)
    ? value
    : { '.value': value }
	res['.key'] = _getKey(snapshot)
	res['.ref'] = _getRef(snapshot);
  return res
}

Then we can more easily access the firebase reference that is associated with a given item. The example in the readme could be changed from:

deleteItem: function (item) {
   this.$firebaseRefs.items.child(item['.key']).remove()
}

to:

deleteItem: function (item) {
   item['.ref'].remove()
}

anachirino avatar Apr 21 '18 13:04 anachirino

mmh, this is interesting. Any situation when $firebaseRefs is not enough?

posva avatar Apr 21 '18 13:04 posva

It's just easier access. Make the resulting user code much more readable IMHO.

anachirino avatar Apr 21 '18 13:04 anachirino

oh wait, you're asking to add a ref for every single element? That is too much. I will have to think about it. I like how practical it is but it introduces other problems

posva avatar Apr 21 '18 14:04 posva

would making it a function like: res['.ref'] = function() { return _getRef(snapshot); }

Help your concerns?

anachirino avatar Apr 21 '18 14:04 anachirino

@posva any updates?

anachirino avatar Apr 28 '18 15:04 anachirino

not yet, I'll look into this later

posva avatar Apr 29 '18 16:04 posva

would making it a function like: res['.ref'] = function() { return _getRef(snapshot); }

Help your concerns?

@anachirino or it could be a getter with Object.defineProperty

function createRecord (snapshot) {
  var value = snapshot.val()
  var res
  if (isObject(value)) {
    res = value
  } else {
    res = {}
    Object.defineProperty(res, '.value', {
      value: value
    })
  }
  Object.defineProperty(res, '.key', {
    value: _getKey(snapshot)
  })
  Object.defineProperty(res, '.ref', {
    get: function () {
      return _getRef(snapshot)
    }
  })
  return res
}

trickstival avatar Oct 07 '18 18:10 trickstival

For the moment, this is now achievable with the serialize option (https://vuefire.vuejs.org/api/vuefire.html#options-serialize)

posva avatar Jul 16 '19 08:07 posva

I was able to achieve the result by doing this in my main.js:

import { VueFire, VueFireAuth, globalFirestoreOptions, firestoreDefaultConverter } from 'vuefire'

globalFirestoreOptions.converter = {
  // the default converter just returns the data: (data) => data
  toFirestore: firestoreDefaultConverter.toFirestore,
  fromFirestore: (snapshot, options) => {
    const data = firestoreDefaultConverter.fromFirestore(snapshot, options)
    // if the document doesn't exist, return null
    if (!data) return null
    // add anything custom to the returned object
    // data.metadata = snapshot.metadata
    data.ref = snapshot.ref
    return data
  },
}

note the line: data.ref = snapshot.ref

Sadly, it does not do the same for the referenced children which are loaded. Those end up not having a ref.

UPDATE: I later noticed that this also get's pushed to the firestore upon update. So it's not read only and I don't know how I would make it read only 🙈.

chrisspiegl avatar Dec 31 '22 14:12 chrisspiegl

Use Object.defineProperty to make it non enumerable 😉

posva avatar Dec 31 '22 21:12 posva

How does this work with TypeScript?

I have tried this without any luck.

globalFirestoreOptions.converter = {
  // the default converter just returns the data: (data) => data
  toFirestore: firestoreDefaultConverter.toFirestore,
  fromFirestore: (snapshot, options) => {
    const data = firestoreDefaultConverter.fromFirestore(snapshot, options);
    // if the document doesn't exist, return null
    if (!data) return null;
    // add anything custom to the returned object
    // Adding a ref (aka DocumentReference)
    Object.defineProperty(data, "ref", {
      value: snapshot.ref,
      enumerable: false,
      writable: true,
      configurable: true,
    });
    return data as DocumentData & {
      readonly id: string;
      readonly ref: DocumentReference<DocumentData>;
    };
  },
};

The code works, because I can access ref on my useDocument objects. But TypeScript still complains with an error.

For example, when I try to access ref on a User document I get this TypeScript error: Property 'ref' does not exist on type 'User & { readonly id: string; }'. ts(2339)

BenJackGill avatar Mar 29 '23 17:03 BenJackGill

Also I have noticed this does not work for useCollection(). It only works for useDocument().

When using useCollection() it would be good to have a collection reference on the collection itself, and a document reference stored on a ref property of on each document in the collection array.

BenJackGill avatar Mar 30 '23 13:03 BenJackGill

For the moment, this is now achievable with the serialize option (https://vuefire.vuejs.org/api/vuefire.html#options-serialize)

For who is looking for the serialize option document, it is moved to here now.

tlserver avatar Dec 04 '23 09:12 tlserver