I want to get from firebese an object that have a specific value and this object parent's name. But in some reasons I cant do it.
So first of all I will describe what I have:
Db with the next structure:
db
|
+--clients
|
+--kua
| |
| +--kuaName1
| |
| +--kuaName2
| |
| +--kuaName3
|
+--uo
In every kuaName I have an object with some info, one of the keys is "kuaEdrpou", the value of this key is unique for every kuaName. So I have next logic:
Find an object by kuaEdrpou.
Take this object name (kuaName1, kuaName2, kuaName3) and data to use them in my app.
Also I have function to get data from firebase db:
const stocksRef = firebase.database().ref().child('clients/kua');
stocksRef.orderByChild('kuaEdrpou').equalTo(this.state.kuaEdrpou).once("value",snapshot => {
let snap = snapshot.exportVal();
console.log("snapshot", snap);
}
Console.log shows me an object with three keys
So my questions are how to get from firebase
{kuaName1:{kuaEdrpou: some id}} object and why snapshot.exportVal() returns an object with three nested objects inside?
you are setting the snapshot on ref
firebase.database().ref().child('clients/kua');
so whenever something change/updates you get the whole kua with all of its child.
if you need any specific child under kua add the reference of that children like
firebase.database().ref().child(`clients/kua/${this.state.kuaEdrpou}`);
maybe this way you can get your desired object
Related
So, we have an app with multiple resources, let's say we have Product, Cart, Whatever resources. For each of those resources you can create activities, the main idea here is that for each resource there is an endpoint to create/update those activities, which looks the same no matter the resource you are trying to update.
So in our app (React) we created a single form to create/update an activity, it looks the same no matter for which resource you want to create an activity for, same fields, same possible values. Therefore we have one single component instead of 3, and a common function that handles the api part.
Something like:
const { mutate } = useUniversalEditActivity(variant); // variant can be 'product', 'cart', 'whatever'
We call mutate when we want to submit the form.
Inside that hook, there is a simple map:
const variantMapper = {
product: {
serviceAction: updateProductActivity, // simple function that wraps fetch, does the network request
},
cart: {
serviceAction: updateCartActivity,
},
whatever: {
serviceAction: updateWhateverActivity,
},
};
// Using it like
const mutatingServiceAction = variantMapper[variant].serviceAction;
...
await mutatingServiceAction();
The body is typed as
type UniversalEditActivityBodyType =
| UpdateProductActivityRequestBody
| UpdateCartActivityRequestBody
| UpdateWhateverActivityRequestBody
Which works when all the properties are the same across the types, but the problem starts now when the BE changed the spec for the Whatever resource.
So, before the request body had a property which had 2 possible values, so it was typed like:
type UpdateProductActivityRequestBody = {
propertyWithIssues: 'a'| 'b';
}
All 3 looked the same, but the spec changed for the Whatever resource to:
type UpdateWhateverActivityRequestBody = {
propertyWithIssues: 'a'| 'b' | 'c' | 'd';
}
Adding 2 more possible values for the same property, now there is a difference on how they look and inside my generic function that handled all body types, now I get the Type '"a" | "b" | "c" | "d"' is not assignable to type '"a" | "b"'.
I kind of understand the error, but not sure how to fix it in order for my function to still work with all those 3 types when just the possible values on a single property is different between them.
I don't know if I explained as good as I should have, but it's a more complex question (I think), so I tried my best. Please also suggest a different title if you think it would better describe my problem. Thanks in advance.
UPDATE 1:
#chris-hamilton, the request is executed like this:
const resp = await mutatingServiceAction(id, activityId, payload);
This is where the issue happens, because payload is a union of all those 3 types, but now they have become incompatible.
Minimum reproducible example here: https://codesandbox.io/s/gallant-bessie-sxjc4x?file=/src/index.ts
I know in theory that the issue could be solved by doing something like:
if (variant === 'product') {
// directly use product function
}
...
But I have the feeling this can be something different, as they have the exact same structure, just one property can have different values.
This error is really just a big red flag for this design pattern. You've specified that these two parameters can be one of many types, meaning you can have any combination of variant and body. ie. this function would accept a variant of "product" and a body of UpdateCartActivityRequestBody. But clearly that is not how the function is meant to be used.
It's a question of what these updateActivity functions are doing, and how similar they are. It may be that the parameters of these functions can accept a common type:
type Body = {
propertyWithIssues: string;
}
function main(
variant: "product" | "cart" | "whatever",
body: Body
) {
const serviceAction = variantMapper[variant].serviceAction;
serviceAction(body); // no error
}
So you need to ask yourself, "do my functions need to know about all these specific properties?".
If the answer is no, then define a type with only the properties needed for those functions. If those properties are common for all functions then the design pattern is fine. The type may just be Object, it depends what the functions are doing.
If the answer is yes, then the design pattern is incorrect, and you shouldn't be coupling these service actions into a single function. You should just be calling updateWhateverActivity directly, with the correct parameter type. No need for this variantMapper object.
Maybe you have other reasons for implementing this pattern, but you'll need to give more details if that's the case.
I can see it maybe being the case that you have an object with variant and body and you don't know what they are until run time. In that case you will have to do type narrowing like you showed. But you should also be checking that the type of body actually matches variant or you're just asking for runtime errors. This is really what the error is trying to tell you.
if (variant === 'product') {
// somehow verify that body is of type UpdateProductActivityRequestBody
// directly use product function
}
I'm currently working on a project with React and Firebase and at the moment my logic to write new data to Firestore is directly in my components. I'm now also integrating Redux, for this I am using react-redux-firebase. My question is now, where is the best place to put this logic, because it makes my component quiet bloated.
In the documentation of react-redux-firebase, they've also put it directly in the component, but their logic is quit simple, mine is a little bit more complex, like this handleSubmit:
const handleSubmit = async (e) => {
e.preventDefault();
setDisabled(true); //disable button to prevent multiple sbumits
setLoading(true);
try {
const docRef = await firestore.collection("goodies").add(newGoodieData); // create new event
await firestore.collection("goodies").doc(docRef.id).update({
// add eventId to event doc
id: docRef.id,
});
if (image) {
const imageName = docRef.id + "." + image.name.split(".").pop(); //create image name with eventID and file extions
const imageRef = storage.ref().child("goodie_images/").child(imageName); //create imageRef
const imageSnapshot = await imageRef.put(image); //upload image to storage
const downloadUrl = await imageSnapshot.ref.getDownloadURL(); //get image download-url
const imagePath = imageRef.fullPath; // get image storage path
await firestore.collection("goodies").doc(docRef.id).update({
//update event with imageUrl and imagePath
image: downloadUrl,
imagePath: imagePath,
});
}
setDisabled(false);
setLoading(false);
dispatch(showSuccessToast(`Created new goodie "${newGoodieData.name}"!`));
history.push("/goodies");
} catch (error) {
dispatch(showErrorToast("Ops, an error occurred!"));
console.error(error.message);
}
};
This is entirely my idiosyncrasy, but I use a structure like the below. In general, component and page (i.e react component) names are initial-capital:
src
|-blocks
|-components
|-css
|-navigation
|-pages
|-services
|-App.js
|-constants.js
|-index.js
BLOCKS
These are my application-specific components, and some larger application-specific functions.
|-blocks
|-Block1
| |-index.js
| |-aSubModule.js
|-Block2
| |-index.js
| |-aSubModule.js
etc, etc
COMPONENTS
These are "generalized" components - i.e could be released as modules, or are customized from external modules. These are NOT application-specific. Similar structure to "blocks"
|-components
|-ComponentThatDoesThisThing
|-ComponentThatDoesThatThing
etc, etc
CSS
As the name says...
NAVIGATION
Headers, Footers, navigation menus. Also navigation helper functions (such as wrapping links to pages, etc)
|-navigation
|-Header
|-Menu
|-Footer
etc, etc
PAGES
All my react pages and sub-pages, to whatever depth are needed. Everything below pages is specific to the application
|-pages
|-Users
| |-Home
| | |index.js
| | |-etc,etc
| |-Account
| |-Bio
| |-etc,etc
|-Owners
| |-Home
| |-Account
| |-Bio
| |-etc,etc
|-index.js
And where your question is more directly addressed:
SERVICES
|-services
|-reduxEnhancers
|-reduxMiddleware
|-reduxRootReducers
|-slices
|-configureStore.js
|-firestore.js
Not yet much different than the tutorials. But this is what I have in my slices - which more-or-less-ish correspond to redux state slices:
SLICES
|-services
|-slices
|-UserSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-OwnerSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-KitchCupboardSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-etc,etc slices
|-index.js
IMPORTANT NOTE: most Redux tutorials show the Redux slices having the same branching structure as the React components. In general THIS IS NOT AT ALL THE CASE, and I have yet to find a case where it is even helpful. The tree-like structure of your data is not necessarily at all like the tree-like structure of your pages and react components - divide your Redux store the way the data wants you to, not the way the user-facing React wants you to. Even more to the point for this forum: let the FIRESTORE structure of your data guide you.
In use:
|-services
|-slices
|-UserSlice
|-index.js
...for example, imports and then exports all the public/exported symbols from actions, reducer and business.
|-services
|-slices
|-UserSlice
|-reducer
...reducer uses store.injectReducer to add itself to the Redux store, using symbols and constants defined in
|-services
|-slices
|-UserSlice
|-actions
...actions, which also creates the specific Redux actions and helper boiler plate (actions, selectors, etc).
To help separate responsibility:
|-services
|-slices
|-UserSlice
|-business
...business sub-directory holds the BUSINESS logic you mentioned - any logic that a React component might need to call that is NOT specifically local to the display & state aspects of the React modules. This is ALSO where I define any access to firestore, or any other external service or fetches - the React components should NOT care how this is done.
To make accessing all of these various exports more convenient, I use
|-services
|-slices
|-index
...which imports and then exports all the public/exported symbols from all the slices. Makes it easier to import into various React comopnents without worrying about changes to the underlying structure of slices
As I said, COMPLETELY my idiosyncrasy, but it helps to separate out responsibility.
I have a response from the server that looks like this
[
{
"metric":{
"id":"b66178b8-dc18-11e8-9f8b-f2801f1b9fd1",
"name":"Detector de fum bucatarie",
"metricValueType":"DOUBLE",
"metricType":"DEVICE_VALUE",
"groupUUID":null,
"projectUUID":null,
"companyUUID":"ccab28ed-3dcf-411f-8748-ec7740ae559c",
"unit":null,
"formula":null
},
"data":{
"1550150771819":"10.310835857351371"
}
}
]
Data property contains a hashMap with Timestamp and a Value.
When I try to get any value I recive this error:
myErrorTypeError: undefined is not an object (evaluating 'metricValues.metric.id')
How can I access a property ? I tried both methods :
metricValues.metric.id
and
metricValues["metric"]["id"]
How can I get the hashMap values?
I already tried this :
const timestamp = Object.keys(metricValues.data)[0];
const values = Object.values(metricValues.data)[0];
Your data is an array of objects. So even though your data has only one object, still it is an array of objects.
your object is at index 0 of the array.
You can access the metric Id as follows,
metricValues[0].metric.id
Try This (put that [0])
metricValues[0].metric.id
Your response from the server is an array. And the metric object is in the first element of that array.
To access the id inside the metric object you need to pass the index at which that object is present in the array which is 0 here.
That's why use :
metricValues[0].metric.id
I have a field collection "user_card" composed of 2 fields: card_name and card_digits.
Right now the field collection is shown like this
+---------+---------------------------+
| User id | title user_card |
+---------+---------------------------+
| 1 | card_name_1 card_digits_1 |
| 2 | card_name_2 card_digits_2 |
+---------+---------------------------+
I need to show in a view that field collection but in 2 different columns, like this.
+---------+--------------+---------------+
| User id | Card name | Card digits |
+---------+--------------+---------------+
| 1 | card_name_1 | card_digits_1 |
| 2 | card_name_2 | card_digits_2 |
+---------+--------------+---------------+
Is there a way to do it? or at least have a mecanism to break the collection in 2 separate fields.
Use Field Collection Views module.
This module provides a formatter leveraging views for the Field Collection module.
Usage
After install this module,then you could goto host entity's fields
display settings page,such as
admin/structure/profiles/manage/resume/display.
at display settings page, you could choose format for you collection
field,This module provide a new option"Views field-collection items"
then at format summary area, you will see "Embed View:
field_collection_view, default", Yes ,this module using
views_embed_view function,That mean you need config the name and
display id of the view you want to use.
Now if you visit a page of host entity, you could only see three
fields," Field collection Item id", "Edit", "Delete",At the bottom
there is a "Add" link.This is not a bug, that because i do not know
which fields that added to collection field.
Clone views "field_collection_view", at the clone view, you need add
more fields to it, change the sort criteria,Please do not change the
existing fields (except "Exclude from display " for
"Field-collection item: Field-collection item ID"),the Contextual
filters of " Field-collection item: Field-collection item ID"
After that you need config the name /display id of the views you
want to use at display settings page, such as
admin/structure/profiles/manage/resume/display,you click the button
at the right place, then there will be a form inlude 2
elements,"name" and "display id".
I have a custom object set up within Salesforce called Solar_Install. I have a S2S connection with another installation of Salesforce. I want to share the custom object with them as well as the parent object (Account), partly because child objects inherit the autoshare property from their parent so I have to.
I have an Apex trigger on the child object (the Solar_Install) which looks like this:
trigger shareWithPartner on Solar_Install__c (after insert) {
PartnerNetworkRecordConnection newConnection =
new PartnerNetworkRecordConnection(
ConnectionId = '12AB3456789CDEF',
LocalRecordId = trigger.new[0].id,
SendClosedTasks = false,
SendOpenTasks = false,
SendEmails = false,
ParentRecordId = ???);
insert newConnection;
}
but I don't know what to put in for the ??? value. I have tried various things:
trigger.new[0].Account_c.AccountId
Error: Compile Error: Invalid foreign key relationship: Solar_Install__c.Account_c at line 10 column 57
Account_c
Error: Compile Error: Variable does not exist: Account_c at line 10 column 42
etc. Does anyone know how I reference the parent (Account) Id from this custom object in order that I can specify it as the ParentRecordId?
Cheers
Did you try traversing the Account relationship with Account_r.Id or Account_c (with two underscores)? These should be the same, but the latter would preferred because it doesn't require a join.