Can you use metafields / metadata with shopify buybutton.js? - shopify

I've been looking for this answer for some time and cannot find anything on it. Does anyone know if metafields or metadata can be included in the Shopify Buy Button products?
Some background - We have a Shopify site that has the Shopify App "Product Reviews" installed and used heavily on the site. There is a secondary site (WordPress) where we are using the Shopify Buy Button code to add some products specific to that site (several collections from the Shopify store). We want to add the Product Reviews to the modal pop-up for the products we have set up on the buy buttons. The data for the reviews is stored in product.metafields.spr.reviews.
We have added custom templates and classes to the buy button embed code to custom style the look, but for the life of me, I cannot find out if getting the metadata for the product is even possible. I am very familiar with WordPress, so I have created an automated way to add the products and collections to the pages we want them on, and the buy button code is added when the page has a product or collection to display. So, customizing the code is not an issue.
I could use the API to pull product metadata and then insert it onto the popup, but that just seems like a lot of extra work and I do not want to go down that path if there is a better solution.
Is there a way to do it using the ui.createComponent? And if so, how would you add that to the code?
Here is some of the code for a reference (it is output via php so there are a few php variables in it):
ui.createComponent( 'collection', {
id: <?php echo $idkey; ?>, // collection id
node: document.getElementById( 'collection-component-<?php echo $idval['hash'];?>' ), //collection unique hash
moneyFormat: '%24%7B%7Bamount%7D%7D',
options: {
"product": {
"iframe": false,
"buttonDestination": "modal",
"variantId": "all",
"contents": {
"imgWithCarousel": false,
"variantTitle": false,
"options": false,
"description": false,
"buttonWithQuantity": false,
"quantity": false
},
"events":{
"openModal": function (product) { /* debug only */ },
},
"text": {
"button": "VIEW PRODUCT INFO",
},
},
"cart": {
"contents": {"button": true},
"styles": {
"button": {
"background-color": "#761b79",
"font-family": "Droid Sans, sans-serif",
":hover": {"background-color": "#6a186d"},
":focus": {"background-color": "#6a186d"},
"font-weight": "normal"
},
"footer": {"background-color": "#ffffff"}
},
"googleFonts": ["Droid Sans"]
},
"modalProduct": {
"iframe": false,
"layout": "horizontal",
"contents": {
"img": false,
"imgWithCarousel": true,
"variantTitle": true,
"buttonWithQuantity": true,
"button": false,
"quantity": false,
"reviews": true
},
"DOMEvents": {
'click .product-reviews': function (evt, target) { /* debug code only */}
},
"templates": {
"title" : '<h1 class="{{data.classes.modalProduct.title}}" data-element="product.title">{{data.title}}</h1>',
"reviews": '<div class="{{data.classes.product.reviews}}">[ review data goes here ]</div>'
},
"classes": {
"reviews" : "product-reviews"
},
"order" :[
"imgWithCarousel",
"title",
"price",
"buttonWithQuantity",
"reviews",
"description"
],
"styles": {
"button": {
"background-color": "#761b79",
"font-family": "Droid Sans, sans-serif",
":hover": {"background-color": "#6a186d"},
":focus": {"background-color": "#6a186d"},
"font-weight": "normal"
},
"reviews" : {
"color" : "#444"
}
},
},
"toggle": {
"styles": {
"toggle": {
"font-family": "Droid Sans, sans-serif",
"background-color": "#761b79",
":hover": {"background-color": "#6a186d"},
":focus": {"background-color": "#6a186d"},
"font-weight": "normal"
}
},
"googleFonts": ["Droid Sans"]
},
"productSet": {"iframe": false,}
}
});

Sorry, metafields are not accessible through BuyButton.js or the JavaScript Buy SDK. For more information see this thread: https://github.com/Shopify/js-buy-sdk/issues/168

Related

Vue PWA Plugin - Manifest doesn't use my config attributes

It's very strange. I installed the VuePWA-Plugin and configured it in the package.json like this:
"pwa": {
"name": "Poolio",
"themeColor": "#205c94",
"msTileColor": "#205c94",
"display": "fullscreen",
"appleMobileWebAppCapable": "yes",
"appleMobileWebAppStatusBarStyle": "#205c94",
"pwa.iconPaths": {
"favicon32": "./img/icons/favicon-32x32.png",
"favicon16": "./img/icons/favicon-16x16.png",
"favicon96": "./img/icons/favicon-96x96.png",
"appleTouchIcon": "./img/icons/apple-icon-152x152.png",
"msTitleImage": "./img/icons/ms-icon-144x144.png"
}
},
But it doesn't use any of my settings in the manifest.json (beside of the name, but this must be used by another setting, cause it doesn't change, if I change it)
I asked myself where it takes the themeColor and so I searched in the hole app folder for the hexcode #4DBA87, which is written in the manifest. But didn't find anything...
Heres the manifest output:
{
"name": "Poolio",
"short_name": "Poolio",
"theme_color": "#4DBA87",
"icons": [
{ "src": "./img/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "./img/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "./img/icons/android-chrome-maskable-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
{ "src": "./img/icons/android-chrome-maskable-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"start_url": ".",
"display": "standalone",
"background_color": "#000000"
}
You can put all your manifest options inside pwa.manifestOptions. It would be something like this inside your vue.config.js:
module.exports = {
pwa: {
manifestOptions: {
name: "App Name",
short_name: "Short Name",
start_url: "./",
display: "standalone",
theme_color: "#000000",
icons: [
{
src: "./favicon.svg",
sizes: "512x512",
type: "image/svg+xml",
purpose: "any maskable",
},
],
},
themeColor: "#4DBA87",
msTileColor: "#000000",
appleMobileWebAppCapable: "yes",
appleMobileWebAppStatusBarStyle: "black",
iconPaths: {
maskicon: null,
favicon32: "./favicon32.png",
favicon16: "./favicon16.png",
appleTouchIcon: null,
msTileImage: null,
},
// configure the workbox plugin
workboxPluginMode: "GenerateSW",
},
};
Check out LinusBorg answers to this issue.
#vue/cli-plugin-pwa plugin internally uses webpack's workbox plugin. The color #4DBA87 you are getting is the default color set by the plugin.
You can read more about this plugin configuration here, https://www.npmjs.com/package/#vue/cli-plugin-pwa
To configure it to your liking via package.json you have to put your configurations inside the vue property. eg:
"vue": {
"pwa": {
"name": "Poolio",
"themeColor": "#205c94",
"msTileColor": "#205c94",
"appleMobileWebAppCapable": "yes",
"appleMobileWebAppStatusBarStyle": "#205c94",
"iconPaths": {
"favicon32": "./img/icons/favicon-32x32.png",
"favicon16": "./img/icons/favicon-16x16.png",
"favicon96": "./img/icons/favicon-96x96.png",
"appleTouchIcon": "./img/icons/apple-icon-152x152.png",
"msTitleImage": "./img/icons/ms-icon-144x144.png"
},
"workboxPluginMode": "InjectManifest",
"workboxOptions": {
"swSrc": "src/service-worker.js",
},
}
}

Shopify Buybutton JS show lowest variant price on collection

I've been struggling to use the shopify Buy button for the past 2 days, My webshop has products with 3 or 4 different variants with different pricing options for larger quantities (so variants look a bit like:
1 box, price per box $50
5 boxes, price per box $30
10 boxes, price per box $20
In my shopify store I removed the quantity selection and when they select 10 boxes, I automatically add 10x that variant in the customer's cart, this way it displays nicely in the catalog (box X starting at $20).
Now I would like to add a buy button channel on another website, and created the embed code to show a collection of the different products.
The problem is, it will always show the first variants price (which is the highest price $50 in the above example)
Is there a way to render the collection to display the cheapest variant price?
What I have so far:
<div id='collection-component-5e11b1274c8'></div>
<script type="text/javascript">
/*<![CDATA[*/
(function () {
var scriptURL = 'https://sdks.shopifycdn.com/buy-button/latest/buy-button-storefront.min.js';
if (window.ShopifyBuy) {
if (window.ShopifyBuy.UI) {
ShopifyBuyInit();
} else {
loadScript();
}
} else {
loadScript();
}
function loadScript() {
var script = document.createElement('script');
script.async = true;
script.src = scriptURL;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(script);
script.onload = ShopifyBuyInit;
}
function ShopifyBuyInit() {
var client = ShopifyBuy.buildClient({
domain: 'mydomain',
apiKey: 'mykey',
appId: '6',
});
ShopifyBuy.UI.onReady(client).then(function (ui) {
ui.createComponent('collection', {
id: 157236225,
node: document.getElementById('collection-component-5e11b1274c8'),
moneyFormat: '%E2%82%AC%7B%7Bamount%7D%7D',
events :{
afterRender : function(e){
console.log('after Render');
}},
options: {
"product": {
"variantId": "all",
"isButton": false,
"contents": {
"imgWithCarousel": false,
"variantTitle": false,
"description": false,
"buttonWithQuantity": false,
"quantity": false
},
templates : {
price: "{{ data.selectedVariant.price }}"
},
DOMEvents: {
'change .shopify-buy__option-select': function (event, target) {
var data = target;
var product = ui.components;
console.log(product);
console.log(product.product);
console.log(product.productSet);
console.log(product.model);
console.log(Object.getOwnPropertyNames(product));
product.updateQuantity(() => parseInt(6, 10));
}
},
"styles": {
"product": {
"text-align": "left",
"#media (min-width: 601px)": {
"max-width": "calc(33.33333% - 30px)",
"margin-left": "30px",
"margin-bottom": "50px"
}
},
"button": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"variantTitle": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"title": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal",
"font-size": "16px"
},
"description": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"price": {
"font-family": "Open Sans, sans-serif",
"font-size": "16px",
"font-weight": "normal"
},
"compareAt": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal",
"font-size": "13.6px"
}
},
"googleFonts": [
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans"
]
},
"cart": {
"contents": {
"button": true
},
"text": {
"title": "Winkelmand",
"total": "subtotaal",
"notice": "",
"button": "Afrekenen"
},
"styles": {
"button": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"footer": {
"background-color": "#ffffff"
}
},
"googleFonts": [
"Open Sans"
]
},
"modalProduct": {
"contents": {
"img": false,
"imgWithCarousel": true,
"variantTitle": false,
"buttonWithQuantity": true,
"button": false,
"quantity": false
},
"text": {
"button": "In winkelmand"
},
"styles": {
"product": {
"#media (min-width: 601px)": {
"max-width": "100%",
"margin-left": "0px",
"margin-bottom": "0px"
}
},
"button": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"variantTitle": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"title": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal",
"font-size": "30px"
},
"description": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"price": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
},
"compareAt": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
}
},
"googleFonts": [
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans",
"Open Sans"
]
},
"toggle": {
"styles": {
"toggle": {
"font-family": "Open Sans, sans-serif",
"font-weight": "normal"
}
},
"googleFonts": [
"Open Sans"
]
},
"option": {
"styles": {
"label": {
"font-family": "Open Sans, sans-serif"
},
"select": {
"font-family": "Open Sans, sans-serif"
}
},
"googleFonts": [
"Open Sans",
"Open Sans"
]
},
"productSet": {
"styles": {
"products": {
"#media (min-width: 601px)": {
"margin-left": "-30px"
}
}
}
}
}
});
});
}
})();
/*]]>*/
</script>
In the DomEvents you see I try to update the quantity to 6, whenever a users selects a different option from a product in the collection. This seems to work, but I only change the quantity for the first product, if you happen to know how I can update the quantity of the changed product feel free to jump in and let me know..
The easiest way is to reorder the variants in the shopify backend.
To update the quantity when an option is selected you can use the userEvent "updateVariant" in the product object
Which is called whenever another option is selected.
In my case the first 2 chars of the variant are the amount of the product.
events: { "updateVariant" : function(e){
console.log('update variant');
console.log(e.model.selectedVariant.title.substr(0,2).trim());
e.updateQuantity(() => parseInt(e.model.selectedVariant.title.substr(0,2).trim()));
}

Why is my ItemFileReadStore data attribute is null?

I am using Dojo 1.10 to create an ItemFileReadStore object using AMD. Though the console did not report any error while creating the store, I could see from the debugger that the store.data is null. I tried the same code on my browser console (both chrome and firefox) but there too its the same issue (store data is null). Could someone please help me figure out if am I missing something?
require(["dojo/ready",
"dojo/on",
"dijit/registry",
"dojo/data/ItemFileReadStore"
], function(ready, on, registry, ItemFileReadStore) {
var resultTablecolumns = [{
label: 'ID',
attr: 'id',
sortable: true,
sorted: 'ascending',
width: 60,
vAlignment: "middle",
alignment: "right"
}, {
label: 'Hop',
attr: 'hop',
sortable: true,
sorted: 'ascending',
width: 100,
vAlignment: "middle",
alignment: "right"
}, {
label: 'Role',
attr: 'role',
sortable: true,
sorted: 'ascending',
width: 100,
vAlignment: "middle",
alignment: "right"
}, {
label: 'Status',
attr: 'status',
sortable: true,
sorted: 'ascending',
width: 100,
vAlignment: "middle",
alignment: "right"
}];
var storeItems = {
"identifier": "id",
"items": [{
"id": "1",
"hop": "first",
"role": "classification",
"status": ""
}, {
"id": "2",
"hop": "second",
"role": "propagation",
"status": "info"
}, {
"id": "3",
"hop": "third",
"role": "propagation",
"status": "warning"
}, {
"id": "4",
"hop": "fourth",
"role": "propagation",
"status": "error"
}, {
"id": "5",
"hop": "fifth",
"role": "enforcement",
"status": ""
}]
};
var resultTableStore = new ItemFileReadStore({
data: storeItems
});
console.log("resultTableStore === ", resultTableStore);
});
Can you show us your HTML?
Here is a working jsFiddle.
require(["dojo/data/ItemFileReadStore","dojo/dom", "dojo/domReady!"], function(ItemFileReadStore, dom ){
var storeItems = {
"identifier": "id",
"items": [{
"id": "1",
"hop": "first",
"role": "classification",
"status": ""
}]
};
var store = new ItemFileReadStore({data: storeItems});
console.log("ItemFileReadStore",ItemFileReadStore);
dom.byId('store').innerHTML = store ;
});
There is a new kid on the block which replaces the above store i.e dstore. Try it.
Ok, I figured out with the help of a co-worker.
I am actually creating a ItemFileReadStore and using it in constructing a data-grid. The grid was not showing up because its parent was hid when it got created, and hence got its width set to zero.
I understood from him that the data argument is processed on first fetch and used to populate internal structures. It is then nulled out as it is no longer used.
So in this case, what was happening is ItemFileReadStore's data argument was processed on first-fetch while creating the grid and then got set to null.

Play an audio resource taken from a remote server on Sencha Touch

I am working on a Sencha touch app. I made a jsonP request which returns an audio url, I will like to write a function that will play that audio on a button click.
The structure of the call and the response is as follows:
Ext.data.JsonP.request({
url: 'https://api.pearson.com/v2/dictionaries/entries',
callbackKey: 'callback',
params: {
apikey: 'ZzNOnelsRcNcE7Npoh2SdAeQbjRA4XE4',
headword: 'school'
}
// RESPONSE.....
{
"status": 200,
"offset": 0,
"limit": 2,
"count": 2,
"total": 245,
"url": "/v2/dictionaries/entries?headword=school&limit=2",
"results": [
{
"datasets": [
"ldoce5",
"dictionary"
],
"headword": "school",
"homnum": 1,
"id": "cqAFqfYHHt",
"part_of_speech": "noun",
"senses": [
{
"definition": "a place where children are taught",
"examples": [
{
"audio": [
{
"type": "example",
"url": "/v2/dictionaries/assets/ldoce/exa_pron/p008-001919005.mp3" // The audio url
}
],
"text": "His mother always used to pick him up from school."
}
],
"gramatical_info": {
"type": "uncountable and countable"
},
"signpost": "where children learn"
}
],
"url": "/v2/dictionaries/entries/cqAFqfYHHt"
},
{
"datasets": [
"wordwise",
"dictionary"
],
"headword": "school",
"id": "cqARFaW3Aw",
"part_of_speech": "noun",
"senses": [
{
"definition": "a place where children are taught, or the time they spend there every day",
"examples": [
{
"text": "Mr Mamood is a teacher at my school ."
}
]
}
],
"url": "/v2/dictionaries/entries/cqARFaW3Aw"
}
]
}
You could use a Ext.Audio component provided by Sencha Touch, hiding it.
{
id: 'audio',
xtype: 'audio',
hidden: true,
url: null
}
As soon as you get the audio url set it on the component:
Ext.getCmp('audio').setUrl(mp3Url);
Then you would use a button to toggle play/pause on it:
{
xtype: 'button',
text: 'Play'
handler: function() {
// get the audio component (using its id)
var audio = Ext.getCmp('audio');
audio.toggle();
this.setText(audio.isPlaying() ? 'Pause' : 'Play');
}
}
Check out http://docs.sencha.com/touch/2.3.1/#!/api/Ext.Audio for a working example.

How to specify rootProperty for nested data if it is one level below?

I am fetching nested data to be shown as nested list but whenever I tap on top level item, it again shows same top level list instead of showing children list and a ajax request is fired to fetch json data again. Here is the store:
Ext.define('MyTabApp.store.CategoriesStore',{
extend:'Ext.data.TreeStore',
config:{
model : 'MyTabApp.model.Category',
autoLoad: false,
storeId : 'categoriesStore',
proxy: {
type: 'ajax',
url: 'resources/data/catTree.json',
reader: {
type: 'json',
rootProperty: 'data.categories'
}
},
listeners:{
load: function( me, records, successful, operation, eOpts ){
console.log("categories tree loaded");
console.log(records);
}
}
}
});
and here is the data in that file which I am using to mock service:
{
"data":{
"categories": [
{
"name": "Men",
"categories": [
{
"name": "Footwear",
"categories": [
{ "name": "Casual Shoes", "leaf": true },
{ "name": "Sports Shoes", "leaf": true }
]
},
{
"name": "Clothing",
"categories": [
{ "name": "Casual Shirts", "leaf": true },
{ "name": "Ethnic", "leaf": true }
]
},
{ "name": "Accessories", "leaf": true }
]
},
{
"name": "Women",
"categories": [
{ "name": "Footwear", "leaf": true },
{ "name": "Clothing", "leaf": true },
{ "name": "Accessories", "leaf": true }
]
},
{
"name": "Kids",
"categories": [
{
"name": "Footwear",
"categories": [
{ "name": "Casual Shoes", "leaf": true },
{ "name": "Sports Shoes", "leaf": true }
]
},
{ "name": "Clothing", "leaf": true }
]
}
]
}
}
This is the list:
Ext.define('MyTabApp.view.CategoriesList', {
extend: 'Ext.dataview.NestedList',
alias : 'widget.categorieslist',
config: {
height : '100%',
title : 'Categories',
displayField : 'name',
useTitleAsBackText : true,
style : 'background-color:#999 !important; font-size:75%',
styleHtmlContent : true,
listConfig: {
itemHeight: 47,
itemTpl : '<div class="nestedlist-item"><div>{name}</div></div>',
height : "100%"
}
},
initialize : function() {
this.callParent();
var me = this;
var catStore = Ext.create('MyTabApp.store.CategoriesStore');
catStore.load();
me.setStore(catStore);
}
});
The list starts working properly without any ajax request on each tap if I remove data wrapper over top categories array and change rootProperty to categories instead of data.categories. Since server is actually returning categories in data object I cannot remove it so how do I fix the store in that case? Also why is that additional ajax request to fetch the file?
[EDIT]
Tried to create a fiddle http://www.senchafiddle.com/#d16kl which is similar but not same because it is using 2.0.1 and data is not loaded from external file or server.
Last time I had this exact situation, it was because one of my top level category was a leaf but I had not set leaf:true. Doing so recalled the top level of the nested list as if it was a child.
It seems from your Fiddle that if your data is in this following format, it would work fine:
{
"categories" : [{
"name" : "Foo",
"categories" : [{
...
}]
}]
}
That is, just remove the "data" property and make defaultRootProperty: 'categories' & rootProperty: 'categories'. Check this: http://www.senchafiddle.com/#d16kl#tIhTp
It works with external data file as well.