The custom scripts need to be added to the content editor before anything else is rendered. Use the <renderContentEditor> pipeline to add a processor (or use a config patch file with a patch:before="*[1]" attribute). The processor should look something like this:
public void Process(PipelineArgs args)
{
if (!Context.ClientPage.IsEvent)
{
HttpContext current = HttpContext.Current;
if (current != null)
{
Page handler = current.Handler as Page;
if (handler != null) {
Assert.IsNotNull(handler.Header, "Content Editor <head> tag is missing runat='value'");
handler.Header.Controls.Add(new LiteralControl("<script type='text/javascript' language='javascript' src='/sitecore/shell/custom/autocomplete.js'></script>"));
}
}
}
}
For the actual custom field class, I decided to go with inheriting from the Sitecore.Web.UI.HtmlControls.Control and stick closely to what a regular DropList field would do. The data source of the custom field would contain three parameters: the url of the service to call, the field that would be used as a key, and the field that would be used as the display text of a service "item".
public Dictionary<string, string> ControlParameters = new Dictionary<string, string>()
{
{"serviceurl", string.Empty},
{"textfield", string.Empty},
{"keyfield", string.Empty}
};
private void LoadControlParameters()
{
var parameters = Sitecore.StringUtil.ParseNameValueCollection(this.Source, '|', ':');
foreach (string p in parameters.AllKeys)
{
ControlParameters[p] = parameters[p];
}
}
The service I was calling used json by default, so I decided that the field would support json response and used System.Json.JasonValue for parsing the data:
protected virtual Dictionary<string, string> GetItems()
{
LoadControlParameters();
Dictionary<string, string> items = new Dictionary<string, string>();
try
{
// Call the service
string serviceResult = Sitecore.Web.WebUtil.ExecuteWebPage(ControlParameters["serviceurl"]);
dynamic json = JsonValue.Parse(serviceResult);
foreach (dynamic item in json)
{
items.Add(item[ControlParameters["keyfield"]].Value.ToString(), item[ControlParameters["textfield"]].Value.ToString());
}
}
catch
{
// invalid endpoint
Sitecore.Diagnostics.Log.Error(string.Format("{0}: Service End-Point Not Found - {1}", this.ToString(), ControlParameters["serviceurl"]), this);
}
return items;
}
private Dictionary<string, string> _suggestions; public Dictionary<string, string> Suggestions { get { if (_suggestions == null) { _suggestions = GetItems(); } return _suggestions; } }
Now that we have the key value pairs of suggestions for the field, all that's left is to override the DoRender method of the base Control.
protected override void DoRender(HtmlTextWriter output)
{
string err = null;
//check if the data source of the field is empty first
if (string.IsNullOrEmpty(this.Source))
{
err = SC.Globalization.Translate.Text("Source is not defined for this field.");
}
else
{
//check if the suggestions contain a previously saved value
//we want to show the value even if it is not returned by the service anymore
bool found = Suggestions.ContainsKey(this.Value);
//add any custom css for the field
output.Write("<link rel='stylesheet' href='/sitecore/shell/custom/autofill.css' />");
List<string> items = Suggestions.Select(a => string.Format("{0}|{1}", a.Value.Replace("'", string.Empty), a.Key)).ToList();
//output the script for the autocomplete plugin
string scr = @"
<script>
$sc(function () {
var availableTags = [
'" + String.Join("', '", items.ToArray()) + @"'
];
$sc('#au_{ID}').autocomplete({
source: availableTags,
mustMatch: true,
focus: function(event, ui) {
$sc('#au_{ID}').val(ui.item.value.split('|')[0]);
return false;
},
select: function( event, ui ) {
$sc('#{ID}').val(ui.item.value.split('|')[1]);
return false;
}
});
});
</script>
<input type='text' class='scContentControl' id=au_{ID}".Replace("{ID}", this.ID) + @" value='{Value}'/>".Replace("{Value}", found ? Suggestions[this.Value] : this.Value);
output.Write(scr);
output.Write("<input type='hidden' value='" + this.Value + "' "+ this.GetControlAttributes()+ " />");
//give the user any information that may be important
if (Suggestions.Count() == 0)
{
err = Sitecore.Globalization.Translate.Text("The service did not return any options.");
}
else if (!found && !string.IsNullOrEmpty(this.Value))
{
err = Sitecore.Globalization.Translate.Text("Value not in the selection list.");
}
}
if (err != null)
{
output.Write("<div style=\"color:#999999;padding:2px 0px 0px 0px\">{0}</div>", err);
}
}
No comments:
Post a Comment