Bind an observable property to a collection function? - javafx

I have a class Market which contains a collection of MarketUpdate objects called m_updates. For the UI I am using type-safe builders to create columns in a tableview like so:
override val root = tableview<Market> {
val sortedMarketList = SortedList<Market>(markets)
sortedMarketList.comparatorProperty().bind(this.comparatorProperty())
items = sortedMarketList
...
column("Strikes", Market::m_strikes)
...
The m_strikes property is just a SimpleIntegerProperty directly owned by a Market object. However, I need to be able to build columns like these:
...
column("Created At", Market::m_updates::first::m_time)
...
...
column("Last Update", Market::m_updates::last::m_time)
...
where m_time is a SimpleLongProperty owned by a MarketUpdate object. When a Market object is updated, a new MarketUpdate object is added to the end of the m_updates collection. This means that the binding needs to automatically transition from one object to another, and that the tableview needs to be notified and update itself to reflect the data in the new object. I think binding by way of the first() and last() functions of the collection as described above captures the idea in a very simple way, but it won't compile.
There are many properties like m_strikes and m_time. How can I achieve this gracefully?

If I understand your use case, what you want to do is to create an observable value that represents the time property for the first and last updates in a given Market object. To do that, you can create an objectBinding based on the updates list inside of each Market object, then extract the first() or last() element's timeProperty. In the following example, the TableView will update as soon as you augment the updates list in any Market object.
Bear in mind that the example requires each Market to have at least one update. If this isn't your case, make sure to handle null accordingly.
class Market {
val updates = FXCollections.observableArrayList<MarketUpdate>()
}
class MarketUpdate {
val timeProperty = SimpleObjectProperty(LocalDateTime.now())
}
class MarketList : View("Markets") {
val markets = FXCollections.observableArrayList<Market>()
val data = SortedFilteredList<Market>(markets)
override val root = borderpane {
prefWidth = 500.0
center {
tableview(markets) {
column<Market, LocalDateTime>("Created at", { objectBinding(it.value.updates) { first() }.select { it!!.timeProperty } })
column<Market, LocalDateTime>("Last update", { objectBinding(it.value.updates) { last() }.select { it!!.timeProperty } })
}
}
bottom {
toolbar {
// Click to add an update to the first entry
button("Add update").action {
markets.first().updates.add(MarketUpdate())
}
}
}
}
init {
// Add some test entries
markets.addAll(
Market().apply { updates.addAll(MarketUpdate(), MarketUpdate(), MarketUpdate()) },
Market().apply { updates.addAll(MarketUpdate(), MarketUpdate(), MarketUpdate()) },
Market().apply { updates.addAll(MarketUpdate(), MarketUpdate(), MarketUpdate()) },
Market().apply { updates.addAll(MarketUpdate(), MarketUpdate(), MarketUpdate()) }
)
}
}
I've used a SortedFilteredList to make it easier to deal with sorting. The reason sort works here, is that the columns are actually represented by LocalDateTime values.
I hope this gives you some ideas :)

Related

App crashes when I attempt to have a dynamic value in spinner

I have the following code and was wondering why it crashes the app, I was hoping you could help me figure out what was going on.
The code below is not exact aside from the spinner code.
The idea is to have the spinner racePicker's array populated by FireStore document IDs, as shown below:
val db = FirebaseFirestore.getInstance()
val raceArray = ArrayList()
raceArray.add("Select race...")
db.collection("races").get().addOnSuccessListener {
DocumentSnapshot ->
for (document in DocumentSnapshot) {
raceArray.add(document.id)
Log.e("info", "raceArray contains values $raceArray")
}
This is a rough approximation. I may have set it .toString() or maybe used .addAll vs .add in the raceArray statement.
(I honestly don't remember exactly if this is how I coded it, but it's close enough to give an idea, I'm typing from memory at the moment).
My intention was to use it like so:
racPicker.onSelectedItemListener = object :
OnItemSelectedListener {
override fun onNothingSelected() {
}
override fun onItemSelected(parent: AdapterView<*>?, view:
View?, position: Int, id: Long) {
val selection = parent?.getItemAtPosition(position).toString()
when (selection) {
"$selection" -> raceArray.remove("Select race...").also{
statAllocator(selection) }
}
}
}
For some reason it crashes, but if I assign a literal such as "race name" -> fun, "2nd race name" -> fun, etc it works.
Would it be better to use
if (selectedItem.equals("$selection") {
// do stufd
}
instead? Or is it absolutely necessary to call each and every when case/statement as a literal string? I essentially am looking for a way to have the spinner's selected item (which is an array of document names generated from FireStore database) then "check for itself" and trigger the other functions.

How to set columnOption looping throw all column?

How to set the columnOptions looping through all in angular devexpress module?
at present I do one by one like :
onInitialized(e) {
e.component.columnOption("Id", {
allowHeaderFiltering : false -> 1
})
e.component.columnOption("Name", {
allowHeaderFiltering : false -> 2
})
e.component.columnOption("SaleAmount", {
editorOptions: {
format: "currency",
showClearButton: true
}
});
}
How to look all column and set the columnOptionon each?
If you need to initialize a column with default settings (I believe you need to do this since you provided the onInitialized event handler code), use the customizeColumns callback function. Its parameter is an array of all columns in the grid. Thus, you can traverse through all columns as you do in a regular array.
Since customizeColumns is a callback function, use square brackets to assign a function to it in Angular as described in the Callback Functions section:
*.component.html
<dx-data-grid [customizeColumns]="customizeColumns">
</dx-data-grid>
*.component.ts
export class AppComponent {
customizeColumns (columns) {
columns.forEach(c => c.width = 100);
}
}

NGRX: Composing state when we have to represent data from two lists

Practical example: Consider an expand/collapse list. Each item expands another list.
export interface MainDomainList {
id: number
name: string;
}
export interface SubDomainList {
id: number
name: string;
}
export interface AppState {
mainDomainList: MainDomainList[];
subDomainList: SubDomainList[];
}
On the UI the list should be represented like this:
MainDomainList[1]
SubDomainList[] (entire list)
MainDomainList[2]
Another SubDomainList[] (entire list)
etc..
When the user clicks on the MainDomain[n] there is a call to the backend which returns a list of SubDomain[]. There are no connections between the two of them.
It seems that the most complicated part is that the SubDomains are being loaded one by one on click not all at once, and multiple MainDomains can be open at the same time like in the example above. Also, it should be possible to easily perform CRUD operations on the subDomainList entities.
I tried using a selector which selects an item from the state by id but every time the state is overridden.
My initial idea was to create a separate state in which after the SubDomainList[] is loaded successfully, then I could add the loaded SubDomainList[] by dispatching an 'ADD' action thus adding the entities and the id of the clicked MainDomainList in the newList state as the user clicks on through the list obtaining something like this:
exportt interface AppState {
mainDomainList: MainDomainList[];
subDomainList: SubDomainList[];
newList: NewList[];
}
{
mainDomainList : {
entities: {
md1: {
id: 'md1',
name: '1'
},
md2: {
id: 'md2',
name: '2'
}
}
},
subDomainList : {
entities : {
sd1 : {
id : 'sd1',
name: 'name1'
},
sd2 : {
id : 'sd2',
name: 'name2'
}
},
newList : {
entities : {
md1 : {
id : 'md1',
subDomainList: [{}, {}]
},
md2 : {
id : 'md2',
subDomainList: [{}, {}]
}
}
}
}
Then somehow i would get all the newList entities and match them in the UI with the id of the MainDomainList[n].id
Is my approach correct or is there any other better or less complicated solution for this issue?
I'm fairly new to the subject but I had a lot of headaches trying to figure out how to implement this with ngrx/Entity and failed so far, although it should be a pretty common case. Any help would be much appreciated.
You can write selectors with argument by passing of main domain list
ref: ngrx parameter to select function
and https://blog.angularindepth.com/ngrx-parameterized-selector-e3f610529f8

Binding custom element in initial loading

In aurelia: I have a string interpulation over object property that works fine in the app.html - it shows number of accounts:
ALL ACCOUNTS (${userAccountsData.length})
In the initial loading, I see that the value changes after few milliseconds from 0 to the actual value (data is retrieving from the service), but - when trying to show aggregate data (count number of active accounts) over the same data in a template (custom element) - the data stays as 0 and not updated as the userAccountsData.length
*When refreshing again after the initial loading - the data is shown as it should be.
This is the custom element instance in the app.html:
<account-status-selection-bar accounts-data.bind="userAccountsData"></account-status-selection-bar>
And this is part of the HTML of the custom element itself:
<template>
<div ref="active"
class="selection">${accountActivationDistribution.numberOfActiveAccounts}
This is the relevant part of the custom element VM:
"use strict";
import { bindable} from 'aurelia-framework';
export class accountStatusSelectionBar {
#bindable accountsData;
constructor() {
this.accounts = [];
this.accountActivationDistribution = { numberOfActiveAccounts: 0,
numberOfInactiveAccounts : 0,
numberOfTotalAccounts : 0
}
get activeAccounts() {
var activeAccounts = this.accounts.filter(function(account) {
return account.IsApproved;
});
return activeAccounts.length;
}
attached()//bind()
{
this.accounts = this.accountsData;
this.accountActivationDistribution.numberOfActiveAccounts =
this.activeAccounts
}
In the app.js I use observerLocator - here is the code related to the working part of userAccountsData.length:
constructor() {
this.userAccountsData = [];
....
this.subscribe = this.observerLocator.getObserver(accounts, "all")
.subscribe((value) => {
if (!value)
return;
this.userAccountsData = value;
**A work around I found (although I'm not sure this is the best way) is to do the aggregation in the app.js (in the observer part) in object and bind the already aggregated object to the custom element - this is working. I'm still looking for the mentioned above solution.
It looks like the problem is you're binding userAccountsData to accountsData on your custom control; then assigning this.accounts = this.accountsData; and finally later you're reassigning userAccountsData in app.js.
Because accounts is not observing or bound to the original userAccountsData, it maintains the reference to the original array (which is set to an empty array) and never gets updated.
There is a race condition on refresh, where some cache probably means that userAccountsData gets the updated value before the binding occurs, which is why it works sometimes.
The solution is to remove some of the reassignment and just bind directly to accounts and forget the intermediate accountsData.
I created a gist here showing the different behaviour.

couchdb how to get doc by id in map function

Suppose we have two types of documents. One - stores meta data, many others - have complicated processing, depending on the state of the first document. We do not want to duplicate the state in all documents of the second type because lots of them. How to develop MAP function to receive data from the first-type document.
{ "_id":"123",
"public":true
}
{
"_id":"321",
"owner_id":"123"
"data":"..."
}
function(doc) {
if (doc._id=="321"){
// How do get another document like in python, for example
var doc2 = db[doc.owner_id];
if (doc2.public) {
emit(doc._id, null);
}
}
}
You can do this;
function(doc) {
emit(doc._id, {"_id":doc.owner_id});
}
Then when you do ?key=321&include_docs=true the included doc will be the one with id of doc.owner_id not 321.
More here:
http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents

Resources