Variable repeater columns - asp.net

I have an objectdatasource which i want to bind to a repeater.
the problem is, I cannot work out how to display a variable amount of columns with a variable amount of rows.
for example:
The dataset I have is structured like this. The objectdatasource is a List<item>.
item {
string name;
List<itemdata> data;
}
itemdata {
DateTime year;
double amount;
}
so basically i want to make a table
| year | year | year | year
name | amount | amount | amount | amount
name | amount | amount | amount | amount
name | amount | amount | amount | amount
name | amount | amount | amount | amount
The number of items are variable, as well as the number of itemdata that the item contains.
Hope someone can point me in the right direction.
Thanks

The solution to your problem will require three different repeaters, one of which is nested inside another. Begin with the markup like this.
<table>
<tr class="headerRow">
<td> </td>
<asp:Repeater ID="rptYearHeader" runat="server" OnItemDataBound="rptYearHeader_ItemDataBound">
<ItemTemplate>
<td class="header"><asp:Literal ID="litYear" runat="server"></asp:Literal></td>
</ItemTemplate>
</asp:Repeater>
</tr>
<asp:Repeater ID="rptName" runat="server" ItemDataBound="rptName_ItemDataBound">
<ItemTemplate>
<tr>
<td><asp:Literal ID="litName" runat="server"></asp:Literal></td>
<asp:Repeater ID="rptAmounts" runat="server" OnItemDataBound="rptAmounts_ItemDataBound">
<ItemTemplate>
<td><asp:Literal ID="litAmount" runat="server"></asp:Literal></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
Binding to this can be a little tricky. The idea is, first we bind the header row - then we bind down the data rows and across the columns. You will want to handle the data binding through code behind using the OnItemDataBound event so that you can wire up the nested repeater with the necessary data.
First we bind the header row with Years. You need to isolate a collection of unique years present in your datasource and keep it in a private variable. You will need to access it during the data binding of the other repeaters later. This will serve as the data source for the header row, creating one cell/column for each year.
List<DateTime> _Years = dataSource.SelectMany(x => x.data).GroupBy(y => y.Year);
rptYear.DataSource = _Years;
rptYear.DataBind();
Now, you need to bind the Name repeater with your original data source. Something like
rptName.DataSource = dataSource;
rptName.DataBind();
This will create one row for each item in your list.
During the OnItemDataBound event for this repeater, you will need to bind the nested repeater to a list of fiscal years - one per each fiscal year in our _Years variable - with any applicable data from the current row's data item. This gets a little tricky, but I'll try to explain:
protected void rptName_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// get the data item being bound
item currentItem = e.Item.DataItem as item;
// bind the item's name to the literal
//...
//
// get a list of amounts to bind to the nested repeater
// because we cant be sure that every item has amount for all years
// we create a list that we know has all years and plug in the items
// data accordingly.
List<double> amounts = new List<double>();
for (int i = 0; i < _Years.Count; i++)
{
// check whether the current item has data for the year
dataItem di = currentItem.data.Where(d => d.Year == _Years[i]).FirstOrDefault();
if(di == null)
{
// the year did not exist, so we add an amount of 0
amounts.Add(0);
}
else
{
// the year did exist, so we add that year's amount
amounts.Add(di.amount);
}
}
// we now have a list of amounts for all possible years, with 0 filling in
// where the item did not have a value for that year
// bind this to the nested repeater
rptAmounts.DataSource = amounts;
rptAmounts.DataBind();
}
Good luck.
I have had to pull this off with multiple nested repeaters for sub-total and grand total rows before. I started seeing nested repeaters in my sleep.

I suggest you convert your data into a new data structure like this:
name_data {
string name;
int[] amounts;
}
You would then bind your repeater to a List(name_data>.
To create this, first, iterate through item and data lists and keep a list (a List probably) of all the unique years you need to report on. Sort the resulting list so that the years are in order. Now, the indexes of the year list correspond to the column numbers in your output table. Next, iterate through the item list again, this time creating a new name_data object for each item object. The name_data constructor would look like this:
public name_data(string name, int yearCount) {
this.name = name;
amounts = new int[yearCount];
}
The yearCount is the number of items in the year list.
Finally, step through the data list for the current item, look up the year in the year list to get the index, then stick the amount value in the amount field in the corresponding amounts slot. Add your completed name_data to the List.
Once you are done, you should be able to bind your name_data list to your repeater

Related

Add fields to ACF repeater based on other repeater

I've seen a few of these, but nothing really like what I'm working on. I have a client creating comparison charts/tables. Repeater #1 creates the titles for the (and therefore the number of) rows. Repeater #2 can create up to five columns to compare. Within Repeater 2 is Repeater #3, which corresponds to the titles in Repeater #1. I'd like a way to set the min and max number of rows for Repeater #3 to the row count for repeater #1 (so it's impossible for them to screw it up).
If I figure it out, I'll post the answer, but if anyone has a solution or ideas, would appreciate it.
If I understand the question correctly I think this might work for you.
https://www.advancedcustomfields.com/resources/acf-load_field/
function my_acf_load_field($repeater_3) {
// Count Rows in Repeater 1
$count = count(get_field('repeater_1', $pg_id));
// Set Min & Max equal to count of Repeater 1
$repeater_3['min'] = $count;
$repeater_3['max'] = $count;
// Return Field
return $field;
}
add_filter('acf/load_field/name=repeater_3', 'my_acf_load_field');

javascript for Array how many and min

I am trying to create a form in Acrobat. I want it to do some calculations. I got almost all of them done aside from 2.
I have an array of cells DF1 to DF78 so I need a calculation script that will give me the minimum value in that array not counting the blank ones.
In the same array of cells DF1 to DF78 I need a calculation script to find how many fields in that array have value and bring me up the number.
I already tried using the min option on the acrobat DC and selecting the fields. Ii want to look at DF1 to DF78. However, it always shows 0 because it's counting the empty fields as well.
I tried looking online, but all the scripts that they show are very confusing. I can't find where to put the array in there.
I wish I had a script to put it in here... sorry.
I have fields DF1 to DF78 so a total of 78 fields, and I need to find the minimum value in that array not including the fields that are blank.
Another script for the same fields DF1 to DF78 needs to count how many of the fields actually have data ex: DF1, DF2, DF3 had data on it and the rest are empty so it should display the number 3 because 3 of the 78 fields have data in them.
I hope somebody can help me with this.
This should work... Add it to the calculate action of a new hidden field you want the numbers to show up. Fix the names on the last two lines first.
valueArray = [];
for (var i = 1; i <= 78 ; i++) {
//Get the fieldvalue by assembling the name with the prefix and the number increment
var fieldVal = this.getField("DF"+i).value;
//Acrobat field values are never null. The value of a blank field is an empty string
if (fieldVal != "") {
//Add non-empty field values to an Array.
valueArray.push(fieldValue);
}
}
// Get the minimum value in the array.
var minValue = Math.min.apply(null, valueArray);
// Get the number of non-blank fields.
var nonBlankFields = valueArray.length;
this.getField("RESULT FOR YOUR 1st QUESTION FIELD NAME HERE").value = minValue;
this.getField("RESULT FOR YOUR 2nd QUESTION FIELD NAME HERE").value = nonBlankFields;

Inserting listview items individually into database as new entries

Tl:dr
I have a listview with items. I want each individual item inserted into my sqlite database as a new entry. Right now, I am only able to insert all items into the database as a single entry.
I am able to populate the list from my database correctly. If I manually input the items in the SqliteStudio. The added items will show up as an individual item.
Code settings up the list
private ObservableList listchosedescription;
listchosedescription = FXCollections.observableArrayList();
this.descriptionschosen.setItems(listchosedescription);
Code for populating the list
while (result.next()) {
listchosedescription.add(result.getString("description"));
}
descriptionschosen.setItems(listchosedescription);
Faulty code for adding listview items to the database
Connection conn = dbConnection.getConnection();
PreparedStatement statement2 = conn.prepareStatement(sqlDesInsert);
statement2.setString(1, String.valueOf(descriptionschosen.getItems()));
statement2.setInt(2, Integer.parseInt(labelidnew.getText()));
statement2.execute();
From looking online. I think that I need a for-loop counting the individual items in the list.
for(int i = listchosedescription.size(); i != 0; i--){
Then I need to add each individual entry to a batch and then execute the batch.
I also understand how to get a single item from the listview. So I feel a little stuck, hence I thought I would post for guidance.
for (int i = listchosedescription.size(); i != 0; i--) {
statement2.setString(1, String.valueOf(listchosedescription.subList(i - 1, i)));
statement2.setInt(2, Integer.parseInt(labelidnew.getText()));
statement2.addBatch();
}
statement2.executeBatch();
In this for-loop, I have three statements:
I create an integer (i) which counts the size() of my observableList.
I run the loop as long as the size() is not equal to 0 (should probably be as long as it is larger than zero).
I decrease my integer (i) by 1 each time the loop is run.
Inside the loop, I add my two statements as I normally would. But the values from the observableList are accessed by using its subList. I acess the location using my integer (i).
i-1 will make sure I reach the correct fromIndex.
i will make sure I reach the correct toIndex.
Lastly, I add to the batch inside the loop and execute the batch after the loop.

Flex - sorting a datagrid column by the row's label

I'm creating a table that displays information from a MySQL database, I'm using foreignkeys all over the place to cross-reference data.
Basically I have a datagrid with a column named 'system.' The system is an int that represents the id of an object in another table. I've used lableFunction to cross-reference the two and rename the column. But now sorting doesn't work, I understand that you have to create a custom sorting function. I have tried cross-referencing the two tables again, but that takes ~30sec to sort 1200 rows. Now I'm just clueless as to what I should try next.
Is there any way to access the columns field label inside the sort function?
public function order(a:Object,b:Object):int
{
var v1:String = a.sys;
var v2:String = b.sys;
if ( v1 < v2 ){
trace(-1);
return -1;
}else if ( v1 > v2 ){
trace(1);
return 1;
}else {
trace(0);
return 0;
}
}
One way to handle this is to go through the objects you received and add the label as a property on each of them based on the cross-referenced id. Then you can specify your label property to display in your data grid column instead of using a label function. That way you would get sorting as you'd expect rather than having to create your own sort function.
The way that DataGrids, and other list based classes work is by using itemRenderers. Renderers are only created for the data that is shown on screen. In most cases there is a lot more data in your dataProvider than what is seen on screen.
Trying to sort your data based on something displayed by the dataGrid will most likely not give you the results you want.
But, there is no reason you can't call the same label function on your data objects in the sortFunction.
One way is to use the itemToLabel function of the dataGrid:
var v1:String = dataGrid.itemToLabel(a);
var v2:String = dataGrid.itemToLabel(b);
A second way is to just call the labelFunction explicitly:
var v1:String = labelFunction(a);
var v2:String = = labelFunction(b);
In my experience I have found sorting to be extremely quick, however you're recordset is slightly larger than what I usually load in memory at a single time.

how to show data at group level in flex advanced grid?

I am working on a grid example in flex using advanced grid control.
I know we can easily group data by specifying the field name.
At the group node level, other than the gorup name I want to be able to show data in the rest of the cells ( calculated data ) and I am looking for some dataRowBound event or similar to be able to hook some data in it.
Example: Grid displaying list of towns grouped by state. At the group level ( for each state) I want to show the total number of towns in each state. Here how can i show the total number in the town column.
You can do this by providing the data like this
<trade TrdId="Trade 1 o" col="0xCC9999" cmenu="YNYNYNYNYNYNYNY" AgreementId="1234">
<trade TrdId="Trade 1.1" col="0xCC9999" cmenu="YNYNYNYNYNYNYNY" AgreementId="1234">
</trade>
<trade TrdId="Trade 1.2"col="0xCC9999" cmenu="YYYYYYYYNYNYYYY" AgreementId="1234">
</trade>
</trade>
And adding columns which read this data like
advancedDataGridColumn.dataField="#TrdId"
so on...
protected override function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void{
var XMLdata:XML=rowNumberToData(dataIndex) as XML;
if(XMLdata!=null){
if(XMLdata.attribute(Constants.col) != undefined && XMLdata.attribute(Constants.col) != ""){
color=XMLdata.attribute(Constants.col);
}else{
color=0xFFFFFF;
}
}
super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}
this is best method to get data out of grid and do some processing...

Resources