amp-htmlamp-emailamp-listamp-form

how can i set state of amp-list and submit the form with that state


<!doctype html>
<html ⚡4email data-css-strict>

<head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
    <script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>
    <script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js"></script>
    <script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
    <style amp4email-boilerplate>
        body {
            visibility: hidden
        }
    </style>
</head>

<body>
    <amp-state id="selectedProduct">
        <script type="application/json">
            {
                "src": "imageSrc",
                "title": "ProductName",
                "price": "400"
            }
        </script>
    </amp-state>

    <div class="container">
        <amp-list width="500" height="500" layout="responsive" src="https://bounce.mailamp.in/similar-products" items="products">
            <template type="amp-mustache">
                <div class="product-item" on="tap:AMP.setState({
                        selectedProduct: {
                            src: {{src}},
                            title: {{title}},
                            price: {{price}}
                        }
                    })">
                    <amp-img class="product-image" width="100" height="100" src="{{src}}" alt="Product Image"></amp-img>
                    <p>{{title}}</p>
                    <p>Price: ₹{{price}}</p>
                    <form method="post" action-xhr="https://bounce.mailamp.in/add-to-cart">
                        <input type="hidden" name="src" [value]="selectedProduct.src">
                        <input type="hidden" name="title" [value]="selectedProduct.title">
                        <input type="hidden" name="price" [value]="selectedProduct.price">
                        <button type="submit" class="add-to-cart-button">Add to Cart</button>
                    </form>
                </div>
            </template>
        </amp-list>
    </div>
</body>
</html>

I'm working on an AMP for Email project and trying to implement an interactive product list using amp-list and amp-state. However, I'm facing issues with updating the state when a product is clicked. Below is the code snippet I'm using. I would appreciate any guidance on how to properly set this up for dynamic user interactions.

Specifically, I am unsure if I am using the AMP.setState correctly to update the selectedProduct state when a product is tapped. Any insights or corrections would be greatly appreciated!


Solution

  • There are a few orthogonal problems with the sample you provided:

    1. You are missing quotes in the JSON returned by your endpoint.

    Here's what the on attribute on the <div> looks like after the Mustache template is rendered:

    tap:AMP.setState({
        selectedProduct: {
            src: https://bummer.in/cdn/shop/files/9000000664.2561_600x.jpg?v=1708346823,
            title: Trunks - Disco82,
            price: 599.00 
        } 
    })
    

    This results in an error when you click on the div to trigger AMP's execution of the setState call:

    Error: Parse error on line 3:
    ...          src: https://bummer.in/cdn/sho
    -----------------------^
    Expecting '-', '+', '*', '/', '%', '&&', '||', '<=', '<', '>=', '>', '!=', '==', '?', '.', ',', '[', '}', got ':'
    

    You should see these errors in the JS console.

    The values for src and title need to be quoted if the values are string literals. See examples at https://amp.dev/documentation/components/email/amp-bind#deep-merge-with-amp.setstate().

    2. There's no order dependency between the setState call and the form submission

    When you click on "Add to Cart", the form submission doesn't wait for the amp-bind evaluation on the hidden form input elements to execute after the state changes when the outer div is clicked. They happen in parallel in undefined order. As a result, what typically happens is that the first time you click on "Add To Cart", the form submits with the input fields set to the default values of the AMP state (src: imageSrc and price: 400) you set inside the <amp-state> tag, as opposed to the values you intend to set using AMP.setState.

    What you can do is to ensure that the form is only submitted after the setState happens. This can be done by

    1. Giving your form a DOM ID
    2. Changing the button to a regular button and not a submit button
    3. Chaining multiple actions upon the same event and use form.submit action to trigger the form submission:

    https://amp.dev/documentation/guides-and-tutorials/email/learn/amp-actions-and-events#multiple-actions-for-one-event

    If you give your <form> element a DOM ID of form, then you can do this:

    <button on="tap:AMP.setState({
                  selectedProduct: {
                    src: '{{src}}',
                    title: '{{title}}',
                    price: '{{price}}',
                  }
                }), form.submit" type="button" class="add-to-cart-button">
      Add to Cart
    </button>
    

    You can debug AMP states by executing AMP.printState() in the JS console, in the context of the AMP frame.

    3. Your form endpoint has CORS issues

    Even after fixing the above issues, your form submission doesn't work because it doesn't respond with the correct CORS headers:

    Access to fetch at 'https://bounce.mailamp.in/add-to-cart?__amp_source_origin=https%3A%2F%2Fplayground.amp.dev' from origin 'https://playground.amp.dev' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.