I have an JSF 2.0 application that has a bean that holds a list of Strings.
I want to add the String from an <h:inputText>/> to my List and display my list.
The following code just put references in my List. So every element from my List is set to the last input.
#ManagedBean
#ApplicationScoped
public class Bean {
private String name;
private ArrayList<String> test = new ArrayList<String>();
public Bean() {
}
public Bean(String name) {
this.name = name;
}
public String addtoList(String _name){
test.add(_name);
return "./index.xhtml";
}
/***************GETTER/SETTER/HASHCODE/EQUALS**************************/
...
}
here a part of my index.xhtml:
<h:inputText id="name"
value="#{bean.name}"
required="true">
</h:inputText>
<h:commandButton value="Post"
action="#{bean.addtoList(name)}"/>
<br/>
<h:dataTable var="bean"
value="#{bean.test}">
<h:column>
<h:outputText value="#{bean.name}"/>
</h:column>
</h:dataTable>
Try this:
public String addtoList() { // no parameter
test.add(this.name); // add value of bean's property
return "./index.xhtml";
}
and in the facelet:
<h:commandButton
value="Post"
action="#{bean.addtoList}"/> <!-- no parameter passed -->
The point is to have the addToList method without parameters and the string you add to the list should be the value of the name property of the backing bean.
And also, in the datatable, do not name the var the same as the backing bean. It's confusing and potentially leads to bugs. Use something like this:
<h:dataTable
var="it"
value="#{bean.test}">
<h:column>
<h:outputText value="#{it}" />
</h:column>
</h:dataTable>
<h:dataTable var="beany"
value="#{bean.test}">
<h:column>
<h:outputText value="#{beany}"/>
</h:column>
</h:dataTable>
the prob was that var="bean" is the same name of my class bean
better should be another name for var
Related
I have an ObservableCollection<List<Model>> Data in my ViewModel.
In my Page I need a CarouselView, in which each ItemTemplate shows the data of the Data list in a ListView.
Currently, I am doing that in that way:
<CarouselView ItemsSource="{Binding Data}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
...
<ListView ItemsSource="{Binding .}">
...
</ListView>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
In the way I am doing that I get a "Specified cast not valid" exception, in which I see the following additional information:
{System.InvalidCastException: Specified cast is not valid.
at (wrapper castclass) System.Object.__castclass_with_cache(object,intptr,intptr)
at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].ActivateContent (System.Int32 index, System.Object item) [0x00032]
in <62e3629c74b84e3d834046331d2bb5f8>:0
at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].CreateContent (System.Int32 index, System.Object item, System.Boolean insert) [0x00000]
in <62e3629c74b84e3d834046331d2bb5f8>:0
at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].GetOrCreateContent (System.Int32 index, System.Object item) [0x00023]
in <62e3629c74b84e3d834046331d2bb5f8>:0
at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].get_Item (System.Int32 index) [0x0000e]
in <62e3629c74b84e3d834046331d2bb5f8>:0
at Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource.GetCellForPath (Foundation.NSIndexPath indexPath) [0x00007]
in D:\a\_work\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:1397
at Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource.GetCell (UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) [0x00021]
in D:\a\_work\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:1105
at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.Type principalClass, System.Type delegateClass) [0x0003b]
in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:85
at App.iOS.Application.Main (System.String[] args) [0x00001]
in <Path>\Main.cs:18 }
The Model holds only string values, so the exception cannot come from this.
I'm not sure why you're getting that specific exception. I couldn't get the ListView inside of a CarouselView to work either.
However, it works when you use a bindable StackLayout instead of a ListView. My guess is that the bindable StackLayout doesn't support scrolling and thus doesn't fight with the CarouselView but I don't know.
MainPage, MainViewModel and items
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms;
namespace App1
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
public class MainViewModel
{
public ObservableCollection<Item> Data { get; }
public MainViewModel()
{
Data = new ObservableCollection<Item>(GenerateItems());
}
private IEnumerable<Item> GenerateItems()
{
return Enumerable.Range(1, 10)
.Select(a => new Item
{
ItemTitle = $"Item {a}",
SubItems = Enumerable.Range(1, 10).Select(b => new SubItem { SubItemTitle = $"SubItem {b}" }).ToList()
});
}
}
public class Item
{
public string ItemTitle { get; set; }
public List<SubItem> SubItems { get; set; }
}
public class SubItem
{
public string SubItemTitle { get; set; }
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="App1.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1">
<ContentPage.BindingContext>
<local:MainViewModel />
</ContentPage.BindingContext>
<CarouselView ItemsSource="{Binding Data}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding ItemTitle}" />
<StackLayout BindableLayout.ItemsSource="{Binding SubItems}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label Text="{Binding SubItemTitle}" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</ContentPage>
Result:
When I run a Unit Test for my plugin I get the following Exception being Thrown:
Message: Test method Plugins.Tests.UnitTest1.TestUnitPlugin threw exception:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
The following link will give you the stack trace:
Stacktrace
Even when I deploy & register my plugin to the online instance, I would get the same message!!
My Plugin code looks like this:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin takes the data provided in the contract lines and makes Unit Orders.. Inside the unit orders, an Alter Unit Orders table is present.
/// The Alter Unit Orders table describes the daily order for each day in the contract's duration.
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PreOperation,
ExecutionModeEnum.Synchronous,
"title",
"Post-Update Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "title")]
public class UnitPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Wil be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters colletion contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
//obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//Obtain Target Entity Id
var targetId = entity.Id.ToString();
//verify that the target entity represents the the contract entity and is active
if (entity.LogicalName != "contract" && entity.GetAttributeValue<OptionSetValue>("statecode").Value != 0)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
//Get Contract StartDate
DateTime startDate = (DateTime)entity["activeon"];
//Get Contract EndDate
DateTime endDate = (DateTime)entity["expireson"];
//Get all weekdays in the contract duration
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate); //weekdays list
//Get Contract Number
string contractNumber = (string)entity["contractnumber"];
//Query and aggregate each Weekday's order for the 3 different meal times...
//AM SNACK
string unitsum_am = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_amsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_amsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_amsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_amsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_amsum' aggregate='sum' />
<filter type='and' >
<condition value='100000001' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_am_result =
service.RetrieveMultiple(new FetchExpression(unitsum_am));
var am_list = new List<int>();
foreach(var unit in unitsum_am_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_amsum"]).Value);
am_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_amsum"]).Value);
am_list.Add(tuesdaysum);
var wednesdaysum= ((int)((AliasedValue)unit["new_unitswednesday_amsum"]).Value);
am_list.Add(wednesdaysum);
var thursdaysum= ((int)((AliasedValue)unit["new_unitsthursday_amsum"]).Value);
am_list.Add(thursdaysum);
var fridaysum= ((int)((AliasedValue)unit["new_unitsfriday_amsum"]).Value);
am_list.Add(fridaysum);
}
//LUNCH
string unitsum_lunch = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_lunchsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_lunchsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_lunchsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_lunchsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_lunchsum' aggregate='sum' />
<filter type='and' >
<condition value='100000002' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_lunch_result =
service.RetrieveMultiple(new FetchExpression(unitsum_lunch));
var lunch_list = new List<int>();
foreach (var unit in unitsum_lunch_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_lunchsum"]).Value);
lunch_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_lunchsum"]).Value);
lunch_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_lunchsum"]).Value);
lunch_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_lunchsum"]).Value);
lunch_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_lunchsum"]).Value);
lunch_list.Add(fridaysum);
}
//PM SNACK
string unitsum_pm = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_pmsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_pmsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_pmsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_pmsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_pmsum' aggregate='sum' />
<filter type='and' >
<condition value='100000003' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_pm_result =
service.RetrieveMultiple(new FetchExpression(unitsum_pm));
var pm_list = new List<int>();
foreach (var unit in unitsum_pm_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_pmsum"]).Value);
pm_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_pmsum"]).Value);
pm_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_pmsum"]).Value);
pm_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_pmsum"]).Value);
pm_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_pmsum"]).Value);
pm_list.Add(fridaysum);
}
foreach(var day in weekdays)
{
var alterunit = new Entity("new_alterunitorder");
alterunit.Attributes.Add("new_orderdate", DateTime.Parse(day));
switch (day.Split(',')[0])
{
case "Monday":
alterunit.Attributes.Add("new_amsnack", am_list[0]);
alterunit.Attributes.Add("new_lunch", lunch_list[0]);
alterunit.Attributes.Add("new_pmsnack", pm_list[0]);
break;
case "Tuesday":
alterunit.Attributes.Add("new_amsnack", am_list[1]);
alterunit.Attributes.Add("new_lunch", lunch_list[1]);
alterunit.Attributes.Add("new_pmsnack", pm_list[1]);
break;
case "Wednesday":
alterunit.Attributes.Add("new_amsnack", am_list[2]);
alterunit.Attributes.Add("new_lunch", lunch_list[2]);
alterunit.Attributes.Add("new_pmsnack", pm_list[2]);
break;
case "Thursday":
alterunit.Attributes.Add("new_amsnack", am_list[3]);
alterunit.Attributes.Add("new_lunch", lunch_list[3]);
alterunit.Attributes.Add("new_pmsnack", pm_list[3]);
break;
case "Friday":
alterunit.Attributes.Add("new_amsnack", am_list[4]);
alterunit.Attributes.Add("new_lunch", lunch_list[4]);
alterunit.Attributes.Add("new_pmsnack", pm_list[4]);
break;
default:
Console.WriteLine($"An unexpected value ({day.Split(',')})");
break;
}
alterunit.Attributes.Add("new_name", contractNumber); //set the record name
//set the unit order record relation
alterunit.Attributes["new_orderlineid"] =
new EntityReference("new_alterunitorder", unitOrderId);
service.Create(alterunit);
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible.", ex);
}
catch (Exception ex)
{
tracing.Trace("An Error Occured: {0}", ex.ToString());
throw;
}
}
}
}
}
Here is the code for Eachday:
using System;
using System.Collections.Generic;
namespace DCWIMS.Plugins
{
public class Eachday
{
public List<string> WeekDay(DateTime from, DateTime thru)
{
List<string> days_list = new List<string>();
for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
{
days_list.Add(day.ToLongDateString());
if (day.DayOfWeek == DayOfWeek.Sunday || day.DayOfWeek == DayOfWeek.Saturday)
days_list.Remove(day.ToLongDateString());
}
return days_list;
}
}
}
My unit test looks like this:
using DCWIMS.Plugins;
using Microsoft.Crm.Sdk.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
namespace Plugins.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
[TestCategory("Unit Test")]
public void TestUnitPlugin()
{
using (var pipline = new PluginPipeline(FakeMessageNames.Update, FakeStages.PreOperation, new Entity("contract")))
{
var plugin = new UnitPlugin();
pipline.Execute(plugin);
}
}
}
}
Even when I registered the plugin on the actual CRM instance I got this error message:
Here is the log file retrieved from CRM Online!
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>4025e0f8-eed5-4b7f-a3b1-52a9f2a6f2cc</ActivityId>
<ErrorCode>-2147220956</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.</Message>
<Timestamp>2018-07-20T17:47:36.4061434Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText>
[Plugins: DCWIMS.Plugins.UnitPlugin]
[dbb33fa1-448c-e811-815c-480fcff4b5b1: Pre-Create Contract]
An Error Occured: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)
at DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)
</TraceText>
</OrganizationServiceFault
>
Unit testing ain't my forte. But I will try to guide you.
First, my opinion on unit testing. I don't value it much in Dynamics because it's an environment that is hard to control. You can test that your business logic is well implemented in the plugin but a javascript, a business rule, a workflow or an action can derail your logic. Your plugin can say blue but an async workflow change it to red right after.
Instead, I prefer UI testing. It will test the process from A to Z and force the execution of all the components mentioned above. Here's a nice framework I recommend: https://github.com/Microsoft/EasyRepro
That being said. To solve your problem you also need to mock the Organization Service and return fake data. By that, I mean this line:
EntityCollection unitsum_am_result = service.RetrieveMultiple(new FetchExpression(unitsum_am));
Sadly, even if you mock, you can't really parse the fetchXml and validate it.
Then, after returning the fake data you should assert that this line, create an entity with the right sums:
service.Create(alterunit);
I am new to Android and found so many helpful threads on lists. But somehow I am not able to open a new activity on List Item Click.
Till now My app has 4 classes.
Splash- works fine
MyActivity- Works fine
Poems- Works fine
test
Till Poems my app works fine. But on Poems when i click a list item it doesn't do anything.
Here is my code.
Poems.class
package apps.panky.poemsnrhymes;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class Poems extends ActionBarActivity implements AdapterView.OnItemClickListener {
ListView list;
String poems[] = {"Aiken Drum", "A Was an Apple Pie", "A Wise Old Owl", "A-Tisket, A-Tasket"};
#Override
protected void onCreate(Bundle savedInstanceState) {
/** Hiding Title bar of this activity screen */
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
/** Making this activity, full screen */
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_poems);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, poems);
list = (ListView) findViewById(R.id.listView);
list.setAdapter(adapter);
list.setTextFilterEnabled(true);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
Intent myIntent = new Intent(Poems.this, test.class);
startActivity(myIntent);
break;
case 1:
//code specific to 2nd list item
Intent myIntent1 = new Intent(Poems.this, test.class);
startActivity(myIntent1);
break;
default:
break;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_poems, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Here is activity_poems layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#drawable/bg"
tools:context="apps.panky.poemsnrhymes.Poems">
<ListView
android:id="#+id/listView"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="47dp">
</ListView>
Here is Manifest file:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".splash"
android:label="#string/title_activity_splash"
android:theme="#style/Theme.AppCompat.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/Theme.AppCompat.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAINACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".Poems"
android:label="#string/title_activity_poems"
android:theme="#style/Theme.AppCompat.NoActionBar" >
<intent-filter>
<action android:name="apps.panky.poemsnrhymes.POEMS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".test"
android:label="#string/title_activity_test" >
</activity>
</application>
Try assigning yourself as the onItemClickListener with:
list.setAdapter(adapter);
list.setTextFilterEnabled(true);
list.setOnItemClickListener(this);
(The last line is the only addition)
Your onItemClick is never hit because you don't have a way to reach it without that call to setOnItemClickListener. You need to explicitly set yourself as the listener since you implement AdapterView.OnItemClickListener
Many pages in a typical JSF applications are dynamic, meaning that there is a template view that would be used to render every object of a given type. For these pages PrettyFaces rewriting solution works great and effortless. An example is a web application that displays a product, basing on its id, or other unique field. There is typically one view related to such a display, say product.xhtml, and one view parameter, holding the unique field of a product, say name.
With a simple setting we get all requests like /product.xhtml?name=exact-product-name rewritten as, for example, /products/exact-product-name:
The URL mapping:
<url-mapping id="viewProduct">
<pattern value="/products/#{ name : productBean.name }" />
<view-id value="/store/product.xhtml" />
<action> #{ productBean.loadData } </action>
</url-mapping>
The view:
<f:metadata>
<f:viewParam id="name" name="name" required="true" />
</f:metadata>
The model:
public class ProductBean implements Serializable {
private ProductService productService;
private String name;
private Product product;
public String loadData() {
if(!((name == null) || (name.equals(""))) {
Product product = productService.findByName(name);
this.product = product;
return null;
}
return "error";
}
}
However, there are also many pages with static data, that are not templated in a way described above, using view parameters. These pages simply display what was put in them. For example, there may be many articles that were created as separate views (like /pages/articles/article1.xhtml and so on). Using PrettyFaces we would need to create as many URL mapping as the number of such pages. But, in fact this behavior can also be templated in one URL mapping. Unfortunately, this is not supported in current PrettyFaces release.
The proposed enhancement of the PrettyFaces framework is the following:
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleName }" />
<view-id value="/store/#{ articleName }.xhtml" />
</url-mapping>
or, using an ArticleBean (containing, for example, two fields: articleName and articleId, where name is defined in setter of id field as a unique value):
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleId : articleBean.articleId }" />
<view-id value="/store/#{ articleBean.articleName }.xhtml" />
</url-mapping>
or using other predefined dependence based on an EL-expression, which is in turn based on a unique correspondence.
I want to emphasize that this is not going to be a DynaView because there is no uncertainty in the view-id: there is a one-to-one correspondence between a <pattern> and a <view-id>.
What do you think about implementing this feature in PrettyFaces?
I think Stackoverflow is not the right place to discuss proposals for PrettyFaces. You should have a look at the PrettyFaces Support Forum for that.
There are some options for you to implement something like this. IMHO you could try to do this view DynaView. Even if there is a one-to-one relationship between pattern and view-Id like your wrote. However dynaview has some problems especially with outbound rewriting.
But you should have a look at Rewrite, which is the successor of PrettyFaces. With Rewrite it is very simple to implement such a requirement:
.addRule(Join.path("/articles/{articleName}").to("/store/{articleName}.xhtml"))
Have a look at the configuration examples of Rewrite.
As far as the setup of pretty-config.xml doesn’t currently support this feature, there are some workarounds to achieve this functionality. I will describe them below.
A dummy view with <f:event> that handles navigation to the final pages based on a view parameter in a dummy bean.
URL mapping:
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleName : articleBean.articleName }" />
<view-id value="/handle-article-redirection.xhtml" />
</url-mapping>
View handle-article-redirection.xhtml:
<f:metadata>
<f:viewParam id="articleName" name="articleName" required="true" />
<f:event type="preRenderView" listener="#{articleBean.handleRedirect}" />
</f:metadata>
Model:
public class ArticleBean {
private ArticleService articleService;
private String articleName;
private String articleUrl;
public void handleRedirect() {
if(!((articleName == null) || (articleName.equals(""))) {
String url = articleName;
//String url = articleService.getUrlForArticleName(articleName);
//articleUrl = url;
FacesContext.getCurrentInstance().getExternalContext().redirect("/" + url + ".xhtml");
return null;
}
FacesContext.getCurrentInstance().getExternalContext().redirect("/home.xhtml");
}
}
A meaningful view with a dynamic <ui:include> that imports the necessary page content as a snippet, basing on the bean value / view parameter.
URL mapping:
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleName : articleBean.articleName }" />
<view-id value="/article.xhtml" />
</url-mapping>
View article.xhtml:
<f:metadata>
<f:viewParam id="articleName" name="articleName" required="true" />
</f:metadata>
<h:head></h:head>
<h:body>
<ui:include src="/#{articleBean.articleUrl}.xhtml" />
</h:body>
Model:
public class ArticleBean {
private ArticleService articleService;
private String articleName;
private String articleUrl;
public void setArticleName(String articleName) {
this.articleName = articleName;
if((!(articleName == null)) || (articleName.equals("")) {
articleUrl = articleName;
//articleUrl = articleService.getUrlForArticleName(articleName);
} else {
articleUrl = null;
}
}
}
A DynaView URL mapping with a method that returns a proper outcome.
URL mapping:
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleName : articleBean.articleName }" />
<view-id value="#{articleBean.getViewPath}" />
</url-mapping>
No extra view needed.
Model:
public class ArticleBean {
private ArticleService articleService;
private String articleName;
private String articleUrl;
public String getViewPath() {
this.articleName = articleName;
if(!((articleName == null) || (articleName.equals(""))) {
articleUrl = articleName;
//articleUrl = articleService.getUrlForArticleName(articleName);
return articleUrl;
}
return "error";
}
}
A template view that loads the page data from the database, hence there will be no separate views for those pages.
URL mapping:
<url-mapping id="viewArticle">
<pattern value="/articles/#{ articleName : articleBean.articleName }" />
<view-id value="/article.xhtml" />
<action> #{ articleBean.loadData } </action>
</url-mapping>
View article.xhtml:
<f:metadata>
<f:viewParam id="articleName" name="articleName" required="true" />
</f:metadata>
<h:head></h:head>
<h:body>
<h:panelGroup>
#{articleBean.content}
<h:panelGroup>
</h:body>
Model:
public class ArticleBean {
private ArticleService articleService;
private String articleName;
private String articleUrl;
private String content;
public void loadData() {
if(!((articleName == null) || (articleName.equals(""))) {
articleUrl = articleName;
//articleUrl = articleService.getUrlForArticleName(articleName);
content = articleService.getContentForArticleName(articleName);
} else {
articleUrl = null;
content = null;
}
}
}
Write a custom WebFilter or a NavigationHandler.
What is the best alternative, well, it depends. All of them have their pros and cons.
I have a list which is populated by list.dataProvider:
<fx:Script>
<![CDATA[
list.dataProvider = new ArrayCollection(getClass.listArticles(group, subgroup));
]]>
</fx:Script>
<s:List id="list" x="0" y="0" width="100%" height="100%" labelField="ArticleName" change="navigator.pushView(DetailView, list.selectedItem)">
<s:itemRenderer>
<fx:Component>
<s:IconItemRenderer labelField="ArticleName"
messageField="EAN"/>
<s:Image source="#Embed('assets/images/{EAN}.jpg')" width="70" height="70" horizontalCenter="0" verticalCenter="0" />
</fx:Component>
</s:itemRenderer>
<s:layout>
<s:TileLayout columnWidth="200" rowHeight="200" columnAlign="justifyUsingWidth" horizontalGap="10" orientation="rows" verticalGap="10"/>
</s:layout>
</s:List>
labelField and messageField values are properly filled with the result of the dataProvider. The image source variable is not. How do I get/access this variable from the dataProvider ({EAN})?
Maybe by overriding set/get data?
override public function set data(value:Object):void {
super.data = value;
if (!data)
return;
img.source = data['EAN'];
// or maybe: var xml:XML = data as XML; etc.
}