Form design: grid of text or numeric input

As a form designer, I want to be able to group several related text or numeric inputs into a grid so that they can be input quickly by enumerators.

There are many kinds of forms that involve collecting several values of the same kind. A simple example:


This would currently need to be represented as 9 different text fields in vertical sequence (e.g. "How many grams of vegetables did you eat for breakfast?").

Several users have asked for grid or table input and included concrete use cases:

I propose we start by supporting just text or numeric input. @Grzesiek2010 had pointed to here, he had previously worked on a grid view for repeats in a Collect fork. Any type is allowed but the input is done in a modal rather than directly in the grid. A similar idea could be used to support all question types in a grid without repeats but I'm not sure the complexity is worth it. My sense is that supporting text and numeric input initially will provide a lot of value and address most cases.

Once there is agreement on the feature's behavior, I will provide proposals for ODK XForms and XLSForm.

Dear Helene,

An ODK XLSForm grid of text or numeric input would help a lot in speeding up the data collection and provide a lot of value. Not just sure on the complexity of how to create such a code. Otherwise its worthy the effort

3 Likes

Dear Hélène

Are you using ODK Build or are you using excel?
If you are using excel place the questions you want in a group (i.e. use the begin group function); then set the appearance of that group to 'table'. You do this by just putting the word table in the appearance column. You will have to do this to all groups of questions you want to have displayed in table form.

Please note that the size of the device screen will determine how easy the form is to use and the amount of scrolling from side to side will increase proportional to the size of the matrix.
So this is really not good for large tables.

There was a discussion on this some time back when ODK was still on google groups, the link is here: Collect data for a table/matrix

I hope if your table is small my quick solution helps.

Cheers

Thank you, @Leangelindiku17! There is currently no such feature in core ODK Collect and XLSForm. From that thread, it sounds like perhaps products derived from Collect have added the functionality but that has not made its way back to the core.

Note that it is possible to build a grid of selects using the list-nolabel and label appearances. See the documentation for more. I am proposing doing something similar but with text input.

Is a grid of text or numeric input something that you have used with other tools? Is it a feature you would be interested in?

1 Like

@martijnr, is this something that would be useful for Enketo users?

Definitely useful. Enketo users can approximate something with the Grid theme in pages mode, but that's not quite perfect. Am curious to see syntax proposals. I don't have one myself at the moment.

A post was split to a new topic: Form design for household roster

Hello there, I am jumping into this thread from the github issue https://github.com/opendatakit/collect/issues/49

I am also interested in both developing and using this feature. As far as the feature's behavior, I'll break down my proposal into using the form in android and creating the XLSForm.

using the form in android

  • For my purposes, I would also need only number and text. I agree with @LN that this will likely add a lot of utility for most people.
  • Since tables will likely need to be bigger than other widgets, we probably need the ability to scroll, both horizontally and vertically
  • In order to scroll and still be able to fill out the form easily, it would be useful to optionally freeze rows or columns (similar to excel's freeze row feature)

creating the XLSForm

  • considering a table might have several repeated rows, it would be a hassle to have to create a new question for each cell in the table. It would be nice to create a question once for each column, and specify the number of rows required to repeat

  • A straightforward way to represent the table in XLSForm would be as a repeat-group with a "table" appearance. The elements inside the repeat-group would be the columns of the table, the labels would be used as column headers. The resulting XForm would look something like this:

<repeat appearance="table" ... </repeat>

This format would make it difficult to include pre-filled cell values (i.e. "Vegetables","Grains", and "Proteins" in LN's original post). The only solution I can think of now is to use a calculation widget for the first column. That would require writing a very long if() function - not exactly ideal.

I am very excited for the development of this feature as it will greatly help the lab I work at!

Any update I really need this

2 Likes

I'd love to see this also!

I'm not sure how this can be accomplished with current XForm datatypes; in particular, it would seem to require introducing an entirely new datatype into XForms (!) Why? because...

In a grid of selects all you are really doing is having several rows of select questions (select1 or select-multi), all of which - as a consequence of being in a grid - should (ie best-practice) have the same labels, and simply only displaying these labels once at the top. Which I think is exactly how @LN explained going about it. At the end of the day, each select1/select-multi question is filled in with the appropriate value for the corresponding column label(s) that were selected; in the case of select-multi, these will be saved a space separated list of values.

However, when allowing text input for each grid cell, now each 'question' - namely a row - may potentially record multiple user-entered text values, and for each value we must record, in the question's corresponding XML instance element, not only the value entered but also the label (or minimally the index) of the column it corresponds to, and this for potentially multiple entered value-columns. There is no existing XForms datatype that represents multiple tuple pairs (index/key & value) in an XML instance element.

So I think the first question probably needs to be how to represent the necessary indexed 1D-array (and no, its not a list!) needed to appropriately store the (entire) value of a grid row in XForms. Until we even know how to represent the result data, it seems a bit premature to worry about how to define a widget to display it :grin:

[oh, we'll face the same issue with a grid of numbers]

All that aside, and going back to the simpler case of a grid of select1/select-multis, I think we already have the means to both represent the result and define the necessary controls to display it in the form. All that might be desirable would be a shortcut for XLSForm to generate the necessary label/nolabel appearances on the select1/select-multi controls in a related grid group. Perhaps, as a strawman, something as simple as <group appearance="grid"...> which when encountered by XLSForm will add the appropriate label or no-label appearance on any selects within that group.
This "grid" appearance could also optionally be use to trigger a more grid-like display of the enclosed select questions; eg vertical bar between select options, line under last select in group, etc (either inheriting the grid appearance from the enclosing group, or adding it explicitly as an additional appearance setting on each select control within it)

Alternatively, if the desire here is to define a (single) widget to capture the entire grid as a single control (and by implication represent it via a single binding, and so further store it in a single XML instance element) then we first have to resolve the issue of how to store a 2D-array - ie an {x,y,value} tuple - which again is an entirely new XForms datatype...

And I think we should probably solve how to store a 1D-array (in XForms) before biting off how to store a 2D-array... :slight_smile:

1 Like

@Xiphware, thanks for the great feedback. I am a bit new here, but I would appreciate if you would oblige my naivety.

Logically, a grid or table seems very similar to a repeat group; each column represents a different question in the repeat group, and each row represents a replicate. Then, rather than introduce a new data type, could we just treat the data as we do with repeat groups?

Edit: This relates more to the grid of text, rather than selects

After re-reading your note I think I understand a bit better...

Yes, you could use a repeat-group to implement the rows of the table, much as you can do today with the current 'table' of selects workaround.

I think what you are proposing is to have an new appearance on a repeat group such that the text/number questions in that group are now displayed horizonally, and without their label (except for the first row, where you want to display the labels, and not the associated text/number entry box). eg in XML:

<group ref="/data/table">
	<label>table</label>
	<repeat appearance="table" nodeset="/data/table/row">
		<label>row name</label>
		<input ref="/data/table/row/column1">
			<label>column1</label>
		</input>
		<input ref="/data/table/row/column2">
			<label>column2</label>
		</input>
		<input ref="/data/table/row/column3">
			<label>column3</label>
		</input>
		<input ref="/data/table/row/column4">
			<label>column4</label>
		</input>
	</repeat>
</group>

would be rendered as (for 3 repeats):

Of course, you'd need some logic to change the row name between each repeat, but this could be accomplished via existing means (eg see this).

2 Likes

BTW, my previous comment, about the need to introduce a new datatype, was driven by the (incorrect?) assumption that, along with displaying the desired grid/table, there is a need to process the resulting dataset in a similar fashion; ie as an array of some sort where the contents of the grid (containing all the users responses) can be retreived conveniently via an index (either 1D or 2D).

If the primary desire is instead just to display a table/grid in a natural fashion for the user to enter data in logical rows and columns, then yes I think this could probably be accomplished via repeat groups for rows, and a suitably appearanced horizontally-displayed group for the columns, as suggested by @David_Kaftan. The problem I see with this is that once you have the data entered, its rather tricky to lookup an individual row+column 'cell' (if, say, you wanted to refer to some particular element in a subsequent calculation): the 'rows' of the grid could potentially be 'indexed' via the index of the associated repeat group (ie position(..)), but there is no index associated with the columns, rather they are just uniquely user-named questions within the 'columns' of the grid (eg ${column1}, ${column2}, ${foo}, ${bar}, ...).

So I imagine this design will make it pretty awkward to reference individual elements in the grid, hence my suggestion of maybe introducing an actual (new) array datatype for such purposes. But again, if the immediate need is merely to display a grid, then I'd agree with @David_Kaftan that this can probably be accomplished via group appearances, without introducing any new aggregate datatypes.

Thanks for the reply. It seems we are understanding each other now! I appreciate you refining my post - looking back now it does seem poorly written. I see your concern with processing the resulting dataset. For my own purposes, displaying the grid is enough for now, and the "awkward" indexing you described would be sufficient (though I agree, awkward). @Shilan_Amin, @Bramleyl, what are your thoughts?

In my biased opinion (my group wants this ASAP), the perfect is the enemy of the good. What are your thoughts on the time difference for "group appearances" vs "new datatype"?

Hi @Xiphware and @David_Kaftan, thanks for keeping this discussion going! I'd certainly agree about the perfect being the enemy of the good - we'd also like this ASAP, and in fact we're already using a set of repeats for the underlying functionality we want, we'd just like to be able to display the questions in grid format.

Apologies if this is a silly question, but regarding the "awkward" indexing, wouldn't it be possible to reference individual inputs with an indexed-repeat() using position(..) and the name of the column required? That's what we're currently doing in our form, so if the plan is to use repeats and just allow a new display format, it should in theory work the same way? If that will still work, it'd be enough for us...I'm curious about how simpler indexing would actually look - but maybe that's getting ahead of ourselves. :slight_smile:

How would adding additional repeat instances (in this case, rows) work - would that be possible to do dynamically with a new row getting added to the display?

Yup, that's exactly how you'd have to do it. You can index specific rows via their numeric position(..), which is all fine; the 'awkwardness' comes from having to reference columns by their arbitrary name/question id.

If the columns of the table are distinct and ostensibly unrelated to each other - for example, name, phone number, address, .. - then there's no issue because you are probably only ever going to reference the columns directly by name in any case.

But what if the columns are inter-related, or in a specific partial order. For example, if the data columns are "Age 0-10", "Age 11-20", "Age 21-30", etc, and say you want to count the number of people under 50. Or your columns are for numeric data entry and you want to find the maximum median value for each row. These problems are easily solved if your dataset is stored in an actual array which you can enumerate via a numeric index, but can be quite awkward, and tedious, to solve if each datum is stored individually in arbitrarily named variables.

Again, I think for the purpose of showing a repeated list of (distinct) questions in a table, that a group/repeat group with a new custom appearance to display the group's question horizontally (without label) is a good workaround (in lieu of perhaps a more general-purpose grid, based on an underlying 1D/2D array datatype). And in line with what folks are already familiar with for a table of selects.

I dont see why this would be any different than what we do today for a regular repeat group. The only twist is how to present the top row, containing the group's question labels (but which is otherwise non-editable). But maybe we can somehow mimic how this is accomplished with select tables today?

Great discussion!

If I understand correctly, the proposal is to add a "table" appearance to a repeat (or a repeat <group> parent as I believe that's where pyxform would put it currently).

This would (1) display the questions inside this repeat as a table-row with the labels of the questions as table-column headings and (2) display the optional repeat label as the row label.

It feels quite hacky to have the first repeat refer to vegetables, and the second repeat refer to grains and the third repeat to proteins. The XML submission won't provide any clue as to what each repeat instance represents. Any thoughts on how we could improve this? One idea is to populate an attribute on the repeat instance with the value of the dynamically-generated label (i.e. vegetables, grains or protein), but that would be a pretty significant feature (on data-collection clients as well as data backends).

How would we deal with rows that are too wide to fit a screen (e.g. low-res mobile in portrait mode)?

1 Like

Why not have it as an appearance on a group in general, regular or repeat. Which basically says, display everything in this group on one line, with the first line being all the control labels, and the second line being all the control the values/entry boxes? This could then be used for a non-repeat, single line grid.

And then handle not duplicating the labels (when it s a repeat group), much the same we done presently with select repeat groups; ie an appearance tag to say hide labels.

thoughts?

1 Like