I'm writing a MVC portlet framework and I plan to use Rythm inside my views. I would like to pass various arguments to the view and was wondering if there is a way to declare these arguments for the view using Java at runtime? I know that I can declare arguments in the view using the #args tag and that I can add custom tags from Java, but I wanted to do something similar to how ASP.NET MVC passes helper classes (HtmlHelper #Html, UrlHelper #Url, Object #Model) to the view.
If all the arguments you planned to pass to the view are global (i.e. they applied to all render session and to all templates) then you should treated them as implicit variables, meaning template author don't need to declare them but they are free to use them. Examples of implicit variables are session, request, context etc.
For how to declare implicit variables, you can refer to:
Spring-rythm
Actframework
For how to configure Rythm engine with your implicit variable, refer to
Spring-rythm
ActFramework
For how to inject implicity variables into rythm engine for each render session, you can also refer to:
Spring-rythm
Actframework
For things like HtmlHelper my recommendation is to provide reusable rythm template as tags instead of Java object. Because you need to render html snippet, thus using rythm is a natural way to go instead of let the Java code to output the content.
For things like UrlHelper if it is all about String manipulation, you can go straight with Java code, but probably the public static method is more appropriate than helper instance
Related
I use Enterprise Architect for code generation and I would like to automatically retrieve all tags (in my case Java annotations) of the interfaces that a class realizes. Consider the following example:
From this model, I want to generate a class that looks like this:
#AnnotationOfMyInterface
public class MyClass {
...
}
So I want to add annotations as tags to MyInterface that should be applied to MyClass during code generation. In the UI, tags of implemented interfaces are shown so I was hoping there is a way to get these tags during code generation.
I tried to edit the code generation templates and found macros to get
All interfaces that a class implements: %list="ClassInterface" #separator=", "%
All tags with a given name (of the class that code is being generated for): %classTag:"annotations"%
But unfortunately, I cannot combine these macros, i.e., I cannot pass one interface to the classTag macro so that I can retrieve the tags of that particular interface (and not the one I'm generating code for). Is there a way to get classTags of a specific class / interface?
I also tried to create a separate code generation template and "call" it from the main class code generation template. But inside my template, the classTag macro still only gets the tags of the class.
Thanks to the comments above and especially because of an answer to my question in EA's forum, I was able to setup a little proof of concept achieving what I wanted. I'm answering my question to document my solution in case someone has a similar problem in the future.
After Eve's hint in EA's forum I looked into creating an AddIn for Enterprise Architect to use this AddIn from a code generation template. I started by writing a basic AddIn as explained by #Geert Bellekens in this tutorial. Afterwards I changed the AddIn to fit my needs. This is how I finally got the tagged values (annotations) of the interfaces a class realizes:
First step:
Inside a code generation template, I get all the interfaces a class realizes and pass them to my AddIn:
$interfaces=%list="ClassInterface" #separator=", "%
%EXEC_ADD_IN("MyAddin","getInterfaceTags", $interfaces)%
Second step:
As documented here the repository objects gets passed along with the EXEC_ADD_IN call. I use the repository object and query for all interfaces using the names contained in $interfaces. I can then get the tagged values of each interface element. Simple prototype that achieves this for a single interface:
public Object getInterfaceTags(EA.Repository repo, Object args)
{
String[] interfaceNames = args as String[];
String firstInterfaceName = interfaceNames[0];
EA.Element interfaceElement = repo.GetElementsByQuery("Simple", firstInterfaceName).GetAt(0);
String tag = interfaceElement.TaggedValues.GetAt(0);
return interfaceElement.Name + " has tag value" + tag.Value;
}
I know, there are a couple of shortcomings but this is just a simple proof of concept for an idea that will most likely never be production code.
Usually an AEM component is retrieving its data from a JCR node, but I was wondering whether it's possible to pass data to it in HTL. Sure, there's data-sly-resource, but as far as I know this way you can only pass a JCR node.
So in an actual case I've got data in a model that's retrieved from elsewhere. Yet I'd like to use existing components. I'm aware that the data must at least match the component-types' model.
But what if the component I'd like to use is using an model that got its data injected like
#Inject
#Optional
String[] itemList;
So in my stubborn thoughts it should be possible to somehow pass a string array like
<div data-sly-resource="${myModel.aStringArray # resourceType='my/component' }"></div>
But like mentioned above this seems to be meant for passing nodes only.
Is there any way to accomplish passing data directly to a component (other than creating a template)?
You can pass additional information in the form of request attributes or selectors
Selectors
Selectors are the most straight forward of passing simple information. This is an array of strings that can be passed. This is quite useful to data that can act as flags ex:
Variant/Mode of the component
index of the component in a list if it is being included in a loop.
ID of the parent when building things like accordion, tabs
This approach is an abuse of selectors, but IMHO as long as you know what you are doing this shouldn't be a major concern.
<article data-sly-resource="${'path/to/resource' # selectors=['s1', 's2']}"></article>
You can add, replace or remove selectors while including the component. Checkout the documentation for the syntax. https://docs.adobe.com/content/help/en/experience-manager-htl/using/htl/block-statements.html#resource
Request Attributes
This option allows you to add custom request attributes to the component request. This can be used to pass objects as parameters to the component while including them.
These are standard http request attributes with convince of scoping them to a particular instance of script/resource inclusion. To use this you will end up needing a model class or use-js as there is little support to compose the data to be passed along in sightly.
<sly data-sly-use.settings="com.adobe.examples.htl.core.hashmap.Settings"
data-sly-include="${ 'productdetails.html' # requestAttributes=settings.settings}" />
https://docs.adobe.com/content/help/en/experience-manager-htl/using/htl/block-statements.html#request-attributes
There is another way. You can pass additional parameters to the Sling Model on initialization using data-sly-use. For example:
<div data-sly-use.model="${'com.model.Teaser' # test='abc'}"
You can read then the variable "test" in model from request:
#PostConstruct
private void initModel() {
String value = request.getAttribute("test");
// value is 'abc'
}
In order this to work correctly you need to make sure your Sling Model is adaptable from request #Model(adaptables = SlingHttpServletRequest.class}
I am currently active PlayFramework learner who came from world of PHP.
For example I have a Head block object in my app, which should hold title, charset encoding, meta information, etc. Something similar to Magento blocks, but without XML declaration
package blocks.Page
object Head {
var title: String = "";
}
In Application.index() method I have
blocks.Page.Head.title
Ok(views.html.application.index());
And finally in html template
#import blocks.Page.Head
<title>#Head.title</title>
However, blocks.Page.Head object is defined for entire application scope, not for single request. This object is the same for each request.
What is the right way to do, what I am trying to do? I can create container with all blocks and instantiate it with each request, then just pass to all templates. But I have a feeling that this is wrong way.
Just use usual class instead of object and pass instance to template as parameter.
Like this:
package blocks.Page
case class Head(title: String = "")
Controller:
val head = Head("Blah")
Ok(views.html.application.index(head))
And template will looks like:
#(head: blocks.Page.Head)
...
<title>#head.title</title>
I know the feeling when coming from a request-oriented language like PHP :). However, consider application-wide access as a gift of a VM (in PHP we need to go the extra mile of using some bytecode and data caching tool like APC or eAccellerator).
I would probably create a blockManager class which gives you static access to blocks by name/tag/id from the template: Block.get("MyBlock"). Then you can define and later modify your caching / storing strategy (holding in memory vs. loading from storage) without affecting your templates.
Actually I wanted to know that is there any way to define and call a function in Zend framework which does not need to put with $this like pr() function in cake php. I want to define a such a function which can be called directly in all controllers and views in zend framework. Can it be done by putting all functions in helper and use them without writing $this in front of them.
For example I wanted to make a function arrprint() to print the array in helper and use it globally with only by writing simply arrprint();
Please help me
You could just create a file anywhere and put the function in there. The just include it like you would any other files.
Or create a library called Utils and put your custom functions there as static and call them using:
Utils::myFunction();
Contrary to cake zf does not enforce much anything. You can leverage the framework to do whatever you want.
BTW $this is just a pointer to the current class and when you use $this->myFunction(); it's because that function is a member of the current class (or its parent).
Looks like the class that is generated for PlaceHistoryMapper is hard-coded to use AbstractPlaceHistoryMapper as the super class.
So, I am trying to work around this by trying to replace this AbstractPlaceHistoryMapper with a custom mapper of mine using deferred binding . I am using the following rule in my *.gwt.xml:
<replace-with class="com.google.gwt.place.impl.AbstractPlaceHistoryMapper">
<when-type-is class="com.test.sampleapp.CustomPlaceHistoryMapper" />
</replace-with>
But for some reason the replace does not seem to be happening. CustomPlaceHistoryMapper is not getting kicked in and the generated class still uses AbstractPlaceHistoryMapper.
Any thoughts/pointers as to what might be resulting this behavior are much appreciated.
Note: I have also posted this on the GWT group but haven't received an answer so far.
To make the deferred binding work a class must be created with GWT.create(). However, AbstractPlaceHistoryMapper is only used as an extended class. So it will never be created via GWT.create, but always by instantiation the subclass. And therefor deferred binding won't work in this case. If you want a complete different implementation you have to implement a custom PlaceHistoryMapper, and manage the known tokens yourself. This also means you can't use the History annotations either.
As a side note the classnames in your rule should be swapped. But for the end result this doesn't matter, since it won't work in the first place.
It is absolutely possible to have custom history tokens (eg. #mail or #mail/bla instead of only #mail:inbox) using the out-of-the-box Place-related classes that GWT (2.0) provides.
Instead of replacing AbstractPlaceHistoryMapper you could instantiate the default PlaceHistoryMapper passing in it's constructor your implementation of PlaceHistoryMapper<T> or PlaceHistoryMapperWithFactory<T>.
eg.:
final PlaceHistoryHandler placeHistoryHandler = new PlaceHistoryHandler(new CustomHistoryMapper());
You will be able then to map tokens as you wish.
I personally recommend you to use an unique PlaceTokenizer in you mapper custom implementation so that I dont have to have an inner PlaceTokenizer class in each of your Places.
Hope that helps. Feel free to ask any doubts.