Understand Visual Widgets
Understand Visual Widgets
In the following tutorial you will learn to understand and use Rhino Visual Widgets.
Overview
What are Visual Widgets?
Visual Widgets are small independent working program code snippets with top performance. They provide flexibility, pose economical resources and they are completely customizable.
Why use Visual Widgets?
Visual Widgets allow for content editors to flexibly create and design web content. Our base code already contains many different examples including banners, carousels, accordions and more. Widgets may be instantiated to save loading time altogether while offering the opportunity of creating a highly intricate site. Their functionality can be expanded by defining flex-attributes unique to every widget. By using the Visual Widget editor any user can easily edit their own Visual Widget without having to touch the code.
The first simple visual widget - a headline
Create descriptor content
What does the descriptor do?
Basically, the descriptor itself does not do anything. Rather than that it is a list of attributes/properties that the respective widgets should be initialized with. Its standard mime type is JSON.
{ "renderer" : { "rendererClass" : "manual.ikona.visualwidgets.step1.DemoRenderer" }, "validators" : [ { "validatorClass" : "rhino.visualwidgets.validators.DefaultValidator" } ], "dataResolver" : { "dataResolverClass" : "rhino.visualwidgets.DefaultDataResolver" }, "properties" : { "title" : { "defaultValue" : "", "required" : true, "type" : "string", "editor" : { "editorClass" : "rhino.smp.visualwidgets.editors.StringSingleLineEditorWidget", "displayLabel" : "Title" } } } }
Descriptor attributes explanation
- renderer (Implement in descriptor "rendererClass" path to your renderer)
- validators (Get the value by resolving and validate values)
- dataResolver (Parse properties for bindings and check if property has a binding)
- properties (Define properties and get the property from object)
Example for descriptor properties
"properties": { "title": { "defaultValue": "", "required": true, "type": "string", "editor": { "tags": "Title", "editorClass": "rhino.smp.visualwidgets.editors.StringSingleLineEditorWidget", "displayLabel": "Title" } } }
Descriptor attributes properties explanation
- "title" - defines a title property for the widget
- "defaultValue" - this value is used as fallback if no value is defined in the instance
- "required" - defines whether the value must be supplied
- "type" - data type
- "editorClass" - choose editor class corresponding with data type
- "displayLabel" - label for the editor input
Create the renderer
What does the renderer do?
The renderer is a server-side applet which renders any DOM information provided by the Visual Widget while including any available descriptor. It is a JavaScript file.
Renderer attributes explanation
- descriptor (Define widgets attributes)
- entity (To create an entity in JavaScript, you need a constructor pattern)
- resolver (Resolver get properties and them values)
You have to implement this.render as function(descriptor, entity, resolver) ...
this.render = function (descriptor, entity, resolver) { //retrieve title from resolver var title = resolver.get("title"); return "<h1>" + title + "</h1>"; };
Create the instance:
You have to create manual.ikona.visualwidgets.step1.DemoInstance
{ "title": "Your title content step 1" }
The result
Create test page...
... and embed this instance into:
... #{rhino.visualwidgets.render("manual.ikona.visualwidgets.step1.DemoInstance")} ...
The result should look like:
<h1>Your title content step 1</h1>
Using the Visual Widget Editor
Step 1: Select in staging dropdown menu SMP and select Rhino Visual Widgets.
Step 2: Select in menu your page and go to add widget.
Step 3: Create new visual widget.
Step 4: Open editor set your title content and save it.
Naming convention
The resulting FQN will be [namespace] + ".visualwidgets" + [type] + "." + [name], e.g. testing.visualwidgets.accordion.TestWidget.
The second simple visual widget - using template
Create two templates and implement rino visual widget get method into:
<div class="container"> <div class="row"> <div class="col-sm-6"> #{rvwget('title')} #{rvwget('image')} <p>Default template</p> </div> </div> </div>
Use #{rvwget()} method to display data in HTML.
<div class="container"> <div class="row"> <div class="col-sm-6"> <h4>Your title content step 1</h4> #{rvwget('image')} <p>Custom template</p> </div> </div> </div>
The third visual widget - using template with widget functions
Create widget:
panther.ensureDomPath("manual.ikona.visualwidgets.step3"); manual.ikona.visualwidgets.step3.DemoWidget = (function (panther) { panther.widgets.AbstractWidget.extend(DemoWidget); function DemoWidget(widgets) { DemoWidget.prototype.super.constructor.call(this, widgets); this.popoverAction = null; } DemoWidget.prototype.init = function (element) { DemoWidget.prototype.super.init.call(this, element); console.log('Ok widget get instantiated.'); }; DemoWidget.prototype.clickButtonAction = function (event, action) { event.preventDefault(); this.action.popoverAction.popover(); }; return DemoWidget; })(panther);
Create template and implement your DemoWidget into template:
<div class="container" data-widget="#{portal.remap('manual.ikona.visualwidgets.step3.DemoWidget')}"> <div class="row"> <div class="col-sm-6"> #{rvwget('title')} #{rvwget('image')} <p>Custom template</p> <a href="#" role="button" class="btn btn-default popovers" data-toggle="popover" title="popover title" data-content="Some content inside the popover. <br><a href='http://www.jenomics.de/' target='_blank' title='link'>Link on content</a>" data-original-title="Title" data-demo="PopoverAction">Popover with link inside</a> </div> </div> </div>
The fourth visual widget - using bindings
The most important reason to use a JSON binding is for interoperability with other components or external systems that use JSON objects.This is because JSON bindings expose JSON object.
Create descriptor content
{ "renderer": { "rendererClass": "manual.ikona.visualwidgets.step4.DemoRenderer", "templateFlavors": { "default": "manual.ikona.visualwidgets.step4.DefaultTemplate", "custom": "manual.ikona.visualwidgets.step4.CustomTemplate" } }, "validators": [ { "validatorClass": "rhino.visualwidgets.validators.DefaultValidator" } ], "dataResolver": { "dataResolverClass": "rhino.visualwidgets.DefaultDataResolver" }, "properties": { "templateFlavor": { "defaultValue": "default", "required": true, "type": "stringEnum", "values": [ "default", "custom" ], "editor": { "editorClass": "rhino.smp.visualwidgets.editors.StringEnumEditorWidget", "displayLabel": "Flavor", "valueDisplayLabels": [ "Default", "Custom" ] } }, "categoryName": { "defaultValue": null, "required": true, "type": "string", "editor": { "editorClass": "rhino.smp.visualwidgets.editors.SelectCategoryEditorWidget", "displayLabel": "Category" } }, "category": { "defaultValue": null, "required": true, "type": "category", "binding": { "type": "asCategory", "property": "categoryName" } }, "title": { "defaultValue": null, "required": true, "type": "string", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "title" } }, "linkTitle": { "defaultValue": null, "required": false, "type": "string", "maxLength": 50, "binding": { "type": "asDefault", "property": "title" }, "editor": { "editorClass": "rhino.smp.visualwidgets.editors.StringSingleLineEditorWidget", "tags": "Link", "displayLabel": "Link Titel" } }, "url": { "defaultValue": null, "required": true, "type": "string", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "url" } }, "price": { "defaultValue": null, "required": true, "type": "string", "binding": { "type": "asMoney", "property": "priceInCent" } }, "priceInCent": { "defaultValue": null, "required": true, "type": "number", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "lowestPrice" } }, "currency": { "defaultValue": null, "required": true, "type": "product", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "currency" } }, "imageId": { "defaultValue": null, "required": false, "type": "uuid", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "imageId" } }, "maxImageWidth": { "defaultValue": 48, "required": true, "type": "integer", "editor": { "editorClass": "rhino.smp.visualwidgets.editors.IntegerEditorWidget", "displayLabel": "Max Image Width" } }, "maxImageHeight": { "defaultValue": 48, "required": true, "type": "integer", "editor": { "editorClass": "rhino.smp.visualwidgets.editors.IntegerEditorWidget", "displayLabel": "Max Image Height" } }, "description": { "defaultValue": "", "required": false, "type": "string", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "description" } }, "shortDescription": { "defaultValue": "", "required": false, "type": "string", "binding": { "type": "asProperty", "referenceProperty": "category", "valueProperty": "shortDescription" } } } }
Create the renderer
this.render = function (descriptor, entity, resolver) { //retrieve templateFlavors from descriptor var templateFlavors = descriptor.renderer.templateFlavors; //ensure templateFlavors is not null if (!templateFlavors) { throw new Error("Descriptor descriptor.renderer.templateFlavors is null"); } //retrieve attribute templateFlavor from resolver var templateFlavor = resolver.get("templateFlavor"); //ensure templateFlavor is not null if (!templateFlavor) { throw new Error("Resolver attribute templateFlavor is null"); } //resolve template from flavor var templateName = templateFlavors[templateFlavor]; //ensure template is not null if (!templateName) { throw new Error("Template flavor " + templateName + " not found"); } //try to resolve a remapping from portal var rendererTemplateContentName = portals.current.get(templateName); //default to itself if no remapping is given if (!rendererTemplateContentName) { rendererTemplateContentName = templateName; } var rendererTemplateContent = contents.findByName(rendererTemplateContentName); if (!rendererTemplateContent) { throw new Error("Renderer template content could not get resolved - " + rendererTemplateContentName); } var category = resolver.get("category"); var title = resolver.get("title"); var linkTitle = resolver.get("linkTitle"); var url = resolver.get("url"); var price = resolver.get("price"); var currency = resolver.get("currency"); var imageId = resolver.get("imageId"); var maxImageWidth = resolver.get("maxImageWidth"); var maxImageHeight = resolver.get("maxImageHeight"); var description = resolver.get("description"); var shortDescription = resolver.get("shortDescription"); //set binding for attributeList resolver.bindings.attribute = category, title, linkTitle, url, price, currency, imageId, maxImageWidth, maxImageHeight, description, shortDescription; return rendererTemplateContent.content; };
Create template
<div class="container"> <div class="row"> <div class="col-sm-6"> <p>Custom template</p> <h4>#{rvwget('title')}</h4> <h4>#{rvwget('description')}</h4> <a href="#{rvwget('url')}"> <img class="img-responsive" src="/static/image/get?id=#{rvwget('imageId')}&size=#{rvwget('maxImageWidth')}x#{rvwget('maxImageHeight')}" alt="#{rvwget('title')}" /> </a> <p>ab #{rvwget('price')} #{rvwget('currency')}</p> <p> <a class="btn btn-primary" href="#{rvwget('url')}"> <span class="fa fa-chevron-circle-right"></span> #{rvwget('linkTitle')}</a> </p> </div> </div> </div>