Use filtered dataProvider contents when FileDownlo

2019-08-29 10:07发布

问题:

I'm trying to download a csv file after applying filters to the DataProvider.

For some reason the filtered results are shown in the Grid, but the downloaded csv file still contains all data.

@AutoView
class FinancialTransactionsView : VerticalLayout(), View {    
    private val grid: Grid<FinancialTransaction>
    private val yearField: ComboBox<Int>

    private val dataProvider = DataProvider.ofCollection(FinancialTransaction.findAll())
    private val fileDownloader: FileDownloader

    init {
        label("Financial Transactions") {
            styleName = ValoTheme.LABEL_H1
        }

        yearField = comboBox("Select Year") {
            setItems(listOf(2016, 2017, 2018))

            addSelectionListener {
                // Filter the data based on the selected year
                if (it.value != it.oldValue) setDataProvider()
            }
        }

        // Create FileDownloader and initialize with all contents in the DataProvider
        fileDownloader = FileDownloader(createCsvResource())

        val downloadButton = button("Download csv") {
            styleName = ValoTheme.BUTTON_PRIMARY

            onLeftClick {
                // The idea here is to assign values from the filtered DataProvider to the FileDownloader
                fileDownloader.fileDownloadResource = createCsvResource()
            }
        }

        fileDownloader.extend(downloadButton)
        fileDownloader.fileDownloadResource = createCsvResource()

        grid = grid(dataProvider = dataProvider) {

            expandRatio = 1f
            setSizeFull()
            addColumnFor(FinancialTransaction::companyId)
            addColumnFor(FinancialTransaction::fiscalYear)
            addColumnFor(FinancialTransaction::fiscalPeriod)
            addColumnFor(FinancialTransaction::currency)
            addColumnFor(FinancialTransaction::finalizedDebitAmountInCurrency)
            addColumnFor(FinancialTransaction::finalizedCreditAmountInCurrency)

            appendHeaderRow().generateFilterComponents(this, FinancialTransaction::class)
        }
    }

    private fun createCsvResource(): StreamResource {
        return StreamResource(StreamResource.StreamSource {

            val csv = dataProvider.items.toList().toCsv()

            try {
                return@StreamSource csv.byteInputStream()
            } catch (e: IOException) {
                e.printStackTrace()
                return@StreamSource null
            }
        }, "financial_transactions.csv")
    }

    private fun setDataProvider() {
        dataProvider.clearFilters()

        if (!yearField.isEmpty)
            dataProvider.setFilterByValue(FinancialTransaction::fiscalYear, yearField.value)
    }
}

toCsv() is an extension function List<FinancialTransaction> which returns a string containing csv data.

What can I do to get the filtered results in my csv file?

回答1:

val csv = dataProvider.items.toList().toCsv()

I am not Kotlin guy, but I assume dataProvider.items is a shorthand to dataProvider.getItems() in Java, i.e. this method (and you use ListDataProvider)

https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/ListDataProvider.html#getItems--

In Vaadin getItems() returns all items by passing all filters.

So instead you should do either of the following

dataProvider.fetch(..)

https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/DataProvider.html#fetch-com.vaadin.data.provider.Query-

Where you give the filters you want to apply in the query, or

grid.getDataCommunicator.fetchItemsWithRange(..)

https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/DataCommunicator.html#fetchItemsWithRange-int-int-

Which returns list of items with filters you have set applied, which I think is ideal for you



回答2:

Thank you for using Vaadin-on-Kotlin!

I've just updated the Databases Guide which should hopefully answer all of your questions. If not, just let me know and I'll update the guides accordingly.

  1. The ListDataProvider.items will not apply any filters and will always return all items. You need to use the getAll() extension function in order to obey the filters set by the Grid. This is now explained in the Exporting data from DataProviders chapter of the Databases Guide.

  2. In your code, both the grid and the yearField will set the filter to the same data provider, thus overwriting values set by each other. Please read the Chaining Data Providers chapter in the Databases Guide to learn how to AND multiple filters set by multiple components.

  3. When you use private val dataProvider = DataProvider.ofCollection(FinancialTransaction.findAll()), that will load all transactions from the database in-memory. You can use a more memory-efficient way: private val dataProvider = FinancialTransaction.dataProvider (given that FinancialTransaction is an Entity)

Please let me know if this answers your questions. Thanks!