]> gitweb.hamatoma.de Git - flutter_bones.git/commitdiff
daily work: validation works
authorHamatoma <author@hamatoma.de>
Wed, 11 Nov 2020 19:13:23 +0000 (20:13 +0100)
committerHamatoma <author@hamatoma.de>
Thu, 12 Nov 2020 22:27:35 +0000 (23:27 +0100)
53 files changed:
data/rest/custom.configuration.yaml [new file with mode: 0644]
lib/app.dart
lib/flutter_bones.dart
lib/src/helper/string_helper.dart
lib/src/model/all_db_fields_model.dart
lib/src/model/button_model.dart
lib/src/model/checkbox_model.dart
lib/src/model/column_model.dart
lib/src/model/combo_base_model.dart
lib/src/model/combobox_model.dart
lib/src/model/db_reference_model.dart
lib/src/model/empty_line_model.dart
lib/src/model/field_model.dart
lib/src/model/model_base.dart
lib/src/model/module_model.dart
lib/src/model/page_model.dart
lib/src/model/section_model.dart
lib/src/model/standard/configuration_model.dart
lib/src/model/standard/menu_model.dart
lib/src/model/standard/role_model.dart
lib/src/model/standard/user_model.dart
lib/src/model/table_model.dart
lib/src/model/text_field_model.dart
lib/src/model/text_model.dart
lib/src/model/widget_model.dart
lib/src/page/configuration/configuration_change_page.dart
lib/src/page/configuration/configuration_controller.dart
lib/src/page/configuration/configuration_create_page.dart
lib/src/page/configuration/configuration_list_page.dart
lib/src/page/menu/menu_change_page.dart
lib/src/page/menu/menu_controller.dart
lib/src/page/menu/menu_create_page.dart
lib/src/page/menu/menu_list_page.dart
lib/src/page/role/role_change_page.dart
lib/src/page/role/role_controller.dart
lib/src/page/role/role_create_page.dart
lib/src/page/role/role_list_page.dart
lib/src/page/user/user_change_page.dart
lib/src/page/user/user_controller.dart
lib/src/page/user/user_create_page.dart
lib/src/page/user/user_list_page.dart
lib/src/page/user/user_login_page.dart
lib/src/page/user/user_password_page.dart
lib/src/persistence/persistence_cache.dart
lib/src/private/bdrawer.dart
lib/src/widget/page_controller_bones.dart
lib/src/widget/view.dart
model_tool/lib/src/net/email.dart [new file with mode: 0644]
test/helpers/string_helper_test.dart
test/model/model_test.dart
test/model/standard_test.dart
test/persistence_cache_test.dart
test/widget/widget_validators_test.dart

diff --git a/data/rest/custom.configuration.yaml b/data/rest/custom.configuration.yaml
new file mode 100644 (file)
index 0000000..fe2d1a5
--- /dev/null
@@ -0,0 +1,13 @@
+---
+# configuration of the bones backend for configuration:
+created: 2020.11.11 11:47
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+  - module: configuration
+    sqlInfos:
+      - name: combobox_by_scope
+        type: list
+        sql: "SELECT configuration_id,configuration_property,configuration_value FROM configuration
+          WHERE configuration_scope=:scope
+          ORDER BY configuration_order;"
index d79e985add9288445892dff73076fccba27fe732..452ebfe9d1d9e73fe8bb277b0f66909dc69ee8dd 100644 (file)
@@ -2,17 +2,17 @@ import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
 
 import 'src/helper/settings.dart';
-import 'src/page/demo_page.dart';
 import 'src/page/async_example_page.dart';
+import 'src/page/configuration/configuration_create_page.dart';
+import 'src/page/configuration/configuration_list_page.dart';
+import 'src/page/demo_page.dart';
+import 'src/page/menu/menu_create_page.dart';
+import 'src/page/menu/menu_list_page.dart';
 import 'src/page/role/role_create_page.dart';
 import 'src/page/role/role_list_page.dart';
 import 'src/page/user/user_create_page.dart';
 import 'src/page/user/user_list_page.dart';
 import 'src/page/user/user_login_page.dart';
-import 'src/page/menu/menu_list_page.dart';
-import 'src/page/menu/menu_create_page.dart';
-import 'src/page/configuration/configuration_create_page.dart';
-import 'src/page/configuration/configuration_list_page.dart';
 import 'src/private/bsettings.dart';
 
 class BoneApp extends StatefulWidget {
@@ -76,10 +76,10 @@ Route<dynamic> _getRoute(RouteSettings settings) {
       page = ConfigurationCreatePage(BSettings.lastInstance.pageData);
       break;
     case '/menu/list':
-      page = MenuCreatePage(BSettings.lastInstance.pageData);
+      page = MenuListPage(BSettings.lastInstance.pageData);
       break;
     case '/menu/create':
-      page = MenuListPage(BSettings.lastInstance.pageData);
+      page = MenuCreatePage(BSettings.lastInstance.pageData);
       break;
     case '/user/login':
     default:
index f2760bfec9a7c2bd2eb0a17062078b00b333cf32..18f3e844781834395cf29f389002ee3ac9d37167 100644 (file)
@@ -2,12 +2,13 @@
 ///
 /// Allows a model driven design of flutter apps:
 /// The design of the screens is done by a yaml file (or a compatible map).
+export 'src/helper/helper_async.dart';
 export 'src/helper/settings.dart';
 export 'src/helper/string_helper.dart';
 export 'src/helper/validators.dart';
-export 'src/helper/helper_async.dart';
 export 'src/model/button_model.dart';
 export 'src/model/checkbox_model.dart';
+export 'src/model/combo_base_model.dart';
 export 'src/model/combobox_model.dart';
 export 'src/model/empty_line_model.dart';
 export 'src/model/field_model.dart';
@@ -17,22 +18,22 @@ export 'src/model/module_model.dart';
 export 'src/model/page_model.dart';
 export 'src/model/section_model.dart';
 export 'src/model/standard/configuration_model.dart';
+export 'src/model/standard/menu_model.dart';
 export 'src/model/standard/role_model.dart';
 export 'src/model/standard/standard_modules.dart';
 export 'src/model/standard/user_model.dart';
-export 'src/model/standard/menu_model.dart';
 export 'src/model/text_field_model.dart';
 export 'src/model/text_model.dart';
 export 'src/model/widget_model.dart';
-export 'src/page/login_page.dart.01';
 export 'src/page/application_data.dart';
+export 'src/page/configuration/configuration_create_page.dart';
+export 'src/page/login_page.dart.01';
 export 'src/page/role/role_create_page.dart';
 export 'src/page/user/user_create_page.dart';
-export 'src/page/configuration/configuration_create_page.dart';
 export 'src/page/user_page.dart';
 export 'src/persistence/persistence.dart';
-export 'src/persistence/rest_persistence.dart';
 export 'src/persistence/persistence_cache.dart';
+export 'src/persistence/rest_persistence.dart';
 export 'src/widget/raised_button_bone.dart';
 export 'src/widget/simple_form.dart';
 export 'src/widget/text_form_field_bone.dart';
index 484b30874eb9799e917ea8f0fb38bb1207a2de91..ea9e5ee57f84d4bc75cf0474f4b82799d928e785 100644 (file)
@@ -128,6 +128,28 @@ class StringHelper {
     return rc;
   }
 
+  /// Replaces all placeholders defined by [syntaxPlaceholder] or [regExpPlaceholder]
+  /// in [source] and return this result.
+  /// [syntaxPlaceholder] or [regExpPlaceholder] must contain a single group
+  /// identifying the name of the placeholder.
+  /// Example:
+  /// replacePlaceholders('Hi ~user~', { "user": "joe"}, syntaxPlaceholder: r'~(\w+)~')
+  /// returns "Hi Joe".
+  static String replacePlaceholders(
+      String source, Map<String, String> placeholders,
+      {String syntaxPlaceholder, RegExp regExpPlaceholder}) {
+    regExpPlaceholder ??= RegExp(syntaxPlaceholder);
+    final rc =
+        regExpPlaceholder.allMatches(source).fold(source, (string, match) {
+      final name = match.group(1);
+      final rc2 = placeholders.containsKey(name)
+          ? string.replaceFirst(match.group(0), placeholders[name])
+          : string;
+      return rc2;
+    });
+    return rc;
+  }
+
   /// Splits a argument list into an [option] list and a true arguments list.
   /// [args]: the argument list to split.
   /// [options]: OUT: the options list
index f1c6eb222e4ad7b2a69d28cfae9f4651fa5aa1b7..cc1530d064c6aa267e61beacbca4d0f5c4da50a8 100644 (file)
@@ -9,14 +9,12 @@ import 'widget_model.dart';
 /// Describes a text widget without user interaction.
 class AllDbFieldsModel extends WidgetModel {
   static final regExprOptions = RegExp(r'^(unknown)$');
-  List<String> options;
   String text;
   bool isRichText;
-  final Map<String, dynamic> map;
 
   AllDbFieldsModel(
-      SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, WidgetModelType.allDbFields, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.allDbFields, null, logger);
 
   /// Returns the name including the names of the parent
   @override
@@ -28,12 +26,13 @@ class AllDbFieldsModel extends WidgetModel {
     return stringBuffer;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map] and stores the data in the instance.
+  void parse(Map map) {
+    super.parse(map);
     checkSuperfluousAttributes(map, 'options widgetType'.split(' '));
     options = parseOptions('options', map);
     addFieldsOfTable(page.module.mainTable());
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
   }
 
   @override
index 600f2c0f01dda2065c60176d60d98755411886cb..cfc10dd45f682cc5d79b0a278103c6b0ce0760d4 100644 (file)
@@ -10,14 +10,12 @@ class ButtonModel extends WidgetModel {
   String label;
   String name;
   String toolTip;
-  final Map<String, dynamic> map;
-  List<String> options;
   ButtonModelType buttonModelType;
   VoidCallbackBones onPressed;
 
   /// Constructs an instance by parsing the map for some properties.
-  ButtonModel(SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, WidgetModelType.button, logger);
+  ButtonModel(SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.button, null, logger);
 
   /// Constructs an instance with all properties given.
   ButtonModel.direct(
@@ -27,9 +25,8 @@ class ButtonModel extends WidgetModel {
       String text,
       ButtonModelType buttonModelType,
       List<String> options,
-      this.map,
       BaseLogger logger)
-      : super(section, page, WidgetModelType.button, logger) {
+      : super(section, page, WidgetModelType.button, options, logger) {
     this.name = name;
     this.label = text;
     this.toolTip = toolTip;
@@ -48,9 +45,10 @@ class ButtonModel extends WidgetModel {
   @override
   String fullName() => '${section?.name}.$name';
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     name = parseString('name', map, required: true);
+    super.parse(map);
     checkSuperfluousAttributes(map,
         'buttonType label name options text toolTip widgetType'.split(' '));
     label = parseString('label', map, required: true);
@@ -59,7 +57,7 @@ class ButtonModel extends WidgetModel {
         parseEnum<ButtonModelType>('buttonType', map, ButtonModelType.values);
     buttonModelType ??= ButtonModelType.custom;
     options = parseOptions('options', map);
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
   }
 
   @override
index dd6f281e7cd148ffc4b7dd8ab385cd22ab68911d..7d99b81bee2f1b4dbaa4b28e7b7aaa35ebbfd990 100644 (file)
@@ -9,19 +9,16 @@ import 'widget_model.dart';
 class CheckboxModel extends FieldModel {
   static final regExprOptions = RegExp(r'^(readonly|disabled|required|undef)$');
 
-  final Map<String, dynamic> map;
-
   CheckboxModel(
-      SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, map, WidgetModelType.checkbox, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.checkbox, logger);
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
-    super.parse();
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
+    super.parse(map);
     checkSuperfluousAttributes(
         map, 'name label options toolTip widgetType'.split(' '));
-    options = parseOptions('options', map);
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     parseFinish();
   }
 
index eb57c90f016fe2667e3911359996c68170172e69..255ce95478c91958e3b46de10f89c4251d56b115 100644 (file)
@@ -20,8 +20,8 @@ class ColumnModel extends ComboBaseModel {
   String foreignKey;
 
   /// A constructor used in the parser.
-  ColumnModel(this.table, Map map, BaseLogger logger)
-      : super(null, null, map, WidgetModelType.column, logger);
+  ColumnModel(this.table, BaseLogger logger)
+      : super(null, null, WidgetModelType.column, logger);
 
   /// A constructor for columns created by the program.
   ColumnModel.direct(
@@ -40,7 +40,6 @@ class ColumnModel extends ComboBaseModel {
       : super.direct(
             section: null,
             page: null,
-            map: null,
             name: name,
             label: label,
             toolTip: toolTip,
@@ -64,20 +63,14 @@ class ColumnModel extends ComboBaseModel {
   @override
   String fullName() => '${table.name}.$name';
 
-  /// Tests whether a given [option] is part of [options].
-  bool hasOption(String option) {
-    bool rc = options?.contains(option) ?? false;
-    return rc;
-  }
-
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     name = parseString('column', map, required: true);
+    super.parse(map);
     checkSuperfluousAttributes(
         map,
         'column dataType defaultValue foreignKey label listOption listType options rows size texts tooTip validators validatorsText values widgetType'
             .split(' '));
-    super.parse();
     dataType =
         parseEnum<DataType>('dataType', map, DataType.values, required: true);
     size = parseInt('size', map, required: dataType == DataType.string);
@@ -88,7 +81,7 @@ class ColumnModel extends ComboBaseModel {
       logger.error(
           'invalid foreign key in ${fullName()}: $foreignKey expected: "<table>.<primary> <colName>" e.g. "role.role_id role_name"');
     }
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     if (options.contains('primary')) {
       if (!options.contains('notnull')) {
         options.add('notnull');
@@ -112,8 +105,8 @@ class ColumnModel extends ComboBaseModel {
         logger.error(
             'curious item (position $no) in column list of ${table.fullName()}: ${StringUtils.limitString("$item", 80)}');
       } else {
-        final current = ColumnModel(table, item, logger);
-        current.parse();
+        final current = ColumnModel(table, logger);
+        current.parse(item);
         table.addColumn(current);
       }
     }
index 7e4880b24ced9d2a52fab8cd3544ad8f449e8b53..171474a726e978085060dc0ccdd023a0289e116c 100644 (file)
@@ -10,21 +10,21 @@ import 'widget_model.dart';
 abstract class ComboBaseModel extends FieldModel {
   static final regExprListDbOption =
       RegExp(r'^\w+\.\w+\.\w+;\w+ \w+(?:;( ?:\w+=[^ ])*)?$');
-  static final regExprListConfiguration = RegExp(r'^scope:\w+$');
+  static final regExprListConfiguration =
+      RegExp(r'^\w+\.scope.\w+;\w+ \w+(?:;( ?:\w+=[^ ])*)?$');
   List<String> texts;
   List values;
   String listOption;
   ComboboxListType listType;
   ComboboxData data;
 
-  ComboBaseModel(SectionModel section, PageModel page, Map map,
+  ComboBaseModel(SectionModel section, PageModel page,
       WidgetModelType widgetType, BaseLogger logger)
-      : super(section, page, map, widgetType, logger);
+      : super(section, page, widgetType, logger);
 
   ComboBaseModel.direct(
       {SectionModel section,
       PageModel page,
-      Map map,
       String name,
       String label,
       String toolTip,
@@ -37,7 +37,7 @@ abstract class ComboBaseModel extends FieldModel {
       WidgetModelType widgetType,
       dynamic defaultValue,
       BaseLogger logger})
-      : super.direct(section, page, map, widgetType, name, label, toolTip,
+      : super.direct(section, page, widgetType, name, label, toolTip,
             dataType, options, defaultValue, logger);
 
   /// Transfers the list data from [texts] and [values] to [data].
@@ -52,9 +52,9 @@ abstract class ComboBaseModel extends FieldModel {
     }
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
-    super.parse();
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
+    super.parse(map);
     texts = parseStringList('texts', map);
     listType =
         parseEnum<ComboboxListType>('listType', map, ComboboxListType.values);
@@ -106,12 +106,15 @@ abstract class ComboBaseModel extends FieldModel {
   }
 }
 
+/// Stores the [texts] and [_values] of a combobox.
 class ComboboxData {
   final List<String> texts;
   List _values;
   WaitState waitState;
 
-  ComboboxData(this.texts, this._values, [this.waitState = WaitState.undef]);
+  ComboboxData(this.texts, this._values, [this.waitState = WaitState.undef]){
+    this._values ??= this.texts;
+  }
 
   /// Returns the length of [_values].
   int get valuesLength => _values == null ? 0 : _values.length;
index 23aa10849067dd71547f765b3b0260b5d5ed6ea3..819dfe7809788fbf576c8ed0358ddf73a90833fe 100644 (file)
@@ -10,8 +10,8 @@ class ComboboxModel extends ComboBaseModel {
   static final regExprOptions = RegExp(r'^(disabled|readonly|required|undef)$');
 
   ComboboxModel(
-      SectionModel section, PageModel page, Map map, BaseLogger logger)
-      : super(section, page, map, WidgetModelType.combobox, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.combobox, logger);
 
   /// Dumps the instance into a [StringBuffer]
   StringBuffer dump(StringBuffer stringBuffer) {
@@ -20,14 +20,14 @@ class ComboboxModel extends ComboBaseModel {
     return stringBuffer;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     checkSuperfluousAttributes(
         map,
         'dataType defaultValue filterType label listOption listType name options texts toolTip values validators validatorsText widgetType'
             .split(' '));
-    super.parse();
-    checkOptionsByRegExpr(options, regExprOptions);
+    super.parse(map);
+    checkOptionsByRegExpr(regExprOptions);
     parseFinish();
   }
 }
index 76a7efbc6e17af0d87dea1e394683b6a2fccb3e5..38b99d8d4052d191260206d55af4c8e8d972f809 100644 (file)
@@ -15,10 +15,10 @@ class DbReferenceModel extends ComboBaseModel {
   var value;
   ColumnModel column;
 
-  /// A constructor fetching the properties by parsing the [map].
+  /// A constructor fetching the properties by parsing the map.
   DbReferenceModel(
-      SectionModel section, PageModel page, Map map, BaseLogger logger)
-      : super(section, page, map, WidgetModelType.dbReference, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.dbReference, logger);
 
   /// A constructor without map parsing.
   DbReferenceModel.fromColumn(SectionModel section, PageModel page,
@@ -26,18 +26,19 @@ class DbReferenceModel extends ComboBaseModel {
       : super.direct(
             section: section,
             page: page,
-            map: null,
             widgetType: WidgetModelType.dbReference,
             name: column.name,
             label: column.label,
             toolTip: column.toolTip,
             dataType: column.dataType,
-            options: column.options ?? <String>[],
+            options: <String>[],
             texts: column.texts,
             values: column.values,
             listOption: column.listOption,
             listType: column.listType,
             logger: logger) {
+    options ??= [];
+    options.addAll(column.options ?? []);
     this.column = column;
     maxSize = column.size;
     rows = column.rows;
@@ -57,13 +58,13 @@ class DbReferenceModel extends ComboBaseModel {
     return stringBuffer;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     checkSuperfluousAttributes(
         map,
         'column filterType label maxSize listOption listType name options rows toolTip texts widgetType values'
             .split(' '));
-    super.parse();
+    super.parse(map);
     final columnName = parseString('column', map, required: true);
     column = page.module.getColumn(columnName, this);
     maxSize = parseInt('maxSize', map, required: false);
@@ -78,7 +79,7 @@ class DbReferenceModel extends ComboBaseModel {
     values ??= column.values;
     listOption ??= column.listOption;
     listType ??= column.listType;
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     options += column.options;
     parseFinish();
   }
index f233c54b22c5424e77187de3b467d830ce92208e..e133b707b83fdc1751d642c1814057d17840987a 100644 (file)
@@ -7,13 +7,11 @@ import 'widget_model.dart';
 /// Describes an empty line used as separator between two other widgets.
 class EmptyLineModel extends WidgetModel {
   static final regExprOptions = RegExp(r'^(unknown)$');
-  List<String> options;
   bool isRichText;
-  final Map<String, dynamic> map;
 
   EmptyLineModel(
-      SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, WidgetModelType.emptyLine, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.emptyLine, null, logger);
 
   /// Dumps the instance into a [StringBuffer]
   StringBuffer dump(StringBuffer stringBuffer) {
@@ -25,11 +23,11 @@ class EmptyLineModel extends WidgetModel {
   @override
   String fullName() => '${section.name}.${widgetName()}';
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
-    options = parseOptions('options', map);
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
+    super.parse(map);
     checkSuperfluousAttributes(map, 'options widgetType'.split(' '));
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
   }
 
   @override
index db484f85844f443be99841eb39d3a29bafd97986..174c6428ed94359ea5005b041bd16c47e52ca41e 100644 (file)
@@ -25,7 +25,6 @@ abstract class FieldModel extends WidgetModel {
   String label;
   String toolTip;
   DataType dataType;
-  List<String> options;
   FilterType filterType;
   dynamic _value;
   dynamic defaultValue;
@@ -45,26 +44,24 @@ abstract class FieldModel extends WidgetModel {
   /// key: validator name, e.g. 'regExpr'
   /// value: the error message
   final messageOfValidator = <String, String>{};
-  final Map<String, dynamic> map;
 
-  FieldModel(SectionModel section, PageModel page, this.map,
+  FieldModel(SectionModel section, PageModel page,
       WidgetModelType fieldModelType, BaseLogger logger)
-      : super(section, page, fieldModelType, logger);
+      : super(section, page, fieldModelType, null, logger);
 
   FieldModel.direct(
       SectionModel section,
       PageModel page,
-      this.map,
       WidgetModelType widgetModelType,
       this.name,
       this.label,
       this.toolTip,
       this.dataType,
-      this.options,
       this.defaultValue,
+      List<String> options,
       BaseLogger logger,
       [this.filterType])
-      : super(section, page, widgetModelType, logger);
+      : super(section, page, widgetModelType, options, logger);
 
   get value => _value;
 
@@ -178,20 +175,12 @@ abstract class FieldModel extends WidgetModel {
   @override
   String fullName() => '${page.name}.$name';
 
-  /// Tests whether a given [option] is part of [options].
-  bool hasOption(String option) {
-    if (options == null) {
-      return false;
-    }
-    bool rc = options.contains(option);
-    return rc;
-  }
-
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     name = parseString(
         widgetModelType == WidgetModelType.column ? 'column' : 'name', map,
         required: true);
+    super.parse(map);
     label = parseString('label', map, required: false);
     toolTip = parseString('toolTip', map, required: false);
     filterType = parseEnum<FilterType>('filterType', map, FilterType.values);
index 947c0390423f31c9b115eb087ee3dede7795db70..1ada1d68ded3473d638e17281a73072c3ef74dba 100644 (file)
@@ -6,14 +6,19 @@ import 'model_types.dart';
 /// Base class of all models.
 abstract class ModelBase {
   BaseLogger logger;
+  List<String> options;
 
-  ModelBase(this.logger);
+  ModelBase(this.options, this.logger);
+
+  void parse(Map map) {
+    options = parseOptions('options', map);
+  }
 
   /// Tests the validity of the entries in [options] comparing with an [regExpr].
   /// Errors will be logged.
-  bool checkOptionsByRegExpr(List<String> options, RegExp regExpr) {
+  bool checkOptionsByRegExpr(RegExp regExpr) {
     bool rc = false;
-    options.forEach((element) {
+    options?.forEach((element) {
       if (regExpr.firstMatch(element) == null) {
         logger.error('wrong option $element in ${fullName()}');
         rc = true;
@@ -22,6 +27,15 @@ abstract class ModelBase {
     return rc;
   }
 
+  /// Tests whether a given [option] is part of [options].
+  bool hasOption(String option) {
+    if (options == null) {
+      return false;
+    }
+    bool rc = options.contains(option);
+    return rc;
+  }
+
   /// Tests a [map] for superfluous [keys].
   /// Keys of the [map] that are not listed in [keys] will be logged as errors.
   /// Keys that do not have the type String are logged as errors.
@@ -111,7 +125,7 @@ abstract class ModelBase {
   /// Fetches an entry from a map addressed by a [key].
   /// An error is logged if [required] is true and the map does not contain the key.
   List<String> parseOptions(String key, Map<String, dynamic> map) {
-    List<String> rc = [];
+    List<String> rc;
     if (map.containsKey(key)) {
       final value = map[key];
       if (value.runtimeType == String && value.isNotEmpty) {
@@ -121,7 +135,7 @@ abstract class ModelBase {
             'wrong datatype of options ${value.runtimeType} in ${fullName()}');
       }
     }
-    return rc;
+    return rc ?? [];
   }
 
   /// Fetches an entry from a map addressed by a [key].
index a23e9a8ee57cc97e33e1c1b3f4202c169aecf78a..e283b4581b3651f7e3f73ee16b877164241c3c26 100644 (file)
@@ -18,12 +18,11 @@ class ModuleModel extends ModelBase {
   static final regExprOptions = RegExp(r'^(noPages)$');
   final Map<String, dynamic> map;
   String name;
-  List<String> options;
   @protected
   final pages = <PageModel>[];
   final tables = <TableModel>[];
 
-  ModuleModel(this.map, BaseLogger logger) : super(logger);
+  ModuleModel(this.map, BaseLogger logger) : super(null, logger);
 
   /// Adds the column [name] from [source] to [target] if that column is not member of [target].
   void addColumnIfMissing(
@@ -319,9 +318,6 @@ modules:
     return rc;
   }
 
-  /// Tests whether an options named 'name' is set.
-  bool hasOption(String name) => options != null && options.contains(name);
-
   /// Returns the main table of the module.
   /// This is the first defined table.
   TableModel mainTable() {
@@ -336,14 +332,15 @@ modules:
     return found;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse([Map map]) {
+    map ??= this.map;
     name = parseString('module', map, required: true);
+    super.parse(map);
     checkSuperfluousAttributes(map, 'module options pages tables'.split(' '));
     if (map.containsKey('tables')) {
       TableModel.parseList(this, map['tables'], logger);
     }
-    options = parseOptions('options', map);
     if (!hasOption('noPages')) {
       if (!map.containsKey('pages')) {
         logger.error('Module $name: missing pages');
@@ -357,7 +354,7 @@ modules:
         }
       }
     }
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
   }
 
   /// Returns a child table given by [name], null otherwise.
index 956bd338ef55d2d8d95ea7bf5ea3112258637eda..20ceed6d7fc89a59aefb036dd359efa58ab8bb3f 100644 (file)
@@ -13,11 +13,9 @@ typedef FilterWidget = bool Function(WidgetModel item);
 class PageModel extends ModelBase {
   static final regExprOptions = RegExp(r'^(noAutoButton)$');
   final ModuleModel module;
-  final Map<String, dynamic> map;
   String name;
   final List<SectionModel> sections = [];
   PageModelType pageModelType;
-  List<String> options;
   final fields = <FieldModel>[];
   final buttons = <ButtonModel>[];
   final widgets = <WidgetModel>[];
@@ -25,7 +23,7 @@ class PageModel extends ModelBase {
   List<String> tableTitles;
   List<String> tableColumns;
 
-  PageModel(this.module, this.map, BaseLogger logger) : super(logger);
+  PageModel(this.module, BaseLogger logger) : super(null, logger);
 
   /// Adds a [button] to the [this.buttons].
   /// If already defined and error is logged.
@@ -106,8 +104,8 @@ class PageModel extends ModelBase {
     return rc;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     name = parseString('page', map, required: true);
     checkSuperfluousAttributes(
         map,
@@ -150,25 +148,25 @@ class PageModel extends ModelBase {
         case PageModelType.list:
           if (buttonByName('search', required: false) == null) {
             addButton(ButtonModel.direct(section, this, 'search', 'Suchen',
-                ButtonModelType.search, [], null, logger));
+                ButtonModelType.search, [], logger));
           }
           break;
         case PageModelType.create:
         case PageModelType.change:
           if (buttonByName('cancel', required: false) == null) {
             addButton(ButtonModel.direct(section, this, 'cancel', 'Abbruch',
-                ButtonModelType.cancel, [], null, logger));
+                ButtonModelType.cancel, [], logger));
           }
           if (buttonByName('store', required: false) == null) {
             addButton(ButtonModel.direct(section, this, 'store', 'Speichern',
-                ButtonModelType.store, [], null, logger));
+                ButtonModelType.store, [], logger));
           }
           break;
         default:
           break;
       }
     }
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
   }
 
   @override
@@ -183,9 +181,9 @@ class PageModel extends ModelBase {
         module.logger.error(
             'curious item in section list of ${module.fullName()}: ${StringUtils.limitString("$item", 80)}');
       } else {
-        final page = PageModel(module, item, logger);
+        final page = PageModel(module, logger);
         // we need a name before adding to module
-        page.parse();
+        page.parse(item);
         module.addPage(page);
       }
     }
index cf52506b1bb0e86632d46e482ea04484f59af657..35b54306df1dc601b7cc16dcdf20f6966955bf2f 100644 (file)
@@ -19,13 +19,11 @@ class SectionModel extends WidgetModel {
   String name;
   final children = <WidgetModel>[];
   final buttons = <WidgetModel>[];
-  List<String> options;
   final int no;
-  final Map<String, dynamic> map;
 
-  SectionModel(this.no, PageModel page, SectionModel section, this.map,
+  SectionModel(this.no, PageModel page, SectionModel section,
       BaseLogger logger)
-      : super(section, page, WidgetModelType.section, logger);
+      : super(section, page, WidgetModelType.section, null, logger);
 
   /// Dumps the internal structure into a [stringBuffer]
   StringBuffer dump(StringBuffer stringBuffer) {
@@ -43,8 +41,8 @@ class SectionModel extends WidgetModel {
   String fullName() =>
       section == null ? '${page.name}.$name' : '${section.name}.$name';
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     sectionModelType = parseEnum<SectionModelType>(
         'sectionType', map, SectionModelType.values);
     name = parseString('name', map) ??
@@ -53,7 +51,7 @@ class SectionModel extends WidgetModel {
         map, 'children fields name options sectionType widgetType'.split(' '));
     options = parseOptions('options', map);
 
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     if (!map.containsKey('children')) {
       logger.error('missing children in ${fullName()}');
     } else {
@@ -80,28 +78,28 @@ class SectionModel extends WidgetModel {
               WidgetModel widget;
               switch (widgetType) {
                 case WidgetModelType.allDbFields:
-                  widget = AllDbFieldsModel(this, page, child, logger);
+                  widget = AllDbFieldsModel(this, page, logger);
                   break;
                 case WidgetModelType.checkbox:
-                  widget = CheckboxModel(this, page, child, logger);
+                  widget = CheckboxModel(this, page, logger);
                   break;
                 case WidgetModelType.combobox:
-                  widget = ComboboxModel(this, page, child, logger);
+                  widget = ComboboxModel(this, page, logger);
                   break;
                 case WidgetModelType.textField:
-                  widget = TextFieldModel(this, page, child, logger);
+                  widget = TextFieldModel(this, page, logger);
                   break;
                 case WidgetModelType.button:
-                  widget = ButtonModel(this, page, child, logger);
+                  widget = ButtonModel(this, page, logger);
                   break;
                 case WidgetModelType.text:
-                  widget = TextModel(this, page, child, logger);
+                  widget = TextModel(this, page, logger);
                   break;
                 case WidgetModelType.emptyLine:
-                  widget = EmptyLineModel(this, page, child, logger);
+                  widget = EmptyLineModel(this, page, logger);
                   break;
                 case WidgetModelType.dbReference:
-                  widget = DbReferenceModel(this, page, child, logger);
+                  widget = DbReferenceModel(this, page, logger);
                   break;
                 default:
                   //@ToDo: nested section
@@ -111,7 +109,7 @@ class SectionModel extends WidgetModel {
               }
               if (widget != null) {
                 children.add(widget);
-                widget.parse();
+                widget.parse(child);
                 page.widgets.add(widget);
                 switch (widget.widgetModelType) {
                   case WidgetModelType.button:
@@ -145,8 +143,8 @@ class SectionModel extends WidgetModel {
         page.logger.error(
             'curious item in section list of ${page.fullName()}: ${StringUtils.limitString("$item", 80)}');
       } else {
-        final current = SectionModel(no, page, null, item, logger);
-        current.parse();
+        final current = SectionModel(no, page, null, logger);
+        current.parse(item);
         if (section != null) {
           section.children.add(current);
         } else {
index 4958f09eaa97f78ef55038f9ef78dc93d4729d85..910a25b6b123f6577b534cf68aa5b925b667ec1a 100644 (file)
@@ -25,6 +25,8 @@ class ConfigurationModel extends ModuleModel {
             'label': 'Bereich',
             'size': 64,
             'options': 'notnull',
+            'validators': r'required regExpr=i/^\w+$',
+            'validatorsText': 'regExpr=Nur Buchstaben, Ziffern und Unterstrich erlaubt',
           },
           {
             'column': 'configuration_property',
@@ -42,6 +44,9 @@ class ConfigurationModel extends ModuleModel {
             'dataType': 'string',
             'size': 16,
             'label': 'Datentyp',
+            'listType': 'explicite',
+            'texts': ';bool;date;float;int;string',
+            'validators': 'required',
           },
           {
             'column': 'configuration_value',
index 21b321ba846879b876e9f0b184e2c1014e1d020c..53eed8a994866cfa841cfb4bf01fa83d324730bd 100644 (file)
@@ -26,10 +26,10 @@ class MenuModel extends ModuleModel {
             'column': 'menu_icon',
             'dataType': 'reference',
             'label': 'Bild',
-            'foreignKey':
-                'configuration.configuration_id configuration_property',
+            'foreignKey': 'configuration.configuration_id configuration_value',
             'listType': 'configuration',
-            'listOption': 'scope:icons',
+            'listOption':
+                'std.scope.icons;configuration_value configuration_id;',
           },
         ]
       },
index 87c0ad3c4e096fd2a4461ebebd76696edd18f0a4..24e71796c83d2d5221005108d50a0dd9c15e628c 100644 (file)
@@ -26,6 +26,7 @@ class RoleModel extends ModuleModel {
             'column': 'role_priority',
             'dataType': 'int',
             'label': 'Priorität',
+            'validators': 'required minInt=1 maxInt=99',
           },
           {
             'column': 'role_active',
index 3b04a9411c2aac75212b3e1de7f4ae86aa144409..0dc4c622e124835e12ba28c71dfd361a46c13703 100644 (file)
@@ -100,6 +100,11 @@ class UserModel extends ModuleModel {
           {
             "sectionType": "simpleForm",
             "children": [
+              {
+                "widgetType": "text",
+                "text": "Ändern des Passworts von Benutzer ~user~",
+                "options": "placeholder"
+              },
               {
                 "widgetType": "textField",
                 "name": "user_password",
index c592f8b61156dee51284f84fe2cbcb1292baf724..83ebf8331821369c2a8ef39b3eaec66654336e71 100644 (file)
@@ -9,13 +9,11 @@ import 'module_model.dart';
 class TableModel extends ModelBase {
   static final regExprOptions = RegExp(r'^(unknown)$');
   final ModuleModel module;
-  final Map<String, dynamic> map;
   String name;
-  List<String> options;
   final columns = <ColumnModel>[];
   ColumnModel primary;
 
-  TableModel(this.module, this.map, BaseLogger logger) : super(logger);
+  TableModel(this.module, BaseLogger logger) : super(null, logger);
 
   /// Adds a [button] to the [this.buttons].
   /// If already defined and error is logged.
@@ -69,8 +67,8 @@ class TableModel extends ModelBase {
   @override
   String fullName() => '${module.name}.$name';
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     name = parseString('table', map, required: true);
     checkSuperfluousAttributes(map, 'columns options table'.split(' '));
     options = parseOptions('options', map);
@@ -85,7 +83,7 @@ class TableModel extends ModelBase {
         ColumnModel.parseColumns(this, item, logger);
       }
     }
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     addIfMissing(
         '${name}_createdat', 'Erzeugt', DataType.dateTime, ['hidden', 'null']);
     addIfMissing(
@@ -108,8 +106,8 @@ class TableModel extends ModelBase {
         module.logger.error(
             'curious item in section list of ${module.fullName()}: ${StringUtils.limitString("$item", 80)}');
       } else {
-        final table = TableModel(module, item, logger);
-        table.parse();
+        final table = TableModel(module, logger);
+        table.parse(item);
         module.addTable(table);
       }
     }
index 627bd380ef2ee7f9238ff15de1e61fb0baad37de..8946e88fe06b3387a841a76aed48f0a3b6013052 100644 (file)
@@ -15,13 +15,12 @@ class TextFieldModel extends FieldModel {
   var value;
 
   TextFieldModel(
-      SectionModel section, PageModel page, Map map, BaseLogger logger)
-      : super(section, page, map, WidgetModelType.textField, logger);
+      SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.textField, logger);
 
   TextFieldModel.direct(
       SectionModel section,
       PageModel page,
-      Map map,
       String name,
       String label,
       String toolTip,
@@ -29,7 +28,7 @@ class TextFieldModel extends FieldModel {
       List<String> options,
       dynamic defaultValue,
       BaseLogger logger)
-      : super.direct(section, page, null, WidgetModelType.textField, name,
+      : super.direct(section, page, WidgetModelType.textField, name,
             label, toolTip, dataType, options, defaultValue, logger);
 
   /// Dumps the internal structure into a [stringBuffer]
@@ -39,9 +38,9 @@ class TextFieldModel extends FieldModel {
     return stringBuffer;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
-    super.parse();
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
+    super.parse(map);
     checkSuperfluousAttributes(
         map,
         'dataType filterType label maxSize name options rows toolTip validators validatorsText value widgetType'
@@ -58,7 +57,7 @@ class TextFieldModel extends FieldModel {
         break;
     }
     options = parseOptions('options', map);
-    checkOptionsByRegExpr(options, regExprOptions);
+    checkOptionsByRegExpr(regExprOptions);
     parseFinish();
   }
 }
index 55ec29ca208a4f60805fd1d0a701a73e9abddf25..59994c271a7f93562de7eb14247d80ac44edf7ee 100644 (file)
@@ -5,14 +5,12 @@ import 'widget_model.dart';
 
 /// Describes a text widget without user interaction.
 class TextModel extends WidgetModel {
-  static final regExprOptions = RegExp(r'^(rich)$');
-  List<String> options;
+  static final regExprOptions = RegExp(r'^(richText|placeholder|h[1-5])$');
   String text;
   bool isRichText;
-  final Map<String, dynamic> map;
 
-  TextModel(SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, WidgetModelType.text, logger);
+  TextModel(SectionModel section, PageModel page, BaseLogger logger)
+      : super(section, page, WidgetModelType.text, null, logger);
 
   /// Returns the name including the names of the parent
   @override
@@ -25,13 +23,13 @@ class TextModel extends WidgetModel {
     return stringBuffer;
   }
 
-  /// Parses the map and stores the data in the instance.
-  void parse() {
+  /// Parses the [map]and stores the data in the instance.
+  void parse(Map map) {
     checkSuperfluousAttributes(map, 'options text widgetType'.split(' '));
     text = parseString('text', map, required: true);
     options = parseOptions('options', map);
-    checkOptionsByRegExpr(options, regExprOptions);
-    isRichText = options.contains('richtext');
+    checkOptionsByRegExpr(regExprOptions);
+    isRichText = options.contains('richText');
   }
 
   @override
index 3db5f1113d85cac17610585e3e396715566f0207..a7d4b02daf803f286048b6d020e0d328585c514a 100644 (file)
@@ -13,8 +13,9 @@ abstract class WidgetModel extends ModelBase {
   final WidgetModelType widgetModelType;
   int id;
 
-  WidgetModel(this.section, this.page, this.widgetModelType, BaseLogger logger)
-      : super(logger) {
+  WidgetModel(this.section, this.page, this.widgetModelType,
+      List<String> options, BaseLogger logger)
+      : super(options, logger) {
     this.id = ++lastId;
   }
 
@@ -28,7 +29,9 @@ abstract class WidgetModel extends ModelBase {
   /// Dumps the internal structure into a [stringBuffer]
   StringBuffer dump(StringBuffer stringBuffer);
 
-  void parse();
+  void parse(Map map){
+    super.parse(map);
+  }
 
   String widgetName();
 }
index 865e1c2552058c692034f9a6f717c625fd250f80..1465cfb1c285da990dbd12ca6543a5ca5e64272b 100644 (file)
@@ -29,7 +29,7 @@ class ConfigurationChangePage extends StatefulWidget {
   }
 }
 
-class ConfigurationChangePageState extends State<ConfigurationChangePage> {
+class ConfigurationChangePageState extends State<ConfigurationChangePage> implements RedrawPage {
   final ApplicationData applicationData;
   final int primaryId;
   final Map initialRow;
@@ -43,24 +43,6 @@ class ConfigurationChangePageState extends State<ConfigurationChangePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller = ConfigurationController(
-          _formKey, this, 'change', context, applicationData, redrawCallback:
-              (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-    }
     // controller.buildWidgetList() is called in editForm
     return Scaffold(
         appBar: applicationData.appBarBuilder('Konfiguration ändern'),
@@ -74,8 +56,22 @@ class ConfigurationChangePageState extends State<ConfigurationChangePage> {
         ));
   }
 
+  @override
+  void initState() {
+    super.initState();
+    controller = ConfigurationController(
+        _formKey, this, 'change', context, applicationData);
+    controller.initialize();
+  }
   void dispose() {
     controller.dispose();
     super.dispose();
   }
+
+  @override
+  void redraw(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState( () {
+      controller.afterSetState(reason, customString: customString, callback: callback);
+    });
+  }
 }
index cc579c2cc7ecb86301363d67accd37afdde4e40e..5fc1f6171102c45644bd5682bbaddda3b56ed404 100644 (file)
@@ -10,7 +10,7 @@ class ConfigurationController extends PageControllerBones {
   /// Controller for a page named [pageName].
   ConfigurationController(
       GlobalKey<FormState> formKey,
-      State<StatefulWidget> parent,
+      RedrawPage parent,
       String pageName,
       BuildContext context,
       ApplicationData applicationData,
index a6f8797c925c47130e8c19ec9fa015902fca92ce..cb43aec078c9ed3942af069ece1d15a9906fa22e 100644 (file)
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
-
 import '../../helper/settings.dart';
 import '../../widget/edit_form.dart';
+import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
 import 'configuration_controller.dart';
 
@@ -21,7 +21,7 @@ class ConfigurationCreatePage extends StatefulWidget {
   }
 }
 
-class ConfigurationCreatePageState extends State<ConfigurationCreatePage> {
+class ConfigurationCreatePageState extends State<ConfigurationCreatePage> implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -33,12 +33,7 @@ class ConfigurationCreatePageState extends State<ConfigurationCreatePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller = ConfigurationController(
-          _formKey, this, 'create', context, applicationData);
-      controller.initialize();
-    }
-    controller.buildWidgetList();
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Neue Konfiguration'),
         drawer: applicationData.drawerBuilder(context),
@@ -48,4 +43,18 @@ class ConfigurationCreatePageState extends State<ConfigurationCreatePage> {
           configuration: applicationData.configuration,
         ));
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller = ConfigurationController(
+        _formKey, this, 'create', context, applicationData);
+    controller.initialize();
+  }
+  @override
+  void redraw(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState( () {
+      controller.afterSetState(reason, customString: customString, callback: callback);
+    });
+  }
 }
index dcc9c709f60a95caf222fdddfc0c961c7abb9572..b71130ad465396e0bf6fcecb9bdc1287d1f652d2 100644 (file)
@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-
+import '../../model/page_model.dart';
 import '../../widget/list_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
@@ -21,7 +21,8 @@ class ConfigurationListPage extends StatefulWidget {
   }
 }
 
-class ConfigurationListPageState extends State<ConfigurationListPage> {
+class ConfigurationListPageState extends State<ConfigurationListPage>
+    implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -31,35 +32,9 @@ class ConfigurationListPageState extends State<ConfigurationListPage> {
 
   ConfigurationListPageState(this.applicationData);
 
-  @override
-  void initState() {
-    super.initState();
-    controller = ConfigurationController(
-        _formKey, this, 'list', context, applicationData, redrawCallback:
-            (RedrawReason reason,
-                {String customString, RedrawCallbackFunctionSimple callback}) {
-      switch (reason) {
-        case RedrawReason.fetchList:
-          controller.buildRows();
-          break;
-        case RedrawReason.callback:
-          callback(RedrawReason.custom, customString);
-          setState(() {});
-          break;
-        default:
-          setState(() {});
-          break;
-      }
-    });
-    controller.initialize();
-    controller.buildWidgetList();
-    controller.buildRows();
-    controller.completeAsync();
-  }
-
   @override
   Widget build(BuildContext context) {
-    controller.buildHandler(context);
+    controller.beginOfBuild(context);
     return Scaffold(
       appBar: applicationData.appBarBuilder('Konfigurationen'),
       drawer: applicationData.drawerBuilder(context),
@@ -77,10 +52,7 @@ class ConfigurationListPageState extends State<ConfigurationListPage> {
             RaisedButton(
               child: Text('Neue Konfiguration'),
               onPressed: () {
-                Navigator.pushNamed(context, '/configuration/create');
-
-                /// Force the redraw on return:
-                controller.listRows = null;
+                controller.goTo(pageType: PageModelType.create);
               },
             ),
           ]),
@@ -91,4 +63,36 @@ class ConfigurationListPageState extends State<ConfigurationListPage> {
       ),
     );
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller = ConfigurationController(
+        _formKey, this, 'list', context, applicationData, redrawCallback:
+            (RedrawReason reason,
+                {String customString, RedrawCallbackFunctionSimple callback}) {
+      switch (reason) {
+        case RedrawReason.fetchList:
+          controller.buildRows();
+          break;
+        case RedrawReason.callback:
+          callback(RedrawReason.custom, customString: customString);
+          setState(() {});
+          break;
+        default:
+          setState(() {});
+          break;
+      }
+    });
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 5127b134aa5df437844c329ce12250d18ca02729..b25913785b17c87001d290812d72023609fad322 100644 (file)
@@ -28,7 +28,7 @@ class MenuChangePage extends StatefulWidget {
   }
 }
 
-class MenuChangePageState extends State<MenuChangePage> {
+class MenuChangePageState extends State<MenuChangePage> implements RedrawPage {
   final ApplicationData applicationData;
   final int primaryId;
   final Map initialRow;
@@ -41,24 +41,7 @@ class MenuChangePageState extends State<MenuChangePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          MenuController(_formKey, this, 'change', context, applicationData,
-              redrawCallback: (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-    }
+    controller.beginOfBuild(context);
     // controller.buildWidgetList() is called in editForm
     return Scaffold(
         appBar: applicationData.appBarBuilder('Startmenü ändern'),
@@ -76,4 +59,26 @@ class MenuChangePageState extends State<MenuChangePage> {
     controller.dispose();
     super.dispose();
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller = MenuController(
+      _formKey,
+      this,
+      'change',
+      context,
+      applicationData,
+    );
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 581143a4ae887d24f19327744b88e7c97b4bcc12..857f0af913dfcccd65ebe4a257d796d65249e9c1 100644 (file)
@@ -8,7 +8,7 @@ import 'menu_change_page.dart';
 
 class MenuController extends PageControllerBones {
   /// Controller for a page named [pageName].
-  MenuController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+  MenuController(GlobalKey<FormState> formKey, RedrawPage parent,
       String pageName, BuildContext context, ApplicationData applicationData,
       {Function redrawCallback})
       : super(formKey, parent, MenuModel(Settings().logger), pageName, context,
index d7acb013b71f98118551ed4803f44f977238a4fe..f9cbcd0cbe4d3f6668255e0d0bb94ab0019dc8d4 100644 (file)
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import '../../helper/settings.dart';
 import '../../widget/edit_form.dart';
+import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
 import 'menu_controller.dart';
 
@@ -21,7 +22,7 @@ class MenuCreatePage extends StatefulWidget {
   }
 }
 
-class MenuCreatePageState extends State<MenuCreatePage> {
+class MenuCreatePageState extends State<MenuCreatePage> implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -33,12 +34,7 @@ class MenuCreatePageState extends State<MenuCreatePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          MenuController(_formKey, this, 'create', context, applicationData);
-      controller.initialize();
-    }
-    controller.buildWidgetList();
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Neues Startmenü'),
         drawer: applicationData.drawerBuilder(context),
@@ -48,4 +44,21 @@ class MenuCreatePageState extends State<MenuCreatePage> {
           configuration: applicationData.configuration,
         ));
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        MenuController(_formKey, this, 'create', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 5a0b1a7413fe978368079afb87eccd470bb7bf1c..2caa054b8b34f8e654824ea43c787d63d3a1726d 100644 (file)
@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-
+import '../../model/page_model.dart';
 import '../../widget/list_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
@@ -21,7 +21,7 @@ class MenuListPage extends StatefulWidget {
   }
 }
 
-class MenuListPageState extends State<MenuListPage> {
+class MenuListPageState extends State<MenuListPage> implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -31,35 +31,9 @@ class MenuListPageState extends State<MenuListPage> {
 
   MenuListPageState(this.applicationData);
 
-  @override
-  void initState() {
-    super.initState();
-    controller =
-        MenuController(_formKey, this, 'list', context, applicationData,
-            redrawCallback: (RedrawReason reason,
-                {String customString, RedrawCallbackFunctionSimple callback}) {
-      switch (reason) {
-        case RedrawReason.fetchList:
-          controller.buildRows();
-          break;
-        case RedrawReason.callback:
-          callback(RedrawReason.custom, customString);
-          setState(() {});
-          break;
-        default:
-          setState(() {});
-          break;
-      }
-    });
-    controller.initialize();
-    controller.buildWidgetList();
-    controller.buildRows();
-    controller.completeAsync();
-  }
-
   @override
   Widget build(BuildContext context) {
-    controller.buildHandler(context);
+    controller.beginOfBuild(context);
     return Scaffold(
       appBar: applicationData.appBarBuilder('Startmenüs'),
       drawer: applicationData.drawerBuilder(context),
@@ -77,10 +51,7 @@ class MenuListPageState extends State<MenuListPage> {
             RaisedButton(
               child: Text('Neues Startmenü'),
               onPressed: () {
-                Navigator.pushNamed(context, '/menu/create');
-
-                /// Force the redraw on return:
-                controller.listRows = null;
+                controller.goTo(pageType: PageModelType.create);
               },
             ),
           ]),
@@ -91,4 +62,21 @@ class MenuListPageState extends State<MenuListPage> {
       ),
     );
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        MenuController(_formKey, this, 'list', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 3368f049d2f7a55bc6cb3bb348570e548285d42a..b05b475c477794c3f35da9ef08e62227511344ef 100644 (file)
@@ -1,5 +1,4 @@
 import 'package:flutter/material.dart';
-
 import '../../helper/settings.dart';
 import '../../widget/edit_form.dart';
 import '../../widget/page_controller_bones.dart';
@@ -28,7 +27,7 @@ class RoleChangePage extends StatefulWidget {
   }
 }
 
-class RoleChangePageState extends State<RoleChangePage> {
+class RoleChangePageState extends State<RoleChangePage> implements RedrawPage{
   final ApplicationData applicationData;
   final int primaryId;
   final Map initialRow;
@@ -41,25 +40,7 @@ class RoleChangePageState extends State<RoleChangePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          RoleController(_formKey, this, 'change', context, applicationData,
-              redrawCallback: (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-    }
-    // controller.buildWidgetList() is called in editForm
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Rolle ändern'),
         drawer: applicationData.drawerBuilder(context),
@@ -71,7 +52,22 @@ class RoleChangePageState extends State<RoleChangePage> {
           initialRow: initialRow,
         ));
   }
+  @override
+  void initState() {
+    super.initState();
+    controller = RoleController(
+        _formKey, this, 'change', context, applicationData);
+    controller.initialize();
+  }
 
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
   void dispose() {
     controller.dispose();
     super.dispose();
index 13a7406b73f8266f3ea73a1bf2623b5152d630ad..0ccf112fdad252b84b8f7aa3a9b0d0e80419648d 100644 (file)
@@ -8,7 +8,7 @@ import 'role_change_page.dart';
 
 class RoleController extends PageControllerBones {
   /// Controller for a page named [pageName].
-  RoleController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+  RoleController(GlobalKey<FormState> formKey, RedrawPage parent,
       String pageName, BuildContext context, ApplicationData applicationData,
       {Function redrawCallback})
       : super(formKey, parent, RoleModel(Settings().logger), pageName, context,
index dc670762be49564e198a64a00c55e5236f375511..3d0cf8dcb8e0688c877b5501e59369fc57669e13 100644 (file)
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import '../../helper/settings.dart';
 import '../../widget/edit_form.dart';
+import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
 import 'role_controller.dart';
 
@@ -21,7 +22,7 @@ class RoleCreatePage extends StatefulWidget {
   }
 }
 
-class RoleCreatePageState extends State<RoleCreatePage> {
+class RoleCreatePageState extends State<RoleCreatePage> implements RedrawPage{
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -33,12 +34,7 @@ class RoleCreatePageState extends State<RoleCreatePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          RoleController(_formKey, this, 'create', context, applicationData);
-      controller.initialize();
-    }
-    controller.buildWidgetList();
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Neue Rolle'),
         drawer: applicationData.drawerBuilder(context),
@@ -48,4 +44,20 @@ class RoleCreatePageState extends State<RoleCreatePage> {
           configuration: applicationData.configuration,
         ));
   }
+  @override
+  void initState() {
+    super.initState();
+    controller = RoleController(
+        _formKey, this, 'create', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index b373ba4bfb7514e9e2f4e5033957d2b823587155..6e53d7dde97f6b316831a5f0285e6e28d27a6332 100644 (file)
@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-
+import '../../model/page_model.dart';
 import '../../widget/list_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
@@ -21,7 +21,7 @@ class RoleListPage extends StatefulWidget {
   }
 }
 
-class RoleListPageState extends State<RoleListPage> {
+class RoleListPageState extends State<RoleListPage> implements RedrawPage{
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -31,35 +31,9 @@ class RoleListPageState extends State<RoleListPage> {
 
   RoleListPageState(this.applicationData);
 
-  @override
-  void initState() {
-    super.initState();
-    controller =
-        RoleController(_formKey, this, 'list', context, applicationData,
-            redrawCallback: (RedrawReason reason,
-                {String customString, RedrawCallbackFunctionSimple callback}) {
-      switch (reason) {
-        case RedrawReason.fetchList:
-          controller.buildRows();
-          break;
-        case RedrawReason.callback:
-          callback(RedrawReason.custom, customString);
-          setState(() {});
-          break;
-        default:
-          setState(() {});
-          break;
-      }
-    });
-    controller.initialize();
-    controller.buildWidgetList();
-    controller.buildRows();
-    controller.completeAsync();
-  }
-
   @override
   Widget build(BuildContext context) {
-    controller.buildHandler(context);
+    controller.beginOfBuild(context);
     return Scaffold(
       appBar: applicationData.appBarBuilder('Rollen'),
       drawer: applicationData.drawerBuilder(context),
@@ -77,10 +51,7 @@ class RoleListPageState extends State<RoleListPage> {
             RaisedButton(
               child: Text('Neue Rolle'),
               onPressed: () {
-                Navigator.pushNamed(context, '/role/create');
-
-                /// Force the redraw on return:
-                controller.listRows = null;
+                controller.goTo(pageType: PageModelType.create);
               },
             ),
           ]),
@@ -91,4 +62,21 @@ class RoleListPageState extends State<RoleListPage> {
       ),
     );
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        RoleController(_formKey, this, 'list', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 61471aa3939b035e61dd71a9fc2ed55ae15253dc..3409db8392e1850fef0535ce7e48dfd91b761f86 100644 (file)
@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+
 import '../../helper/settings.dart';
 import '../../model/button_model.dart';
 import '../../widget/edit_form.dart';
@@ -29,7 +30,7 @@ class UserChangePage extends StatefulWidget {
   }
 }
 
-class UserChangePageState extends State<UserChangePage> {
+class UserChangePageState extends State<UserChangePage> implements RedrawPage {
   final ApplicationData applicationData;
   final int primaryId;
   final Map initialRow;
@@ -42,26 +43,7 @@ class UserChangePageState extends State<UserChangePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          UserController(_formKey, this, 'change', context, applicationData,
-              redrawCallback: (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-      customize();
-    }
-    // controller.buildWidgetList() is called in editForm
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Benutzer ändern'),
         drawer: applicationData.drawerBuilder(context),
@@ -91,4 +73,22 @@ class UserChangePageState extends State<UserChangePage> {
     controller.dispose();
     super.dispose();
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        UserController(_formKey, this, 'change', context, applicationData);
+    controller.initialize();
+    customize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 30d72db9c0f500bfee96b3462879029d03f55690..91d75e15383e1adf4727a2302f3c82ee986203c3 100644 (file)
@@ -8,7 +8,7 @@ import 'user_change_page.dart';
 
 class UserController extends PageControllerBones {
   /// Controller for a page named [pageName].
-  UserController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+  UserController(GlobalKey<FormState> formKey, RedrawPage parent,
       String pageName, BuildContext context, ApplicationData applicationData,
       {Function redrawCallback})
       : super(formKey, parent, UserModel(Settings().logger), pageName, context,
@@ -23,4 +23,5 @@ class UserController extends PageControllerBones {
         MaterialPageRoute(
             builder: (context) => UserChangePage(id, applicationData, row)));
   }
+
 }
index c4d34f96e7eacddc163c4f2afe372d110271fb58..cb100c93fbc84f4b1d6c1163af32a9441c5fc85d 100644 (file)
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import '../../helper/settings.dart';
 import '../../widget/edit_form.dart';
+import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
 import 'user_controller.dart';
 
@@ -21,7 +22,7 @@ class UserCreatePage extends StatefulWidget {
   }
 }
 
-class UserCreatePageState extends State<UserCreatePage> {
+class UserCreatePageState extends State<UserCreatePage> implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -33,14 +34,9 @@ class UserCreatePageState extends State<UserCreatePage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          UserController(_formKey, this, 'create', context, applicationData);
-      controller.initialize();
-    }
-    controller.buildWidgetList();
+    controller.beginOfBuild(context);
     return Scaffold(
-        appBar: applicationData.appBarBuilder('Neue Rolle'),
+        appBar: applicationData.appBarBuilder('Neuer Benutzer'),
         drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
@@ -48,4 +44,21 @@ class UserCreatePageState extends State<UserCreatePage> {
           configuration: applicationData.configuration,
         ));
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        UserController(_formKey, this, 'create', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 0cdc3783ac696f2984c6d6a232ee233a7dc880f0..a18f2cd741cf0d18f8e8c97362694ac9c6cf08a9 100644 (file)
@@ -1,5 +1,7 @@
 import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
 
+import '../../model/page_model.dart';
 import '../../widget/list_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
@@ -21,7 +23,7 @@ class UserListPage extends StatefulWidget {
   }
 }
 
-class UserListPageState extends State<UserListPage> {
+class UserListPageState extends State<UserListPage> implements RedrawPage {
   final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
@@ -31,35 +33,9 @@ class UserListPageState extends State<UserListPage> {
 
   UserListPageState(this.applicationData);
 
-  @override
-  void initState() {
-    super.initState();
-    controller =
-        UserController(_formKey, this, 'list', context, applicationData,
-            redrawCallback: (RedrawReason reason,
-                {String customString, RedrawCallbackFunctionSimple callback}) {
-      switch (reason) {
-        case RedrawReason.fetchList:
-          controller.buildRows();
-          break;
-        case RedrawReason.callback:
-          callback(RedrawReason.custom, customString);
-          setState(() {});
-          break;
-        default:
-          setState(() {});
-          break;
-      }
-    });
-    controller.initialize();
-    controller.buildWidgetList();
-    controller.buildRows();
-    controller.completeAsync();
-  }
-
   @override
   Widget build(BuildContext context) {
-    controller.buildHandler(context);
+    controller.beginOfBuild(context);
     return Scaffold(
       appBar: applicationData.appBarBuilder('Benutzer'),
       drawer: applicationData.drawerBuilder(context),
@@ -77,10 +53,7 @@ class UserListPageState extends State<UserListPage> {
             RaisedButton(
               child: Text('Neuer Benutzer'),
               onPressed: () {
-                Navigator.pushNamed(context, '/user/create');
-
-                /// Force the redraw on return:
-                controller.listRows = null;
+                controller.goTo(pageType: PageModelType.create);
               },
             ),
           ]),
@@ -91,4 +64,21 @@ class UserListPageState extends State<UserListPage> {
       ),
     );
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        UserController(_formKey, this, 'list', context, applicationData);
+    controller.initialize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index f451d2d2d020b06edde86b61d63b5dc5648570ca..0e6da0a2b098557454e467c96d15f56c012714e8 100644 (file)
@@ -6,8 +6,8 @@ import '../../model/button_model.dart';
 import '../../widget/edit_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
-import 'user_controller.dart';
 import 'hash.dart';
+import 'user_controller.dart';
 
 class UserLoginPage extends StatefulWidget {
   final ApplicationData applicationData;
@@ -27,7 +27,7 @@ class UserLoginPage extends StatefulWidget {
   }
 }
 
-class UserLoginPageState extends State<UserLoginPage> {
+class UserLoginPageState extends State<UserLoginPage> implements RedrawPage {
   final ApplicationData applicationData;
   final GlobalKey<FormState> _formKey =
       GlobalKey<FormState>(debugLabel: 'user.login');
@@ -38,28 +38,9 @@ class UserLoginPageState extends State<UserLoginPage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          UserController(_formKey, this, 'login', context, applicationData,
-              redrawCallback: (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-      customize();
-    }
-    // controller.buildWidgetList() is called in editForm
+    controller.beginOfBuild(context);
     return Scaffold(
-        appBar: applicationData.appBarBuilder('Passwort ändern'),
+        appBar: applicationData.appBarBuilder('Anmelden'),
         drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
@@ -109,4 +90,22 @@ class UserLoginPageState extends State<UserLoginPage> {
     controller.dispose();
     super.dispose();
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        UserController(_formKey, this, 'login', context, applicationData);
+    controller.initialize();
+    customize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 4e24cb7f704a8c4254ada9615e00a2cce6236df8..2b126171bf0a17fd369572f366edd3c359c5f39a 100644 (file)
@@ -5,8 +5,8 @@ import '../../model/button_model.dart';
 import '../../widget/edit_form.dart';
 import '../../widget/page_controller_bones.dart';
 import '../application_data.dart';
-import 'user_controller.dart';
 import 'hash.dart';
+import 'user_controller.dart';
 
 class UserPasswordPage extends StatefulWidget {
   final ApplicationData applicationData;
@@ -32,7 +32,7 @@ class UserPasswordPage extends StatefulWidget {
   }
 }
 
-class UserPasswordPageState extends State<UserPasswordPage> {
+class UserPasswordPageState extends State<UserPasswordPage> implements RedrawPage {
   final ApplicationData applicationData;
   final int primaryId;
   final String userName;
@@ -45,26 +45,7 @@ class UserPasswordPageState extends State<UserPasswordPage> {
 
   @override
   Widget build(BuildContext context) {
-    if (controller == null) {
-      controller =
-          UserController(_formKey, this, 'password', context, applicationData,
-              redrawCallback: (RedrawReason reason,
-                  {String customString,
-                  RedrawCallbackFunctionSimple callback}) {
-        switch (reason) {
-          case RedrawReason.callback:
-            callback(RedrawReason.custom, customString);
-            setState(() {});
-            break;
-          default:
-            setState(() {});
-            break;
-        }
-      });
-      controller.initialize();
-      customize();
-    }
-    // controller.buildWidgetList() is called in editForm
+    controller.beginOfBuild(context);
     return Scaffold(
         appBar: applicationData.appBarBuilder('Passwort ändern'),
         drawer: applicationData.drawerBuilder(context),
@@ -77,6 +58,7 @@ class UserPasswordPageState extends State<UserPasswordPage> {
   }
 
   void customize() {
+    controller.placeholders['user'] = userName;
     ButtonModel button = controller.page.buttonByName('store');
     button?.onPressed = () {
       if (_formKey.currentState.validate()) {
@@ -107,4 +89,22 @@ class UserPasswordPageState extends State<UserPasswordPage> {
     controller.dispose();
     super.dispose();
   }
+
+  @override
+  void initState() {
+    super.initState();
+    controller =
+        UserController(_formKey, this, 'password', context, applicationData);
+    controller.initialize();
+    customize();
+  }
+
+  @override
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    setState(() {
+      controller.afterSetState(reason,
+          customString: customString, callback: callback);
+    });
+  }
 }
index 90de6beb64f159c2dbee4977fceb86f75c1168aa..0ea887018258322378a8efeb31a825352c98317f 100644 (file)
@@ -22,8 +22,13 @@ enum CacheEntryType {
 /// A cache for objects coming from a persistence layer.
 /// Uses the least recently used algorithm to avoid overflow.
 class PersistenceCache {
-  static final regExpCombobox =
+  /// format: <nickname>.<sqlType>.<sqlName>;<col_text> <col_value>;<parameters>...
+  static final regExpComboboxDbColumn =
       RegExp(r'^\w+\.\w+\.\w+;\w+ \w+;( ?:\w+=\S*)*$');
+
+  /// format: <nickname>.scope.<scope>;<col_text> <col_value>;<parameters>...
+  static final regExpComboboxConfiguration =
+      RegExp(r'^\w+\.scope\.\w+;\w+ \w+;( ?:\w+=\S*)*$');
   static final regExpRecord = RegExp(r'^\w+\.\w+\.\w+;( ?:\w+=\S*)+$');
   static int nextRecordNo = 0;
   final leastReasentlyUsed = <CacheEntry>[];
@@ -52,14 +57,33 @@ class PersistenceCache {
   /// The format of [key]:
   /// "<id>.<module>.<sqlName>;<colText> <colValue>;<param1> <param2>..."
   /// e.g. "x99.role.list;role_name role_id;:role_name=% :role_active=T"
+  /// or
+  /// "<id> scope=<scope>
+  /// e.g. "icons scope=icons"
   /// If [hasUndef] is true the first combobox list entry is '-' with the value null.
   /// If [oneTime] is true the result is only used one time, the LRU algoritm is
   /// not used.
-  Future<ComboboxData> comboboxAsync(String key,
+  Future<ComboboxData> comboboxAsync(ComboboxListType listType, String key,
       {bool hasUndef = false, bool oneTime = false}) async {
     ComboboxData rc;
-    if (regExpCombobox.firstMatch(key) == null) {
-      logger.error('wrong key syntax: $key');
+    String errorMessage;
+    switch (listType) {
+      case ComboboxListType.dbColumn:
+        if (regExpComboboxDbColumn.firstMatch(key) == null) {
+          errorMessage = 'wrong key syntax: $key';
+        }
+        break;
+      case ComboboxListType.configuration:
+        if (regExpComboboxConfiguration.firstMatch(key) == null) {
+          errorMessage = 'wrong key syntax: $key';
+        }
+        break;
+      default:
+        errorMessage = 'comboboxAsync(): unknown listType: $listType';
+        break;
+    }
+    if (errorMessage != null) {
+      logger.error(errorMessage);
     } else if (map.containsKey(key)) {
       final entry = updateLRU(key);
       rc = entry.comboboxData;
@@ -75,7 +99,11 @@ class PersistenceCache {
       });
       final texts = hasUndef ? ['-'] : <String>[];
       final values = hasUndef ? <dynamic>[null] : <dynamic>[];
-
+      if (listType == ComboboxListType.configuration) {
+        params2[':scope'] = idModuleName[2];
+        idModuleName[1] = 'configuration';
+        idModuleName[2] = 'combobox_by_scope';
+      }
       final rows = await persistence.list(
           module: idModuleName[1], sqlName: idModuleName[2], params: params2);
       final entry = CacheEntry(key, CacheEntryType.combobox,
index 7b3dd6b7e6ca0ddf7e547234f2b492666965d5f0..d01a618cbe20b399dbe9d591d83e57ebf722c814 100644 (file)
@@ -1,11 +1,12 @@
 import 'package:flutter/material.dart';
 
+import '../page/application_data.dart';
 import '../page/configuration/configuration_list_page.dart';
 import '../page/demo_page.dart';
+import '../page/menu/menu_list_page.dart';
 import '../page/role/role_list_page.dart';
 import '../page/user/user_list_page.dart';
 import '../page/user/user_login_page.dart';
-import '../page/application_data.dart';
 import 'bsettings.dart';
 
 class MenuItem {
@@ -29,10 +30,10 @@ class MenuItem {
         rc = RoleListPage(applicationData);
         break;
       case 'configuration.list':
-        rc = RoleListPage(applicationData);
+        rc = ConfigurationListPage(applicationData);
         break;
       case 'menu.list':
-        rc = RoleListPage(applicationData);
+        rc = MenuListPage(applicationData);
         break;
       case 'demo':
         rc = DemoPage(applicationData);
index 3c841105cea7620358d6c8b7c4560c6faccf422e..8aedd993790f27e32776f169f838f62bb0bcd1c8 100644 (file)
@@ -20,7 +20,7 @@ import 'widget_validators.dart';
 typedef RedrawCallbackFunction = Function(RedrawReason reason,
     {String customString, RedrawCallbackFunctionSimple callback});
 typedef RedrawCallbackFunctionSimple = Function(
-    RedrawReason reason, String customString);
+    RedrawReason reason, {String customString});
 
 class PageControllerBones implements ValidatorController {
   final ModuleModel moduleModel;
@@ -30,30 +30,51 @@ class PageControllerBones implements ValidatorController {
   final GlobalKey<FormState> globalKey;
   final String pageName;
   PageModel page;
+  ThemeData themeData;
   Future<bool> waitForCompletion;
   final ApplicationData applicationData;
-  State parent;
+  RedrawPage parent;
   final BuildContext context;
   Iterable listRows;
   final textControllers = <String, TextEditingController>{};
-  final comboboxDataMap = <String, ComboboxData>{};
   final asyncCompletedModels = <ComboBaseModel>[];
   int refreshCounter = 0;
+  final placeholders = <String, String>{};
+
+  /// Opens another page.
+  /// Use exactly one of the parameter:
+  /// [pageType]: in this case the route will be constructed
+  /// [route]: the route of the new page, e.g. '/role/change'
+  void goTo({ PageModelType pageType, String route}) {
+    applicationData.pushCaller(this);
+    if (pageType != null){
+      route = '/${moduleModel.name}/${StringUtils.enumToString(pageType)}';
+    }
+    Navigator.pushNamed(context, route);
+  }
 
   PageControllerBones(this.globalKey, this.parent, this.moduleModel,
       this.pageName, this.context, this.applicationData,
       [this.redrawCallback]);
 
+  void afterSetState(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback}) {
+    if (callback != null) {
+      callback(reason, customString: customString);
+    }
+  }
+
   Future<ComboboxData> buildComboboxDataFromPersistence(
       ComboBaseModel model) async {
-    final rc = await applicationData.persistenceCache
-        .comboboxAsync(model.listOption, hasUndef: model.hasOption('undef'));
+    final rc = await applicationData.persistenceCache.comboboxAsync(
+        model.listType, model.listOption,
+        hasUndef: model.hasOption('undef'));
     return rc;
   }
 
   /// This method should be called in each stateful widget of a page in the
   /// build() method.
-  void buildHandler(BuildContext context) {
+  void beginOfBuild(BuildContext context) {
     if (asyncCompletedModels.isNotEmpty && ++refreshCounter == 1) {
       wait(millisec: 2000).then((any) => reload());
     }
@@ -117,69 +138,6 @@ class PageControllerBones implements ValidatorController {
     page.fields.forEach((element) => prepareModel(element));
   }
 
-  @deprecated
-  ComboboxData comboboxData(String name) {
-    ComboboxData rc = comboboxDataMap.containsKey(name)
-        ? comboboxDataMap[name]
-        : ComboboxData([], null);
-    comboboxDataMap[name] = rc;
-    if (rc.waitState == WaitState.undef) {
-      ComboBaseModel model = page.fieldByName(name) as ComboBaseModel;
-      if (model != null) {
-        switch (model.listType) {
-          case ComboboxListType.explicite:
-            rc = ComboboxData(model.texts, model.values, WaitState.ready);
-            comboboxDataMap[name] = rc;
-            break;
-          case ComboboxListType.undef:
-            rc = ComboboxData([], [], WaitState.ready);
-            comboboxDataMap[name] = rc;
-            break;
-          case ComboboxListType.dbColumn:
-            comboboxDataDb(model, rc);
-            break;
-          case ComboboxListType.configuration:
-            break;
-        }
-      }
-    }
-    return rc;
-  }
-
-  @deprecated
-  void comboboxDataDb(ComboBaseModel model, ComboboxData data) {
-    // example: xxx.role.list;role_displayname role_id;:role_name=% :excluded=0'
-    final keyColumnsParams = model.listOption.split(';');
-    final nameModuleSql = keyColumnsParams[0].split('.');
-    final cols = keyColumnsParams[1].split(' ');
-    final params = <String, dynamic>{};
-    if (keyColumnsParams.length > 2 && keyColumnsParams[2].isNotEmpty) {
-      keyColumnsParams[2].split(' ').forEach((element) {
-        final keyValue = element.split('=');
-        params[keyValue[0]] = keyValue[1];
-      });
-    }
-    applicationData.persistence
-        .list(
-            module: nameModuleSql[1], sqlName: nameModuleSql[2], params: params)
-        .then((rows) {
-      rows.forEach((row) {
-        data.texts.add(row[cols[0]]);
-        var value = row[cols[1]];
-        if (value is String) {
-          value = StringHelper.fromString(value, model.dataType);
-        }
-        data.addValue(value);
-      });
-      if (model.hasOption('undef')) {
-        data.texts.insert(0, '-');
-        data.insertValue(0, null);
-      }
-      data.waitState = WaitState.ready;
-      redraw(RedrawReason.redraw);
-    });
-  }
-
   /// Completes the widgets asynchronously if needed.
   void completeAsync() {
     waitForCompletion = completeModelsAsync();
@@ -188,9 +146,11 @@ class PageControllerBones implements ValidatorController {
   /// Completes models synchronously if possible or initializes the asynchronous
   /// completion.
   void completeModels(WidgetModel model) {
-    if (model is ComboBaseModel && model.listOption != null) {
-      model.data =
-          applicationData.persistenceCache.comboboxFromCache(model.listOption);
+    if (model is ComboBaseModel && model.listType != null) {
+      if (model.listOption != null) {
+        model.data = applicationData.persistenceCache
+            .comboboxFromCache(model.listOption);
+      }
       if (model.data == null) {
         if (model.mustCompletedAsync) {
           asyncCompletedModels.add(model);
@@ -206,6 +166,7 @@ class PageControllerBones implements ValidatorController {
     Future.forEach(asyncCompletedModels, (ComboBaseModel model) async {
       switch (model.listType) {
         case ComboboxListType.dbColumn:
+        case ComboboxListType.configuration:
           final data = await buildComboboxDataFromPersistence(model);
           model.data = data;
           break;
@@ -244,24 +205,6 @@ class PageControllerBones implements ValidatorController {
     });
   }
 
-  /// Returns a [WidgetList] filled with widgets
-  @deprecated
-  WidgetList filterSet({@required String pageName}) {
-    final rc = WidgetList('${page.fullName()}.widgets', moduleModel.logger);
-    moduleModel
-        .pageByName(pageName)
-        .fields
-        .where((element) => element.filterType != null)
-        .forEach((element) {
-      Widget widget = View().modelToWidget(element, this);
-      if (element.widgetModelType == WidgetModelType.combobox) {
-        rc.waitCandidates.add(element);
-      }
-      rc.addWidget(element.name, widget);
-    });
-    return rc;
-  }
-
   /// Returns the container for application specific information.
   ApplicationData getApplicationData() {
     return applicationData;
@@ -425,8 +368,14 @@ class PageControllerBones implements ValidatorController {
   /// e.g. ModuleModel.parse() is called.
   /// This method must be called early after the constructor.
   void initialize() {
+    themeData = Theme.of(context);
     page = moduleModel.pageByName(pageName);
     widgetList = WidgetList('${page.fullName()}.widgets', moduleModel.logger);
+    buildWidgetList();
+    if (page?.pageModelType == PageModelType.list) {
+      buildRows();
+    }
+    completeAsync();
   }
 
   /// Copies the values of [initialRow] into the values of the fields.
@@ -450,14 +399,13 @@ class PageControllerBones implements ValidatorController {
   /// [reason] and [customString] will be forwarded to the callback function.
   void redraw(RedrawReason reason,
       {String customString, RedrawCallbackFunctionSimple callback}) {
-    if (redrawCallback == null) {
-      moduleModel.logger.error(
-          'not overridden: fetchListRows() in ${moduleModel.widgetName()}.$pageName');
-    } else {
-      if (reason == RedrawReason.setError) {
-        applicationData.setLastErrorMessage(page.fullName(), customString);
-      }
-      redrawCallback(reason, customString: customString, callback: callback);
+    switch(reason){
+      case RedrawReason.fetchList:
+        buildRows();
+        break;
+      default:
+        parent.redraw(reason, customString: customString, callback: callback);
+        break;
     }
   }
 
@@ -498,6 +446,13 @@ class PageControllerBones implements ValidatorController {
 }
 
 // This interface allows the generic handling of the edit form by a model driven module.
+/// An interface for enforcing the redraw of a page.
+abstract class RedrawPage {
+  /// Enforces a redraw (= setState()) of a page.
+  void redraw(RedrawReason reason,
+      {String customString, RedrawCallbackFunctionSimple callback});
+}
+
 enum RedrawReason {
   custom,
   callback,
index 9b56fd1ff0a5b3356807449182a70d4cbfd97ea6..8986444d140688776d5b2a957d32146e7542d363 100644 (file)
@@ -22,7 +22,7 @@ class View {
   static View _instance;
   Settings settings;
   BaseConfiguration widgetConfiguration;
-
+  static final regExpPlaceholder = RegExp(r'~(\w~)~');
   final BaseLogger logger;
 
   factory View([BaseLogger logger]) {
@@ -85,7 +85,7 @@ class View {
   Widget combobox(
       ComboBaseModel model, PageControllerBones controller, initialValue) {
     Widget rc;
-    if (model.data != null && model.data.waitState == WaitState.ready) {
+    if (model != null && model.data?.waitState == WaitState.ready) {
       rc = comboboxRaw(model, controller, initialValue);
     } else {
       rc = FutureBuilder<bool>(
@@ -94,7 +94,7 @@ class View {
           if (snapshot.hasData) {
             return comboboxRaw(model, controller, initialValue);
           } else if (snapshot.hasError) {
-            return errorMessage(snapshot.error);
+            return errorMessage(snapshot.error.toString());
           } else {
             return Container(
               alignment: Alignment.topCenter,
@@ -150,7 +150,7 @@ class View {
     if (comboboxData == null || comboboxData.texts.length == 0) {
       items.add(DropdownMenuItem(
           onTap: controller.getOnTap(model.name, controller),
-          value: null,
+          value: initialValue,
           child: Text('-')));
     } else {
       for (var ix = 0; ix < comboboxData.texts.length; ix++) {
@@ -240,7 +240,7 @@ class View {
         rc = emptyLine(model);
         break;
       case WidgetModelType.text:
-        rc = text(model);
+        rc = text(model, controller);
         break;
       case WidgetModelType.checkbox:
         rc = checkbox(model, controller, initialValue);
@@ -301,8 +301,24 @@ class View {
   }
 
   /// Creates a text from the [model].
-  Widget text(TextModel model) {
-    final rc = Text(model.text);
+  Widget text(TextModel model, PageControllerBones controller) {
+    var text = model.text;
+    if (model.hasOption('placeholders')){
+      text = StringHelper.replacePlaceholders(text, controller.placeholders, regExpPlaceholder: regExpPlaceholder);
+    }
+    TextStyle style;
+    if (model.hasOption('h1')) {
+      style = controller.themeData.textTheme.headline1;
+    } else if (model.hasOption('h2')){
+      style = controller.themeData.textTheme.headline2;
+    } else if (model.hasOption('h3')){
+      style = controller.themeData.textTheme.headline3;
+    } else if (model.hasOption('h4')){
+      style = controller.themeData.textTheme.headline4;
+    } else if (model.hasOption('h5')){
+      style = controller.themeData.textTheme.headline5;
+    }
+    final rc = Text(model.text, style: style);
     return rc;
   }
 
diff --git a/model_tool/lib/src/net/email.dart b/model_tool/lib/src/net/email.dart
new file mode 100644 (file)
index 0000000..1aef55d
--- /dev/null
@@ -0,0 +1,38 @@
+import 'package:http/http.dart' as http;
+
+class EMail {
+  static sendRegistrationNotification(String email) async {
+    Map<String, String> headers = new Map();
+    headers["Authorization"] = "Bearer SENDGRIDAPIKEY";
+    headers["Content-Type"] = "application/json";
+
+    var url = 'https://api.sendgrid.com/v3/mail/send';
+    var response = await http.post(url, headers: headers, body: '''{
+          \"personalizations\": [
+            {
+              \"to\": [
+                {
+                  \"email\": \"jerrod@liftaixxx.com\"
+                },
+                {
+                  \"email\": \"darran@gmailxxx.com\"
+                }
+              ]
+            }
+          ],
+          \"from\": {
+            \"email\": \"app@liftaixxx.com\"
+          },
+          \"subject\": \"New user registration\",
+          \"content\": [
+            {
+              \"type\": \"text\/plain\",
+              \"value\": \"New user register: $email\"
+            }
+          ]
+        }
+        ''');
+    print('Response status: ${response.statusCode}');
+    print('Response body: ${response.body}');
+  }
+}
index ca7db9669ca421467282c3bf6654f8e61349dc9a..32f0766e4f39b427e340db1e3e067ce8c2cf5e3b 100644 (file)
@@ -45,6 +45,13 @@ void main() {
       expect(StringHelper.fromString('2020.1.2', null),
           startsWith('<StringHelper.fromString(): unknown datatype'));
     });
+    test('replacePlaceholders', () {
+      expect(
+          StringHelper.replacePlaceholders(
+              'a: ~aa~ b: ~b c: ~c~ ~d~', {'aa': 'x', 'b': 'y', 'c': 'zz'},
+          syntaxPlaceholder: r'~(\w+)~'),
+          equals('a: x b: ~b c: zz ~d~'));
+    });
   });
   group('dateXToString', () {
     test('dateToString', () {
@@ -94,16 +101,24 @@ void main() {
           equals('4.3.2020 09:33'));
     });
     test('asDatabaseString', () {
-      expect(StringHelper.asDatabaseString('abc', DataType.string), equals('abc'));
+      expect(
+          StringHelper.asDatabaseString('abc', DataType.string), equals('abc'));
       expect(StringHelper.asDatabaseString(345, DataType.int), equals('345'));
-      expect(StringHelper.asDatabaseString(34.5, DataType.float), equals('34.5'));
+      expect(
+          StringHelper.asDatabaseString(34.5, DataType.float), equals('34.5'));
       expect(StringHelper.asDatabaseString(true, DataType.bool), equals('T'));
       expect(StringHelper.asDatabaseString(false, DataType.bool), equals('F'));
-      expect(StringHelper.asDatabaseString(null, DataType.bool), equals('NULL'));
-      expect(StringHelper.asDatabaseString(null, DataType.currency), equals('NULL'));
-      expect(StringHelper.asDatabaseString(DateTime(2020, 3, 4, 9, 33, 44), DataType.dateTime),
+      expect(
+          StringHelper.asDatabaseString(null, DataType.bool), equals('NULL'));
+      expect(StringHelper.asDatabaseString(null, DataType.currency),
+          equals('NULL'));
+      expect(
+          StringHelper.asDatabaseString(
+              DateTime(2020, 3, 4, 9, 33, 44), DataType.dateTime),
           equals('2020-03-04 09:33:44'));
-      expect(StringHelper.asDatabaseString(DateTime(2020, 3, 4, 9, 33, 44), DataType.date),
+      expect(
+          StringHelper.asDatabaseString(
+              DateTime(2020, 3, 4, 9, 33, 44), DataType.date),
           equals('2020-03-04'));
     });
   });
index bbfc055e43fa16b8d2ece6a5fc24a5b40ad5d383..91859c4843f7d01493776855bcf6dae726e47bd2 100644 (file)
@@ -109,7 +109,6 @@ void main() {
           'b',
           ButtonModelType.store,
           [],
-          null,
           logger));
       page.addButton(ButtonModel.direct(
           null,
@@ -118,13 +117,11 @@ void main() {
           'b',
           ButtonModelType.store,
           [],
-          null,
           logger));
       page.buttonByName('unknown');
       page.addField(TextFieldModel.direct(
           null,
           page,
-          null,
           'x',
           'y',
           'z',
@@ -135,7 +132,6 @@ void main() {
       page.addField(TextFieldModel.direct(
           null,
           page,
-          null,
           'x',
           'y',
           'z',
index 4aa7bef52105936469f7c7beae8c9cf0dad2b61a..be644c63e94e993ea471d213456aba82a9f9fbcb 100644 (file)
@@ -17,21 +17,21 @@ void main() {
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, '''= module role: options: 
 == table role: options: 
-    column role_id: DataType.int "Id" options: primary notnull unique readonly
+    column role_id: DataType.int "Id" options: primary notnull unique
     column role_name: DataType.string "Rolle" options: unique notnull
     column role_priority: DataType.int "Priorität" options: 
     column role_active: DataType.bool "Aktiv" options: 
-    column role_createdat: DataType.dateTime "Erzeugt" options: hidden null
-    column role_createdby: DataType.string "Erzeugt von" options: hidden
-    column role_changedat: DataType.dateTime "Geändert" options: hidden null
-    column role_changedby: DataType.string "Geändert von" options: hidden
+    column role_createdat: DataType.dateTime "Erzeugt" options: null
+    column role_createdby: DataType.string "Erzeugt von" options: null
+    column role_changedat: DataType.dateTime "Geändert" options: null
+    column role_changedby: DataType.string "Geändert von" options: null
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     allDbFields 10 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 17 options: 
+    allDbFields 21 options: 
     ] # change.simpleForm1
 == page list: PageModelType.list options: 
     = section filterPanel1: SectionModelType.filterPanel options:  [
@@ -51,27 +51,28 @@ void main() {
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, equals('''= module user: options: 
 == table user: options: 
-    column user_id: DataType.int "Id" options: primary notnull unique readonly
+    column user_id: DataType.int "Id" options: primary notnull unique
     column user_name: DataType.string "User" options: unique notnull
     column user_displayname: DataType.string "Anzeigename" options: unique notnull
     column user_email: DataType.string "EMail" options: unique notnull
     column user_password: DataType.string "Passwort" options: password hidden
     column user_role: DataType.reference "Rolle" options: undef
-    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
-    column user_createdby: DataType.string "Erzeugt von" options: hidden
-    column user_changedat: DataType.dateTime "Geändert" options: hidden null
-    column user_changedby: DataType.string "Geändert von" options: hidden
+    column user_createdat: DataType.dateTime "Erzeugt" options: null
+    column user_createdby: DataType.string "Erzeugt von" options: null
+    column user_changedat: DataType.dateTime "Geändert" options: null
+    column user_changedby: DataType.string "Geändert von" options: null
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     allDbFields 12 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 20 options: 
+    allDbFields 24 options: 
     button set_password: text: options: Passwort ändern 
     ] # change.simpleForm1
 == page password: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
+    text 38 text: Ändern des Passworts von Benutzer ~user~: options: placeholder
     textField user_password: options: password
     textField repetition: options: password
     ] # password.simpleForm1
@@ -100,24 +101,24 @@ void main() {
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, equals('''= module configuration: options: 
 == table configuration: options: 
-    column configuration_id: DataType.int "Id" options: primary notnull unique readonly
+    column configuration_id: DataType.int "Id" options: primary notnull unique
     column configuration_scope: DataType.string "Bereich" options: notnull
     column configuration_property: DataType.string "Eigenschaft" options: 
     column configuration_order: DataType.int "Reihe" options: 
     column configuration_type: DataType.string "Datentyp" options: 
     column configuration_value: DataType.string "Wert" options: 
     column configuration_description: DataType.string "Beschreibung" options: 
-    column configuration_createdat: DataType.dateTime "Erzeugt" options: hidden null
-    column configuration_createdby: DataType.string "Erzeugt von" options: hidden
-    column configuration_changedat: DataType.dateTime "Geändert" options: hidden null
-    column configuration_changedby: DataType.string "Geändert von" options: hidden
+    column configuration_createdat: DataType.dateTime "Erzeugt" options: null
+    column configuration_createdby: DataType.string "Erzeugt von" options: null
+    column configuration_changedat: DataType.dateTime "Geändert" options: null
+    column configuration_changedby: DataType.string "Geändert von" options: null
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     allDbFields 13 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 23 options: 
+    allDbFields 27 options: 
     ] # change.simpleForm1
 == page list: PageModelType.list options: 
     = section filterPanel1: SectionModelType.filterPanel options:  [
@@ -138,20 +139,20 @@ void main() {
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, equals('''= module menu: options: 
 == table menu: options: 
-    column menu_id: DataType.int "Id" options: primary notnull unique readonly
+    column menu_id: DataType.int "Id" options: primary notnull unique
     column menu_name: DataType.string "Name" options: unique notnull
     column menu_icon: DataType.reference "Bild" options: 
-    column menu_createdat: DataType.dateTime "Erzeugt" options: hidden null
-    column menu_createdby: DataType.string "Erzeugt von" options: hidden
-    column menu_changedat: DataType.dateTime "Geändert" options: hidden null
-    column menu_changedby: DataType.string "Geändert von" options: hidden
+    column menu_createdat: DataType.dateTime "Erzeugt" options: null
+    column menu_createdby: DataType.string "Erzeugt von" options: null
+    column menu_changedat: DataType.dateTime "Geändert" options: null
+    column menu_changedby: DataType.string "Geändert von" options: null
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     allDbFields 9 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 15 options: 
+    allDbFields 19 options: 
     ] # change.simpleForm1
 == page list: PageModelType.list options: 
     = section filterPanel1: SectionModelType.filterPanel options:  [
index fc352213cfbc8fb30525aff0977382048cc932a9..86421c9d30a91f4d114274cceaff6409fd43f1dc 100644 (file)
@@ -27,15 +27,17 @@ void main() async {
     cache = PersistenceCache(persistence, logger, maxEntries: 2);
   });
   group('basics', () {
-    test('combobox', () async {
+    test('combobox-dbcolumn', () async {
       logger.clear();
       final cache2 = PersistenceCache(persistence, logger);
       expect(cache2, isNotNull);
       cache.clear();
       const key = 'id1.role.list;role_name role_id;:role_name=%';
       const key2 = 'id2.role.list;role_name role_id;:role_name=%';
-      var data = await cache.comboboxAsync(key, hasUndef: true);
-      var data2 = await cache.comboboxAsync(key2, hasUndef: false);
+      var data = await cache.comboboxAsync(ComboboxListType.dbColumn, key,
+          hasUndef: true);
+      var data2 = await cache.comboboxAsync(ComboboxListType.dbColumn, key2,
+          hasUndef: false);
       expect(logger.errors.length, equals(0));
       expect(data, isNotNull);
       expect(data.texts.length, greaterThan(4));
@@ -46,7 +48,8 @@ void main() async {
       expect(data.texts.length, equals(data2.valuesLength + 1));
       expect(cache.leastReasentlyUsed.length, equals(2));
       const key3 = 'id3.role.list;role_name role_id;:role_name=%';
-      var data3 = await cache.comboboxAsync(key3, hasUndef: false);
+      var data3 = await cache.comboboxAsync(ComboboxListType.dbColumn, key3,
+          hasUndef: false);
       expect(data3, isNotNull);
       expect(cache.leastReasentlyUsed.length, equals(2));
       expect(cache.map.containsKey(key), isFalse);
@@ -57,7 +60,7 @@ void main() async {
       logger.clear();
       cache.clear();
       const key = 'id1.role.list;role_name+role_id;:role_name=%';
-      final data = await cache.comboboxAsync(key);
+      final data = await cache.comboboxAsync(ComboboxListType.dbColumn, key);
       expect(data, isNull);
       expect(logger.errors.length, equals(1));
       expect(
@@ -85,12 +88,27 @@ void main() async {
       cache.clear();
       expect(cache, isNotNull);
       const key = 'id1.role.list;role_name role_id;:role_name=%';
-      var data = await cache.comboboxAsync(key, hasUndef: true);
+      var data = await cache.comboboxAsync(
+          ComboboxListType.dbColumn, key, hasUndef: true);
       final data2 = cache.comboboxFromCache(key);
       expect(logger.errors.length, equals(0));
       expect(data, isNotNull);
       expect(data2, isNotNull);
       expect(data2, equals(data));
     });
+    test('combobox-configuration', () async {
+      logger.clear();
+      final cache2 = PersistenceCache(persistence, logger);
+      expect(cache2, isNotNull);
+      cache.clear();
+      const key = 'id1.scope.icons;configuration_value configuration_id;:role_name=%';
+      var data = await cache.comboboxAsync(
+          ComboboxListType.configuration, key, hasUndef: true);
+      expect(logger.errors.length, equals(0));
+      expect(data, isNotNull);
+      expect(data.texts.length, greaterThan(4));
+      expect(data.texts.length, equals(data.valuesLength));
+      expect(cache.leastReasentlyUsed.length, equals(1));
+    });
   });
 }
index a233e29db934365016a5ecf0f5d1071a2b54a0be..5af1bd103c41c86aa667b8b5a5e8c5f03b27ace6 100644 (file)
@@ -190,7 +190,7 @@ void main() {
       final table = module.tableByName('test');
       expect(table, isNotNull);
       var model = table.columnByName('test');
-      final page = PageModel(module, null, logger);
+      final page = PageModel(module, logger);
       page.addField(model);
       model.page = page;
       expect(model, isNotNull);