Is there a way to add script tags which include liquid featured template language via API?

Solved
Highlighted
Tourist
6 0 2

I am making one app which needs data configured by user in my app and display these data in product detail pages of user's online shop. I've already build up some function base on Shopify GraphQL Admin APIs which helps users create metafields on product variants when they picked these resource in my app. I want that data to be ported to product-template.liquid in js variables so that my app can access it. I've read documents about adding script tag including remote scripts through Shopify GraphQL Admin APIs  and tried on Shopify GrahpiQL App. (thanks to Build a Shopify App with Node.js and React @shopify/koa-shopify-graphql-proxy, it's very handy for me to use GraphQL Admin APIs honestly). However, when it comes to add script tags which contains codes like 

<script>
window.variantsMeta = {};
{% for variant in product.variants %}
{% if variant.metafields["xxx"]["yyy"] %}
window.variantsMeta["v{{ variant.id }}"] = {{ variant.metafields["xxx"]["yyy"] | json}}
{% endif %}
{% endfor %}
window.initVariant = {{ product.selected_or_first_available_variant.id }};
</script>

Can it be load from a remote script and works in user's online shop theme?

 

Also, I'd like to ask how to add script tags only into a certain page (for example, product-template.liquid) since there is no need for my scripts to appear in any other pages. Maybe this will help prevent some unexpected conflicts between scripts add by various apps.

1 Like
Highlighted
Shopify Staff
Shopify Staff
488 63 73

Hey @laao,

 

ScriptTags don't have access to liquid variables. I'd recommend loading the variables into the theme and then calling your script. This way, you can run the script on certain pages.

 

For example, your product.liquid template might contain something like this:

 

<script>
runSomeFunction('{{ some-liquid-variable }}')
</script>

 

 

1 Like
Highlighted
Tourist
6 0 2

Thanks for your advice. I understand that and tried to use REST API including asset.create, asset.get and asset.update provided by shopify-api-node .

In my koa server's booting script, I registered a GET api using koa-router to do the following things: 

1. create assets/header.js.liquid with liquid objects and js inside the script tag which shows above in the first message of this discussion.

2. get the target file(sections/product-template.liquid)'s content

3. update the target file(sections/product-template.liquid)'s content with an additional line at the beginning of the file.

The following codes which I picked from the router.get() function present these process. (We modified some url to protect some informations which is important to our team)

try {
const themes = await shopify.theme.list();
const themeInUse = themes.find(e => e.role === "main");
if (themeInUse) {
const tmpAsset1 = await shopify.asset.create(themeInUse.id, {
key: "assets/header.js.liquid",
src:
"https://staticssl.mycdn.com/newt/36/application/octetstream/1578995784710/header.js.liquid" // This url has been modified. The origin url works.
});
console.log(tmpAsset1);
const tmpAsset2 = await shopify.asset.get(themeInUse.id, {
key: "sections/product-template.liquid"
});
console.log(tmpAsset2);
const tmpAsset3 = await shopify.asset.update(themeInUse.id, {
key: "sections/product-template.liquid",
value:
'{{ \"header.js.liquid\" | asset_url | script_tag }}\n' +
tmpAsset2.value
});
console.log(tmpAsset3);
ctx.res.statusCode = 200;
ctx.body = tmpAsset3;
await next();
} else {
throw new Error("Can't find theme in use");
}
} catch (err) {
console.error(err);
ctx.res.statusCode = 500;
ctx.body = err;
}

 However, It shows that there's an error when calling shopify.asset.create() function, and it says:

 
{ HTTPError: Response code 422 (Unprocessable Entity)
    at EventEmitter.emitter.on (/node_modules/got/dist/source/as-promise.js:108:31)
    at process._tickCallback (internal/process/next_tick.js:68:7) name: 'HTTPError' }

I'm wondering what causes this error and whether my logic would work for my requirement.

 

P.S. Since I'm modifing the theme which is published by shop owner, I considered to use a webhook which listens to theme change event when shop owner switch themes. I found that there are two topics maybe related to the theme change event : shop/update & theme/update. Is it possible to use these webhooks to trigger asset modification to meet these requirements above? Or is there a way to use script tag to add js.liquid files to the theme, thus I would build these logic easier? 

 
0 Likes
Highlighted
Shopify Staff
Shopify Staff
488 63 73

Hey @laao,

 

src is only available to PUT (update) requests: https://help.shopify.com/en/api/reference/online-store/asset#update-2020-01 and keep in mind assets/header.js.liquid might already exist in a theme.

 

Modifying themes can be dangerous - there might be another approach - which liquid variable are you trying to obtain?

 

 

0 Likes
Tourist
6 0 2

Thanks @scottydont ,

The liquid variable I need is metafields attached on a product's variants. 

 

I'm trying to get metafields attached on product variants when user enters a product detail page. In consider of some performance issues about scripts inserted by script tag, I attached these data to window property so that my scripts can get these data when user enters a product detail page.

 

After trying to use { xxx.js | asset_url | script_tag} (I noticed that including .liquid extension in this script loading logic is incorrect) to load a xxx.js.liquid file from theme's assets directory came to a failure, I succeeded in adding the code directly to the beginning of sections/product-template.liquid using asset.update api provided by shopify-api-node. I alse add annotations to the beginning and the end of the inserted code to make it easy to be removed using Regex .

 

The following code shows all what I'm trying to obtain in product-template.liquid:

<!-- ======Warning:=Dont=modify=the=following=code====== -->
window.EXZPvariantsMeta = {};
{% for variant in product.variants %}
{% if variant.metafields["EXZP"]["viewer-detail"] %}
window.variantsMeta["v{{ variant.id }}"] = {{ variant.metafields["EXZP"]["viewer-detail"] | json}}
{% endif %}
{% endfor %}
{% if product.selected_or_first_available_variant.id %}
window.EXZPinitVariant = {{ product.selected_or_first_available_variant.id }};
{% endif %}
<!-- ======Warning:=Dont=modify=the=code=above====== -->

 

Since the inserted code may only exist in a theme, I'm thinking about register a webhook which recieves 'theme/publish' topic so that it can call the function to insert code to the newly published theme when user publishes a new theme. Also a webhook which recieves 'app/uninstall' topic can remove the inserted code when user uninstalled my app.

 

Am I overlooking some other approaches that maybe easier to fetch metafields in product detail pages? Honestly, I feel that my solution is a bit complex to meet the requirement.

 

0 Likes
Highlighted

Success.

Shopify Staff
Shopify Staff
488 63 73

Thanks for the additional information.

 

Another approach would be to store the questions on your side, instead of in metafields, and use the product handle to pull out the information you need.

 

If you stick with metafields, modifying the theme might make sense. Just be sure to wrap your code in a comment so you can easily detect/add/remove it.  Something like:

 

<!--- Start SomeAppCode --->
<script>
...
</script>
<!--- End SomeAppCode --->

 

It would also be worth backing up the asset before you overwrite it.

 

Good luck!

 

0 Likes