public DataboundDatagridItemCreatorForm(ISomeService someService)
        {
            _view = new DataboundDatagridItemCreatorFormView();

            var someText   = LocalValueFieldBuilder.Build(_view.SomeText, Validator.IsNotNullRef);
            var someNumber = LocalValueFieldBuilder.BuildNullableInt(_view.SomeNumber, Validator.IsNotNull, Validator.MustBePositive <int>());
            var someBool   = LocalValueFieldBuilder.Build(_view.SomeBool);

            _view.SomeTrait.PermittedValues = EnumExtensions.GetEnumValues <SomeTraitType>().Select(x => (SomeTraitType?)x);
            var someTrait = LocalValueFieldBuilder.Build(_view.SomeTrait, Validator.IsNotNull);

            var createProduct = RemoteActionBuilder.Build(_view.CreateAction,
                                                          () => someService.Create(
                                                              new SomeDto {
                SomeNumber = someNumber.Value.GetValueOrDefault(),
                SomeText   = someText.Value,
                SomeBool   = someBool.Value,
                SomeTrait  = someTrait.Value.Value
            }),
                                                          x => {
                CreatedItem = x;
                Ended?.Invoke(this, Outcome.Created);
            });

            var isFormValid = new AggregatedErrorsValue <bool>(false, self => !self.Errors.Any(), x => {
                x.Observes(someText);
                x.Observes(someNumber);
                x.Observes(someBool);
                x.Observes(someTrait);
            });

            createProduct.BindEnableAndInitialize(isFormValid);
        }
        public AllFieldsFilledDataEntryForm()
        {
            var view  = new AllFieldsFilledDataEntryFormView();
            var ended = new Observable.Publisher <Unit>();

            Ended = ended;
            View  = view;

            var strEntry = LocalValueFieldBuilder.Build(
                view.StringEntry,
                (x, errors) => errors.IfTrueAdd(
                    string.IsNullOrWhiteSpace(x) || x.Length < 4,
                    "Text must be at least 4 chars long"),
                (x, errors) => errors.IfTrueAdd(
                    string.IsNullOrWhiteSpace(x) || x.Length > 10,
                    "Text must be no longer than 10 chars long")
                );

            var intEntry = LocalValueFieldBuilder.BuildNullableInt(
                view.IntEntry,
                (x, errors) => errors.IfTrueAdd(!x.HasValue || x < 1234, "Number must be bigger than 1234")
                );

            var decEntry = LocalValueFieldBuilder.BuildNullableDecimal(
                view.DecimalEntry,
                DecimalFormat.WithTwoDecPlaces,
                (x, errors) => errors.IfTrueAdd(!x.HasValue || x < 5.6m,
                                                "Number must be bigger than " + I18n.Localize(5.6m, DecimalFormat.WithOneDecPlace))
                );

            //this field is used to show that values on screen are not necessarily values within model.
            //In other words: when validator rejected value from user wrong value and error message stays on screen BUT model keeps its last-accepted-value
            var summaryLine = LocalValueFieldBuilder.Build("", view.SummaryLine);

            void UpdateSummaryLine()
            {
                var decVal = !decEntry.Value.HasValue ? "" : I18n.Localize(decEntry.Value.Value, DecimalFormat.WithOneDecPlace);

                summaryLine.DoProgrammaticChange(
                    $@"Accepted vals: Text={strEntry.Value} Int={intEntry.Value} Decimal={decVal}"
                    );
            }

            strEntry.Changed += (_, __, ___, ____, _____) => UpdateSummaryLine();
            intEntry.Changed += (_, __, ___, ____, _____) => UpdateSummaryLine();
            decEntry.Changed += (_, __, ___, ____, _____) => UpdateSummaryLine();

            UpdateSummaryLine();

            var confirm = LocalActionBuilder.Build(view.ConfirmAction, () => ended.Fire(Unit.Instance));

            confirm.BindEnableAndInitializeAsObserving(x => {
                x.Observes(strEntry);
                x.Observes(intEntry);
                x.Observes(decEntry);
            });
        }