creating simple reusable component in Apache Tapestry5 - components

I am starting an adventure with Apache Tapestry5. I am trying to make simple component (for tests), consisting of pair of Textfields. Component is named "TestComp". I have following elements:
testComp.tml
<t:container
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<p>
<input t:type="TextField" t:id="testOne" t:value="testOne.input"/><br/>
<input t:type="TextField" t:id="testTwo" t:value="testTwo.input"/><br/>
</p>
</t:container>
TestComp.java
public class TestComp {
private DataContainer testOne;
private DataContainer testTwo;
#SetupRender
public void setup(){
testOne = new DataContainer();
testTwo = new DataContainer();
}
public String getContentOfTestOne() {
return testOne.getInput();
}
public String getContentOfTestTwo() {
return testTwo.getInput();
}
public DataContainer getTestOne() {
return testOne;
}
public void setTestOne(DataContainer testOne) {
this.testOne = testOne;
}
public DataContainer getTestTwo() {
return testTwo;
}
public void setTestTwo(DataContainer testTwo) {
this.testTwo = testTwo;
}
}
And then I am trying to use it in other place, for example in index.tml:
<form t:type="form" t:id="out">
<t:testComp />
<br/><input type="submit" value="Component"/>
</form>
According to dozens of materials and examples I've found (to be honest non of it refereed to case similar to mine) such implementation should result of showing testComp element in the form, but unfotrunately there is nothing rendered above the button (though tapestry is not crashing). What am I missing? And will I be able to put in Index.java property of TestComp type and bind it with my
<t:testComp />
in Index.tml by id (or it requires something more to implement in my custom component?)

Did you provide the full index.tml file? If so, you are missing the tapestry namespace as well as a correctly setup html document. Try the following:
Index.tml
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<head>
<title>My page</title>
</head>
<body>
<form t:type="form" t:id="out">
<div t:id="testComp" />
<br/><input type="submit" value="Component"/>
</form>
</body>
</html>
In your Index.java you can use this to access your component.
#component(id="testComp")
private TestComp testComp;
If this does not work there is probably something wrong in your configuration or setup and you might just be looking at a static tml file not handled by tapestry at all. In this case follow the step-by-step guide on the Getting Started page.

Related

How do I open a browser window/tab from blazor webassembly project behind code:

I'm converting a UWP app to Blazor WebAssembly with ASP.NET Core hosted.
I have my markup code in Index.razor and behind code in Index.razor.cs.
In the UWP project I opened a browser window from an onclick function like this:
var success = Windows.System.Launcher.LaunchUriAsync(targetPage);
What can I use in my Blazor project onclick event, that won't lead to "unhandled error has occurred"?
You can open a tab by making use of the 'window.open' function:
#inject IJSRuntime JSRuntime;
....
await JSRuntime.InvokeAsync<string>("open", $"https://www.mysite/mypage", "_blank");
if the page is internal to your website you can use the base uri:
#inject NavigationManager Navigation
#inject IJSRuntime JSRuntime;
...
await JSRuntime.InvokeAsync<string>("open", $"{Navigation.BaseUri}/mypage", "_blank");
You case use below way
razor file
JSRuntime.InvokeVoidAsync("OpenWindow");
html file
<script>
function OpenWindow() {
window.open('https://www.google.com', 'Test', 'width=800,height=860,scrollbars=no,toolbar=no,location=no');
return false
}
</script>
These are great answers and I took it one step further to make a re-usable component. My app uses anchors and link buttons so I have a quick-and-dirty switch for each.
ExternalLink.razor
#inject IJSRuntime JSRuntime
#if (Type == "link")
{
#(Text ?? Url)
}
else if (Type == "button")
{
<button type="button" class="btn btn-link" #onclick="OpenWindow">#(Text ?? Url)</button>
}
#code {
[Parameter]
public string Url { get; set; }
[Parameter]
public string Text { get; set; }
[Parameter]
public string Type { get; set; } = "link";
private async Task OpenWindow()
{
await JSRuntime.InvokeAsync<string>("open", Url, "_blank");
}
}
Usage
<ExternalLink Url="https://www.ourwebsite.com/" Text="Visit Us!" />
<ExternalLink Url="https://stackoverflow.com/" />
<ExternalLink Url="https://duckduckgo.com/" Type="button" />
Why not just use the build in NavigationManager?
Navigation.NavigateTo(a_url, true);
The trick is to use the force parameter.
Happy coding.

Posting an array of string

I am trying to post a string array to the post action in an Razor Pages project. For this, I thought about using a hidden <select> tag. The user would enter text into a text box, press a button and I would then add a new option to the <select> then post the whole thing with a submit button. However, after everything is posted, the array property of my model is empty.
Does anyone know if there is a better way of doing this or what I am doing wrong?
Razor:
<form method="post">
<input id="string-value" />
<input type="button" id="add-item" value="Add item" />
<select asp-items="#Model.Model.ArrayOfStrings" id="hidden-select"></select>
<table id="table-items">
</table>
<input type="submit" value="Submit" />
</form>
public class ArrayModel
{
public List<SelectListItem> ArrayOfStrings { get; set; } = new List<SelectListItem>();
}
public class IndexModel : PageModel
{
[BindProperty]
public ArrayModel Model { get; set; }
public void OnGet()
{
Model = new ArrayModel();
}
public void OnPost()
{
System.Diagnostics.Debugger.Break();
}
}
JS:
$('#add-item').on('click', function () {
debugger;
var value = $('#string-value').val();
$('#hidden-select').append(new Option(value, value));
$('#table-item tr:last').after('<tr><td>' + value + '</td></tr>')
});
Repository can be found here.
The options of the select will not be posted so this will not work.
The easiest way to do this is append the results to a hidden input with a separator char, then do a string split on the server side.
Another, maybee more elegant way, would be to add hidden inputs with the same name. Each input with it's own value. You should then be able to get this as a List or Array on the server.
Razor:
<input value="#String.Join(",", Model.Model.ArrayOfStrings)" id="tags"></select>
JS
$('#tags').val($('#tags').val() + ',' + value);
Controller
public void OnPost(string tags)
{
var tagsArray = tags.split(',');
}

ASP.NET core persisting values between Get and Post error validation

I'm new to web development so I don't know a good way on how to persist data between requests.
This is my site so far:
The elephant title is being fetched from an API on the GET request, according to the titleId query parameter. When I press login, model validations are being run, for example that email and password must have been entered. However, when error page is returned, elephant text is empty since that value was not persisted. What are the best approaches to persist that value so that is still visible when POST error is returned? Does it has to be included in the POST data? I don't want to request the API again.
Code behind:
public class IndexModel : PageModel
{
private string apiTitle;
public string ApiTitle { get { return apiTitle; } set { apiTitle = value; } }
// Bind form values
[BindProperty]
public User user { get; set; }
public Task<IActionResult> OnGetAsync(string titleId)
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
return Page();
}
public async Task<IActionResult> OnPostLoginAsync()
{
if (!IsLoginFormValid())
{
// When this is returned, for example if no password was entered,
// elephant title goes missing since apiTitle is null?
return Page();
}
var user = await LoginEmailAsync();
return RedirectToPage("/Profile");
}
}
Html:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<link rel="stylesheet" href="css/index.css">
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<div class="login-page">
<span id="label_api_title">#Model.ApiTitle</span>
<div class="form">
<form class="login-form" method="post" asp-page-handler="Login">
<input type="text" placeholder="Email" asp-for="User.Email"/>
<span asp-validation-for="User.Email" class="text-danger"></span>
<input type="password" placeholder="Password" asp-for="User.Password1" />
<span asp-validation-for="User.Password1" class="text-danger"></span>
<button>login</button>
<p class="message">Not registered? Create an account</p>
</form>
</div>
</div>
<script src="js/index.js"></script>
Yes. What you see is the expected behavior. Remember, Http is stateless. You are making 2 separate http calls, one for the GET and one for POST. The second call has no idea what the first call did ( or even there was first call at all!)
If you want to have a way to read the ApiTitle property value in the Post call and return that to the view, you need to persist it somewhere so that it is available between http calls. But in your case, all you need is to include that in the form post and have the framework bind it for you.
In your case, you can simply use a public property (Which is settable and gettable) for this. No need to keep a private variable. Decorate your property with BindProperty attribute so the model binder will bind the data on this property.
public class CreateModel : PageModel
{
[BindProperty]
public string ApiTitle { get; set; }
//Your existing code goes here
}
Now inside your form tag, have an input hidden element for the ApiTitle. This way, when the form is submitted, the value of ApiTitle property will be send in the request data.
<form class="login-form" method="post" asp-page-handler="Login">
<input type="hidden" asp-for="ApiTitle"/>
<input type="text" placeholder="Email" asp-for="User.Username"/>
<!--Your other existing form elements -->
<button>login</button>
</form>
Now in your OnPostLoginAsync method, you can read the ApiTitle value if needed. When you return the Page (when validation fails), the UI will display the ApiTitle property value in your span element.
public async Task<IActionResult> OnPostLoginAsync()
{
var title = this.ApiTitle; // If you want to read this value
if (!ModelState.IsValid)
{
return Page();
}
return RedirectToPage("/Profile");
}
It could be that your not sending it inside your form in the razor and its only in the get request and not in the post request:
public Task<IActionResult> OnGetAsync(string titleId)//<----its here
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
return Page();
}
//-->need to pass title id in post
public async Task<IActionResult> OnPostLoginAsync(string titleId)
{
if (!string.IsNullOrEmpty(titleId))
{
ApiTitle = await GetTitleFromApiAsync(titleId);
}
if (!IsLoginFormValid())
{
// When this is returned, for example if no password was entered,
// elephant title goes missing since apiTitle is null?
return Page();
}
var user = await LoginEmailAsync();
return RedirectToPage("/Profile");
}
and in your razor add the span in the form:
<span id="label_api_title">#Model.ApiTitle</span>
<div class="form">
<form class="login-form" method="post" asp-page-handler="Login">
<span id="label_api_title">#Model.ApiTitle</span>/*<--------here*/
<input type="text" placeholder="Email" asp-for="User.Email"/>
<span asp-validation-for="User.Email" class="text-danger"></span>
<input type="password" placeholder="Password" asp-for="User.Password1"/>
<span asp-validation-for="User.Password1" class="text-danger"></span>
<button>login</button>
<p class="message">Not registered? Create an account</p>
</form>

show and hide <h1> dynamically in JSF [duplicate]

This question already has answers here:
How to conditionally render plain HTML elements like <div>s?
(4 answers)
Closed 7 years ago.
I am using JSF 2.2 and PrimeFaces 5.2.
I have the following inside my body on my view:
<div class="headerDetails">
<h1> Nombre Empresa: #{loginController.login.empresa} </h1>
<h1> Bienvenido: #{loginController.login.nombre}</h1>
</div>
I want to dynamically hide and show the first <h1> depending on what user logged in. What is the best way of accomplishing this?
This is what I did for those who are interested:
<div class="headerDetails">
<h:panelGroup layout="block" rendered="#{loginTipoController.isEmpresarial()}">
<h1> Nombre Empresa: #{loginController.login.empresa} </h1>
</h:panelGroup>
<h1> Bienvenido: #{loginController.login.nombre}</h1>
</div>
My controller looks like this:
#ManagedBean
#ApplicationScoped
public class LoginTipoController {
private LoginTipo tipo;
public LoginTipo getTipo() {
return tipo;
}
public void setTipo(LoginTipo tipo) {
this.tipo = tipo;
}
#PostConstruct
public void init()
{
tipo = new LoginTipo();
//setting the value from get request
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
tipo.setTipo(params.get("tipo"));
}
public boolean isEmpresarial()
{
if(tipo.getTipo().equals("emp")) return true;
else return false;
}
}
If the user is "ind", I don't want to show the <h1> tag. If it is "emp", I want to show the tag.

Render issue with JSF 2, Facelets and content called via proxy

We have a JSF2 Facelets templating implementation that provides the HTML "wrapper" for applications across the enterprise. Applications are deployed on WAS 7 and call our Facelets templating via a shared library.
We have a request to pull in left navigation via a xhtml file from an external web server - which requires going through our proxy. Since the included xhtml file will not be in the web project (it is to be managed separately and sits on a web server) - I'm trying to find a way to pull in the code across the proxy and have it render correctly. using the doesn't work because of the proxy.
I've written a simple bean that sets the proxy configurations and pulls the file's content. If I use the ResponseWriter to output the code -- the tags are not rendered in the HTML output:
<div class="aligncenter"> <img src="/allweb/images/corporate/pcom/logout.gif" alt="Logout" width="138" height="30" /> </div>
<mycom:navBlockHeader type="custom" title="TPA Nav Pilot" />
<mycom:divStart expandableNav="true"/>
<mycom:navLink href="url" text="Link 1" />
<mycom:navSubLinkStart text="Link 2" href="#" expandable="true" expand_onload="true"/>
<mycom:navLink href="url" text="Link a"/>
<mycom:navLink href="url" text="Link b"/>
<mycom:navLink href="url" text="Link c"/>
<mycom:navSubLinkEnd/>
<mycom:navLink href="url" text="Link 3"/>
<mycom:divEnd />
I am calling the managed bean via a custom tag: <mycom:tpaLeftNav/>
The main managed bean code is:
public class LeftNavIncludeProxy extends UIComponentBase {
public void encodeBegin(FacesContext context) throws IOException{
ResponseWriter writer = context.getResponseWriter();
String include = "";
System.setProperty("https.proxyHost", "proxy.mycompany.com");
System.setProperty("https.proxyPort", "80");
try{
URL url = new URL("https://secure.mycompany.com/navigation/tpa/leftnav/leftnav-define.xhtml");
BufferedReader bin = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = bin.readLine()) !=null){
include += line;
}
include=include.toString();
writer.write((String) include);
}
catch (Exception e){
e.printStackTrace();
}
}
}
I can easily retrieve the contents of the leftnav.xhtml file -- but when I try to write it back into the Facelets template -- it doesn't render the Facelets tags as HTML. It feels like I'm missing the render step but I'm not sure how to resolve this easily.
I'm sure this is a beginner question - my apologies if it is - I would appreciate any suggestions or thoughts on how to solve this.
Thanks.
Mel
You're writing the content of Facelets file as plain text to the response. This is indeed not going to work.
You should be using a ResourceResolver instead. Here's a kickoff example for your particular case:
public class ProxyFaceletsResourceResolver extends ResourceResolver {
private ResourceResolver parent;
public FaceletsResourceResolver(ResourceResolver parent) {
this.parent = parent;
}
#Override
public URL resolveUrl(String path) {
if (path.startsWith("/proxy/")) {
return new URL("https://secure.mycompany.com/" + path.split("/proxy/")[1]);
}
else {
return parent.resolveUrl(path);
}
}
}
If you configure it in web.xml as follows
<context-param>
<param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
<param-value>com.example.ProxyFaceletsResourceResolver</param-value>
</context-param>
then you'll be able to get it by for example
<ui:include src="/proxy/navigation/tpa/leftnav/leftnav-define.xhtml" />

Resources