In the previous post , while designing our API we stumbled on a flexibility problem.
How do we add common methods to our API builders without compromising on flexibility?
We are going to use C#’s extension methods. Here is how:
public interface ICustomCssEnabledBuilder < TBuilder >
{
TBuilder GetBuilderInstance ();
ICustomCssEnabledComponent GetComponent ();
}
public static class CustomCssEnabledBuilderExtensions
{
public static TBuilder AddCssClass < TBuilder >
( this ICustomCssEnabledBuilder < TBuilder > builder , string cssClass )
{
builder . GetComponent (). CssClasses . Add ( cssClass );
return builder . GetBuilderInstance ();
}
}
Now when a builder needs to support adding a css class to a css enabled component,
it just has to implement the ICustomCssEnabledBuilder interface.
public class TextBoxBuilder : ICustomCssEnabledBuilder < TextBoxBuilder >
{
TextBox _textBox ;
public TextBoxBuilder ( TextBox textBox )
{
_textBox = textBox ;
}
[ EditorBrowsable ( EditorBrowsableState . Never )]
public TextBoxBuilder GetBuilderInstance ()
{
return this ;
}
[ EditorBrowsable ( EditorBrowsableState . Never )]
public ICustomCssEnabledComponent GetComponent ()
{
return _textBox ;
}
//Textbox specific option
public TextBoxBuilder Name ( string name )
{
_textBox . Name = name ;
return this ;
}
//code omitted
}
Now our builder can be used like this
panel . AddTextBoxFor ( model => model . TextProp2 )
. TextBoxSpecificOption (...)
. AddCssClass ( "custom-css-class" )
. OtherTextBoxSpecificOption (...);
The same technique can be used for the events API.
public interface IEventEnabledBuilder < TBuilder , TEventBuilder >
{
TBuilder GetBuilderInstance ();
TEventBuilder GetEventBuilder ();
}
public static class EventEnabledBuilderExtensions
{
public static TBuilder Events < TBuilder , TEventBuilder >( this IEventEnabledBuilder < TBuilder , TEventBuilder > builder ,
Action < TEventBuilder > eventBuilderExpression )
{
var eventBuilder = builder . GetEventBuilder ();
eventBuilderExpression ( eventBuilder );
return builder . GetBuilderInstance ();
}
}
public class TextBoxEventBuilder
{
private TextBox _textBox ;
public TextBoxEventBuilder ( TextBox textBox )
{
_textBox = textBox ;
}
public TextBoxEventBuilder OnChange ( string jsHandle )
{
_textBox . Events . Add ( new ComponentEvent ()
{
EventName = "change" ,
JsHandler = jsHandle
});
return this ;
}
}
So our TextBoxBuilder is changed to this.
public class TextBoxBuilder : ICustomCssEnabledBuilder < TextBoxBuilder >,
IEventEnabledBuilder < TextBoxBuilder , TextBoxEventBuilder >
{
private TextBox _textBox ;
public TextBoxBuilder ( TextBox textBox )
{
_textBox = textBox ;
}
[ EditorBrowsable ( EditorBrowsableState . Never )]
public TextBoxBuilder GetBuilderInstance ()
{
return this ;
}
[ EditorBrowsable ( EditorBrowsableState . Never )]
public ICustomCssEnabledComponent GetComponent ()
{
return _textBox ;
}
[ EditorBrowsable ( EditorBrowsableState . Never )]
public TextBoxEventBuilder GetEventBuilder ()
{
return new TextBoxEventBuilder ( _textBox );
}
//Textbox specific option
public TextBoxBuilder Name ( string name )
{
_textBox . Name = name ;
return this ;
}
//code omitted
}
And now it can be used like this.
panel . AddTextBoxFor ( model => model . TextProp2 )
. TextBoxSpecificOption (...)
. AddCssClass ( "custom-css-class" )
. OtherTextBoxSpecificOption (...)
. Events ( e => e . OnChange ( "jsHandlerName" ));
Awesomeness achieved !!