AngularFire2 - Append to list child - firebase

I'm trying to add an object to /users/[userKey]/invitedTo but set deletes the existing data, so does update.
What I want to end up with is something like this:
users
-uniqueuserkey
--name: Name
--InvitedTo
---eventuniquekey1
----eventname: event name 1
----etc
---eventuniquekey2
----eventname: event name 2
----etc
-
// this.event.push(eventObj);
this.event.push(eventObj).then((item) => {
if (item) {
const itemKey = item.key;
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
const invitedTo = this.db.object(`/users/${key}/invitedTo`);
invitedObj[itemKey] = eventObj;
invitedTo.set( { invitedObj } );
}
}
}
});
Update does exactly what I need, but it also deletes existing value:
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
invitedObj[itemKey] = eventObj;
this.users.update(key, { invitedTo: invitedObj });
}
}
Should I just get the existing data and add to it?

If you want to add an object, you (usually) should use the push method instead of set or update:
const invitedTo = this.db.list(`/users/${key}/invitedTo`);
invitedTo.push(eventObj);
That way, Firebase will create a unique key and add it to the invitedTo node.
If you want to set the key yourself, then you could use the update method like this:
const invitedTo = this.db.object(`/users/${key}/invitedTo/${itemKey}`);
invitedTo.update(eventObj);

Related

Copy Google Analytics custom definitions/dimensions between GA4 properties with the Google Analytics Data API?

I have a number of GA4 Google Analytics properties that need to share the same custom dimensions (custom definitions). Using the web UI to manually copy them is slow and error prone. How can I automate this process?
Using Google Apps Script and the Google Analytics Data API v1 I've created a function that almost does it:
https://script.google.com/d/1Tcf7L4nxU5zAdFiLB2GmVe2R20g33aiy0mhUd8Y851JLipluznoAWpB_/edit?usp=sharing
However the API is in beta and doesn't seem to have a way to add the dimensions to the destination GA property.
Does anyone have a way to complete this final step?
function main(){
gaSourcePropertyId = '12345678';
gaDestinationPropertyId = '87654321';
copyCustomDefinitions(gaSourcePropertyId, gaDestinationPropertyId);
}
function copyCustomDefinitions(gaSourcePropertyId, gaDestinationPropertyId) {
let sourceDimensions = getCustomDimensions(gaSourcePropertyId);
addCustomDimensions(gaDestinationPropertyId, sourceDimensions);
}
function getCustomDimensions(gaPropertyId) {
var sourceDimensions = AnalyticsData.Properties.getMetadata(`properties/${gaPropertyId}/metadata`)['dimensions'];
var sourceCustomDefinitions = [];
sourceDimensions.forEach((dimension)=>{
if (dimension['customDefinition'] == true){ sourceCustomDefinitions.push(dimension)}
});
return sourceCustomDefinitions;
}
function addCustomDimensions(gaPropertyId, dimensions) {
const destinationDimensions = getCustomDimensions(gaPropertyId);
const destinationDimensionApiNames = destinationDimensions.map(dimension=>dimension['apiName']);
dimensions.forEach((dimension)=>{
// Check if the new dimension already exists in the destination.
if ( !destinationDimensionApiNames.includes(dimension['apiName']) ) {
let newDimension = AnalyticsData.newDimension(dimension['apiName']); // The newDimension method doesn't seem to work yet!
// TODO - add the dimension to the destination property
console.warn('There is not yet an API function for adding dimensions to a property. Something like AnalyticsData.Properties.setMetadata is needed!');
} else {
console.info(`Dimension with apiName '${ dimension['apiName'] }' already present in destination! Dimension not added to destination`);
}
})
}
Turns out there is the Google Analytics Admin API which provides methods for manipulating the custom dimensions of a property.
This includes functions for listing custom dimensions and creating custom dimensions
function main(){
gaSourcePropertyId = '1234';
gaDestinationPropertyId = '4321';
copyCustomDimensions(gaSourcePropertyId, gaDestinationPropertyId);
}
function copyCustomDimensions(gaSourcePropertyId, gaDestinationPropertyId) {
let sourceDimensions = getCustomDimensions(gaSourcePropertyId);
addCustomDimensions(gaDestinationPropertyId, sourceDimensions);
}
function getCustomDimensions(gaPropertyId) {
try {
return AnalyticsAdmin.Properties.CustomDimensions.list(`properties/${gaPropertyId}`)['customDimensions'];
} catch (error) {
console.error(error);
}
}
function addCustomDimensions(gaPropertyId, dimensions) {
let destinationCustomDimensions = getCustomDimensions(gaPropertyId);
// The destination may not have any custom dimensions.
if (typeof destinationCustomDimensions == 'undefined') {
console.info(`Could not get custom definitions for property ID '${gaPropertyId}'.`, destinationCustomDimensions);
destinationCustomDimensions = [];
};
const destinationDimensionParameterNames = destinationCustomDimensions.map(dimension=>dimension['parameterName']);
dimensions.forEach((sourceDimension)=>{
// Check if the new dimension already exists in the destination.
if ( !destinationDimensionParameterNames.includes(sourceDimension['parameterName']) ) {
let destinationDimension = {
"parameterName": sourceDimension['parameterName'],
"displayName": sourceDimension['displayName'],
"description": sourceDimension['description'],
"scope": sourceDimension['scope']
};
let result = null;
try {
result = AnalyticsAdmin.Properties.CustomDimensions.create(destinationDimension, `properties/${gaPropertyId}`);
console.log('[COPIED]',result);
} catch (error) {
console.log('[FAILED]', destinationDimension)
console.error(error);
}
} else {
console.info(`[NO ACTION] Dimension with apiName '${ sourceDimension['parameterName'] }' already present in destination! Dimension not added to destination.`, sourceDimension);
}
});
}

Get the guid of the elements grouped in an ifcgroup

Is there a function in the API of IFCJS to get the guid of the elements grouped in an ifcgroup?
for example, if I group a column with a wall
getElementsFromIfcGroup(guidGroup) ---> return [guidWall, guidColumn]
According to the IFC schema, IfcGroup instances group elements together using an indirect relationship object called IfcRelAssignsToGroup. This means that you can retrieve the elements contained within that group like this:
import { IFCRELASSIGNSTOGROUP as REL } from 'web-ifc';
async function getItemsOfGroup(modelID, groupID) {
const manager = ifcLoader.ifcManager;
const relIDs = await manager.getAllItemsOfType(modelID, REL);
for(relID of groupsIDs) {
const groupRel = await manager.getItemProperties(modelID, relID);
if(groupRel.RelatingGroup.value === groupID) {
return groupRel.RelatedObjects;
}
}
return [];
}
based on Antonio's answer, it looks like this:
async function getItemsOfGroup(modelID, groupID) {
const manager = viewer.IFC.loader.ifcManager
// Get all ifcgroups
const relIDs = await manager.getAllItemsOfType(modelID, IFCRELASSIGNSTOGROUP);
let relID, relObj, props;
var guIDs = [];
for(relID of relIDs) {
const groupRel = await manager.getItemProperties(modelID, relID);
// Find the groupID
if(groupRel.GlobalId.value === groupID) {
// Search all related objects
for(relObj of groupRel.RelatedObjects) {
//get object properties
props = await manager.getItemProperties(modelID, relObj.value);
//Add guid to array
guIDs[guIDs.length] = props.GlobalId.value;
}
return guIDs;
}
}
return guIDs;
}

Redux immutable change array order

So I'm trying to change the order of an array using redux and immutable. What I'm basically doing is grabbing the current array, modifying it, and updating the state with modified copy, like so:
case MOVE_LAYOUT: {
const index = action.payload;
const lower = index - 1;
const element = state.getIn(['someArray', index]);
if (action.direction === 'up') {
const arr = state.get('someArray');
const newArr = arr.splice(lower, 0, element);
const remove = newArr.splice(index, 1);
return state.set('someArray', remove);
}
return false;
}
For the sake of the question, let's assume action.payload is passed properly, and that a console.log(remove.toJS()) will return what I want. However, when I update someArray nothing happens. Why would this be?
So here's the answer:
case MOVE_LAYOUT: {
const index = action.payload;
const arr = state.get('someArray').toJS();
let next;
if (action.direction === 'up') {
next = index - 1;
} else {
next = index + 1;
}
const temp = arr[index];
arr[index] = arr[next];
arr[next] = temp;
return state.set('someArray', fromJS(arr));
}
Basically, I'll make a copy of the array, and modify the index (move by 1 up or 1 down), and set the modified array copy back to state. Problem with initial approach was not using .toJS() and .fromJS(), so the results were immutable list, rather than JS object.

Firebase: combine query and child event listener

I have a structure similar to this
{
posts : {
post1Key: {
title : "First Post",
comments : {
comment1Key : {},
comment2Key : {},
comment3Key : {}
}
}
}
...
}
And I'm using the "child updated" events on the key "/posts/post1Key/comments" to listen for new comments.
How can I combine Firebase Query and the event listeners.
Edit: I know this is possible with Firebase, however, I'm using nativescript-plugin-firebase
You can very well combine a Query defined with an orderBy... method and a listener with the on() method: see detailed doc here
For example you could do something like
var query = db.ref('node1').orderByChild('firstName');
query.on('value', function(dataSnapshot) {
dataSnapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
console.log(childKey);
var childData = childSnapshot.val();
console.log(childData);
});
});
Each time a new sub node is added under "node1" you will get the children list ordered by firstName
As at the time of writing, this isn't possible using the nativescript-plugin-firebase. The workaround is to do the sorting/filtering in the eventHandler after you have received the data.
var onChildEvent = function(result) {
//
// Sort: result.value here
//
};
// listen to changes in the /users path
firebase.addChildEventListener(onChildEvent, "/users").then(
function(listenerWrapper) {
var path = listenerWrapper.path;
var listeners = listenerWrapper.listeners; // an Array of listeners added
// you can store the wrapper somewhere to later call 'removeEventListeners'
}
);

How can I dynamic set a value into a property?

How to set a property to value that should be resolve.. like this one..
const getDataFromServer = (id) => ({id: * 2})
R.set(payloadProp, getDataFromServer)({id: 4}); // WRONG, is setting to a function instend to resolve a function with `{id: 4}`
const fetch = (id) => {
return { num: id * 2 }
};
const idProp = R.lensProp('id');
const getDataFromServer = R.pipe(R.view(idProp), fetch);
const payloadProp = R.lensProp('payload');
const setPayloadFromFetch = R.set(payloadProp, getDataFromServer); // NOT WORK, return payload as function
const obj = { id: 1, payload: { message: 'request' } }
const ret = setPayloadFromFetch(obj);
console.log(ret);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
The problem is that R.set takes a value, not a function, for its second parameter. And you can't switch to R.over, which does take function but calls it with the current value at that lens, not the full data object supplied to the outer function.
The simplest way to solve this is simply to pass the object in both places:
const setPayloadFromFetch = obj => R.set(payloadProp, getDataFromServer(obj), obj);
But if you're intent on making this point-free, lift is your friend:
const setPayloadFromFetch = R.lift(R.set(payloadProp))(getDataFromServer, identity);
although you could also use R.ap, which would be very nice except that R.set takes its parameters in the wrong order for it, and so you have to use R.flip
const setPayloadFromFetch = R.ap(R.flip(R.set(payloadProp)), getDataFromServer);
You can see all these in the Ramda REPL.

Resources