Tornadofx - Как передать параметр фрагменту для каждого экземпляра

Я новичок в javafx, kotlin и, очевидно, tornadofx.
Проблема:
Как передать параметры фрагменту в каждом экземпляре?

Допустим, у меня есть макет табличного представления в качестве фрагмента. Теперь этот фрагмент используется в нескольких местах, но с разными наборами данных.

например. Добавление фрагмента в:

class SomeView : View() {
... 
root += SomeViewFragment::class
}

class SomeAnotherView : View() {
... 
root += SomeViewFragment::class
}

Объявление фрагмента:

class SomeViewFragment : Fragment() {
...
    tableview(someDataSetFromRestApiCall) {
    ...
    }
}

Как передать разные someDataSetFromRestApiCall из SomeView и SomeAnotherView?


person niteesh    schedule 17.12.2016    source источник


Ответы (1)


Начнем с наиболее явного способа передачи данных во фрагменты. В этом примере TableView вы можете открыть наблюдаемый список внутри фрагмента и связать свой TableView с этим списком. Затем вы можете обновить этот список вне фрагмента, и ваши изменения будут отражены во фрагменте. В этом примере я создал простой объект данных с наблюдаемым свойством SomeItem:

class SomeItem(name: String) {
    val nameProperty = SimpleStringProperty(name)
    var name by nameProperty
}

Теперь мы можем определить SomeViewFragment со свойством элемента, привязанным к TableView:

class SomeViewFragment : Fragment() {
    val items = FXCollections.observableArrayList<SomeItem>()

    override val root = tableview(items) {
        column("Name", SomeItem::nameProperty)
    }
}

Если вы позже обновите содержимое элементов, изменения отразятся в таблице:

class SomeView : View() {
    override val root = stackpane {
        this += find<SomeViewFragment>().apply {
            items.setAll(SomeItem("Item A"), SomeItem("Item B"))
        }
    }
}

Затем вы можете сделать то же самое для SomeOtherView, но с другими данными:

class SomeOtherView : View() {
    override val root = stackpane {
        this += find<SomeViewFragment>().apply {
            items.setAll(SomeItem("Item B"), SomeItem("Item C"))
        }
    }
}

Хотя это легко понять и очень ясно, это создает довольно сильную связь между вашими компонентами. Возможно, вы захотите вместо этого рассмотреть возможность использования областей видимости. Теперь у нас есть два варианта:

  1. Использовать инъекцию внутри прицела
  2. Пусть объем содержит данные

Использовать инъекцию внутри прицела

Сначала мы воспользуемся вариантом 1, внедрив модель данных. Сначала мы создаем модель данных, которая может содержать наш список элементов:

class ItemsModel(val items: ObservableList<SomeItem>) : ViewModel()

Теперь мы вставляем эту ItemsModel в наш фрагмент и извлекаем элементы из этой модели:

class SomeViewFragment : Fragment() {
    val model: ItemsModel by inject()

    override val root = tableview(model.items) {
        column("Name", SomeItem::nameProperty)
    }
}

Наконец, нам нужно определить отдельную область для фрагментов в каждом представлении и подготовить данные для этой области:

class SomeView : View() {

    override val root = stackpane {
        // Create the model and fill it with data
        val model= ItemsModel(listOf(SomeItem("Item A"), SomeItem("Item B")).observable())

        // Define a new scope and put the model into the scope
        val fragmentScope = Scope()
        setInScope(model, fragmentScope)

        // Add the fragment for our created scope
        this += find<SomeViewFragment>(fragmentScope)
    }
}

Обратите внимание, что использованная выше функция setInScope будет доступна в TornadoFX 1.5.9. А пока вы можете использовать:

FX.getComponents(fragmentScope).put(ItemsModel::class, model)

Пусть объем содержит данные

Другой вариант - поместить данные прямо в область видимости. Вместо этого создадим ItemsScope:

class ItemsScope(val items: ObservableList<SomeItem>) : Scope()

Теперь наш фрагмент ожидает получить экземпляр SomeItemScope, поэтому мы приводим его и извлекаем данные:

class SomeViewFragment : Fragment() {
    override val scope = super.scope as ItemsScope

    override val root = tableview(scope.items) {
        column("Name", SomeItem::nameProperty)
    }
}

Теперь View нужно делать меньше работы, так как нам не нужна модель:

class SomeView : View() {

    override val root = stackpane {
        // Create the scope and fill it with data
        val itemsScope= ItemsScope(listOf(SomeItem("Item A"), SomeItem("Item B")).observable())

        // Add the fragment for our created scope
        this += find<SomeViewFragment>(itemsScope)
    }
}

Передача параметров

РЕДАКТИРОВАТЬ: в результате этого вопроса мы решили включить поддержку передачи параметров с помощью find и inject. Таким образом, начиная с TornadoFX 1.5.9, вы можете отправить список элементов в виде следующего параметра:

class SomeView : View() {
    override val root = stackpane {
        val params = "items" to listOf(SomeItem("Item A"), SomeItem("Item B")).observable()
        this += find<SomeViewFragment>(params)
    }
}

SomeViewFragment теперь может выбирать эти параметры и использовать их напрямую:

class SomeViewFragment : Fragment() {
    val items: ObservableList<SomeItem> by param()

    override val root = tableview(items) {
        column("Name", SomeItem::nameProperty)
    }
}

Обратите внимание, что это связано с неконтролируемым приведением внутри Фрагмента.

Другие варианты

Вы также можете передавать параметры и данные через EventBus, который также будет скоро выпущен TornadoFX 1.5.9. EventBus также поддерживает области, что упрощает нацеливание на ваши события.

дальнейшее чтение

Вы можете узнать больше о Scopes, EventBus и ViewModel в руководстве:

Области

EventBus

ViewModel и Validation

person Edvin Syse    schedule 17.12.2016
comment
В результате этого вопроса мы сейчас обсуждаем, должны ли мы добавить поддержку параметров в компонентах, вроде параметров HTTP-запроса, что сделает еще более удобной передачу сообщений между представлениями, фрагментами и даже контроллерами :) - person Edvin Syse; 17.12.2016
comment
Мы только что добавили параметр params, я обновил свой ответ выше. - person Edvin Syse; 17.12.2016
comment
УХ ТЫ!! Спасибо за такое прекрасное объяснение. Я только что реализовал использование инъекции внутри области видимости, и это работает как шарм. - person niteesh; 17.12.2016
comment
Я благодарю вас, ребята, за такую ​​отличную работу, за мгновенную поддержку и помощь. Спасибо Эдвину и команде :) - person niteesh; 17.12.2016
comment
Спасибо, это очень много значит. Все положительные отзывы, которые мы получаем, делают чрезвычайно интересным продолжать работу над этим фреймворком :) И ваш вопрос породил некоторые новые функции, так что я благодарен за это. - person Edvin Syse; 17.12.2016