Autopopulate field

default discord avatar
Xavier Pacheco
2 months ago
108

Hello, I am trying to model an online store and for my Order Item I have a collection of Items and I wanted the final price to be calculated as the sum of all the items's prices. How do I do this? I have looked at the field hooks documentation but I am not being able to make it work...

  • default discord avatar
    Tinouti
    2 months ago

    Hey Xavier! Take a look at

    useFormFields

    , the doc example is pretty much exactly what you're trying to achieve (

    https://payloadcms.com/docs/admin/hooks#useformfields

    ). ๐Ÿ˜Š



    Oh actually maybe not, depending on what exactly you're trying to achieve and if you want a field to automatically updates in the Payload admin UI as you're filling out the Order fields, or if you're only using it as an API.


    For the latter, I think using the collection hook

    beforeChange

    would probably be more suited. There you'll have access to all of your Order's data, so you'll be able to use all of your item's prices to calculate your final price, and return the data with that final price field.



    Feel free to share more details as to what exactly you're trying to achieve and/or some of the code of what you've tried so far!

  • default discord avatar
    Xavier Pacheco
    2 months ago

    The problem rn is the items have prices and the orders have prices and we want to populate the order price with the sum of the items's prices, but don't know how to get the item's price from where we are



    We are using this as an API

    image.png
    image.png
  • default discord avatar
    Alessio ๐Ÿฃ
    2 months ago

    interesting! First of all, useFormFields is only for React components - don't use them in hooks.


    The items should be already passed in that hook

  • default discord avatar
    Xavier Pacheco
    2 months ago

    So what happens in the PayloadUI is irrelavant in our case, as long as the order's price can't be manually changed and reflects the sum of the item's prices

  • default discord avatar
    Tinouti
    2 months ago

    Ah gotcha, in that case yeah I'd look at the collection hooks (like

    beforeChange

    ) instead

    https://payloadcms.com/docs/hooks/collections#beforechange
  • default discord avatar
    Xavier Pacheco
    2 months ago

    I think that is indeed what we need but the documentation isn't very helpful on this aspect. The example shows the hook as something apart from the file and we want to use the hook within the the Orders collection

  • default discord avatar
    Alessio ๐Ÿฃ
    2 months ago

    It is part of the Orders collection! You can add a "hooks: {" field to your orders collection and use the hook there



    The current data from your collection will be passed in that hook. From that data, you count all the prices for your items.



    Then, in that hook, you return the same data, but with the sum of all prices added to the "price" field



    (like this)

    Arc_2023-04-02_at_22.03.072x.jpg
  • default discord avatar
    Xavier Pacheco
    2 months ago

    Thank you very much, I think this is working ๐Ÿ™‚ We made the change to support basic operation, now we just need to get it right with the item's prices ๐Ÿ˜„ I'll close the ticket as soon as we can make this work!

  • discord user avatar
    jacobsfletch
    Payload Team
    last month

    This thread is great! We do also have a "virtual fields" example which demonstrates this pretty abstractly,

    https://github.com/payloadcms/payload/tree/master/examples/virtual-fields

    . There's a

    totalPrice

    field on the

    events

    collection that is pretty closely aligned with what you're describing here. This field is not saved to the database but is dynamically populated using an

    afterRead

    hook which calculates the total by adding up ticket prices.

  • default discord avatar
    Torneidou
    last month

    Hello, I'm Xavier's colleague. We have tried to use a "beforeChange" hook with moderate success and we are close to what we want to achieve. However, we found another problem, when trying to fetch an "item" from our "Items" collection, it seems that it can only be fetched asynchronously, but for what we are trying to achieve we need this "item" before the "beforeChange" hook is finished, which doesn't happen since it is asynchronous.



    Is there any way to guarantee that the response is received before the hook "beforeChange" ends? Our code is the following:

  • default discord avatar
    thisisnotchris
    last month

    The code vanished ๐Ÿ˜ฎ

  • default discord avatar
    Torneidou
    last month


    sorry xD, the previous one was too cropped, I changed it to show a little more

    image.png
  • default discord avatar
    thisisnotchris
    last month

    Hmm so it looks like you're calling the getItem function in the beforeChange hook



    But that function's input doesn't require anything from the beforeChange args, right?



    It's setting the args.data.price to the result of that function

  • default discord avatar
    Torneidou
    last month

    it does require a value from args, sorry, that image is hardcoded. Here is the actual way we use args for the getItem function:

    image.png
  • default discord avatar
    thisisnotchris
    last month

    Ah okay, one sec, ill read through

  • default discord avatar
    Torneidou
    last month

    that is what we want, but we want that to happen before the hook ends

  • default discord avatar
    thisisnotchris
    last month

    Doesn't beforeChange provide a reference to the original item?



    Pre-change



    const beforeChangeHook: CollectionBeforeChangeHook = async ({
      data, // incoming data to update or create with
      req, // full express request
      operation, // name of the operation ie. 'create', 'update'
      originalDoc, // original document
    }) => {
      return data; // Return data to either create or update a document with
    }


    and then you assign it to the hook



    hooks: { beforeChange: beforeChangeHook}



    In addition



    You may want to do beforeOperation



    The beforeOperation Hook type can be used to modify the arguments that operations accept or execute side-effects that run before an operation begins.


    side-effects that run before an operation begins


    so you would...



    const beforeOperationHook: CollectionBeforeOperationHook = async ({
      args, // Original arguments passed into the operation
       'update', // name of the operation
    }) => {
      // Logic that will fire beforeChange
      return args; // Return operation arguments as necessary
    }
  • default discord avatar
    Torneidou
    last month

    yes the data has the itemId that we want to use to fetch the item in the Items collection, unless I am misunderstanding what you asked

  • default discord avatar
    thisisnotchris
    last month
      hooks: {
        beforeOperation: [],
        beforeChange: [myBeforeChangeFunction],
      },


    @Torneidou Can you try the same logic, but in the beforeOperation

  • default discord avatar
    Torneidou
    last month

    like this?

    image.png
  • default discord avatar
    thisisnotchris
    last month

    close



    sec



    ({
      args, // Original arguments passed into the operation
      operation, // name of the operation
    }) => {
        if (operation === 'update') {
            
        }
      return args; // Return operation arguments as necessary
    }


    I'm not 100% sure, but i think operation is a string



    of the operation that you want to access before it runs



    So I think if you put the getItem logic in the if statement there



    It should run before the beforeChange hook is fired



    Typically you would have a file that contains the before operation hook you want to use



    so maybe you have a

    hooks

    folder

  • default discord avatar
    Torneidou
    last month

    so something like this? it is hardcoded just so i can get it to work

    image.png
  • default discord avatar
    thisisnotchris
    last month

    and myCustomHook.ts



    ahh okay



    Yeah that looks promising



    I think it gets auto-awaited too



    hmm



    maybe you do need the async operator



    lets see if it errors

  • default discord avatar
    Torneidou
    last month

    but we should definitely switch to a separate file down the line for that reason, thanks



    the async elsewhere? It's already on the getItem function no?

  • default discord avatar
    thisisnotchris
    last month

    ah you're right



    sorry hehe



    Any luck with the hook?



    i like to throw in a bunch of logs too just to see whats going on



    like id log operation, etc

  • default discord avatar
    Torneidou
    last month

    sort of



    now there is a weirder issue xD

  • default discord avatar
    thisisnotchris
    last month

    Ooo what happened?



    ๐Ÿ˜„

  • default discord avatar
    Torneidou
    last month


    image.png
    image.png
  • default discord avatar
    thisisnotchris
    last month

    hmm

  • default discord avatar
    Torneidou
    last month

    it seems that the id is not recognized



    but i remember this exact code working before

  • default discord avatar
    thisisnotchris
    last month

    Isn't the id provided by args

  • default discord avatar
    Torneidou
    last month

    yes, maybe i should try that way instead of hardcoding it for now

  • default discord avatar
    thisisnotchris
    last month

    also you can do



    const {price} = await getItem('fdwefewfewf')



    assuming item isn't undefined



    also @Torneidou I'm heading home from work soon, so when i get back i can help more ๐Ÿ™‚

  • default discord avatar
    Torneidou
    last month

    thank you very much, i will keep posting more information but no pressure to respond

  • default discord avatar
    thisisnotchris
    last month

    I look forward to it, im sure this will get figured out!

  • default discord avatar
    Torneidou
    last month

    so that problem of no finding the resource was due to having written the wrong id ups (figures)



    image.png
  • default discord avatar
    thisisnotchris
    last month

    phew



    ๐Ÿ˜„



    And do the ID's match up?



    or the price

  • default discord avatar
    Torneidou
    last month

    but unfortunately this approach doesn't seem to solve the original problem since the response of getting the item is printed after beforeChange is executed:

    image.png
  • default discord avatar
    thisisnotchris
    last month

    well



    I've seen a couple of examples



    And for instance

  • default discord avatar
    Torneidou
    last month

    and unfortunately the "await" can't be used before calling getItem:

    image.png
  • default discord avatar
    thisisnotchris
    last month
    https://github.com/angelokeirsebilck/payload-svelktekit-test-01/blob/45dd4ae7a545d2ecad8ce88add29f342c6d65c6d/cms/src/hooks/homeBeforeOperation.ts


    the async would go before the openeing parenthesis



    async ({


    operation,


    args,


    })



    which makes me think it does need the async operator before the hook

  • default discord avatar
    Torneidou
    last month

    omg



    it worked



    ๐Ÿ˜„

  • default discord avatar
    thisisnotchris
    last month

    YES



    WOOT



    im so happy you got it!



    I gotta clock out and head home, but that's great news

  • default discord avatar
    Torneidou
    last month

    putting the async here like you said made it possible to use await when calling the getItem function:



    no red underline on "await"

    image.png
  • default discord avatar
    thisisnotchris
    last month

    ๐Ÿ˜„

  • default discord avatar
    Torneidou
    last month

    thank you very much for your time, have a great evening



    our final solution for what we wanted to achieve was:

    image.png
  • default discord avatar
    notchris
    last month

    Heck yeah!



    You did it!

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.