MRCollective / ChameleonForms

Shape-shifting your forms experience in ASP.NET Core MVC
MIT License
254 stars 56 forks source link

Field Configuration Modifiers #106

Open robdmoore opened 9 years ago

robdmoore commented 9 years ago

This is a brainstorm of my idea for https://trello.com/c/1hNDoqon/74-ability-to-add-field-configuration-modifiers

It would probably replace the need for #102; I think this is more flexible and means we don't have to deal with crazy generic statics.

Thoughts @bendetat @MattDavies @zabulus?


// Create this class (happy to field better naming ideas)
public class FormFieldOutput<TModel, T>
{
    public FormFieldOutput(FieldParent parent)
    {
        Parent = parent;
    }

    public FieldParent Parent { get; private set; }

    public IFieldGeneratorHandler<TModel, T> Handler { get; set; }
    public IFieldConfiguration Configuration { get; set; }
    public IFormTemplate Template { get; set; }
    public ModelMetadata Metadata { get; set; }

    public ReadonlyFieldConfiguration GetReadonlyConfiguration()
    {
        return new ReadonlyFieldConfiguration(Configuration);
    }
}

// Change DefaultFieldGenerator.PrepareFieldConfiguration to:
        public FormFieldOutput<TModel, T> PrepareField(IFieldConfiguration fieldConfiguration, FieldParent fieldParent)
        {
            var field = new FormFieldOutput(fieldParent)
            {
                Handler = FieldGeneratorHandlersRouter<TModel, T>.GetHandler(this),
                Configuration = fieldConfiguration ?? new FieldConfiguration(),
                Template = Template,
                Metadata = Metadata
            }

            // This call could result in the handler, field config, template or metadata being changed on the fly for this field
            // In order to make things more testable / extendable we could put the conventions on the IForm object instead of in a static?
            FieldConventions.ForEach(c => c.Apply(field));

            field.Handler.PrepareFieldConfiguration(fieldConfiguration);
            field.Template.PrepareFieldConfiguration(this, handler, fieldConfiguration, fieldParent);

            return field;
        }

// These conventions will be defined by default (but can be removed), which were in the above method previously:
public class ReadonlyFieldsConvention : IFormConvention
{
    public void Apply<TModel, T>(FormFieldOutput<TModel, T> field)
    {
        if (field.IsReadOnly)
            field.Configuration.Readonly();
    }
}
public class FormatStringConvention : IFormConvention
{
    public void Apply<TModel, T>(FormFieldOutput<TModel, T> field)
    {
        if (!string.IsNullOrEmpty(field.Metadata.EditFormatString) && string.IsNullOrEmpty(field.Configuration.FormatString))
            field.Configuration.WithFormatString(field.Metadata.EditFormatString);
    }
}
public class NoneDisplayTextConvention : IFormConvention
{
    public void Apply<TModel, T>(FormFieldOutput<TModel, T> field)
    {
        if (!string.IsNullOrEmpty(field.Metadata.NullDisplayText) && string.IsNullOrEmpty(field.Configuration.NoneString))
            field.Configuration.WithNoneAs(field.Metadata.NullDisplayText);
    }
}

// And people can create and register their own conventions like this:

FieldConventions.Add(new AddCalendarToDateFieldsConvention());

...

public class AddCalendarToDateFieldsConvention : IFormConvention
{
    public void Apply<TModel, T>(FormFieldOutput<TModel, T> field)
    {
        if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTimeOffset))
            field.Configuration.AddClass("datepicker").WithFormatString("{0:dd/MM/yyyy}");
    }
}
zabulus commented 9 years ago

I think that Convention's place in the IFormTemplate. It is a more general place for them. I doubt that anyone need to specialize each form in a project with custom conventions.

robdmoore commented 9 years ago

Possibly.

The way I see it, conventions will be largely project specific. The template you use might be the generic one, but you might want to add conventions still. Also, you might want to swap out the template for a different one.

In saying that I can imagine wanting to define conventions that go with a template as well.

On 22 Nov 2014, at 7:34 pm, zabulus notifications@github.com wrote:

I think that Convention's place in the IFormTemplate. It is a more general place for them. I doubt that anyone need to specialize each form in a project with custom conventions.

— Reply to this email directly or view it on GitHub.

fsateler commented 8 years ago

There is something awkward in this proposal, in that the conventions would override configuration set for a form (it is my understanding that the PrepareField stuff is invoked right before rendering, please correct me if I'm wrong). For some attributes, this is not important (eg, the readonly attribute), but for others it would. Your examples have checks for user settings, but that is impossible for properties that are value types (eg, ShouldInlineLabelWrapElement).

robdmoore commented 8 years ago

Thanks @fsateler. I'll keep that in mind if we get to the point of implementing this.

Happy for you or anyone else to suggest changes or even submit a PR for the change :)