I tried this so far: ```val searchText =Text { ...
# kvision
k
I tried this so far:
Copy code
val searchText =Text {
    placeholder = tr("Search...")
    input.addCssClass("search-text")
}
In the init block of the panel I have:
Copy code
searchText.subscribe {
       MyManager.filter(state.searchQuery.copy(keyword = it))
}
In the Manager: I filtered the original items, using the query object and derived a newObservableList() and trying to update via the dispatch Action
Copy code
MyAction.UpdateItems(newList)
But somehow it goes into recursion :)
r
Is this top Panel bound to the redux store as well?
If so, it's probably rendered on every state change and new subscription is created. The lambda passed to subscribe is called at once (even before any change). So this could result in recursion.
k
Thanks for the reply.. Do you have any sample page which has a Panel bound to a Reduxstore?
To answer your question, I tried .bind on my panel which is an extension of SimplePanel, but couldn't make it work.
r
You can use
bind
or you can use DSL builder with observablestate (e.g. redux store) as a first parameter.
here the SimplePanel is bound to redux store
so on every state change the whole panel is re-rendered
it's different
https://github.com/rjaros/kvision-examples/blob/master/fomantic/src/main/kotlin/com/example/Toolbar.kt#L45 only individual components are bound to the store (in this example is's not redux but stateflow - but it works the same)
k
let me try to pass the store and try... right now I have the state available in all the components..
r
And also componets are not bound to the state itself, but to the "substore" (subflow) - which makes them re-render only when some part of the state is changed
k
I see
r
Of course it's not required for all apps. Most of my apps are created like this first example - with a single binding for the whole app.
It's "fast enough" 😉
but if you use it like this you need to be careful and avoid recursion
instead of using text.subscribe (which is executed at once) you can just use an event handler
Copy code
text.onEvent { 
                input = {
                    // your handler                    
                }
            }
It will be executed only when something is typed in the input box
there should be no recursion
but
the input box will be refreshed as well, so the typed text, focus and caret will be lost
you can initialize value from the state of course, but you can't do anything about the caret
so it's always bad idea to re-render the input when someone is typing
k
will this loose focus when the user types?
ah yes
r
In the patternfly example (https://rjaros.github.io/kvision-examples/patternfly/) where the whole app is bound to the state in the single place the searchbox is working only because nothing is executed when typing (you need to press search button). This could not be changed easily without redesigning the application.
In the fomantinc example (https://rjaros.github.io/kvision-examples/fomantic/), which on the first look works in the same way, it could be easily done, because components are bound to the state on the lower levels.
and the search action could just be dispatched with "input" event (or even subscribe method)
k
but I need to give this dispatch action, for all the filter components right?
in my case two select and one text box,.
For now I will change the interaction with a search button. But onchange event on a select should work I think..
I introduced a button and called the action in onClick.. But the div is refreshing only once..
Copy code
div(className = "row").bind(MyManager.reduxStore) {
    state.myItems?.map {
        displayItem(state, it)
    }
}
r
what is
state
here?
you should use the value passed to your lambda
Copy code
div(className = "row").bind(MyManager.reduxStore) { state ->
// ...
}
this way you get the current state
if you just use some variable, it's probably doesn't change
k
that state variable is the one passed .. which is the Redux Data class
I will shadow or use another variable to see whether it works..
r
the store itself should be in a global variable, but the state is just passed to the lambda used with
bind
you can use
store.getState()
if you really need, but I don't like such constructs 😉
k
with the bind and the variable, it filtered the items, when I pressed the button.. only thing is the input text looses the text too..
r
you can initialize the value of the Text component
from your state
k
is there a way to pass the state.variable in this construct?
Copy code
val searchText =Text {
    placeholder = tr("Search...")
    input.addCssClass("search-text")
}
r
Text(value = ...) { }
k
will that bind?
r
you want to bind Text directly to the state?
k
Well, if I type something in the textbox, and press the button, I need the searchtext preserved..
r
The value is dispatched with an action to the store with
MyManager.filter()
, isn't it?
k
yeah, after the filter finished, this Text searchterm should retain..
r
You should keep the value inside your state
k
I will. but how to bind that value to this construct? Sorry If my question is basic
Copy code
I can see this in example.   val input = textInput(TextInputType.SEARCH, state.search, className = "pf-c-form-control") {
            setAttribute("aria-invalid", "false")
        }
r
and the state is available somewhere as a parameter of your lambda, which re-renders the search box
you should have something like this:
Copy code
Panel().bind(store) { state ->
    select()
    select()
    text()
}
At least that's how I imagine your code 😉
k
Copy code
searchPanel = formPanel<MySearch>(
    className = "form-inline main-search-panel d-flex justify-content-center",
    type = FormType.INLINE
) {
    add(MySearch::keyword, searchText)
r
so now just initialize
text(value = state.searchQuery.keyword)
there is no binding here - so there should be binding on some parent component (something is re-rendering this searchPanel on state change)
(talking about details gets difficult without the broader view 😉)
k
yeah I know it will be difficult to debug over imagination 🙂
Copy code
val searchText =Text(value = state.currentSearch.keyword) {
    placeholder = tr("Search...")
}

searchThingPanel = formPanel<MySearch>(
    className = "form-inline main-search-panel d-flex justify-content-center",
    type = FormType.INLINE
).bind(MyManager.reduxStore) { state->
    add(MySearch::keyword, searchText)
I added .bind in the formPanel
but still it didn;t retain
No worries, I will change it textInput(TextInputType.SEARCH, state.search, ... and refactor the panel, if I couldn't find why the state variable is not binding with formPanel .bind
Atleast now the filter works!
Thank you, by the way!