-
Notifications
You must be signed in to change notification settings - Fork 57
quickstart
A gentle introduction to creating, and deploying your first ZMVC based RIA.
- The location/domain of the config.js file for the store (usually http://www.domain.com/jquery/config.js)
- Browser: Firefox, OR re-configured chrome, safari for running .js locally (IE can't run local .js files)
- Stuck? Sharp_Edges documents wonky errors that confuse noobs (great for getting un-stuck once you've gotten started).
- Still Stuck? Ask us: AnyCommerce App Development
- If you don't know what/why this is? you'll probably want to review the Developer Overview before getting your hands dirty.
- Hosting: these are designed to be SERVER-LESS JavaScript apps (not websites) - so if you're building this for a client - chances are they're just going to want it on dropbox, or hosted in github, so it might make sense to put your project local on your hard disk instead of a remote ftp server.
- Browser Configuration: For your first project you'll almost absolutely want to start in Firefox because it works best for local testing and it's firebug+webconsole is great for troubleshooting. If you prefer, Safari will also allow local ajax load and Chrome can be configured to (google it). IE 8/9 have some terribly inconvenient same-domain-policy issues that prevent apps loaded from the hard disk from being able to properly communicate. We firmly believe that no self-respecting developer would ever use IE anyway.
- IE Issues (Deploying on a remote server): That nasty IE 8/9/10 same-domain-policy issue also makes it difficult to deploy a shopping app on a site other than one hosted by Zoovy. http://jquery-howto.blogspot.com/2009/04/cross-domain-ajax-querying-with-jquery.html
- Currently Zoovy is not providing free app hosting, although it will be online by late January and also able to receive updates and pull from github directly. Frankly we think Github is awesome and we recommend forking this sdk and then configuring the hosting to pull from github.
- for simplicity the quickstart is included in the root of the github folder.
- "quickstart.html" - contains the sample we'll be working with
- "init.js" is commonly the only JS file you should actually need to modify
- "quickstart.js" contains the bulk of the code specific to the shopping app.
- "qs_styles.css" a styles file required by quickstart.html
- "loading.gif", "wait.gif", and the other images in the root directory are needed by quickstart.html and/or qs_styles.css
Step 1: Download the latest version of the MVC from gitHub, and renaming 'quickstart.html' to 'index.html'
You can find the MVC at https://github.com/zoovy/AnyCommerce-Development/ Anytime you start a new project you'll want to un-compress a fresh version of the MVC, and then find the quickstart.html file in the root directory.
The quickstart.html file is the base that will be used for most shopping applications.
RENAME 'quickstart.html' TO 'index.html' note - We are renaming the quickstart.html file so that uncompressing future versions of the MVC into the same folder will NOT overwrite your work. note - You should also create/maintain a separate .css file for all your app-specific classes. This also reduces likelyhood of your changes getting nuked.
For the rest of this tutorial we will refer to 'quickstart.html' as 'index.html'
Within the <head>
section of the index.html file, there is a JavaScript object named 'app' (this object is later merged with the control object). Set httpsURL and httpURL to the secure and non-secure url, respectively, to be used by the app. These should have been provided to you by the Project Manager. If not present, use https://ssl.zoovy.com/s=www.[USERNAME].zoovy.com/ and http:www.[USERNAME].zoovy.com/ where [USERNAME] is replaced with the store username. Be sure to end the url with a '/'. BaseURL will be populated based on whether the session is secure or nonsecure.
These url's will be used to set the <base>
href and also several of the includes, including the config.js file. It's not necessary to know a lot about config.js, but if you have any questions there is a great tutorial here:
https://github.com/zoovy/AnyCommerce-Development/wiki/Config-js
All your head includes should be after the piece of javascript that sets the base url. Otherwise, the files may not load properly when a buyer lands on a page deeper than the homepage.
If you're converting from a PNG/PSD/AI then place your HTML directly into the 'index.html' file.
You'll want to link your CSS file(s) in the <head>
location indicated:
<link rel="stylesheet" type="text/css" href="qs_styles.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" />
<!-- LINK YOUR CSS HERE -->
The fewer number of includes, the faster the site will be. Feel free to merge qs_styles, your custom jqueryui css and the app-specific css into one file with a new name. Add several linebreaks between each and use a comment to denote which section of css comes next.
Go into the index.html file and shortly after the html <body>
tag you should see:
<div id='appPreView'>
<table>
<tr>
<td><!-- put logo here --></td>
<td>
<h3>One moment please.</h3>
<div class='alignCenter'>
<progress id="appPreViewProgressBar" max="100" value="0" /><br />
</div>
<div id='appPreViewProgressText'>0% Complete</div>
</td>
</tr>
</table>
</div>
This is what will first appear when the html file loads in a browser. The progress bar will be auto-updated as the .js includes are loaded. If you received this project from Zoovy, there will be a loading.psd in the working files you received. If the project indicates no loading screen is desired, simply add a class of 'displayNone' to appPreView and remove the 'displayNone' class from the appView element (described below).
In most common designs there will be a 'wrapper' that maintains consistent navigation/menus/etc. and a 'main content area' which will display different content (ex: categories, products, checkout, etc.) based on user interaction. In Model-View-Controller (MVC) terms the 'wrapper' is called the 'view'.
For the rest of this tutorial we will refer to the 'wrapper' (which contains the common navigation) as the 'view'.
Just after the appPreView div you should see:
<div id='appView' class='displayNone'>
<!-- VIEW CONTENT -->
<!-- leave the appView div, put your your html here ... -->
<div id='globalMessaging'></div><!-- DO NOT REMOVE - used for some high level error handling (usually appears below breadcrumb nav or top bar) -->
<div id='mainContentArea'></div><!-- DO NOT REMOVE - this is where the specific page (ex: homepage, product, category) content is going to go -->
<!-- ... your html ends -->
<!-- /VIEW CONTENT -->
</div>
It is so important that you place the two div's below into the 'main content area' of your view. If you forget the globalMessaging and/or mainContentArea then the showContent function in the app (discussed later) will not know where to output any rendered templates (such as the homepage, category, product etc.) and you'll most likely have a frustrating experience with this.
So again - all you need to do is paste the html code below into your view where you want the content to appear:
<div id='globalMessaging'></div><!-- used for some high level error handling -->
<div id='mainContentArea'></div><!-- this is where the page content is going to go -->
You should leave the inside of the 'mainContentArea' blank, because this is where the various templates such as homepage, category, product, cart, checkout will be output by the controller.
WATCH OUT: Many developers have the nasty habit of including the homepage html (homepage template) inside the view. The 'homepage' html content is itself a template which is put into the 'mainContentArea' by the controller when it loads. If you embed the 'homepage' html into the view then it will always be present even when other templates (ex: product, category, cart, checkout, etc.) are shown.
If you were not instructed to hard code the shopping categories, then place this html where you want them to appear:
<ul id='tier1categories'></ul>
The MVC will place the top level categories where this UL is located. The 'categoryListTemplateRootCats' template will be used.
At then end of this step, in the index.html you should have something like:
<!-- view -->
<div id="appView">
your view html
<!-- REMINDER : DO NOT PUT ANY GRAPHICS THAT SHOULD NOT APPEAR ON EVERY PAGE IN THE VIEW -->
<div id='globalMessaging'></div><!-- used for some high level error handling -->
<div id='mainContentArea'></div><!-- this is where the page content is going to go -->
more view html
</div>
<!-- /view -->
Hint: when creating HTML from scratch, comment each template (homepage, product, category, etc.) with the idea that they're independent from the view itself.
note - for samples of what search, newsletter or some other elements should look like, look in (and read) the examples/snippets.html file.
Load the index.html file in any browser and you should see your view. If you use FireFox or Safari at this step, the default homepageTemplate content will also populate.
If you see your view, then congratulations! You can proceed to step 4. If you didn't, then start over and go through steps 1-3 again.
If you get an error about checkout settings, toggle the comment for checkout_nice and checkout_passive.
The next step is adding javascript code to the view to load dynamic content from the API, merge that with the appropriate template and display the output in the mainContentArea. In this step you're going to need to know a little bit about the store you're app will connect to. Specifically you'll need to know the product id of a sample product, and the 'safe name' (a zoovy term) for a navigation category that has a few products in it for testing.
If you don't know a product id, and/or safe name, then the Zoovy MVC SDK includes a "site analyzer" in the folder examples/site-analyzer/index.html. The site analyzer uses the API to explore any store to discover information.
If you aren't sure what to connect to what, then talk to the project manager and/or client. If you want to proceed anyway, and if the site analyzer isn't working then the root navigation category has a safename of '.' and you can usually find a product with a pid 'TEST' (which is a common product id used for testing by many store owners).
This is all done with a single function: app.ext.myRIA.a.showContent which has a global alias to simply 'showContent' (NOTE: they are both the same function, but 'showContent' is just way easier to type)
Since we started from quickstart.html (now called index.html) we already have some sample templates for product, homepage, category, and other common templates built in - and we'll modify those in later steps, but for now let's just get the included sample templates to display.
In the examples below, the brackets [something] indicate a string that needs to be set by you. The real value will NOT have the brackets. So for example '[.category.safe.name]' might be replaced with '.actual.value'
If you are receiving this project directly from Zoovy, you'll find the project build notes include details from implementation team on what should be linked to the view. If not, you should use the examples/site-analyzer/index.html file included with the mvc to discover what you are looking for. Below is example HTML to create a 'HomePage' link.
<a href='#top' onClick="return showContent('homepage',{});">HomePage</a>
We'll cover some advanced usage for the 'showContent' (aka 'app.ext.myRIA.a.showContent') later in this tutorial. But this is a quickstart - so for now, you will need to know is that app.ext.myRIA.a.showContent is part of myRIA - which is the custom extension that is 'your shopping application', and that showContent accepts two parameters: pageType which is a scalar/string, and pageInfo which is an array/object.
Standard pageTypes are: * homepage: the default screen that is displayed when the app loads (homepage is an alias to navcat:[root]) * cart: shows the shopping cart content * product: used to load/render/output product content * category: used to load/render/output category content * customer: used for shopper/buyer functions such as checking order status, managing newsletter subscriptions (includes login) * checkout: used to place orders * help: displays helpful content/policies such as contact us form, about company, faq, shipping, return, payment, privacy policies. * [other]: any non-handled page type will load the homepage by default, however advanced applications can add additional pageTypes in quickstart.js (which is beyond the scope of this tutorial)
The pageInfo parameter depends on the pageType passed. Below are some common usages of app.ext.myRIA.a.showContent(pageType,pageInfo);
<a href='#top' onClick="return showContent('category',{'navcat':'[.category.safe.name]'});">A Category Name</a>
<a href='#top' onClick="return showContent('customer',{'show':'myaccount'});">My Account</a>
pageType:'customer' supports the following 'pageInfo.show' parameters included with the default store_crm extension:
- 'show':'myaccount' : Loads 'My Account', which includes 'Recover Password'
- 'show':'newsletter' : Loads 'Newsletter Signup' which includes 'Create Account'
- 'show':'orders' : Loads 'Order History/Tracking' (will prompt for login)
<a href='#top' onClick="return showContent('cart',{});">Cart</a>
<a href='#top' onClick="return showContent('product',{'pid':'[pid]'});">A Product Name</a>
The company pageType is responsible for loading and displaying content contained in the store 'profile' (a zoovy term). The profile contains policy information for a store, the email address, logo image, and more - the profile code is included in the config.js file. For company links, set the pageType to 'company' and use the list below to set the pageInfo:
<a href='#top' onClick="return showContent('company',{'show':'contact'});">Contact Us</a>
pageType 'company' supports the following standard 'pageInfo.show':
- 'show':'contact' : Contact Us
- 'show':'about' : Company Information
- 'show':'faq' : Frequently Asked Questions
- 'show':'shipping' : Shipping
- 'show':'return' : Return Policy
- 'show':'payment' : Payment Methods
- 'show':'privacy' : Privacy Policy
To complete this step go ahead and try to get a few links and buttons in your view working. Some of the links will be easier than others. You do not need to link up every possible link at this stage in the quickstart, just one or two examples are fine, the main point of this was to verify that the controller is working and by now you've familiarized yourself with 'showContent' -- so let's move on!
By now, when you load index.html page content should be loading and hopefully you've been able to link up a some menus to actual categories. If not, then you'll want to use the JavaScript debugger in your browser to try and figure out what is going on.
You should also be familiar with showContent and understand that the showContent function always accepts two parameters: pageType (a string), pageInfo (an associative array) and that the allowed parameters to pageInfo depend on the pageType.
Now that you've learned the long hand way to use showContent inside of an onClick - we're going to show you another shortcut called 'bind-nav' that works inside the view.
The bind-nav syntax is particularly helpful to individuals (ex: graphic designers) who
may be participating in the maintenance of the shopping application and who may be fluent in HTML but who are not as comfortable with javascript.
To use bind-nav simply add a class of "bindByAnchor" to any href inside the view then simply use the notation
href="#pageType?attrib=value" or in version 201310+ href="#!pageType?attrib=value".
<!-- product example -->
<a class="bindByAnchor" href="#!product?pid=XYZ">A Product Name</a>
<a onClick='return showContent('product',{'pid':'XYZ'});'>A Product Name</a>
<!-- category example -->
<a class="bindByAnchor" href="#!category?navcat=.path.to.category">Category Name</a>
<a onClick='return showContent('category',{'navcat':'.path.to.category'});'>Category Name</a>
<!-- company/returns example -->
<a class="bindByAnchor" href="#!company?show=return">Returns Policy</a>
<a onClick=' return showContent('company',{'show':'return'});'>Returns Policy</a>
You can also enable bind-nav functionality in your own custom templates. Doing so is beyond the scope of this quickstart but can be very useful when you want graphic designers to be able to maintain templates.
From an SEO perspective, link in the appView should use the bindNav/#! approach. The #! let's Google know it's dealing with an app and to spider the website differently.
At this point, we will tune the html/css for the various templates as needed. The templates are found in the index.html below the view there should be an outer
<div id="appTemplates">
<div id="homepageTemplate">
<< html for pageType:homepage >>
<< OR >>
<< pageType:category & pageInfo.navcat:. >>
</div>
<div id="productTemplate">
<< html for pageType:product template >>
</div>
<div id="categoryTemplate">
<<html for pageType:category template >>
</div>
</div>
Before you get here you should already understand that templates are loaded by the javascript function showContent. showContent also loads dynamic content from the API (based on the pageType+pageInfo parameters) then copies the corresponding template from memory, renders it with the dynamic content (from the api), and ultimately output's the result to the mainContentArea.
If you're looking for a specific template then you can usually guess the name. The standard naming convention is [pageType]Template, ex: homepageTemplate, productTemplate, categoryTemplate, companyTemplate.
The templates that you will most likely be interested in are:
#homepageTemplate : The homepage. Should contain all content specific to the homepage, including banners and text.
#productTemplate : an individual product in a list of products (such as on a category or homepage).
#categoryTemplate : the outer most category template (more on this later)
#categoryListTemplate : used in page content for each subcategory displayed. In most cases, appears in a ul
#productListTemplate : commonly used to display product information in a 'list' format (such as on a category page or as related items on a product detail page)
#cartTemplate : used for the shopping cart in modal AND full page view.
#productListTemplateCart : used within cartTemplate for each item (product and coupons) in the cart/order.
To stylize the templates, use the existing classes within the given template or add your own classes as needed. Be cautious about removing existing classes as, in most cases, they're present for a reason. If additional fields are needed in a given template, use bind-data syntax covered in step #8.
WARNING: Before removing any template from the file it is your responsibility to ensure that any extension does not load it.
In addition, within each of the checkout extension folders, there is a templates.html file which contains templates specific to that checkout. Be very cautious when editing the CHECKOUT TEMPLATES.
Editing the names or id's on any of the inputs will have a disabling effect on checkout.
For the rest of this tutorial we will refer to the 'homepage html' as the 'homepage template'. It is possible (in fact it's pretty easy) to create your own templates, but it's outside the scope of this tutorial.
NOTE: In the default qs_styles.css you'll see a class for #appTemplates to make sure the templates are not visible to the app user except when loaded/output by showContent.
There is no need to hide each individual template, just the parent container, but sometimes when you are creating the initial html it is easier to show the templates, in this case override the setting in qs_styles.css.
If you need to execute some javascript after a template has finished loading (such as for tabs on the productTemplate or perhaps a carousel on the homepage, you can easily do so through the init.js file. The file contains the acBaseScripts object which can be used to load .js files (such as jcarousel). Simply put the js file in the root directory or load it from a third party CDN (ex: the jqueryui lib is loaded from googleapis in the init.js file). The 'pass' should be a numerical value < 10. Pass 1 is what is loaded before the app is initiated and should only include essential files. A few lines lower is the acAppIsLoaded function. Within this function you can add to the 'onInits' or 'onCompletes' for a given template, which get executed just before or after said template is loaded or populated. There is an example of each (onInit and onComplete) in the init.js file.
Warning: if you hard code a source for any file to http:// you will cause an ssl error in checkout. be sure to only include scripts that are available from a secure source.
A template, as the name implies, is intended to provide formatting and structure to dynamic data before it is output to the screen. Figuring out which data to display is done via a "data-bind" attribute. The data-bind syntax follows CSS selector parsing conventions, and is documented here: Data Bind Syntax
<div id='productTemplate'>
<h1 data-bind='var: product(zoovy:prod_name); format:text;'></h1>
<table id='prodViewerTable' class='prodViewerContainer'>
<tr>
<td class='imageContainer'>
<div onClick='app.ext.store_product.u.showPicsInModal({"pid":$(this).attr("data-pid")});'
class='pointer' data-bind='var:product(pid);format:assignAttribute; attribute:data-pid;'>
<img src='blank.gif' class='prodBigImage'
data-bind='var: product(zoovy:prod_image1); format:imageURL;' width='350' height='350' />
</div>
</td>
</tr>
</table>
Using data-bind "magic" is simply a user-friendly short-hand notation that adds minimal processing overhead for javascript developers. In addition because data-bind utilizes a familiar structure to CSS3 the data-bind syntax is especially accessible to not-as-javascript-proficient-html5+css3-website-designers who are frequently tasked with making routine day-to-day design changes to the application/site. When adding new templates to an app the javascript-developer (you) should provide data-binding capabilities whenever a non-javascript-developer will be involved in the life-cycle maintenance of the app.
The Zoovy data-bind language relies heavily on the Zoovy platform's ability to dynamically generate images at a requested height/width. Any developer can request any image at any size, if you'd like to know more about how this works it's explained here: Image Handling
Earlier in the tutorial we explained that 'showContent' was actually an alias to 'app.ext.myRIA.a.showContent', here is a brief explanation of the hierarchy of app:
-
- app: controller object (instantiated in the init.js file, referenced in index.html)
-
- app.ext: extension object within the controller (extensions are loaded during the bootstrap)
-
- app.ext.myRIA: the name of your extension (this was specified when the controller was instantiated) inside of the boot strap.
-
- app.ext.myRIA.a: (.a is short for 'action') indicates the function itself is intended to be called from other modules, probably as a result of user interaction.
-
- app.ext.myRIA.a.showContent: the routine that does the magic.
- u: utility functions which may be called from other modules.
- calls: functions which execute calls to the server.
- callbacks: functions which receive data from the api server calls.
- renderFormats: functions which format the data received by the callbacks.
- a: action functions, triggered by a user interaction, which perform output, ex: showContent
- validation: functions which verify responses and update globalMessaging when an error occurs.
The init.js file contains the extensions you need to load, and should look something like this:
app.vars.extensions = [
{"namespace":"store_prodlist","filename":"extensions/store_prodlist.js"},
{"namespace":"convertSessionToOrder","filename":"extensions/checkout_passive/extension.js"}, /* checkout_passive does not require buyer to login */
// {"namespace":"convertSessionToOrder","filename":"extensions/checkout_nice/extension.js"}, /* checkout_nice prompts buyer to login */
{"namespace":"store_checkout","filename":"extensions/store_checkout.js"},
{"namespace":"store_navcats","filename":"extensions/store_navcats.js"},
{"namespace":"store_search","filename":"extensions/store_search.js"},
{"namespace":"store_product","filename":"extensions/store_product.js"},
{"namespace":"store_cart","filename":"extensions/store_cart.js"},
{"namespace":"store_crm","filename":"extensions/store_crm.js"},
{"namespace":"myRIA","filename":"quickstart.js","callback":"startMyProgram"}
];
The last extension 'myRIA' is where all the app-specific magic happens (app.ext.myRIA.a.showContent):
{"namespace":"myRIA","filename":"quickstart.js","callback":"startMyProgram"}
Any extension may have a callback is executed by the controller when the callback finishes loading. You can have multiple custom extensions, or you can modify the quickstart.js file and include whatever functionality you need to add. If you are adding unique business functionality or logic that could (eventually) span multiple applications then we'd recommend adding a custom extension.
At the very top of the index.html file you'll see only a few embedded <script>
tags (jquery.js and loadscript.js). The rest of the script files are loaded in init.js and may include:
* jqueryui
* controller
* model
* includes (contains json2, wiki and variations display code)
* Plus any other supporting files, such as jcarousel, flexigrid, etc that you may need in your build.
If you have used some of the new html5 tags in your HTML that are not universally supported, be sure to include a copy of modernizr. http://modernizr.com/
In addition, you will need to reference a jqueryui theme. You can easily build a custom theme that matches the rest of the build at: http://jqueryui.com/themeroller/
For development purposes the app itself SHOULD run fine from the browser in a local directory, although IE will be much slower because it has to use jsonp requests instead of json due to IE's broken interpretation of cross-domain policies.
Copy the application folder and change all paths in index.html (if necessary) and/or just delete the examples folder.
Any questions about a specific projcet please contact your project manager, if you find any bugs please submit an issue via github.
Finally we highly recommend you at least skim the Sharp_Edges documentation before you get too far in, each time somebody gets stuck we try and document anytime somebody gets stuck.