Handling Submission and Errors

The form has novalidate because of a test result: without it, Chrome 1 validated the form itself, showed its own bubble on the data-validate-on-blur postcode (Framework7 572,986 suppresses it only for validate fields), and never fired submit. Now validateInputs() marks all three fields, then the handler posts the order:

The request and its error handling in placeOrder(), src/pages/checkout.f7CSS
      try {
        const res = await fetch('https://api.example.com/orders', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(order),
        });
        const body = await res.json();
        if (!res.ok) throw new Error(body.message || `HTTP ${res.status}`);
        $f7.form.removeFormData('checkout-form');
        $store.dispatch('clearCart');
        $f7.dialog.alert(`Order ${body.id} is on its way.`, 'Thank you!',
          () => $f7router.back());
      } catch (err) {
        console.log('order failed:', err.name, err.message);
        $f7.dialog.alert(err instanceof TypeError
          ? 'No connection. Your order is saved on this device; try again.'
          : err.message, 'Order not placed');
      } finally {
        $f7.preloader.hide();
      }

fetch() rejects with a TypeError only when no response arrives; a 4xx answer resolves, so the code checks res.ok. With no order server, the test posted as is (the host does not resolve), then with Playwright 26,280 's page.route() answering 422 and finally 201:

Output of 52
...
Failed to load resource: net::ERR_NAME_NOT_RESOLVED
order failed: TypeError Failed to fetch
...
Failed to load resource: the server responded with a status of 422 (Unprocessable Entity)
order failed: Error We do not deliver to postcode 88410 yet.
A four-digit postcode rejected on blur in iOS (A), and the 422 answer in Material (B)
A four-digit postcode rejected on blur in iOS (A), and the 422 answer in Material (B)

The 201 showed "Order BN-1042 is on its way." and emptied the cart and the draft. After a failure the draft stays, since form storage also saves on submit. The preloader shown before try blocks double taps. Validate again on the server: anyone can post to your API.