Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

How do i create a metafield on form submit

How do i create a metafield on form submit

jaikom
Shopify Partner
3 0 1

Here is my app._index.jsx file. I am new to the react and the Shopify model. I am trying to get the key from the formsubmit and save that value to metafield definition so each store/shop can have their own key saved. I want to use this key in my app at later stage. Think of this as a GA config key etc. The path to shopifyserver is actual path because ../shopify.server.js  path was not being found. 

 

I can see that the admin exists on the load but not when the action is triggered and i get undefined for the admin and the graphql is never triggered . Hope you can help.

 

 

 

 

import { useEffect } from "react";
import { json } from "@remix-run/node";
import { authenticate } from "/Users/ja/Desktop/ShopifyApp/new-data-publisher/app/shopify.server.js";
import { useLoaderData,useActionData, useNavigation, useSubmit } from "@remix-run/react";
import {
  Page,
  Layout,
  Text,
  Card,
  Button,
  BlockStack,
  Box,
  List,
  Link,
  InlineStack,
  Form
} from "@shopify/polaris";

//console.log( authenticate);
export const loader = async ({ request }) => {
  const { admin } = await authenticate.admin(request);
  //return { admin }
  return null;

};

export async function action({ request, newappkeyValue }) {
  try {
    const { admin } = await authenticate?.admin(request);

    // Ensure admin object is available before proceeding
    if (!admin) {
      console.error('Error: Admin object not available.');
      return json({ error: 'Admin object not available' }, { status: 500 });
    }

    const newAppKey = newappkeyValue;
    const response = await admin.graphql(`
      mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) {
        metafieldDefinitionCreate(definition: $definition) {
          createdDefinition {
            id
            name
            namespace
            key
            description
          }
          userErrors {
            field
            message
            code
          }
        }
      }
    `, {
      variables: {
        definition: {
          name: "newAppkey",
          namespace: "$app:new-appkey",
          key: `${newAppKey}`, 
          description: "stores customers new app key.",
          type: "list.single_line_text_field"
        }
      },
    });

    const responseJson = await response.json();
    return json({
      createdDefinition: responseJson.data.metafieldDefinitionCreate.createdDefinition,
      userErrors: responseJson.data.metafieldDefinitionCreate.userErrors,
    });
  } catch (error) {
    console.error('Error in action:', error.message);
    return json({ error: 'Internal Server Error' }, { status: 500 });
  }
}



export default function Index() {
  const nav = useNavigation();
  const actionData = useActionData();
  const submit = useSubmit();
  const isLoading =
    ["loading", "submitting"].includes(nav.state) && nav.formMethod === "POST";
  //const newAppKey = actionData?.product?.id.replace(
   // "gid://shopify/Product/",
   // ""
  //);

  //useEffect(() => {
   // if (newAppKey) {
     // shopify.toast.show("newAppKey Added");
    //}
  //}, [newAppKey]);
  //const generateProduct = () => submit({}, { replace: true, method: "POST" });

  return (
    <Page>
      <ui-title-bar title="Publisher">
       
      </ui-title-bar>
      <BlockStack gap="500">
        <Layout>
          <Layout.Section>
            <Card>
              <BlockStack gap="500">
                <BlockStack gap="200">
                  <Text as="h2" variant="headingMd">
                  Publisher app 🎉
                  </Text>
                  <Text variant="bodyMd" as="p">
                    This embedded app template uses{" "}
                    <Link
                      url="https://shopify.dev/docs/apps/tools/app-bridge"
                      target="_blank"
                      removeUnderline
                    >
                      App Bridge
                    </Link>{" "}
                    interface examples like an{" "}
                    <Link url="/app/additional" removeUnderline>
                      additional page in the app nav
                    </Link>
                    , as well as an{" "}
                    <Link
                      url="https://shopify.dev/docs/api/admin-graphql"
                      target="_blank"
                      removeUnderline
                    >
                      Admin GraphQL
                    </Link>{" "}
                    mutation demo, to provide a starting point for app
                    development.
                  </Text>
                </BlockStack>
                <BlockStack gap="200">
                  <Text as="h3" variant="headingMd">
                    Get started with products
                  </Text>
                  <Text as="p" variant="bodyMd">
                    Generate a product with GraphQL and get the JSON output for
                    that product. Learn more about the{" "}
                    <Link
                      url="https://shopify.dev/docs/api/admin-graphql/latest/mutations/productCreate"
                      target="_blank"
                      removeUnderline
                    >
                      productCreate
                    </Link>{" "}
                    mutation in our API references.
                  </Text>
                </BlockStack>
                
                <div>
                <Text as="h3" variant="headingMd">
                    Please Enter your new AppKey.
                  </Text>
                  </div> 
                  <br></br>
                  


                          <Form method="post" onSubmit={async (event) => {
                            console.log('Form submitted!');
                            event.preventDefault();
                            // Additional logic
                            const formElement = event.target;
                            const formData = new FormData(formElement);
                            const newappkeyValue = formData.get('newappkey');
                            console.log('newappkeyValue', newappkeyValue);
                            await action({ request: event.request, newappkeyValue });
                          }}>
                            <input type="text" name="newappkey" placeholder="new App Key" size={60}/>
                            <button type="submit">Submit</button>
                            </Form>

             
                <InlineStack gap="300">
                  
                 
                </InlineStack>
               
              </BlockStack>
            </Card>
          </Layout.Section>
          <Layout.Section variant="oneThird">
            <BlockStack gap="500">
              <Card>
                <BlockStack gap="200">
                  <Text as="h2" variant="headingMd">
                    App template specs
                  </Text>
                  <BlockStack gap="200">
                    <InlineStack align="space-between">
                      <Text as="span" variant="bodyMd">
                        Framework
                      </Text>
                      <Link
                        url="https://remix.run"
                        target="_blank"
                        removeUnderline
                      >
                        Remix
                      </Link>
                    </InlineStack>
                    <InlineStack align="space-between">
                      <Text as="span" variant="bodyMd">
                        Database
                      </Text>
                      <Link
                        url="https://www.prisma.io/"
                        target="_blank"
                        removeUnderline
                      >
                        Prisma
                      </Link>
                    </InlineStack>
                    <InlineStack align="space-between">
                      <Text as="span" variant="bodyMd">
                        Interface
                      </Text>
                      <span>
                        <Link
                          url="https://polaris.shopify.com"
                          target="_blank"
                          removeUnderline
                        >
                          Polaris
                        </Link>
                        {", "}
                        <Link
                          url="https://shopify.dev/docs/apps/tools/app-bridge"
                          target="_blank"
                          removeUnderline
                        >
                          App Bridge
                        </Link>
                      </span>
                    </InlineStack>
                    <InlineStack align="space-between">
                      <Text as="span" variant="bodyMd">
                        API
                      </Text>
                      <Link
                        url="https://shopify.dev/docs/api/admin-graphql"
                        target="_blank"
                        removeUnderline
                      >
                        GraphQL API
                      </Link>
                    </InlineStack>
                  </BlockStack>
                </BlockStack>
              </Card>
              <Card>
                <BlockStack gap="200">
                  <Text as="h2" variant="headingMd">
                    Next steps
                  </Text>
                  <List>
                    <List.Item>
                      Build an{" "}
                      <Link
                        url="https://shopify.dev/docs/apps/getting-started/build-app-example"
                        target="_blank"
                        removeUnderline
                      >
                        {" "}
                        example app
                      </Link>{" "}
                      to get started
                    </List.Item>
                    <List.Item>
                      Explore Shopify’s API with{" "}
                      <Link
                        url="https://shopify.dev/docs/apps/tools/graphiql-admin-api"
                        target="_blank"
                        removeUnderline
                      >
                        GraphiQL
                      </Link>
                    </List.Item>
                  </List>
                </BlockStack>
              </Card>
            </BlockStack>
          </Layout.Section>
        </Layout>
      </BlockStack>
    </Page>
  );
}

 

 

 

 

 

 

 

 

 

Replies 3 (3)

zano
Shopify Partner
2 0 0

Hi, i have the same problem.
Have you already solved it?

zano
Shopify Partner
2 0 0

Hi, I finally did it.
I found these 3 documents:

 

I did something like this:

export const loader = async ({ request }) => {
  const { admin, session } = await authenticate.admin(request);

  const responseJson = await FuntionToReadMetafields();

  return json({
    data: responseJson.data
  });
};

export async function action({ request, params }) {
  // Use the admin client to create or update metafields
  const { admin, session } = await authenticate.admin(request);
  const { shop } = session;

  // read data from MyForm
  const formData = {
    ...Object.fromEntries(await request.formData()),
    shop,
  };

  // read single value
  const { v1 } = formData;

  FuntionToCreateOrUpdateMetafieldsMetafields();
}

const MyForm = ({ onSubmit }) => {
  const formData = useLoaderData(); // read the current metafields value
  
  const [v1, setV1Value] = useState(myFunction(formData));
  const handleV1Change = useCallback(
    (value) => setV1Value(value),
    [],
  );

  // callback onSubmit with form data
  const buttonClickAction = (event) => {
    event.preventDefault();
    onSubmit({ v1 });
  };

  return (
    <Form onSubmit={buttonClickAction}>
      <FormLayout>
        <TextField
          value={v1}
          onChange={handleV1Change}
          label="Value 1"
          type="text"
          autoComplete="off"
          helpText=""
        />
	<Button submit>Save</Button>
      </FormLayout>
    </Form>
  );
};

export default function Index() {
  const nav = useNavigation();
  const submit = useSubmit();
  const actionData = useActionData();

  // call submit with form data to save
  const handleFormSubmit = async (formData) => {
    submit(formData, { replace: true, method: "POST" } );
  };

  return (
    <Page>     
        <MyForm onSubmit={handleFormSubmit} />
    </Page>
  );
};
jaikom
Shopify Partner
3 0 1

How did it work, On form submit to save a metafield value you would need to run a graphql query and will need admin.graphql method but this does not exist on action. Can you please share how did you update the metafields?