{"_id":"584b3e1e01634d1900dc61ff","version":{"_id":"55a6e72f8cc73e0d00096638","project":"55a6e72e8cc73e0d00096635","hasReference":false,"__v":29,"hasDoc":true,"createdAt":"2015-07-15T23:05:19.125Z","releaseDate":"2015-07-15T23:05:19.125Z","categories":["55a6e7308cc73e0d00096639","55b7ed07aea7c8190058badb","5604567c0c78b00d0039b191","5605e6f23a93940d002b3e4a","5605f2bba4574a0d00811365","5605f309a4574a0d00811366","5608e3b98aedf50d0004cf8f","5608e4318aedf50d0004cf90","5608e6b5a7cc2f0d00d9754d","5608e6d331beb60d001b6560","5608f879a7cc2f0d00d97580","560b097887b71d0d000d3bd9","560b13cbafa0990d00979545","560b5cbec341310d00de2a01","560b5cd0c341310d00de2a02","566a35b81e08750d00a0c49b","566a3e8503b4b20d00d02a4a","567889d307bf6a0d0083edc8","569c8b7c15bb390d00db6f9d","56b254dc65ddf50d0076ba8f","57a8ebc4cdeea20e001d2a63","57e48a4000c8680e00fae6e7","5808216773557d0f00a1e428","58105ad54a8aa50f00aa4cba","58105bf298aea40f00afa3ba","58105f548a4aed0f00d67536","581061b898aea40f00afa3be","584b3de7e5f3a42300df6ef7","596839a75965d400155bb750"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"project":"55a6e72e8cc73e0d00096635","__v":0,"user":"55a6caa022cfa321008e01d6","category":{"_id":"584b3de7e5f3a42300df6ef7","project":"55a6e72e8cc73e0d00096635","__v":0,"version":"55a6e72f8cc73e0d00096638","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-12-09T23:27:35.491Z","from_sync":false,"order":16,"slug":"javascript-api","title":"JavaScript Customization"},"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-12-09T23:28:30.402Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":4,"body":"This is a basic example of how to include third-party JavaScript (as an npm package) in a theme. This entry includes:\n\n* [What We're Building](#what)\n* [Set Up Your Store](#products)\n* [Install the Dependencies](#install)\n* [Configure the webpack Loaders in webpack.conf.js](#loaders)\n* [Import the Dependencies](#import)\n* [Configure the loaded() Method](#configure)\n\n## <span id=\"what\"> What We're Building </span>\n\nThis example will use the <a href=\"http://foundation-datepicker.peterbeno.com/example.html\" target=\"_blank\">Foundation-datepicker.js</a> plugin to implement a datepicker for product pages' `Delivery/Event Date` fields.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/2136448-final.png\",\n        \"final.png\",\n        571,\n        332,\n        \"#e0dfdf\"\n      ],\n      \"caption\": \"Screenshot of final product\",\n      \"sizing\": \"80\"\n    }\n  ]\n}\n[/block]\n## <span id=\"products\"> Set Up Your Store </span>\n\nTo test this example, you'll want your (sandbox or production) store to include at least a couple of products that have a `Delivery/Event Date` configured. (In production, you'd typically use this feature for things like seasonally themed products, temporary promotions, or event tickets.)\n\nFor product configuration steps in the BigCommerce control panel, please see [this support article](https://support.bigcommerce.com/articles/Public/Adding-a-Product-Delivery-Event-Date).\n\n## <span id=\"install\"> Install the Dependencies </span>\n\nUse the following command to install this example's required dependencies:\n\n```\nnpm install --save-dev css-loader moment foundation-datepicker style-loader\n```\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"The above command's options are:\\n* `--save-dev` saves the dependencies as `DevDependencies`; this flags them as [required for development, but not at runtime](https://stackoverflow.com/questions/18875674/whats-the-difference-between-dependencies-devdependencies-and-peerdependencies).\\n* `css-loader` and `style-loader` are webpack loaders, used together:\\n  * [`css-loader`](https://github.com/webpack-contrib/css-loader) resolves `:::at:::import` and `url()` expressions in CSS files.\\n  * [`style-loader`](https://github.com/webpack-contrib/style-loader) generically loads stylesheets, by injecting a `<style>` tag.\\n\\n* The [`moment`](https://momentjs.com/) JavaScript component parses, validates, and displays dates and times.\\n* `foundation-datepicker` specifies the datepicker package to install.\",\n  \"title\": \"Installation Options\"\n}\n[/block]\n## <span id=\"loaders\"> Configure the webpack Loaders in webpack.conf.js </span>\n\nThe <a href=\"https://github.com/webpack/css-loader\">css</a> and <a href=\"https://github.com/webpack/style-loader\">style</a> loaders are used to import CSS and to inject it into the DOM, respectively:\n\n```javascript\n{\n    test: /\\.css$/,\n    loader: 'style-loader!css-loader',\n}\n```\n\nHere is a screenshot of these loaders in context:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/8a8adab-webpack-basic.png\",\n        \"webpack-basic.png\",\n        2214,\n        904,\n        \"#1d1d1d\"\n      ],\n      \"caption\": \"webpack.conf.js screenshot\"\n    }\n  ]\n}\n[/block]\n## <span id=\"import\"> Import the Dependencies </span>\n\nImport these new dependencies into `<theme-name>/assets/js/theme/product.js`.\n\nIn `<theme-name>/assets/js/app.js`, notice that there is a mapping between the product page and the `product.js` script:\n\n```javascript\nconst PageClasses = {\n    mapping: {\n        ...\n        'pages/product': product,\n```\n\nThat is, when a user navigates to the product page, the `product.js` script is run. First its constructor will be run, followed by the methods `before`, `loaded`, and `after` – in that order. \n\n\n## <span id=\"configure\"> Configure the loaded() Method </span>\n\nWe'll use the `loaded` method to initialize our datepicker widget:\n\n```javascript\nimport $ from 'jquery';\nimport PageManager from '../page-manager';\nimport Review from './product/reviews';\nimport collapsibleFactory from './common/collapsible';\nimport ProductDetails from './common/product-details';\nimport videoGallery from './product/video-gallery';\nimport { classifyForm } from './common/form-utils';\nimport 'foundation-datepicker/js/foundation-datepicker.min.js';\nimport 'foundation-datepicker/css/foundation-datepicker.min.css';\nimport moment from 'moment';\n\n...\n\nloaded(next) {\n    let validator;\n\n    // Init collapsible\n    collapsibleFactory();\n\n    this.productDetails = new ProductDetails($('.productView'), this.context);\n\n    videoGallery();\n\n    const $reviewForm = classifyForm('.writeReview-form');\n    const review = new Review($reviewForm);\n\n    $('body').on('click', '[data-reveal-id=\"modal-review-form\"]', () => {\n        validator = review.registerValidation();\n    });\n\n    $reviewForm.on('submit', () => {\n        if (validator) {\n            validator.performCheck();\n            return validator.areAll('valid');\n        }\n\n        return false;\n    });\n\n    let $deliveryDateMth = $('#deliveryDateMth');\n    let $deliveryDateDay = $('#deliveryDateDay');\n    let $deliveryDateYr = $('#deliveryDateYr');\n    let earliestDate = moment(this.context.product.event_date.date_start, \"MMM Do YYYY\");\n    let latestDate = moment(this.context.product.event_date.date_end, \"MMM Do YYYY\");\n    \n    $('#deliveryDate').fdatepicker({\n        leftArrow:'<<',\n        rightArrow:'>>',\n        onRender: function (date) {\n            return moment(date).isBetween(earliestDate, latestDate, null, '[]') ? '' : 'disabled';\n        }\n    })\n    .on('changeDate', function(event) {\n        let date = event.date;\n        $deliveryDateMth.val(date.getMonth() + 1);\n        $deliveryDateDay.val(date.getDate());\n        $deliveryDateYr.val(date.getFullYear());\n    })\n    .data('datepicker');\n\n    next();\n}\n```\n\nHighlighted below is the new code added to the `loaded` method:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/adce0ef-product.png\",\n        \"product.png\",\n        1654,\n        1152,\n        \"#1c2631\"\n      ],\n      \"caption\": \"product.js screenshot\",\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\nUpdate the `<theme-name>/templates/components/products/product-view.html` template, replacing the existing `{{#if product.event_date}}` block with the following:\n\n```javascript\n{{#if product.event_date}}\n    {{inject 'product' product}}\n    <div class=\"form-field\">\n        <label class=\"form-label form-label--alternate form-label--inlineSmall\">\n        {{product.event_date.name}}:\n        <small>{{lang 'common.required'}}</small>\n        </label>\n        <input type=\"text\" class=\"form-input\" id=\"deliveryDate\" name=\"EventDate[Date]\" required>\n        <input type=\"hidden\" id=\"deliveryDateMth\" name=\"EventDate[Mth]\">\n        <input type=\"hidden\" id=\"deliveryDateDay\" name=\"EventDate[Day]\">\n        <input type=\"hidden\" id=\"deliveryDateYr\" name=\"EventDate[Yr]\">\n    </div>\n{{/if}}\n```\n\nNote that we're \"injecting\" the product here, so we have access to its properties.  This could be done closer to the root of the tree, but it's been placed here for proximity to the code that requires it. \n\nWe also needed to add form fields for the `EventDate[Mth]`, `EventDate[Day]`, and `EventDate[Yr]` data, which we update whenever the `changeDate` event occurs. This conforms to the data format that the server expects. These fields are hidden from the user.","excerpt":"Third-party JavaScript","slug":"js-npm-basic","type":"basic","title":"Basic npm Example"}

Basic npm Example

Third-party JavaScript

This is a basic example of how to include third-party JavaScript (as an npm package) in a theme. This entry includes: * [What We're Building](#what) * [Set Up Your Store](#products) * [Install the Dependencies](#install) * [Configure the webpack Loaders in webpack.conf.js](#loaders) * [Import the Dependencies](#import) * [Configure the loaded() Method](#configure) ## <span id="what"> What We're Building </span> This example will use the <a href="http://foundation-datepicker.peterbeno.com/example.html" target="_blank">Foundation-datepicker.js</a> plugin to implement a datepicker for product pages' `Delivery/Event Date` fields. [block:image] { "images": [ { "image": [ "https://files.readme.io/2136448-final.png", "final.png", 571, 332, "#e0dfdf" ], "caption": "Screenshot of final product", "sizing": "80" } ] } [/block] ## <span id="products"> Set Up Your Store </span> To test this example, you'll want your (sandbox or production) store to include at least a couple of products that have a `Delivery/Event Date` configured. (In production, you'd typically use this feature for things like seasonally themed products, temporary promotions, or event tickets.) For product configuration steps in the BigCommerce control panel, please see [this support article](https://support.bigcommerce.com/articles/Public/Adding-a-Product-Delivery-Event-Date). ## <span id="install"> Install the Dependencies </span> Use the following command to install this example's required dependencies: ``` npm install --save-dev css-loader moment foundation-datepicker style-loader ``` [block:callout] { "type": "info", "body": "The above command's options are:\n* `--save-dev` saves the dependencies as `DevDependencies`; this flags them as [required for development, but not at runtime](https://stackoverflow.com/questions/18875674/whats-the-difference-between-dependencies-devdependencies-and-peerdependencies).\n* `css-loader` and `style-loader` are webpack loaders, used together:\n * [`css-loader`](https://github.com/webpack-contrib/css-loader) resolves `@import` and `url()` expressions in CSS files.\n * [`style-loader`](https://github.com/webpack-contrib/style-loader) generically loads stylesheets, by injecting a `<style>` tag.\n\n* The [`moment`](https://momentjs.com/) JavaScript component parses, validates, and displays dates and times.\n* `foundation-datepicker` specifies the datepicker package to install.", "title": "Installation Options" } [/block] ## <span id="loaders"> Configure the webpack Loaders in webpack.conf.js </span> The <a href="https://github.com/webpack/css-loader">css</a> and <a href="https://github.com/webpack/style-loader">style</a> loaders are used to import CSS and to inject it into the DOM, respectively: ```javascript { test: /\.css$/, loader: 'style-loader!css-loader', } ``` Here is a screenshot of these loaders in context: [block:image] { "images": [ { "image": [ "https://files.readme.io/8a8adab-webpack-basic.png", "webpack-basic.png", 2214, 904, "#1d1d1d" ], "caption": "webpack.conf.js screenshot" } ] } [/block] ## <span id="import"> Import the Dependencies </span> Import these new dependencies into `<theme-name>/assets/js/theme/product.js`. In `<theme-name>/assets/js/app.js`, notice that there is a mapping between the product page and the `product.js` script: ```javascript const PageClasses = { mapping: { ... 'pages/product': product, ``` That is, when a user navigates to the product page, the `product.js` script is run. First its constructor will be run, followed by the methods `before`, `loaded`, and `after` – in that order. ## <span id="configure"> Configure the loaded() Method </span> We'll use the `loaded` method to initialize our datepicker widget: ```javascript import $ from 'jquery'; import PageManager from '../page-manager'; import Review from './product/reviews'; import collapsibleFactory from './common/collapsible'; import ProductDetails from './common/product-details'; import videoGallery from './product/video-gallery'; import { classifyForm } from './common/form-utils'; import 'foundation-datepicker/js/foundation-datepicker.min.js'; import 'foundation-datepicker/css/foundation-datepicker.min.css'; import moment from 'moment'; ... loaded(next) { let validator; // Init collapsible collapsibleFactory(); this.productDetails = new ProductDetails($('.productView'), this.context); videoGallery(); const $reviewForm = classifyForm('.writeReview-form'); const review = new Review($reviewForm); $('body').on('click', '[data-reveal-id="modal-review-form"]', () => { validator = review.registerValidation(); }); $reviewForm.on('submit', () => { if (validator) { validator.performCheck(); return validator.areAll('valid'); } return false; }); let $deliveryDateMth = $('#deliveryDateMth'); let $deliveryDateDay = $('#deliveryDateDay'); let $deliveryDateYr = $('#deliveryDateYr'); let earliestDate = moment(this.context.product.event_date.date_start, "MMM Do YYYY"); let latestDate = moment(this.context.product.event_date.date_end, "MMM Do YYYY"); $('#deliveryDate').fdatepicker({ leftArrow:'<<', rightArrow:'>>', onRender: function (date) { return moment(date).isBetween(earliestDate, latestDate, null, '[]') ? '' : 'disabled'; } }) .on('changeDate', function(event) { let date = event.date; $deliveryDateMth.val(date.getMonth() + 1); $deliveryDateDay.val(date.getDate()); $deliveryDateYr.val(date.getFullYear()); }) .data('datepicker'); next(); } ``` Highlighted below is the new code added to the `loaded` method: [block:image] { "images": [ { "image": [ "https://files.readme.io/adce0ef-product.png", "product.png", 1654, 1152, "#1c2631" ], "caption": "product.js screenshot", "sizing": "full" } ] } [/block] Update the `<theme-name>/templates/components/products/product-view.html` template, replacing the existing `{{#if product.event_date}}` block with the following: ```javascript {{#if product.event_date}} {{inject 'product' product}} <div class="form-field"> <label class="form-label form-label--alternate form-label--inlineSmall"> {{product.event_date.name}}: <small>{{lang 'common.required'}}</small> </label> <input type="text" class="form-input" id="deliveryDate" name="EventDate[Date]" required> <input type="hidden" id="deliveryDateMth" name="EventDate[Mth]"> <input type="hidden" id="deliveryDateDay" name="EventDate[Day]"> <input type="hidden" id="deliveryDateYr" name="EventDate[Yr]"> </div> {{/if}} ``` Note that we're "injecting" the product here, so we have access to its properties. This could be done closer to the root of the tree, but it's been placed here for proximity to the code that requires it. We also needed to add form fields for the `EventDate[Mth]`, `EventDate[Day]`, and `EventDate[Yr]` data, which we update whenever the `changeDate` event occurs. This conforms to the data format that the server expects. These fields are hidden from the user.