6 Custom Widgets. Guide to Widget developers - Reference Documentation
Authors: Kaleidos Open Source
Version: 0.6.4
6 Custom Widgets. Guide to Widget developers
Besides the built in widgets, you can create your own custom widgets.Edit your appConfig.groovy
and add this line for a domain class and property, where you define
the custom widget class and some properties to configure.Example:grails.plugin.admin.domain.Conference = { widget "coordinates", "sample.MapWidget", height:400, width:600 }
Test
and property address
a custom widget call
MyTestWidget
. Some properties could be specified and will we copied directly to HTML component.
Groovy side
Inherit from Widget
Your custom widget should inherit from the class net.kaleidos.plugins.admin.widget.Widget. That is an abstract class, that has only one methods that you must implement:- String render(): The method to render your widget as html
- String renderBeforeForm(): Render code html before the form that contains all the widgets
- String renderAfterForm(): Render code html after the form that contains all the widgets
- Object getValueForJson(): Returns an object (usually an string) that whould be used in order to represent the current value of the widget when the widget is represented as JSON.
- List<Map> getAssets(): Returns the list of assets (css and js) needed for the html representation of this widget. The list should be a Map containing the same elements that the tag "g:resource" accepts http://grails.org/doc/latest/ref/Tags/resource.html.
- Object value: The current value of the widget. Its type will depend on the actual widget. For example, on a TextInputWidget will be a String, and on DateInputWidget will be a Date.
- Map htmlAttrs: A map of properties to be rendered on the html representation, like the name, if it is required, etc.
- Map internalAttrs: A map of properties internal for the widget. Remember that the widget represents a property of a domain object, so:
- domainObject: This is that domain object. Note that it could be null (for example, on the create screen)
- propertyName: This is the name of that property.
- domainClass: This is the class of that object.
- relatedDomainClass: If the property is a relation with another domain class, this is that domain class. It could be null.
Error handling
If you want you can handle errors (expected or unspected) thrown by your widget. You should extend the method.String renderError(Throwable t)
Sample
With all this, lets make a sample Widget. We will create a custom widget that show an addres on a map. It will be used for properties of type String.Create the Widget ClassWe will create our widget on the file src/groovy/sample/MapWidget.groovypackage sampleimport net.kaleidos.plugins.admin.widget.Widgetclass MapWidget extends Widget { @Override String render() { return "" } }
String render() { def html = new StringBuilder() html.append("<div>") html.append("<iframe width='425' height='350' frameborder='0' scrolling='no' marginheight='0' marginwidth='0'") html.append("src='https://maps.google.com/maps?f=q&q=${value}&output=embed'") html.append("></iframe>") html.append("</div>") return html }
String render() { def html = new StringBuilder() html.append("<div>") html.append("<iframe width='425' height='350' frameborder='0' scrolling='no' marginheight='0' marginwidth='0'") html.append("src='https://maps.google.com/maps?f=q&q=${value}&output=embed'") html.append("></iframe>") html.append("</div>") html.append("<input class='form-control' type='text' ") htmlAttrs.each{key, value -> html.append("$key='${value}' ") } html.append("value='${value}' />") return html }
When you write html as this example, there is a risk of an XSS attack (for example, if the value is something like "<script>alert(0)</script>). So, instead of write the value directly, you should write it as value.encodeAsHTML(). Also, you should be carefull with the null values.So you can write rhe values as${value?:value.encodeAsHTML():''}
Frontend side
Most widgets won't need special css nor js. But if you need them, you can use them you can specify a custom method getAssets on the widget.Sample
We will upgrade our MapWidget with a button that refresh the map with the current address. In order to do so, we will use a js and a css file.List<Map> getAssets() { return [ [ dir: "js", file: "admin/map.js", absolute: true ], [ dir: "css", file: "admin/map.css", absolute: true ] ] }
String render() { def html = new StringBuilder() html.append("<div class='map-widget'>") html.append("<div>") html.append("<span class='map-container'>") html.append("<iframe width='425' height='350' frameborder='0' scrolling='no' marginheight='0' marginwidth='0'") html.append("src='https://maps.google.com/maps?f=q&q=${value}&output=embed'") html.append("></iframe>") html.append("</span>") html.append("<input type='button' class='map-widget-refresh js-map-widget-refresh' value='Refresh' />") html.append("</div>") html.append("<div>") html.append("<input type='text' class='form-control js-map-widget-text' ") htmlAttrs.each{key, value -> html.append("$key='${value}' ") } html.append("value='${value}' />") html.append("</div>") html.append("</div>") return html }
.map-widget-refresh { background-color: #4040EA; border: 0 none; color: #FFFFFF; margin-bottom: 10px; margin-left: 15px; padding: 5px; }
$(".map-widget").on( "click", ".js-map-widget-refresh", function(event) { var value = $(this).closest(".map-widget").find(".js-map-widget-text").val(); var html = "<iframe width='425' height='350' frameborder='0' scrolling='no' marginheight='0' marginwidth='0'"; html += "src='https://maps.google.com/maps?f=q&q=" + value + "&output=embed'></iframe>"; $(this).closest(".map-widget").find(".map-container").html(html); });
NOTE: GrailsAdminPlugin uses jquery, so it is available for your custom widgets also
More Frotend features
To customize the widget resources you must override the getAssets method@Override List<Map> getAssets() { return [ [ dir: "mywidget", file: "example1.js", absolute: true ], [ dir: "mywidget", file: "example2.js", absolute: true ], [ dir: "mywidget", file: "example1.css", absolute: true ], [ dir: "mywidget", file: "example2.css", absolute: true ], [ dir: "mywidget/templates", file: "template1.handlebars", absolute: true ], [ dir: "mywidget/templates", file: "template2.handlebars", absolute: true ], ] }
GrailsAdmin Javascript utils
GrailsAdmin has views & services that you can use in your widget if you wantViews//js file //creating view app.view('formView', ['$el'], function ($el) { //view code });//html file // call view <div view='deleteModal'></div>
//create
app.service('paginationService', [], function () {
//service code return {
method: function () {
//method code
}
};
});//use
app.view('formView', ['$el', 'paginationService'], function ($el, paginationService) {
paginationService.method();
});
Javascript libraries
parsley bootstrap bootstrap-datepicker handlebars jQuery lodash select2HandlebarsIf you add handlebars in your assets grailsAdmin creates a script tag at the end of page with the content of the handlebars file. The id of the script tag is the filename without the extension<script id="filename" type="text/x-handlebars-template"> <h1>handlebars template</h1> <p>{{text}}</p> </script>
app.view('formView', ['$el', 'templateService'], function ($el, templateService) { var templateVars = {title: 'exaple var'}; var html = templateService.get('filename', templateVars); });