Demo 2a

Create, Read, Update, Delete

Demo 2a Screenshot

What We Will Learn

The most important demo for custom forms

What Does the Plugin Do?

Plugin Architecture: Three Files, Three Roles

Model

The Model configures inputs to the form. It is used by the Repeater to dynamically create input widgets for each layer attribute in the entries table.

QtObject{ property var fields: [{"name":"name", "label":"Entry Name"}, {"name":"s1", "label":"Thoughts"}, {"name":"s2", "label":"Feelings"}] }

Plugin Component with Model and Controller

The Plugin Component is our View. It contains the Model and Controller.

Item{ id: pluginFrame // not a singleton (has state), must be instantiated FormController{ id: formController } //singleton (static) FormDataModel instance is shared across all plugin instances property var fieldModel: FormDataModel.fields }

Model-View Binding using Repeater

The Repeater element in QML allows you to dynamically create multiple instances of a component based on a model. This is useful for creating forms where the number of sections or fields can vary.

component FormField: Rectangle{ id: formField property var fieldData RowLayout { id: fieldRow Text { text: fieldData.label } TextField { id: input text: controller.fieldValues[fieldData.name] onTextChanged: { controller.updateField(fieldData.name, text) } } } } Repeater { model: fieldModel delegate: FormField { fieldData: modelData } }

⚠️ Warning

Never bypass the QField API

Never access through the data provider

Why?

Always use FeatureUtils & LayerUtils

CREATE: Insert Feature

// 1. Start editing layer.startEditing() // 2. Create empty feature var newFeature = FeatureUtils.createFeature(layer) // 3. Set attributes newFeature.setAttribute("f_uid", StringUtils.createUuid()) newFeature.setAttribute("plot_id", plotId) newFeature.setAttribute("log_date", new Date().toISOString()) // 4. Add feature to layer LayerUtils.addFeature(layer, newFeature) // 5. Commit changes try{ layer.commitChanges() } catch (e) { layer.rollBack() }

READ: Get Feature

var expression = "plot_id = '" + plotId + "'" var iterator = LayerUtils.createFeatureIteratorFromExpression(layer, expression) if (iterator.hasNext()) { currentFeature = iterator.next() iterator.close() // Always close as soon as you are done with the iterator return currentFeature } iterator.close() // Always close

ALWAYS close iterator

UPDATE: Update Feature

// 1. Start editing layer.startEditing() // 2. get the feature id and the fields from your feature var fid = currentFeature.id var fields = currentFeature.fields var fieldName = 'plot_id' var value = 'new_plot_id_value' // 3. Get field index and change value var fieldIndex = fields.indexOf(fieldName) if (fieldIndex >= 0) { layer.changeAttributeValue(fid, fieldIndex, value) } // 4. Commit changes try{ layer.commitChanges() } catch (e) { layer.rollBack() }

DELETE: Delete Feature

// 1. Start editing // 1. Start editing layer.startEditing() // 2. Delete feature var featureId = feature.id var success = layer.deleteFeature(featureId) if (!success) { // 3a. On error: Rollback layer.rollBack() return false } // 3b. On success: Commit var commitSuccess = layer.commitChanges() if (!commitSuccess) { layer.rollBack() }

Important Warning: Field Index

Never use hard-coded indices

Index in QGIS is not the same as index in QField.

❌ WRONG:

value = feature.attribute(3)

✅ CORRECT:

value = feature.attribute("plot_id") // Or for updates: var fieldIndex = fields.indexOf("plot_id") layer.changeAttributeValue(fid, fieldIndex, value)

Exercises for the Next 60 Minutes

Tasks:

1 / 14