AEM - Multifield Add Item by default - adobe

I am using multifield of type pathfield in my dialog. To add new path field, user has to click Add Item link.
Is it possible to display at least one path field in the dialog by default (without clicking Add Item)?

Yes, it is possible. There was a similar question for which I have answered it here.
However, for future references, providing the answer here too.
You can listen to the loadcontent event fired by the Multifield after the content has been loaded and use the addItem() method.
<paths
jcr:primaryType="cq:Widget"
fieldLabel="Select Paths"
name="./paths"
xtype="multifield">
<fieldConfig
jcr:primaryType="nt:unstructured"
xtype="pathfield" />
<listeners
jcr:primaryType="nt:unstructured"
loadcontent="function(field, record)
{
if(record.get('paths') == undefined)
{
field.addItem(); field.doLayout();
}
}" />
</paths>

Related

What is the difference between OnClick and #onclick in blazor components

I found out something curious and I am wondering if anyone knows the answer:
First of all this is not this question:
Different method calls in Blazor
That question refers to HTML elements. I am talking about Components.
So I have my own component named MyButton; and it has OnClick Parameter specified:
MyButton.razor
<button #onclick="OnClick">Do Something</button>
#code {
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
When I use MyButton I can use either the name exactly, i.e.
<MyButton OnClick="SomeMethod" />
But this is also working:
<MyButton #onclick="SomeMethod" />
When I remove the whole #code block from the MyButton.razor they both give me the exact same error message:
<Mybutton OnClick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'OnClick'.
<Mybutton #onclick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'onclick'.
The only difference is in the caps; "OnClick" vs "onclick"... that is logical. So it seems #onclick is the same as OnClick... but are they?
Is this simply an overload of some sorts?
#onclick is the native HTML click event and OnClick is the event parameter you explicitly expose in your MyButton component.
So in this case you should use
<MyButton OnClick="SomeMethod" />
I tried to reproduce the usage with #onclick but it didn't work in my case.
For science, you could try to add a text <p>Like this</p> to your MyButton component and see if the #onclick event still works and if it only fires if you click the button or also if you click the text.
Also, see Microsoft Docs for detailed information.
Do you use MudBlazor ? Because I think OnClick is part of the MudBlazor Button API and #onclick is part of the ASP.NET Core Blazor event handling features.

Widget dialogue that creates new nodes - Adobe CQ5

I'm trying to create an edit widget for a page that asks for various details and then stores those details as properties of a newly created node.
So for instance:
Make
Model
Mileage
and I want to store those as a node under a parent node in /content/cars or something similar.
How can I get a widget to create a new node under /content/cars and store those properties there?
Do I have to send the data to a servlet that I create? Or are there options to enable this?
What you'd probably want to do is include a component in the containing page at the path you want the nodes created.
For example, if you have a page /content/cars, and you wanted to create a Chevy node under there at /content/cars/jcr:content/chevy, you'd include the component with this fragment:
<cq:include path="chevy" resourceType="myapp/components/manufacturer" />
This would include the content and render it and allow it to be edited with the component located at /apps/myapp/components/manufacturer.
If you want you can even have the node be at an absolute path, for example if you want to reference the content at /content/data/cars/chevy on another page you could use:
<cq:include path="/content/data/cars/chevy" resourceType="myapp/components/manufacturer" />
Based on the provided cheatsheet from Adobe:
http://dev.day.com/content/ddc/blog/2008/07/cheatsheet/_jcr_content/par/download/file.res/cheatsheet.pdf
The default Sling POST servlet recognizes what JCR properties to update by examining the 'name' attribute of the input tags that are included in the ExtJS dialogs. To configure a widget like a textfield or dropdownfield to target and update a specific property, you will need to specify the property's name in the widget definition.
For example, if you are using an XML file to define your page dialog at /content/cars and you wished for there to be widgets that let users store model, make, and year to the jcr:content node of the cars page. Your associated dialog definition would contain nodes that look like:
<make
jcr:primaryType="cq:Widget"
xtype="textfield"
name="./make"/>
<model
jcr:primaryType="cq:Widget"
xtype="textfield"
name="./model"/>
<year
jcr:primaryType="cq:Widget"
xtype="textfield"
name="./year"/>
To change the widget type you would adjust the xtype attribute and to change the property to target you would adjust the name attribute. Note that it is relative from the content node of the page/component where the dialog is being invoked (in this case, relative from /content/cars/jcr:content).
Another example: say you wanted to store the value of make to a property called 'make' on a node like /content/cars/jcr:content/brands, then your definition would look like:
<make
jcr:primaryType="cq:Widget"
xtype="textfield"
name="./brands/make"/>
For a reference on available xtypes, check out:
http://dev.day.com/docs/en/cq/current/widgets-api/index.html
Try this :
Add this js function to your pages
function doOpenDlg(url, path) {
var d = CQ.WCM.getDialog(url);
var reloadPage = true;
if(d) {
if( reloadPage ) {
d.success = function(form, action) {
CQ.Util.reload(CQ.WCM.getContentWindow());
};
}
d.show();
d.loadContent(path);
}
}
and then create a dialog like file. In this exemple :
/apps/myapp/components/page/mypage/myxmlfile.xml
then you can open a dialog matching this file with :
<input type="button" value="Open my dialog" style="font-size: 14px;font-weight: bold;"
onclick="doOpenDlg('/apps/myapp/components/page/mypage/myxmlfile.infinity.json', '<%=currentNode.getPath() %>');" />
with this, your dialog data will be saved in the currentNode, but you can put any path you want.

How to enable the button which is created using custom control

I need to create buttons one below the other in tridion ribbon.
I have created an usercontrol and it was appearing on the ribbon but in disabled mode.
In the "http://tridiondeveloper.com/ribbon-item-group"; it was mentioned to include <ext:issmallbutton>true</ext:issmallbutton> inside my extension element in the configuration. I have included it in the extension.config file. But i am facing error like "Loading extension failed - has invalid child element 'issmallbutton'. So, currently i ignored this step and the buttons were in disabled mode.
Could you please let me understand where i need to add this.(<ext:issmallbutton>true</ext:issmallbutton> ) and to make the buttons enable.
As indicated by Jeremy's answer, you don't need the ext:issmallbutton to enable your button (you mention my article on Tridion Developer, where I specifically state that the ext:issmallbutton is not to be used when you want to stack buttons on top of eachother).
You probably should try to debug your JavaScript and see what is happening in your _isAvailable(selection, pipeline) and _isEnabled(selection, pipeline) methods.
The isAvailable method should indicate whether the command is applicable for the selected item(s) and the isEnabled method indicates whether the command can be executed. I usually just let the isEnabled method return the result of the isAvailable one (since when the button is available, it should most of the time also be enabled). An example of how to enable a button when you have selected a Page would look something like this:
Example.PageBtn.prototype._isAvailable = function PageBtn$_isAvailable(selection, pipeline) {
if (pipeline) {
pipeline.stop = false;
}
if (selection.getCount() == 1) {
var itemType = $models.getItemType(selection.getItem(0));
return itemType && (itemType == $const.ItemType.PAGE);
}
return false;
};
Example.PageBtn.prototype._isEnabled = function PageBtn$_isEnabled(selection, pipeline) {
if (pipeline) {
pipeline.stop = false;
}
return this._isAvailable(selection);
};
Now the ext:issmallbutton element has nothing to do with this all, but if you would like to know where that should be used exactly, it is supposed to go inside the ext:extensionelement like so:
<ext:extension assignid="PageBtn" groupid="MyGroup" name="Example" pageid="HomePage">
<ext:command>PageBtn</ext:command>
<ext:title>Example</ext:title>
<ext:issmallbutton>true</ext:issmallbutton>
<ext:dependencies>
<cfg:dependency>Example.Commands</cfg:dependency>
</ext:dependencies>
<ext:apply>
<ext:view name="DashboardView">
<ext:control id="DashboardToolbar" />
</ext:view>
</ext:apply>
</ext:extension>
You can find more information in Setting up a SDL Tridion 2011 GUI extension in 8 steps.
To enable a button you need its isEnabled method to return true. issmallbutton only determines the size of the button in the toolbar. For information on how to create a button extension please look at the many other questions on this same subject...

Button to Show a Window from Listbox Row

I want to create a button that can show a window to show details of elements in listbox when it's clicked. the listbox itsetf was created from a list of JSONObject like this:
<listbox id="userListbox">
<listhead>
<listheader laber="Action"></listheader>
<listheader label="Id"></listheader>
<listheader label="Name"></listheader>
<listheader label="Address"></listheader>
<listheader label="Phone"></listheader>
</listhead>
<listitem forEach="${userController.list}">
<listcell>
<button label="Detail" id="detailButton"></button>
</listcell>
<listcell label="${each.id}" ></listcell>
<listcell label="${each.name}" ></listcell>
<listcell label="${each.address}" ></listcell>
<listcell label="${each.phone}" ></listcell>
</listitem>
</listbox>
for every row (listcell) there is always a button to show the details. but when I load the page, it failed to show the list with error message:
Not unique in ID space < Window cP8Q0#userWindow>: detailButton.
any idea to show a window when the button clicked? here is the code when button is clicked:
#Listen("onClick = #detailButton")
public void showModal(Event event) {
Component comp = Executions.createComponents("/widgets/window/modal_dialog/employee_dialog.zul", null, null);
if(comp instanceof Window) {
((Window)comp).doModal();
}
}
thank you for your help.
The problem is that if you click on different buttons you are running the createComponents again and again. Your employee_dialog.zul is only safe to include into the page once as it has IDs in it; if you do the operation twice you are creating a second set of components with the same ID and the IDs should be unique within a given idSpace (see the ZK developer guide for the theory).
There are other issues here: why create the components twice? Why not keep one and only one set around, showing and hiding them based on button clicks. That is more efficient.
Look at Button to Show a Window from Listbox Row which shows that you can:
<zk>
<!-- hidden -->
<window id="wnd" title="My Modal" visible="false" width="300px">
<button label="close" onClick="wnd.visible = false"/>
</window>
<!-- click button to pop the hidden window! -->
<button label="do it" onClick="wnd.doModal()"/>
</zk>
So you can use a
<include src="/widgets/window/modal_dialog/employee_dialog.zul">
to pull the model dialog into the bottom of the main page once and only once. Then in the code you can set the data into it and do
win1.doModal();
where win1 is the modal window defined in the fragment. You have to tell the model window what to display before you pop it but that is not hard. Use desktopScope.put( "myName", this) to have the controller/view-model of the dialog box register itself in a location where the controller/view-model in the main window can find it and talk to it to pass it the data to render.
Two other tips.
Hide your fragments as /WEB-INF/zul/employee_dialog.zul as anything under WEB-INF cannot be directly accessed by a browser for better security.
Try not to put any zul into your java. That is mixing behaviour with presentation. Sometimes it is unavoidable but always try first to keep the zul in the zul then interact with it via java IDs only (much like my suggestion). It is not always possible but separation of logic from layout is a core design pattern.
Simon
<button label="Detail" />
#Listen("onClick = listbox listitem listcell button")

How can I add a random number to an ASP.NET menu item url on each click

One step to prevent caching (in addition to adding the appropriate headers etc..) is to add a random number to the end of my URLs.
I'm using an ASP.NET menu and would like to add a random number to each menu item's navigate URL as it is clicked.
I can do this in the MenuItemDataBound event, but haven't had much luck doing the same with the MenuItemClicked Event.
Answer (can't answer my own question for 8 hours, and I don't have time to wait that long so here's my server side solution.)
To do this server side, I've had to remove the sitemap and the databinding from the menu.
I simply added all of the items from the sitemap as menuitems to the items collection in the menu markup removing the url property. The key here is removing the url property.
<asp:menu>
<items>
<asp:menuitem Text="Home" ToolTip="Go Home" Selectable="True" />
</items>
</asp:menu>
Then in your code behind you can handle the MenuItemClicked event (which should now fire, because there is no longer a navigateurl in the markup).
In the MenuItemClicked event codebehind I simply do the following:
string TimeStamp = DateTime.Now.ToString("yyyyMMddHHmmssfffffff");
// get iframe control - must have 'runat=server' attribute
HTMLControl display = CType(this.FindControl("display"), HTMLControl);
// dispatch menuitem
switch (e.item.valuepath)
{
case "Home":
display.attributes("src") = "home.aspx?=" + TimeStamp()
break;
.
.
.
}
This is the server side solution with an iframe.
I don't know if you're considering client-side URL manipulation as an option, but running this little bit of JavaScript on each page load would give you the behavior you're looking for by appending a timestamp to each of the links. You can modify it to target links in a specific area/div of the site, but this example will change them all:
<!-- include the jQuery library -->
<script type="text/javascript">
$(function(){
var time = new Date().getTime();
$('a').each(function() {
var append = (this.href.indexOf('?') > -1 ? '&' : '?');
$(this).attr('href', this.href + append + 't=' + time.toString());
});
});
</script>
Since every time the page loads the timestamp will be different, you should always get a unique set of links.
EDIT Here's a working jsFiddle demoing the behavior: http://jsfiddle.net/2HzqU/2/
I don't think that's the best solution. Have you tried using something like this:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();

Resources