Hi,
Does Flow UI support fragments?
Hi Jimmy,
No, there is no direct equivalent of Frame in Flow UI.
But we are going to provide some recommendations on how to create reusable parts of views in Flow UI.
Thanks for your quick response
Looking forward to seeing related articles soon
Hi Konstantin, can these recommendations be found somewhere?
Also, your take on tackling composite records (one-to-many composite, typical example Order vs OrderProducts) within FlowUI detail view would be appreciated. There seems to be no straightforward way how to address master-detail views in either list or detail views and this is much needed in many real-world business scenarios. Thanks in advance!
J.
After upgrading to FlowUI I basically built my own fragments to be able to actually migrate my non-FlowUI project…
For basic (!) fragments it’s not that hard either. For the controllers you just have to inherit from View or some other abstract class that inherits from it - I created a ScreenFragment class. And then you need a custom fragment loader.
Maybe this kotlin code can help you a bit:
open class ScreenFragment : View<ViewLayout>(), HasSize, HasViewProperties {
val view: View<ViewLayout>
get() = this
val viewProperties: MutableMap<String, String> = mutableMapOf()
override fun initContent(): ViewLayout {
val content = super.initContent()
return content
}
override fun addProperty(key: String, value: String) {
viewProperties[key] = value
}
}
open class FragmentElementLoader : AbstractComponentLoader<View<*>>() {
val datatypeRegistry by lazy {
applicationContext.getBean(DatatypeRegistry::class.java, context)
}
// <app:fragment id="searchTermFragment" screen="SearchTermFragment" width="AUTO"/>
override fun loadComponent() {}
override fun createComponent(): View<*> {
val viewId = element.attributeValue("screen")
val viewRegistry = applicationContext.getBean(ViewRegistry::class.java)
val viewInfo = viewRegistry.getViewInfo(viewId)
val controllerClass = viewInfo.controllerClass
resultComponent = factory.create(controllerClass)
loadId(resultComponent, element)
resultComponent.let { resultComponent ->
if (resultComponent is HasSize) {
componentLoader().loadSizeAttributes(resultComponent, element)
}
if (resultComponent is HasViewProperties) {
val propertiesEl = element.element("properties")
if (propertiesEl != null) {
for (propertyEl in propertiesEl.elements("property")) {
loaderSupport.loadString(propertyEl, "name") { name ->
val value = propertyEl.attributeValue("value")
resultComponent.addProperty(name, value)
}
}
}
}
}
// inject data containers from parent
//ViewControllerDependencyInjector().inject()
ViewControllerUtils.fireEvent(resultComponent, ReadyEvent(resultComponent))
return resultComponent
}
}
Not everything works as usual in theses fragments. You can’t easily use the parent data context for example. You have to inject the data context from the parent after initialization.
We’ve just published a new example of creating composite components: https://demo.jmix.io/ui-samples/sample/composite-component
They can be a viable alternative to fragments.
Following the example of composite-component, I have the impression this is very heavy to write a schema and xsd for every potential screen that needs to be embedded in a view (for example a view with multiple tabs, where each tab correspond to a potential screen).
Are there better alternatives?
We are going to provide the ability to describe composite component layout in XML similar to views, see XML descriptor for composite component · Issue #2707 · jmix-framework/jmix · GitHub
It will make composite components very close to Classic UI fragments.