Get scala template in a variable in Play Framework - java

Lets say there are two scala templates in view folder
file1.scala.html
container.scala.html
Now I want to pass first template into 2nd template (container.scala.html) from controller. Like:
public class Application extends Controller {
static Result isItPossible()
{
Result theFile=ok(file1.render());
return ok(container.render(theFile));
}
}
Is it possible? If it is, How I can do it?

You can pass a rendered template to the container template. container would need to have some Html parameter:
container.scala.html:
#(content: Html)
<p>Here's my content: #content </p>
And from within the controller:
public class Application extends Controller {
return ok(container.render(file1.render()));
}

It's worth to mention that you don't need to combine your wrapping containers in the controller, as template engine has an ability to use Layouts for it (as described in docs). In that case you can use it like that:
container.scala.html
#()(content: Html)
<p>Here's my content: #content </p>
file1.scala.html
#container() {
<b>this is content of <i>file1</i> template</b>
}
controller
public class Application extends Controller {
static Result itIsPossible() {
return ok(file1.render());
}
}

Related

How insert view (child) into div of parent layout? Vaadin 10 / Flow

I have a component implementing RouterLayout like this:
#Tag("side-menu")
#HtmlImport(value = "src/components/side-menu.html")
public class SideMenu extends PolymerTemplate<TemplateModel> implements RouterLayout {
#Id("menu")
private PaperListBox listBox = new PaperListBox();
public SideMenu() {
listBox.addMenu(new PaperItem("tutorial", TutorialView.class));
listBox.addMenu(new PaperItem("icons", IconsView.class));
}
}
I route a view child of the parent layout
#Route(value=IconsView.VIEW_ROUTE, layout = SideMenu.class)
public class IconsView extends Div {
public static final String VIEW_ROUTE = "icons";
public IconsView() {
add(new Label("ICONS VIEW"));
}
}
But the result overwrote all the content of side-menu.html file.
side-menu.html template base format
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<div id=contenido><!-- I want to show here my view Icons --></div>
</side-menu>
But the result it's
<side-menu>
<div>
<label>ICONOS VIEW</label>
</div>
</side-menu>
The expected result is:
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<div id=contenido>
<div>
<label>ICONOS VIEW</label>
</div>
</div>
</side-menu>
In vaadin documentation said this:
You can add child components to templates using the Component or
Element API, but because PolymerTemplate uses the shadow DOM the
shadow tree is rendered instead of the elements children that are in
the light DOM.
This means that the template needs to have a <slot></slot> to mark
the place where the light DOM elements should be rendered.
I found a solution for this composite layout:
I only needed to be modified my template side-menu.html and add a <slot> tag like this:
<side-menu>
<div>App Name</div>
<div id="menu"></div>
<slot></slot>
</side-menu>
And then when my view is loaded, it's rendered into the <slot> tag into the template
The problem in your case is that since you have bare template without functionality and databinding and custom template model, the server side is not aware of its contents. Thus Div.add() thinks it as empty div and overwrites its content.
One approach in your case would be to modify the content via Element API, which could be something like:
public IconsView() {
label = new Label("ICONS VIEW");
getElement().appendChild(label.getElement());
}
See API spec
https://demo.vaadin.com/javadoc/com.vaadin/vaadin-core/10.0.0.rc5/com/vaadin/flow/dom/Node.html#appendChild-com.vaadin.flow.dom.Element...-
Another approach would be to extend the html template to be fully functional polymer element
There is more about that e.g. here:
https://vaadin.com/docs/v10/flow/polymer-templates/tutorial-template-list-bindings.html

How to avoid WSOD (blank screen) while loading long-running initialization data in Struts2?

I need to do the following:
User logs in.
Redirected to welcome screen.
Looks at the welcome screen while lots of records are loaded.
Redirected to the working screen.
I am looking for a way to do in Action class something like this:
public class LinkAction extends ActionSupport implements SessionAware {
#Autowired
private ServiceDelegate myService;
public String welcome()
{
new Runnable() {
#Override
public void run() {
myService.getLoadsOfData();
//redirect to the next action
}
}.run();
// this is where the user
// goes to look at the welcome screen
return "welcome";
}
}
May be it's a wrong approach, please tell if so, I am new at Struts.
The right way is the one already suggested by AleksandrM in comments: open the page, show an indicator while you call an ajax action (let's say with jQuery, for convenience), then render the result and remove the indicator. It's easier than you think:
public class MainAction extends ActionSupport {
public String execute() {
return SUCCESS;
}
}
public class AjaxAction extends ActionSupport {
#Autowired
private ServiceDelegate myService;
private Stuff myStuff; // with getter
public String execute() {
myStuff = myService.loadLongStuff();
return SUCCESS;
}
}
Your AJAX action can either return JSON data, a JSP snippet or a Stream of binary data. Choose the way you prefer. For example, if you map SUCCESS of AjaxAction to a JSP snippet, your JSP snippet will be:
ajaxSnippet.jsp
<%# taglib prefix="s" uri="/WEB-INF/struts-tags.tld" %>
Stuff: <s:property value="myStuff" />
Then in your main.jsp, show the indicator in the div you will overwrite with the AJAX call's result:
main.jsp
<body>
<div id="main">
<img src="/images/mesmerizingProgressBar.gif" />
</div>
<script>
$(function(){ // onDocumentReady...
$.ajax({ // call ajax action...
type : 'GET',
url : '/ajaxAction.action',
success : function(data,textStatus,jqXHR){
// then render your result in "main" div,
// overwriting the loading GIF
$("#main").html(data);
},
error : function(jqXHR, textStatus, errorThrown){
$("#main").html("Error ! " + textStatus);
}
});
});
</script>
</body>
Thank you for the AJAX idea.
However, the answer I was looking for was in fact Struts interceptor "execAndWait".
I decided to use it over AJAX because I am dealing with existing application and all the Struts plumbing is in place.
This is the Struts guide on this

Communicating with parent component

I have the MyPage.tml page and MyComponent.tml component.
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<body>
<t:mycomponent />
</body>
</html>
I need to display some data on MyPage based on what has happened in MyComponent. How can I make some data from MyComponent available to MyPage? Is there something like "reverse" parameters (child passing parameter to parent)?
Your component is available to you within your page as a variable where you can access the variables required from within your page like so:
#Component(id = "myComponent")
private MyComponent myComponent;
#SetupRender //or any other render event method
private void setup() {
Object compVariable = myComponent.getYourVariable();
}
More elegant if you ask me is to use event bubbling as it makes it easer to refactor some logic out to a deeper component if needed.
Component:
#Inject
private ComponentResources resources;
#SetupRender //or any other lifecycle event method
private void triggerEvent() {
Object yourVariable = new Object();
resources.triggerEvent("YOUR_EVENT_NAME", new Object[]{yourVariable}, null);
//add an event callback if needed where I use null here
}
Page:
#OnEvent(value = "YOUR_EVENT_NAME")
private void handleComponentEvent(Object yourVariable) {
//do something with yourVariable
//even return something which would then can be handled by your component callback handler
}
You can use usual tapestry parameter.
<t:mycomponent value="myValue"/>
If this value will be changed on the component side, it will be available on the container side and vice versa.
I've used all three of these approaches, depending on context. I generally prefer event bubbling, where that makes sense.

Wicket: make a generated csv available to a dygraphs JavaScript

I'm trying to figure out how to make a dynamically generated csv available to a dygraphs JavaScript.
I'm using a wicket behavior to add the dygraph (JavaScript graph) to my markup like shown in the codesample bellow. Right now I've hardcoded it to use a csv file named "dygraph.csv". I want to change this, and instead make dygraph use the values from String csv, how do I achieve this?
Any help help is greatly appreciated.
public class DygraphBehavior extends AbstractBehavior {
private static final long serialVersionUID = -516501274090062937L;
private static final CompressedResourceReference DYGRAPH_JS = new CompressedResourceReference(DygraphBehavior.class, "dygraph-combined.js");
#Override
public void renderHead(IHeaderResponse response) {
response.renderJavascriptReference(DYGRAPH_JS);
}
#Override
public void onRendered(Component component) {
final String id = component.getId();
Response response = component.getResponse();
response.write(JavascriptUtils.SCRIPT_OPEN_TAG);
response.write("new Dygraph(document.getElementById(\""+id+"\"), \"dygraph.csv\", {rollPeriod: 7, showRoller: true, errorBars: true});");
response.write(JavascriptUtils.SCRIPT_CLOSE_TAG);
}
}
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
add(new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes())));
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
<div>
<h1>Dygraph:</h1>
<div wicket:id="graphdiv" id="graphdiv" style="width:500px; height:300px;"></div>
<a wicket:id="csv" href="#">dl generated csv</a>
</div>
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
ResourceLink<File> link = new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes()));
add( link );
//this is the url that should be passed to the javascript code
CharSequence url = link.urlFor( IResourceListener.INTERFACE );
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
There are other solutions based on the scope of your resource, maybe a dynamic shared resource would work better (if your graph parameters can simply be passed as url parameters), but this will work.
The JavaScript needs to see the data in some way after the page has been rendered. So you have two options:
Embed the data in the page (say in a hidden div) and then let JavaScript read the data from there as text.
Create a servlet where the JavaScript can download the data from.
The second option means that your page rendering code has to pass the data somehow to the servlet. You can try to put it into the session but then, it will sit there, occupying RAM. Probably not a problem if it's just a little bit of data and you have only a few users. But if that's not true, option #1 is probably better.

How do I create a custom text field in Tapestry5 that renders some Javascript onto the page?

I have been trying to create a custom textfield in tapestry which will render some javascript when it gains focus. But I have been having trouble trying to find an example of this.
Here is some of the code i have started off with:
package asc.components;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.ComponentDefaultProvider;
public class DahserTextField implements Field {
#Parameter (defaultPrefix = "literal")
private String label;
#Inject
private ComponentResources resources;
#Inject
private ComponentDefaultProvider defaultProvider;
#Parameter
private boolean disabled;
#Parameter
private boolean required;
String defaultLabel(){
return defaultProvider.defaultLabel(resources);
}
public String getControlName() {
return null;
}
public String getLabel() {
return label;
}
public boolean isDisabled() {
return disabled;
}
public boolean isRequired() {
return required;
}
public String getClientId() {
return resources.getId();
}
}
I have been unsure on what to do next. I do not know what to put into the .tml file. I would be grateful if anyone could help or point me in the right direction.
There is no need to replicate any of TextField's functionality in your own component, instead you should create a component mixin. Mixins are designed to add behaviour to existing components.
From the Tapestry 5 docs:
Tapestry 5 includes a radical feature,
component mixins. Component mixins are
a tricky concept; it basically allows
a true component to be mixed together
with special limited components called
mixins. The component plus its mixins
are represented as just a single tag
in the component template, but all the
behavior of all the elements.
You would use the mixin like this:
<input type="text" t:type="TextField" t:mixins="MyMixin" t:someParam="foo" />
A mixin stub:
#IncludeJavaScriptLibrary("MyMixin.js")
public class MyMixin {
/**
* Some string param.
*/
#Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String someParam;
#Environmental
private RenderSupport renderSupport;
#InjectContainer
private AbstractTextField field;
#AfterRender
void addScript() {
this.renderSupport.addScript("new MyJavascriptClass('%s', '%s');",
this.field.getClientId(), this.someParam);
}
}
Note the #InjectContainer annotation, which injects the containing TextField into your Mixin. In this case, we want the TextField's clientId.
Also note the #IncludeJavaScriptLibrary("MyMixin.js") annotation, which includes the required Javascript file.
The Javascript could look like this:
MyJavascriptClass = Class.create({
initialize: function(textField, someParam)
{
this.textField = $(textField);
this.someParam = someParam;
this.textField.observe('focus', this.onFocus.bindAsEventListener(this));
},
onFocus: function(event)
{
//do something
}
}
The key difference to your approach is that this involves defining your own JS class and using Tapestry's built-in facilities to load and initialize the JS. The use of mixins is also relatively light-weight and elegant in comparison to creating your own components.
The .tml
<t:textfield onfocus="somethingCool()" />
The Java should probably extent TextField? It will need to import a new stylesheet too probably.
--
Pages are actually components, so you would build a component just like you would have any other page. You can embed any other component into them. I hope this is a good starting point for you.

Categories