Content Publishing
NOTE: the concepts presented in this article are now considered obsolete possibly because better alternatives are available.
Traditionally, Dynamicweb has been about rendering full pages with HTML that has been rendered at the server by pages, modules, items, etc. However, more and more, web sites and applications require other means to interact with data on the server. It’s very common nowadays to have some jQuery code that calls out to the server to get some data without reloading the complete page. Examples of this include product and filter data to perform client side filtering (as demonstrated here) and dynamic login pages.
Dynamicweb 8.4 introduced a few new features that make this easier than ever. You can now return different content types such as JSON or XML with just a few simple steps:
- Create a layout file that is set up to return nothing but the page content (e.g. content coming from paragraphs and modules)
- Create a page that contains the content you want to return (for example, a paragraph that contains a paragraph with the Product Catalog module to return product data as JSON)
- Configure the page to use the new layout file and mark its content type to be something like application/json
With that setup, you can call into this page from client side code such as jQuery or Angular to get the data and display it on the client.
Below you find the steps you need to go through to make this work and expose Ecommerce product data as JSON. You can use the exact same concept to expose other data such as Ecommerce filters, users (useful for a client side maps implementation), and everything else that can be published on a page. It’s assumed you’re running this code on a site that has Ecommerce enabled if you want to follow along.
1. Create a new layout file in your site’s Designs folder and call it json.html. Add the following code to it:
<!--@DwContent(contentmain)--><!--@If(1=2)--><div id="content-main" class="dwcontent" title="Main content" data-settings="template:moduleonly.html"> </div><!--@EndIf-->
Note: as of Dynamicweb 8.5 you don’t need to use this “If-hack” anymore. You can then use the new unwrap setting to tell Dynamicweb to exclude the wrapping <div> element. This means you can simplify your code to the following:
<div class="dwcontent" id="content-main" title="Main content" data-settings="unwrap: true; template:moduleonly.html"></div>
For more details, see: http://developer.dynamicweb.com/releases/dynamicweb-8-5.aspx#15171
This layout file can be used to expose any type of data and is not limited to this Ecommerce example.
2. Create a new template for an ecommerce product list in the folder Templates/Designs/DesignName/eCom/ProductList. Call it something like product-list-json.html and add code like this:
{ "pageid": "123", //ID of the details page; could come from an Area setting "numberofproducts": "<!--@Ecom:ProductList.PageProdCnt-->", "pagenum": "<!--@Ecom:ProductList.CurrentPage-->", "products": [<!--@LoopStart(Products)--> <!--@If(Products.LoopCounter>1)-->,<!--@EndIf--> { "id": "<!--@Ecom:Product.ID.JSEncoded()-->", "name": "<!--@Ecom:Product.Name.JSEncoded()-->", "price": "<!--@Ecom:Product.Price.JSEncoded()-->", "link": "<!--@Ecom:Product.LinkGroup.Clean.JSEncoded()-->", "description": "<!--@Ecom:Product.ShortDescription.JSEncoded()-->", <!--@If(Ecom:Product.ImageSmall.Default.Clean<>'')-->" image": "/Files/Files/<!--@Ecom:Product.ImageSmall.Default.Clean.Replace("/Files", "").Replace("/Files", "").JSEncoded()-->",<!--@Else-->"image": "",<!--@EndIf()--> "smallImage": "<!--@Ecom:Product.CategoryField.Screens.SmImage.Value.Clean-->", "languageId": "<!--@Ecom:Product.LanguageID-->", "variantId": "<!--@Ecom:Product.VariantID-->" } <!--@LoopEnd(Products)--> ] }
This code returns a list of products as well as some additional data that could be used for client side paging. Obviously, this is just a sample; you can use all the tags and concepts you can use in regular templates. You could also convert this template to Razor and get even more flexibility.
3. Create a new page in Dynamicweb and give it a name like GetProducts. I prefer to put pages like this in a folder called Ajax but this is not required:
4. Open the Properties dialog for this new page and select the JSON layout file from the dropdown list in the Layout section. Under Content type, choose
application/json:
5. Add a new paragraph to the page and as the paragraph template, choose ModuleOnly. If you don’t have this template, create it now (in the Paragraphs folder) and add the following code:
<!--@ParagraphModule-->
This step is important as you only want the output from the module. Any other output, such as additional HTML, will break the JSON format the page will return.
6. Add the Product Catalog module to this paragraph. Select one or more groups of products you want to expose as JSON. For the Product List template,
select the template you created in step 2:
7. At the bottom of this settings screen, make sure you set Show on paragraph to another page in your site that displays products.
This way, the link to the product details page will point to a normal page with the Product Catalog on it, and not to the current, JSON-only page.
8. Save all your changes. If you now request the page, you should see the data being returned as JSON, as shown in the following image:
9. You can now make a request for this page from client side code using whatever AJAX technology you prefer. Here’s an example that uses Angular to retrieve the products and display them as a list. Note that this code features in-line JavaScript for the Angular app and controller. In a real app, you would move this code to a separate JavaScript file.
<script src="//code.angularjs.org/1.2.15/angular.min.js"></script> <script src="//code.angularjs.org/1.2.15/angular-sanitize.min.js"></script> <h2 class="page-title">Products</h2> <hr> <div ng-app="ProductsModule"> <div ng-controller="ProductsController"> <div ng-repeat="product in products" > <div class="row"> <div class="product-thumbnail span4"> <img ng-src="{{product.image}}"> </div> <div class="product-summary span7"> <h4 class="item-title"> <a href="{{product.link}}">{{product.name}}</a> </h4> <div ng-bind-html="product.description"></div> <br /> <a href="{{product.link}}">View details</a> </div> </div> </div> </div> </div> <script> var ProductsModule = angular.module('ProductsModule', ['ngSanitize']); ProductsModule.factory('products', function ($http) { var returnValue = {}; returnValue.getProducts = function (callback) { return $http.get('/getproducts').success(callback); }; return returnValue; }); function ProductsController($scope, $http, products) { $scope.products = []; products.getProducts(successProducts); function successProducts(data) { $scope.products = data.products; } } </script>
On a web site based on the Solution Set, this produces the following output:
What's cool about this solution is that all the Dynamicweb functionality continues to work. This means for example you can still use query string parameters against the Ecommerce catalog to filter the list of products, provide sorting and paging information and so on.