Is it possible to use Wicket Fragments within an Extend component? - wicket

I'm trying to use Fragments within an Extend component but it throws an MarkupNotFoundException.
The code is similar to this:
BasePanel.html
<?xml version="1.0" encoding="UTF-8" ?>
<wicket:panel xmlns:wicket="http://wicket.apache.org">
<div wicket:id="dialog">
<div class="titlebar">
<h3 wicket:id="dialogTitle">[[ dialog title ]]</h3>
<div wicket:id="dialogCloseBtn" class="closeBtn right"></div>
</div>
<wicket:child />
</div>
</wicket:panel>
ChildPanel.html
<?xml version="1.0" encoding="UTF-8" ?>
<wicket:extend xmlns:wicket="http://wicket.apache.org">
<wicket:panel wicket:id="panel1" />
<wicket:fragment wicket:id="fragment1">
</wicket:fragment>
</wicket:extend>
ChildPanel.java
public class ChildPanel extends Panel
{
public ChildPanel(String id, IModel<?> model)
{
super(id, model);
add(new Fragment("panel1", "fragment1", this);
}
}
The problem resides in Wicket Markup.java class:
public final IMarkupFragment find(final String id)
{
Args.notEmpty(id, "id");
MarkupStream stream = new MarkupStream(this);
stream.setCurrentIndex(0);
while (stream.hasMore())
{
MarkupElement elem = stream.get();
if (elem instanceof ComponentTag)
{
ComponentTag tag = stream.getTag();
if (tag.isOpen() || tag.isOpenClose())
{
if (tag.getId().equals(id))
{
return stream.getMarkupFragment();
}
if (tag.isOpen() && !tag.hasNoCloseTag() && !(tag instanceof WicketTag) &&
!"head".equals(tag.getName()) && !tag.isAutoComponentTag())
{
stream.skipToMatchingCloseTag(tag);
}
}
}
stream.next();
}
return null;
}
When the application iterates over the stream it looks in the BasePanel markup. After looking open tag <div wicket:id="dialog"> it iterates to the closing tag </div>, so it didn't look inside the ChildPanel markup.
Do you have any suggestion or solution?
Thanks,
Manuel

There is an opened jira issue related with this topic. You could follow instructions to solve it here: https://issues.apache.org/jira/browse/WICKET-4545.

You can declare a <wicket:fragment> inside <wicket:extend>, but not a <wicket:panel> (panel1). Try using <wicket:container> instead.

Related

How to make a parent invisible with Ajax in Wicket?

Last cause: The component(s) below failed to render. Possible reasons could be that:
1) you have added a component in code but forgot to reference it in the markup (thus the component will never be rendered),
2) if your components were added in a parent container then make sure the markup for the child container includes them in <wicket:extend>.
1. [WebMarkupContainer [Component id = child, page = com.test.TestPage, path = parent:child, type = org.apache.wicket.markup.html.WebMarkupContainer, isVisible = true, isVersioned = true]]
I'm trying to hava a WebMarkupContainer that can be made invisible by clicking their child, which is also a WebMarkupContainer. I get the exception above and I don't understand what to change to make it work.
My code is the following:
#MountPath("test")
public class TestPage extends WebPage {
public TestPage(PageParameters parameters) {
super(parameters);
IModel<Boolean> isVisible = Model.of(false);
WebMarkupContainer parent = new WebMarkupContainer("parent"){
#Override
public boolean isVisible() {
return !isVisible.getObject();
}
};
parent.setOutputMarkupId(true);
add(parent);
WebMarkupContainer child = new WebMarkupContainer("child");
child.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
isVisible.setObject(true);
target.add(parent);
}
});
parent.add(child);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head>
</head>
<body>
<wicket:extend>
<div wicket:id="parent"
style="height: 300px; width: 100%; background-color: mediumvioletred; padding: 10px; margin: 70px;">
<div wicket:id="child"
style="height: 200px; width: 200px; color: cadetblue; background-color: rebeccapurple">
click me
</div>
</div>
</wicket:extend>
</body>
</html>
I don't know exactly what's going on with the isVisible override, but it's causing the odd behavior.
Try this instead:
#Override
protected void onConfigure() {
super.onConfigure();
setVisible(isVisible.getObject());
}
This will be called only once per lifecycle, while isVisible is called multiple times.
You need to remove <wicket:extend> from the HTML! Your page does not extend from another page that uses <wicket:child/>.
Extra notes:
It is not very clear what you want to do. You say want to make visible but it starts as visible and click on the child will make it invisible due to the negation in
public boolean isVisible() {
return !isVisible.getObject();
}
Does it fail to render the initial page or after the Ajax click ?
Important: when changing visibility with Ajax always use parent.setOutputMarkupPlaceholderTag(true); instead of parent.setOutputMarkupId(true); because otherwise you won't be able to make it visible again - there won't be an HTML DOM element to be replaced with the given id.

how i can can get the id weird issue it is in wicket 1.4

I am facing a weird issue it is in wicket 1.4.
We have a ReservationDateField which is extended from Datefield.
The problem is that in datefield, I have an input Datetext field and it has an id generated by wicket, which I need to change.
I need to change the markupId for the input tag; please see the photo:
Here is the HTML:
<div class="arena-reservation-from">
<div class="arena-form-content" wicket:id="validFromDateContainer">
<label class="arena-field" wicket:for="validFromDate"><wicket:message key="ReservationValidFrom.label"/></label>
<label class="arena-input-text" wicket:id="validFromDate"></label>
<p id="arena-add-reservation-validFromDate-error-msg" class="arena-input-error-msg" aria-hidden="true" />
</div>
The java is here :
private void addFromDateFieldContainer() {
DateField field = new ReservationDateField("validFromDate");
WebMarkupContainer container = new
WebMarkupContainer("validFromDateContainer");
container.add(field);
container.setVisible(configParams.isShowReservationFromDate());
add(container);
}
You need to override newDateTextField(String id, PropertyModel dateFieldModel)
DateField field = new ReservationDateField("validFromDate") {
#Override
protected DateTextField newDateTextField(String id, PropertyModel dateFieldModel) {
DateTextField dateTextField = DateTextField.forShortStyle(id, dateFieldModel);
dateTextField.setMarkupId("mySpecialId");
return dateTextField;
}

AureliaJS - Call child function from parent

I'm using AureliaJS to build a dynamic forms scenario, where I have a parent form with the gross operations needed and multiple child's form's, that change based on user input.
These child's form's have only two specific things themselves. Their model and the validation rules for their model.
So my question is, how can the parent form call the validation rules from the current child form? From child I know that is possible call parent's view model. But from parent, how can I invoke any function from the child?
The scenario is similar off having one base class, that has one method and this method could be overriding on the child classes.
Any suggestion? I'm glad to change the approach if needed.
Here's an example: https://gist.run?id=1865041a15af60600cb7b538018bdccd
app.html
<template>
<span>This is an APP</span>
</p>
<compose view-model.bind="'parentForm'"></compose>
</template>
app.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class App {
}
childForm1.html
<template>
<label> Price : </label>
<input value.bind="model.data.price">
<p/>
<label> VAT : </label>
<input value.bind="model.data.vat">
<p/>
</template>
childForm1.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm1 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.price != '' && this.model.data.vat == '' )
this.model.validateMessage = 'VAT is mandatory';
}
}
childForm2.html
<template>
<label>Address : </label>
<input value.bind="model.data.address">
<p/>
<label>Phone : </label>
<input value.bind="model.data.phone">
<p/>
</template>
childForm2.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm2 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.phone != '' && this.model.data.address == '' )
this.model.validateMessage = 'Address is mandatory';
}
}
index.html
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
parentForm.html
<template>
<button click.delegate="changeChildForm1()">Change Child Form 1</button>
<button click.delegate="changeChildForm2()">Change Child Form 2</button>
<p/>
<p/>
<form>
<label>User : </label>
<input value.bind="model.data.user">
<p/>
<compose view-model.bind="childFormVM" model.bind="model"></compose>
<button click.delegate="save()">Save</button>
<p/>
<span> Validation Message : ${model.validateMessage}</span>
</form>
<p/>
<span>Price : ${model.data.price}</span><p/>
<span>Vat : ${model.data.vat}</span><p/>
<span>Phone : ${model.data.phone}</span><p/>
<span>Address : ${model.data.address}</span><p/>
</template>
parentForm.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ParentForm {
model = {
validateMessage : '',
data : {
user : 'My Name'
}
};
childFormVM = 'childForm1';
validateMessage = '';
changeChildForm1() {
this.childFormVM = 'childForm1';
}
changeChildForm2() {
this.childFormVM = 'childForm2';
}
save(){
this.validateRules();
// How to call the validation rules from child ?
}
validateRules (){
this.model.validateMessage = 'Validate by parent';
}
}
Bind a function call to the child so that you have a handle to invoke it from the parent. I usually prefer to directly bind the child components rather than using compose, but you can make it work with compose by passing a complex model object rather than only the model, and passing the binding function as one of the model properties.
Parent View-Model:
class Parent {
model = {};
child1Validate = null;
changeChildForm1() {
if (typeof this.child1Validate === 'function') {
// the binding was successful; proceed with function call
let result = this.child1Validate();
console.log(result);
}
}
}
Parent View:
<my-child1 model="parentModel" go-validate="child1Validate"></my-child1>
Child View-Model:
class MyChild1 {
#bindable model;
#bindable goValidate;
bind() {
// bind the child function to the parent that instantiates the child
this.goValidate = this.runValidation.bind(this);
}
runValidation() {
// do the validation and pass result to parent...
return 'Success!';
}
}
That's how you can do it:
parent-form.html
<compose view-model.bind="childFormVM" view-model.ref="childFormInstance" model.bind="model"></compose>
parent-form.js
save() {
this.childFormInstance.currentViewModel.validateRules();
}
Helpful Notes
Only use <compose> when necessary. For example, in the app.html you should replace <compose> for:
<require from="parentForm"></require>
<parent-form></parent-form>
Use kebab-case instead of camel-case to name your files. For example, instead of parentForm.html and parentForm.js, use parent-form.html and parent-form.js. This won't change a thing in your code but you will be following nice javascript standards :)
When binding directly to a string you don't need to use .bind. For example, view-model.bind="'parentForm'" could be replaced for view-model="./parentForm"
Hope this helps!
One thing that immediately comes to mind is that you can inject the parent model into your child model in the constructor -- the injected instance will be the same, not a newly created one. This way, your parent can define a method that allows the child to register itself on the parent, and the parent can then invoke whatever methods exist on the child at the time of its choosing.
This creates a rather strong coupling between the components, though, so you will need to consider whether or not that is acceptable to you.
If it isn't, another way to approach the issue is to use the event aggregator. The parent form can dispatch an event on the aggregator, and the children will be subscribers listening for the event. In this case, depending on whether or not you host multiple such combinations on one page, you may want to include a unique identifier for the form that is sent along with the event and bind that ID to the child components, so they will know to only listen for events from their parent.

jsTree "select_node" returns false

I am using jsTree in angularjs and using "select_node" in "ready". This method is returning false. On code debugged, it is observed that tree.instance._model.data doesn't have that node so this.get_node(obj); returns false. (below is code snippet)
select_node : function (obj, supress_event, prevent_open, e) {
var dom, t1, t2, th;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.select_node(obj[t1], supress_event, prevent_open, e);
}
return true;
}
obj = this.get_node(obj); //here it returns false
if(!obj || obj.id === '#') {
return false;
}
}
I am not sure why tree.instance._model.data doesn't have data at that time because it often works when I refresh the browser.
Any help? Below is my code snippet.
me.onTreeReady = function (eve, tree) {
tree.instance.deselect_all();
tree.instance.refresh(true, true);
var response = tree.instance.select_node(defaultNode);
}
This must be some kind of race condition related to your angular directive provider.
This issue has been resolved. I had added "id" to html, so when multiple instances were created with same id it gives the above error. Issue resolved by removing this id.
Old Code
<div class="row no-margin" cg-busy="ctrl.promise">
<div class="bg-white">
<div **id="treeView"** js-tree="ctrl.treeConfig"
should-apply="ctrl.applyModelChanges()"
ng-model="ctrl.treeData"
tree="ctrl.treeInstance"
tree- events="ready:ctrl.onTreeReadyEvent;select_node:ctrl.onNodeSelectEvent;deselect_node:ctrl.onNodeDeselectEvent">
</div>
</div>
</div>
New Code:
<div class="row no-margin" cg-busy="ctrl.promise">
<div class="bg-white">
<div js-tree="ctrl.treeConfig"
should-apply="ctrl.applyModelChanges()"
ng-model="ctrl.treeData"
tree="ctrl.treeInstance"
tree- events="ready:ctrl.onTreeReadyEvent;select_node:ctrl.onNodeSelectEvent;deselect_node:ctrl.onNodeDeselectEvent">
</div>
</div>
</div>

CQ5/AEM6/Sightly - Return custom type from Java Use-Api

Using JavaScript Use-Api I am able to create a custom object and return it to a html file. This feature allows me to create a list of custom objects, which can be used to create a menu or other complex list-like component.
Let's assume that I have following content structure:
/content
/project
/homepage
/contentpage1
/contentpage1.1
/contentpage1.2
/contentpage1.3 (hidden)
/contentpage2
/contentpage1.1 (hidden)
/contentpage1.2 (hidden)
/contentpage1.3 (hidden)
/contentpage3
/contentpage4
Menu should contains only first-level contentpages. Each menu item should have dropdown list with second-level contentpages, if they exist and are not hidden. I can do it in JavaScript with the following code:
"use strict";
use(function() {
function getMenuItems() {
var currentPageDepth = currentPage.getDepth();
var menuObjects = [];
if(currentPageDepth >= 3) {
var homePage = currentPage.getAbsoluteParent(2);
var list = homePage.listChildren();
while(list.hasNext()) {
var tempPage = list.next()
var customPageObject = createMenuItemObject(tempPage);
menuObjects.push(customPageObject);
}
}
return menuObjects;
}
function createMenuItemObject(page) {
// ...
// looking for any other properties of page or its children
// ...
return {page: page,
visibleChildrenExists: visibleChildrenExists(page)};
}
function visibleChildrenExists(page) {
var list = page.listChildren();
var visibleChildrenExists = false;
while(list.hasNext()) {
var subPage = list.next();
if(!subPage.isHideInNav()) {
visibleChildrenExists = true;
break;
}
}
return visibleChildrenExists;
}
return {
menuObjectsList: getMenuItems(),
};
}
HTML:
<headerComponent data-sly-use.headerComponentJS="headerComponent.js" data-sly-unwrap />
<menuItems data-sly-list.menuItem="${headerComponentJS.menuObjectsList}" data-sly-unwrap >
<li class='${menuItem.visibleChildrenExists ? "" : "direct"}' data-sly-test="${!menuItem.page.hideInNav}">
${menuItem.page.title}
<ul data-sly-test="${menuItem.visibleChildrenExists}" data-sly-list.submenuItem="${menuItem.page.listChildren}">
<li data-sly-test="${!submenuItem.hideInNav}">
${submenuItem.title}
</li>
</ul>
</li>
</menuItems>
Why do I want to use Java Use-Api? It's easier to operate on interfaces like Resource or Node. It looks like it does not work pretty well in JavaScript, but I need to have possibility to return custom objects with multiple properties.
The question is: is it even possible to do something similar using Java Use-Api? What do I have to return? I can't return a map, because it won't be possible to access its elements since it's not possible to pass a parameter to Java Use-Api method... Any suggestion?
It is possible to return maps using the java-use api see an example below:
Method in the Java class
//Return a map
public Map<String, String> getTestMap() {
//TODO some coding
Map<String,String> testMap = new HasMap<String,String>();
testMap.put("IDA", "test value");
testMap.put("IDB", "test value 2");
return testMap;
}
HTML code to access each element of the map:
<div data-sly-use.param="JavaClass">
<div data-sly-test.map="${param.testMap}">
<div class="pos">
<span class="classA">${map['IDA']}</span><br>
<span class="classB">${map['IDB']}</span>
</div>
</div>
</div>