{"id":877,"date":"2017-02-21T08:53:23","date_gmt":"2017-02-21T07:53:23","guid":{"rendered":"http:\/\/tomaszdziurko.com\/?p=877"},"modified":"2017-02-21T08:53:23","modified_gmt":"2017-02-21T07:53:23","slug":"forcing-wicket-place-javascript-files-bottom","status":"publish","type":"post","link":"http:\/\/tomaszdziurko.com\/2017\/02\/forcing-wicket-place-javascript-files-bottom\/","title":{"rendered":"Forcing Wicket to place JavaScript files at the bottom"},"content":{"rendered":"
When you add any Wicket component library e.g. \u00a0Wicket-Bootstrap<\/a> to your project, usually it adds some JavaScript and CSS resources to every\u00a0page or panel. But the problem is that these JS files are added in the <head> section of your page\u00a0while all Internet knowledge says that\u00a0JS should be placed at the bottom of the page for a better performance and faster loading times. In this post I will show how we could configure Wicket to place JS files wherever we want.<\/p>\n Let’s start with a simple one-page-one-form Wicket application counting days from user’s birthday:<\/p>\n <\/p>\n It has a dependency to mentioned earlier Wicket-Bootstrap because we need its DatePicker component:<\/p>\n Our web page contains a form, one text field and one date picker:<\/p>\n When we run our application we will see that all Bootstrap related JS files are\u00a0placed in the page header:<\/p>\n It is not what we wanted, so we need to configure\u00a0our application to add JS files somewhere at the bottom.<\/p>\n First, let’s start with some background. Each time when a component wants to add something to page header (for example using IHeaderContributor<\/a>) it creates a new instance of HeaderItem <\/a>object. By default all HeaderItems are rendered in <head><\/em>, but we could modify this behaviour using\u00a0IHeaderResponseDecorator<\/a>\u00a0and\u00a0FilteringHeaderResponse<\/a>\u00a0to separate HeaderItems into two places to render: header and defined place in our page body.<\/p>\n First, we should define a place where all JS files should be rendered by adding a <wicket:container><\/em> element with id:<\/p>\n and add it to the Page class:<\/p>\n Next step is to configure a response decorator and response filter that will direct some items to our footer-container<\/em> bucket. This should be done in init()<\/em> method of our WicketApplication class:<\/p>\nInitial project setup<\/h2>\n
<dependency>\r\n <groupId>de.agilecoders.wicket<\/groupId>\r\n <artifactId>wicket-bootstrap-core<\/artifactId>\r\n <version>0.10.11<\/version>\r\n<\/dependency>\r\n<dependency>\r\n <groupId>de.agilecoders.wicket<\/groupId>\r\n <artifactId>wicket-bootstrap-extensions<\/artifactId>\r\n <version>0.10.6<\/version>\r\n<\/dependency><\/pre>\n
public class HomePage extends WebPage {\r\n \r\n\tprivate String name;\r\n\tprivate Date birthDate;\r\n\r\n\tpublic HomePage(final PageParameters parameters) {\r\n\t super(parameters);\r\n\r\n\t add(new FeedbackPanel(\"feedbackPanel\"));\r\n Form<HomePage> form = new Form<HomePage>(\"form\", new CompoundPropertyModel<>(this)) {\r\n @Override\r\n protected void onSubmit() {\r\n info(format(\"Hello %s. It's nice to meet you! You are %d days old!\", name, calculateDaysSinceBirthDay(birthDate)));\r\n }\r\n };\r\n \r\n form.add(new TextField(\"name\").setRequired(true).setLabel(Model.of(\"Name\")));\r\n form.add(new DateTextField(\"birthDate\", new DateTextFieldConfig()\r\n .autoClose(true)\r\n .withLanguage(\"pl\")\r\n .withFormat(\"dd\/MM\/yyyy\"))\r\n .setLabel(Model.of(\"Date of birth\"))\r\n .setRequired(true)\r\n );\r\n add(form);\r\n } \r\n\r\n private int calculateDaysSinceBirthDay(Date birthDate) {\r\n return Days.daysBetween(LocalDate.fromDateFields(birthDate), LocalDate.now()).getDays();\r\n }\r\n \r\n}<\/pre>\n
Problem<\/h2>\n
<head>\r\n <!-- ... -->\r\n <script type=\"text\/javascript\" src=\".\/wicket\/resource\/de.agilecoders.wicket.extensions.markup.html.bootstrap.references.BootstrapDatepickerJsReference\/js\/datepicker-ver-1484131947646.js\"><\/script>\r\n <script type=\"text\/javascript\" src=\".\/wicket\/resource\/de.agilecoders.wicket.extensions.markup.html.bootstrap.references.BootstrapDatepickerLangJsReference\/js\/lang\/bootstrap-datepicker.pl-ver-1484131947646.js\"><\/script>\r\n <script type=\"text\/javascript\" id=\"bootstrap-js\" src=\".\/wicket\/resource\/de.agilecoders.wicket.webjars.request.resource.WebjarsJavaScriptResourceReference\/webjars\/bootstrap\/3.3.7-1\/js\/bootstrap-ver-1484131946909.js\"><\/script>\r\n <!-- ... -->\r\n<\/head><\/pre>\n
Solution<\/h2>\n
<!DOCTYPE html>\r\n<html xmlns:wicket=\"http:\/\/wicket.apache.org\">\r\n\t<body>\r\n\t\t<!-- ... -->\r\n\t\t<wicket:container wicket:id=\"footer-container\"\/>\r\n\t\t\r\n\t<\/body>\r\n<\/html><\/pre>\n
add(new HeaderResponseContainer(\"footer-container\", \"footer-container\"));<\/pre>\n
@Override\r\npublic void init()\r\n{\r\n super.init();\r\n \r\n IBootstrapSettings settings = new BootstrapSettings();\r\n Bootstrap.install(this, settings);\r\n\r\n setHeaderResponseDecorator(new JavaScriptToBucketResponseDecorator(\"footer-container\"));\r\n}<\/pre>\n