Angularfire2 and updating different objects using data fan-out - javascript

I need to update objects in my database, so I do this:
const items = af.database.list('/items');
items.update('key-of-some-data1', { size: newSize1 });
items.update('key-of-some-data2', { size: newSize2 });
This works like a charm, but I would like to update items with a "data fan-out" as described here:
https://firebase.google.com/docs/database/web/read-and-write
https://firebase.googleblog.com/2015/10/client-side-fan-out-for-data-consistency_73.html
So I do:
var updates = {};
updates['key-od-some-data1']= { size: newSize1 };
updates['key-od-some-data2']= { size: newSize2 };
items.update(updates);
Unfortunately I get this error:
zone.js:140 Uncaught Error: Error in ./AppComponent class AppComponent - inline template:30:2 caused by: Method requires a key, snapshot, reference, or unwrapped snapshot. Got: object
Is it even possible to update with data fan-out to the database with angularfire2? Does data fan-out work only with the same object key or something?

Replace:
const items = af.database.list('/items');
with:
const items = af.database.object('/items');

Related

Objection JS & Postgres & Exporess returning 'TypeError: Cannot read property 'isPartial' of null' on many to many relation query

I am trying to relate table named 'products' to another table named 'tags'.
I have a many to many table titled 'products_tags'.
When running const product = await Product.relatedQuery('tags').findById(1); I receive the following error:
TypeError: Cannot read property 'isPartial' of null
at findFirstNonPartialAncestorQuery (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\relations\RelationOwner.js:194:18)
at Function.createParentReference (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\relations\RelationOwner.js:24:48)
at QueryBuilder._findOperationFactory (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\model\Model.js:819:25)
at addFindOperation (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\queryBuilder\QueryBuilder.js:1551:31)
at addImplicitOperations (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\queryBuilder\QueryBuilder.js:1539:5)
at beforeExecute (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\queryBuilder\QueryBuilder.js:1439:13)
at QueryBuilder.execute (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\queryBuilder\QueryBuilder.js:681:13)
at QueryBuilder.then (C:\Users\AmazeCPK\Documents\_Web\fractal-insight2\node_modules\objection\lib\queryBuilder\QueryBuilder.js:634:26)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
My product model:
const {Model} = require('objection');
class Product extends Model {
static get tableName() {
return 'products'
}
static get relationMappings() {
const Tag = require('./Tag');
return {
tags: {
relation: Model.ManyToManyRelation,
modelClass: Tag,
join: {
from: 'products.id',
through: {
from: 'products_tags.product_id',
to: 'products_tags.tag_id'
},
to: 'tags.id'
}
}
}
}
}
module.exports = Product;
Tags.js
const { Model } = require('objection');
class Tag extends Model {
static get tableName() {
return 'tags';
}
static get relationMappings() {
const Product = require ('./Product');
return {
products: {
relation: Model.ManyToManyRelation,
modelClass: Product,
join: {
from: 'tags.id',
through: {
from: 'products_tags.tag_id',
to: 'products_tags.product_id'
},
to: 'products.id'
}
}
}
}
}
module.exports = Tag;
I've made sure the table names and relations are correct, as well as made sure there is matching data in the database. Help is appreciated. Thank you.
edit: here is the migration setup
//...
knex.schema.createTable('tags', table => {
table.increments('id').notNullable();
table.text("name");
table.boolean("is_deleted").defaultTo(false);
table.timestamps(true, true);
}),
knex.schema.createTable('products', table => {
table.increments('id').notNullable();
table.text("name");
table.text("description");
table.float("price");
table.integer('category_id').references('id').inTable('categories');
table.boolean("can_be_personalized").defaultTo(false);
table.specificType('images', 'text ARRAY');
table.boolean("is_deleted").defaultTo(false);
table.timestamps(true, true);
}),
knex.schema.createTable('products_tags', table => {
table.increments('id').notNullable();
table.integer('product_id').references('id').inTable('products');
table.integer('tag_id').references('id').inTable('tags');
table.boolean("is_deleted").defaultTo(false);
table.timestamps(true, true);
}),
Surely you're not in need of a solution anymore, but to future readers there are three things which can cause this. I just spent the last two and a half hours debugging this same error so in order from easiest to verify up to hardest to verify, they are:
You aren't specifying a primary id for the parent model of your related query. If you're using ModelB.relatedQuery('modelARelation') then you must also call .for(modelBId) or you will get this error. (This is what happened to me because I mixed up req.query and req.params :facepalm:)
ModelB.relatedQuery('modelARelation'); // when run alone, you get this answer
...
const modelBId = req.query.modelBId;
ModelB.relatedQuery('modelARelation').for(modelBId); // works just fine as long as modelBId is properly defined.
Require loops Circular dependency loops. If ModelA relates to ModelB and ModelB relates to ModelA, make sure you are using one of the circular dependency strategies outlined in the docs.
Typos. I hate to say it but if you have gotten this far, it is almost certain that you have typos somewhere. Go eat something or have somebody else look at it. This is actually just good advice generally.
In my limited experience, this error usually comes when it cannot create the object for whatever reason. Commonly it's an exports issue, or the constructor issue.
Are you sure the filename is correct? You named it Tags.js, but your const Tag = require('./Tag');

Feathers-mongoose : Get by custom attribute in feathers-mongoose

I have a very basic feathers service which stores data in mongoose using the feathers-mongoose package. The issue is with the get functionality. My model is as follows:
module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const { Schema } = mongooseClient;
const messages = new Schema({
message: { type: String, required: true }
}, {
timestamps: true
});
return mongooseClient.model('messages', messages);
};
When the a user runs a GET command :
curl http://localhost:3030/messages/test
I have the following requirements
This essentially tries to convert test to ObjectID. What i would
like it to do is to run a query against the message attribute
{message : "test"} , i am not sure how i can achieve this. There is
not enough documentation for to understand to write or change this
in the hooks. Can some one please help
I want to return a custom error code (http) when a row is not found or does not match some of my criterias. How can i achive this?
Thanks
In a Feathers before hook you can set context.result in which case the original database call will be skipped. So the flow is
In a before get hook, try to find the message by name
If it exists set context.result to what was found
Otherwise do nothing which will return the original get by id
This is how it looks:
async context => {
const messages = context.service.find({
...context.params,
query: {
$limit: 1,
name: context.id
}
});
if (messages.total > 0) {
context.result = messages.data[0];
}
return context;
}
How to create custom errors and set the error code is documented in the Errors API.

How to access nested arrays of objects

I am subscribing to my data from an http get method:
getEds(): void {
this.edService.getEds()
.subscribe((eds: Education) => {
this.eds = eds.educationData;
console.log(this.eds:codeschool);
});
}
I am trying to display my courses for codeschool in an *ngFor loop but do not know how to access the data. My console log will show the entire array of objects so I know I am receiving the correct info. I've tried various syntax:
.subscribe((eds: any) => {
this.eds = eds.educationData.course;
.subscribe((eds: any) => {
this.eds = eds.educationData['codeschool'];
.subscribe((eds: any) => {
this.eds = eds.educationData.codeschool;
None of these syntax work and the log shows undefined. I found this page which has great info and what I tried to use as a baseline.
Access / process (nested) objects, arrays or JSON
However, I do not know what is wrong or why I cannot get the data I need. When I use
.subscribe((eds: any) => {
this.eds = eds.educationData;
and I log out (this.eds), my log shows:
[{…}]
0:{codeschool: Array(14), egghead: Array(6)}
length:1
__proto__:Array(0)
Beyond this I haven't been able to get the data I want...... :(
use this :
eds.educationData[0].codeschool

Firebase React Binding

I'm somewhat new to React, and using the re-base library to work with Firebase.
I'm currently trying to render a table, but because of the way my data is structured in firebase, I need to get a list of keys from two locations- the first one being a list of user keys that are a member of a team, and the second being the full user information.
The team node is structured like this: /teams/team_id/userkeys, and the user info is stored like this: /Users/userkey/{email, name, etc.}
My table consists of two react components: a table component and a row component.
My table component has props teamid passed to it, and I'm using re-base's bindToState functionality to get the associated user keys in componentWillMount(). Then, I use bindToState again to get the full user node, like so:
componentWillMount() {
this.ref = base.bindToState(`/teams/${this.props.data}/members`, {
context: this,
state: 'members',
asArray: true,
then() {
this.secondref = base.bindToState('/Users', {
context: this,
state: 'users',
asArray: true,
then() {
let membersKeys = this.state.members.map(function(item) {
return item.key;
});
let usersKeys = this.state.members.map(function(item) {
return item.key;
});
let onlyCorrectMembersKeys = intersection(membersKeys, usersKeys);
this.setState({
loading: false
});
}
});
}
});
}
As you can see, I create membersKeys and usersKeys and then use underscore.js's intersection function to get all the member keys that are in my users node (note: I do this because there are some cases where a user will be a member of a team, but not be under /Users).
The part I'm struggling with is adding an additional rebase call to create the full members array (ie. the user data from /Users for the keys in onlyCorrectMembersKeys.
Edit: I've tried
let allKeys = [];
onlyCorrectMembersKeys.forEach(function(element) {
base.fetch(`/Users/${element}`, {
asArray: true,
then(data) {
allKeys.prototype.concat(data);
}
});
});
But I'm receiving the error Error: REBASE: The options argument must contain a context property of type object. Instead, got undefined
I'm assuming that's because onlyCorrectMembersKeys hasn't been fully computed yet, but I'm struggling with how to figure out the best way to solve this..
For anyone dealing with this issue as well, I seemed to have found (somewhat) of a solution:
onlyCorrectMembersKeys.map(function(item) {
base.fetch(`/Users/${item}`, {
context: this,
asObject: true,
then(data) {
if (data) {
allKeyss.push({item,data});
this.setState({allKeys: allKeyss});
}
this.setState({loading: false});
},
onFailure(err) {
console.log(err);
this.setState({loading: false});
}
})
}, this);
}
This works fine, but when users and members state is updated, it doesn't update the allkeys state. I'm sure this is just due to my level of react knowledge, so when I figure that out I'll post the solution.
Edit: using listenTo instead of bindToState is the correct approach as bindToState's callback is only fired once.

Object's data missing when passing to Meteor.call

In my React/Meteor application, I am trying to pass an object with data from the state to a method on the server, for insertion into the database. However, there seems to be an issue passing the object from the React component to the Meteor method - one of the child objects ends up in the Meteor method, but all of its children are gone. I do nothing to the object except use check() to ensure it is an Object:
'Appointments.saveData'(dataObj) {
check(dataObj, Object);
console.log(dataObj);
// ....
}
Here's what happens on the front-end:
Meteor.call('Appointments.saveData', {
vitalsData: this.state.vitalsData || {},
subjectiveData: this.state.subjectiveData || '',
physicalExamData: this.state.physicalExamData || {},
rosData: this.state.rosData || {},
impressionData: this.state.impressionData || [],
extraNotes: this.state.extraNotes || ''
}, (err, res) => {
if (res && !err) {
this.refs.toasts.success(
'Data for this encounter has been saved.',
'Records saved!'
);
} else {
this.refs.toasts.error(
'An unknown error has occurred. Reload the page and try again.',
'Error!'
);
}
});
I combine all of my state variables into an object using {}, which in turn becomes dataObj in the method. However, dataObj.impressionData exists, and is an array containing objects, however, data is missing from any of the objects in the array.
For example, dataObj.impressionData[0].diagnosis should be an object, in fact, it is supposed to be an exact copy of an object already pulled from the database. However, if I console.log it, the object is empty.
I have verified that the data exists as it should at each step before passing to the Meteor method. I console.log the object immediately before calling Meteor.call and immediately after calling check in my method. I cannot for the life of me understand why data is missing.
What am I forgetting?
EDIT: I've changed my code so that the data is now added to the state directly from a ref. Now the server method does properly receive the object. However, in the following code:
if (dataObj.impressionData && dataObj.impressionData.length > 0) {
dataObj.impressionData.forEach(obj => {
console.log(obj); // obj.diagnosis exists and is as expected
const x = ICD10Codes.findOne({ _id: obj.diagnosis._id });
console.log(x); // this also works as it should
impressionFields.push({ patientId: appt.patient._id, diagnosis: x, note: obj.note, x });
});
}
Setting diagnosis to x, which I KNOW is a valid copy of the object straight from the database yields the same results:
meteor:PRIMARY> db.EncounterData.findOne()
...
"impression" : {
"patientId" : "47de32b428d8c4aaac284af3",
"appointmentId" : "TwL7DF9FoXPRgmrjR",
"fields" : [
{
"patientId" : "47de32b428d8c4aaac284af3",
"diagnosis" : {
}
}
]
},
...
I think I'm going crazy.
So your issue comes down to the fact that this.setState is an asynchronous function, so when you make your Meteor call, this.state hasn't actually be updated yet. As such, you need to wait for the this.setState call to finish. The only way to do this is to use the React lifecycle methods. You can use either componentWillUpdate (called before the next render) or componentDidUpdate (called after the next render).
var MyComponent = React.createClass({
save: function() {
...
case 'impression':
this.setState({ impressionData: data }, this.callServerMetho‌​d);
break;
...
},
// This is one of the React lifecycle methods
componentWillUpdate: function(nextProps, nextState) {
// Put your Meteor call here
// Make sure to use nextState instead of this.state
// This way you know that this.state has finished updating
}
});
I solved the issue myself - it turns out that I was importing my SimpleSchema objects as default but exporting my SimpleSchema objects as named. The SimpleSchema objects were thus invalid.

Categories