I have a requirement to create breadcrumb in sightly. I have following code which is working fine in JSP. But i am struggling to convert the code to sightly because i am not getting the right methods in currentStyle object to get the "absParent" and others. Any help will be highly appreciated!!
<%# include file="/libs/foundation/global.jsp" %>
<%
final int startLevel = currentStyle.get("absParent", 3);
final int endLevel = currentPage.getDepth() - currentStyle.get("relParent", 0);
final int minItems = currentStyle.get("minItems", 2);
if (startLevel <= endLevel - minItems) {
%><section class="breadcrumbs"><%
for (int level = startLevel+1; level < endLevel; ++level) {
Page itemPage = currentPage.getAbsoluteParent(level);
if (itemPage == null || !itemPage.isValid() || itemPage.isHideInNav()) {
continue;
}
final String pagePath = itemPage.getPath() + ".html";
final String pageTitle = itemPage.getNavigationTitle();
String className = "breadcrumb-item-"+level;
if (level == startLevel) className += " breadcrumb-first";
if (level == endLevel-1) className += " breadcrumb-last";
pageContext.setAttribute("className", className);%>
<section class="breadcrumbs ">
<%= xssAPI.encodeForHTML(pageTitle) %>
</section>
<%} %>
</section><%
}
%>
To create breadcrumb you have to write a WCMuse class and include that in this component.
<div
data-sly-use.breadcrumb="${'com.mySite.components.BreadcrumbUse'}">
<!-- + Breadcrumb component + -->
<div class="breadcrumb component">
<div class="breadcrumb_nav_bar clearfix"
data-sly-test="${breadcrumb.navList}"
data-sly-list.element="${breadcrumb.navList}">
<p data-sly-test="${!elementList.last}">
<a href="${element.path}.html">${element.title ||
element.navigationTitle || element.name}</a>
</p>
<p data-sly-test="${elementList.last}">${element.title ||
element.navigationTitle || element.name}</p>
</div>
</div>
<!-- - Breadcrumb component - -->
</div>
Code Sample for WCMUse class:
Public class BreadcrumbUse extends WCMUse
{
private List<Page> navList = new ArrayList<Page>();
#Override
public void activate() throws Exception
{
setBreadCrumbItems();
}
private void setBreadCrumbItems()
{
long level = 4L;
long endLevel = 1L;
int currentLevel = getCurrentPage().getDepth();
while (level < currentLevel - endLevel)
{
Page trailPage = getCurrentPage().getAbsoluteParent((int) level);
if (trailPage == null)
{
break;
}
this.navList.add(trailPage);
level++;
}
}
public List<Page> getNavList()
{
return this.navList;
}
}
The below code will work for creating breadcrumbs in AEM6.2 using Javascript and HTL(previously sightly).It worked well for me..here we go
Javascript to be used in the server side script(it can also be created using java)
script.js
use(function () {
var title = currentPage.getTitle();
//To get the title of the current page
var level = currentPage.getDepth();
//To find the depth of the current page from the root
var cts = new Array();
// To store the traversed page (object) from the root
for(var i=1;i<level;i++)
{ // Here I used i=1 for mycase(i=0 will be /content)
var titl = currentPage.getAbsoluteParent(i);
//To get the absolute parent at each level from root
pageStack.push(titl);
//Stack to maintain the pages
}
return {
title: title,
pageStack:pageStack
};
});
Breadcrumbs.html
<sly data-sly-use.cpt="script.js">
<h1>${cpt.title}</h1>
<div data-sly-list="${cpt.pageStack}">
<span> ${item.title}/</span>
</div>
</div>
Thus we get the breadcrumbs ready for our presentation!!!
Related
I can't seem to figure out the right way to get the target object. Here is the code front end code that is supposed to have drag and drop functionality:
<div class="pos-top">
<div id="imgGrid" class="grid bg-white" style="grid-template-columns:repeat(#columnCount, #(unitWidth)); grid-auto-flow:dense;">
#foreach (var b in blocks)
{
#if ((b.Placement[_pageType].Visible == true || showHiddenBlocks == true) && b.Zones != null && b.Zones.Contains(zone))
{
var id = b.Id;
<div class="col-span-#b.ColSpan row-span-#b.RowSpan relative border border-black box-border #(b.isDragOver?"dropped":"")" style=" order:#b.Placement[_pageType].Order;" #ondblclick="(() => ChangeBlockProperties(b))"
draggable="true"
data-block-id="#b.Id"
id="#id" #ondrop="#((e) => ondropOver(b, blocks.ToList()))"
ondragover="event.preventDefault();"
#ondragstart="#((e) => ondragstart(e, b, blocks.ToList()))"
#ondragenter="#(() => ondragenter(b))"
#ondragleave="#(() => { b.isDragOver = false; })"
#ondragend="#(() => ondragend(b, blocks.ToList()))">
<div class="blockNumberInfo absolute rounded-full bg-red-700 flex justify-center items-center border-2 border-black"
style="width:40px;height:20px">
#b.LocalIndex / #b.Placement[_pageType].Order
</div>
</div>
}
}
</div>
}
}
</div>
This is my code that is moving the blocks around:
public async void ondropOver(FlyerBlock item, List<FlyerBlock> flyerBlocks)
{
DragEnter = null;
if (DraggedItem == null) return;
if (DraggedItem == item) return;
DraggedItem.Placement[_pageType].Order = item.Placement[_pageType].Order;
await BlockplacementEditService.SaveBlockChangesAsync(flyerBlocks, DraggedItem, DraggedItem.Placement[_pageType].Order, _pageType, zone);
DraggedItem = null;
item.isDragOver = false;
//StateHasChanged();
}
public void ondragstart(DragEventArgs e, FlyerBlock item, List<FlyerBlock> flyerBlocks)
{
e.DataTransfer.EffectAllowed = "move";
DraggedItem = item;
DraggedItemPosition = item.Placement[_pageType].Order;
}
public void ondragenter(FlyerBlock item)
{
item.isDragOver = true;
DragEnter = item;
}
public async void ondragend(FlyerBlock item, List<FlyerBlock> flyerBlocks)
{
item.isDragOver = false;
if (DraggedItem == null) return;
DragEnter = null;
// item.isDragOver = true;
}
The issue I am running into is that I dont know how to pass the targetblock into my drop over event. I tried using DragEventArgs but that does not work I have also tried using jsruntime but I can only extract the dragged block. I want to be able to get the target block so I can update the order correctly. Any help would be appreciated!
I am building a two-level menu for my application using wicket framework. Using ListView#populateItem is redrawing the markup (SubMenuPanel.html) and unable to display menu items which were buried inside markup tags in SubMenuPanel.html. Code below -
html:
<div wicket:id="navMenu"></div>
Main.java:
NavigationMenu navigationMenu = new NavigationMenu("navMenu", navigationMenuAdapter);
add(navigationMenu);
NavigationMenu.java
public NavigationMenu(final String id, final NavigationMenuAdapter adapter)
{
super(id);
this.adapter = adapter;
add(new SubMenuPanel("mainMenu", adapter.getNavigationMenus(this)));
}
SubMenuPanel.java
public SubMenuPanel(final String id, List<NavigationMenuItem> list)
{
super(id);
add(new Rows("firstLevel", list));
setVersioned(false);
}
private static class Rows extends ListView<NavigationMenuItem>
{
private static final long serialVersionUID = 1L;
public Rows(String name, List<NavigationMenuItem> list)
{
super(name, list);
}
#Override
protected void populateItem(ListItem<NavigationMenuItem> listItem)
{
Object modelObject = listItem.getDefaultModelObject();
WebMarkupContainer rowAnchor = new WebMarkupContainer("firstLevelLink");
rowAnchor.add(new Label("firstLevelLabel", ((NavigationMenuItem) modelObject).getName()));
rowAnchor.add(new AttributeAppender("onclick", new Model("handleMenuClick()");
listItem.add(rowAnchor);
listItem.add(new SubMenuPanel("secondLevelMenu", ((NavigationMenuItem) modelObject).getChildMenuItem()));
}
}
SubMenuPanel.html
<body>
<wicket:panel>
<li wicket:id="firstLevel">
<a wicket:id="firstLevelLink" class="dropdown-toggle" aria-expanded="true" data-toggle="dropdown" href="javascript:void(0);" >
<span wicket:id="firstLevelLabel"></span>
</a>
<ul wicket:id="secondLevelMenu" class="dropdown-menu">
</ul>
</li>
</wicket:panel>
Working version
Non-working
I have added a Form on submit of which I have to add more wicket controls like Labels, textfields and button with an Ajex Link. But not able to get the correct HTML. Can anyone please help me to get rid of it ?
voucherPanel.html
<html xmlns:wicket>
<head>
</head>
<body>
<wicket:panel>
<div class="form-block">
<div wicket:id="form">
<wicket:message key="lbl.vouchercode" />
<div wicket:id="list">
<input wicket:id="word" type="text" />
</div>
<div wicket:id="vouchercode"></div>
<button wicket:id="submit"><wicket:message key="submitText"/></button>
</div>
</div>
</wicket:panel>
</body>
</html>
voucherPanel.java
public class VoucherPanel extends Panel
{
private static final long serialVersionUID = 1L;
public VoucherPanel(final String id)
{
super(id);
final TextField<String> voucherCodeField = new TextField<String>("vouchercode", Model.of(""));
voucherCodeField.setRequired(true);
final Button button = new Button("submit");
Form<?> form = new Form<Void>("form")
{
#Override
protected void onSubmit()
{
numberOfFields = new ArrayList<String>();
int noOfVocuhers = getNoOfAllowedVoucher();// just returing the number
for (int i = 0; i < noOfVocuhers; i++) {
numberOfFields.add(new String(""));
}
add(new ListView<Object>("list", numberOfFields) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(ListItem<Object> item) {
final String word = (String) item.getModelObject();
System.out.println( "word =" +word );
TextField<String> textField = new TextField<String>("word", Model.of(""));
textField.setOutputMarkupId(true);
item.add(textField);
}
});
}
}
};
add(form);
form.add(voucherCodeField);
form.add(button);
}
}
You're trying to assign a Textfield to a <div> element (voucherCode), you need <input type="text"> instead.
You need to add list to your form right away, onSubmit is too late. Just set it outside, similar to button and call myListView.setList when submitting the form.
These are the two things I spotted... if you still have problems please let us know about the error messages you get.
I have built a wicket component that contains input/labels and methods to change presentation (required, enabled, etc.). The components render fine, but what happens is when the form submits I see only 1 form parameter 'input', and it's the last InputRow component.
InputRow.html
<html xmlns:wicket="http://wicket.apache.org">
<head>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<wicket:panel>
<label wicket:id="label">abc: <span class="req">*</span></label>
<span class="input">
<input wicket:id="input" type="text" id="name"></input>
</span>
<span wicket:id="input_feedback"></span>
</wicket:panel>
</body>
</html>
InputRow.java
package com.wicket;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
public class InputRow extends Panel{
#SuppressWarnings("unused")
private String id;
public InputRow(String id, String label) {
super(id);
this.id = id;
Label memberIdLabel = new Label("label",label);
memberIdLabel.setEscapeModelStrings(false)
.add(new AttributeAppender("for", new Model<String>(id),""));
add(memberIdLabel);
TextField<String> name = new TextField<String>("input");
name.setType(String.class)
.setMarkupId(id)
.setOutputMarkupId(true);
add(name);
add(new Label("input_feedback",""));
}
public InputRow disable()
{
get("input")
.setEnabled(false)
.add(new AttributeAppender("class", new Model<String>("disabled"),""));
get("label")
.add(new AttributeAppender("class", new Model<String>("disabled"),""));
return this;
}
public InputRow required()
{
Model model = (Model)get("label").getInnermostModel();
StringBuffer label = new StringBuffer((String)model.getObject());
label.append(" <span class=\"req\">*</span>");
model.setObject(label);
((TextField)get("input")).setRequired(true);
return this;
}
#Override
protected void onBeforeRender() {
super.onBeforeRender();
Label feedback = (Label)get("input_feedback");
if (get("input").getFeedbackMessage() != null)
{
feedback.setDefaultModel(new Model<String>("Required"));
}
}
}
Adding to the form component
add(new InputRow("name","Name:").required());
edit
I didn't set up a ListView or repeater since I know what rows / fields I want to add to the form at build time.
Your InputFields are missing their models. This way, wicket doesn't know where to store the formdata. If you add models to the fields they will be populated automatically.
There's not just one form parameter submitted. The submits are of the named like name:input, name2:input, ...
But as Nicktar suggests in the comment you should use a model to bind the value of the form component to your entity object. You have to accept an IModel in the constructor and use it in the constructor of TextField.
A better approach to what you are trying to do is to write a Behavior which adds decorating markup for your FormComponent. That way it works for more than just simple text input fields and you can fully customize the instances of your FormComponents.
It could look like this:
public class FormComponentBehavior extends Behavior {
#Override
public void bind(Component component) {
if (!(component instanceof FormComponent)) {
throw new IllegalArgumentException();
}
}
#Override
public void beforeRender(Component component) {
FormComponent<?> fc = (FormComponent<?>) component;
Response r = component.getResponse();
r.write("<label" + (fc.isRequired() ? " class='required'" : "") + ">");
r.write(fc.getLabel().getObject());
r.write("</label>");
r.write("<span class='input'>");
}
#Override
public void afterRender(Component component) {
component.getResponse().write("</span>");
// if feedback errors write them to markup...
}
}
Then you have to add this behavior to your FormComponent instances.
Maybe the problem with your form is that your input text fields have all the same id. Try using attribute 'name' instead of 'id'
I have used html.ValidationSummary to get all errors displayed on top of the page.
This will render list with errors on top of the page.
Example:
<ul>
<li>UserName is invalid</li>
</ul>
I have how ever need to render every item instead of list as custom div with additional html tags inside.
I need every line to be rendered as short example below (this is only one line):
<div>
<div class="right"><a href="#closeError">Close error</div>
<div class="right"><a href="#Update">Update Field</div>
<label>Error:</label> Name on the page is invalid.
</div>
What is your opininon how to achieve this rendering?
I have considered to create html helper where i will take ModelState and get all errors, but not sure this will work...
I have considered to create html helper where i will take ModelState and get all errors, but not sure this will work...
Why wouldn't that work?
public static class ValidationExtensions
{
public static IHtmlString MyValidationSummary(this HtmlHelper htmlHelper)
{
var formContext = htmlHelper.ViewContext.ClientValidationEnabled
? htmlHelper.ViewContext.FormContext
: null;
if (formContext == null && htmlHelper.ViewData.ModelState.IsValid)
{
return null;
}
var sb = new StringBuilder();
var htmlSummary = new TagBuilder("div");
var modelStates = htmlHelper.ViewData.ModelState.Values;
sb.AppendLine("<div class=\"right\"><a href=\"#closeError\">Close error</div>");
sb.AppendLine("<div class=\"right\"><a href=\"#Update\">Update Field</div>");
if (modelStates != null)
{
foreach (ModelState modelState in modelStates)
{
foreach (ModelError modelError in modelState.Errors)
{
var userErrorMessageOrDefault = GetUserErrorMessageOrDefault(modelError);
if (!string.IsNullOrEmpty(userErrorMessageOrDefault))
{
sb.AppendFormat("<label>Error:</label> {0}{1}", htmlHelper.Encode(userErrorMessageOrDefault), Environment.NewLine);
}
}
}
}
htmlSummary.InnerHtml = sb.ToString();
if (formContext != null)
{
formContext.ReplaceValidationSummary = true;
}
return MvcHtmlString.Create(htmlSummary.ToString(TagRenderMode.Normal));
}
private static string GetUserErrorMessageOrDefault(ModelError error)
{
if (!string.IsNullOrEmpty(error.ErrorMessage))
{
return error.ErrorMessage;
}
return null;
}
}
and then:
<%= Html.MyValidationSummary() %>