Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Greg Berlin 818 posts 634 karma points
    Nov 14, 2010 @ 11:09
    Greg Berlin
    0

    Ajax Autocomplete List FieldType

    hey all,

    I need to make an autocomplete list which is populated with data from a database, and autocompletes as the user types.. similar to this:  http://jqueryui.com/demos/autocomplete/  (in fact i'll probably use the jquery one for the client side).

    I've already created a custom dropdownlist for use as a contour fieldtype.. which was challenging but i got it working... however my client wants me to ajaxify it because of the massive amount of values in the ddl.

    I guess what i'd need to do is something like create a custom .net datatype (ajax ddl), and then create a contour field type to render this data type... i'm just wondering if there are any issues i'm likely to come across with this approach?  I burned a ton of time on this last time, and my client (a charity) is hoping there's not too much more work involved... i too hope not to spend too long on it, so any assistance would be massively appreciated.

    Are there any autocomplete .net datatypes floating around that i could repurpose for this too?

    Thanks heaps

    greg

  • Greg Berlin 818 posts 634 karma points
    Nov 14, 2010 @ 12:12
    Greg Berlin
    0

    Hmmm... I've been looking at options, and this is looking like its going to be really difficult to accomplish.  I'm considering creating a macro with a user control containing the ajax autocomplete textbox, and depending on the value selected, forward the user to the form, or show them an error.  That way I dont need to create a custom control to use in Contour.

    If i do this though, what would be the best way to pass the selected value on this user control through to the Contour form?  I'd need to have a field type on the contour form that could retrieve this value, and autopopulate itself with the selected value... querystring?  session value?

    I'll give it a bash tomorrow night... but hoping to get some guidance to cut down on dev time due to modest client budget, and limited time on my part :(

  • Harald Ulriksen 207 posts 249 karma points
    Nov 14, 2010 @ 22:47
    Harald Ulriksen
    0

    Hi Greg

    as you have written yourself, this can be solved using jQuery autocomplete. I have a feeling you make it more complicated than it has to.

    As I see it you already got data, a custom fieldtype and jQuery. If you don't need to handle non-script clients you can wrap it all together with

    0. Add jquery to the page - or use some lib such as Client Dependency http://clientdependency.codeplex.com/ to include jQuery

    1. Create a /base method for output of autocomplete data http://our.umbraco.org/wiki/reference/umbraco-base

    2. A fieldtype which renders a text box and initialization script for autocomplete using the /base data as source.

    I'm quite sure this will not be easier if you use a separate control and then try to feed the contour form. Should not the input be on the same page as the contour form?

    A sample asp.net ajax control can be found at http://www.asp.net/ajax/ajaxcontroltoolkit/samples/autocomplete/autocomplete.aspx or http://convincingemail.com/autosuggest-autocomplete.aspx  - but I'm not sure this will really help you as I think the best and fastest way would be the one described above.

  • Greg Berlin 818 posts 634 karma points
    Nov 14, 2010 @ 23:23
    Greg Berlin
    0

    hey Harald,

    Thanks for the advice.. I guess the bit i dont quite understand is the field type/ web control.. in order to insert a special control into a contour form, i need to create a custom web control... do you suggest i make a custom control that inherits from a textbox control, and in that add some jquery initialisation data?  Or initialise the control in the template for the page containing the contour form?  The latter seems easier, but a bit messier... making this reusable seems impossible to me.

    Base is interesting, never knew what it was about, however my list will not be umbraco data, so i'll just use jquery ajax to get the list.

    And to answer your question, this input is a pre-requisite for the form - if they cannot find the town they're in, they cannot enter the form... so i could manage it by having it separate, but again feeding the value into the form is the problem with that approach.

    Thanks again, i'll try this tonight, unless anybody else has other suggestions?

  • Comment author was deleted

    Nov 15, 2010 @ 08:47

    @Greg, you'll need to create a custom fieldtype , to get started you could check the contour shared sourcecode http://our.umbraco.org/projects/umbraco-contour-shared-source

  • Greg Berlin 818 posts 634 karma points
    Nov 15, 2010 @ 08:53
    Greg Berlin
    0

    hey Tim.. yeah i get the custom field type bit, that's to tell contour to render my new control... its the creation of the new control, and how to render the javascript elements, and wrapping it all up in a custom web control that's seeming really difficult to me.  

    Have you created a custom web control with javascript like this before tim?  is it straightforward? 

  • Comment author was deleted

    Nov 15, 2010 @ 09:00

    @Greg, using the http://www.asp.net/ajax/ajaxcontroltoolkit/samples/autocomplete/autocomplete.aspx it's pretty simply, you'll just have to create a webservice, but it's all described on that page

  • Harald Ulriksen 207 posts 249 karma points
    Nov 15, 2010 @ 11:40
    Harald Ulriksen
    0

    @Tim - is it possible to populate the hidden fieldtype with data form querystring?

    @Greg - no need to create custom fieldtype if you need this field on another page than the form.

    1. Create the state dropdown using either textbox + jquery or the asp.net ajax autocomplete

    2. Use hidden field

    3. Populate hidden field using either workflow or some other methd (let's see what Tim has to say about this).

    H.

  • Greg Berlin 818 posts 634 karma points
    Nov 15, 2010 @ 11:54
    Greg Berlin
    0

    @Harald.. yup of course i wont need to the field type if i have it on a separate user control.. wont have any problems with that.. but ultimately i'd prefer to do it as a field type in contour, and have that field as the first step of a wizard style form (with validation allowing or preventing user from moving forward in the wizard).

    @Tim Ajax control could be a good option.. i usually steer clear of asp.net ajax where possible, but in this case it might make my life a whole lot easier. 

    I'm going to get started on this early tomorrow morning, so will see what else gets discussed between now and then.. .thanks for all the help guys - much appreciated

     

     

  • Greg Berlin 818 posts 634 karma points
    Nov 16, 2010 @ 22:36
    Greg Berlin
    0

    Heyyyyy... i wrote a really long and involved forum reply post this morning, thought i posted it, but now its not here.  dammit.

    Anyway, i dont have time to write it all again.. in summary, using the asp.net ajax autocomplete extender is not so easy in contour as its not designed to be instantiated via code behind from what i can tell. also it needs to bind to a textbox, and doesnt have a controlcollection in which to add controls to be rendered.

    i looked again at the jquery solution, and figure if i can make a custom control (using textbox as a base), and output the required jquery script via label controls added to the custom control's controlcollection, then that might be my best option.  is that the recommended method to do such a thing.

    i have kind of built it, but then ran into an issue with my local contour install... posted to forum about that a few minutes ago... will revisit this later provided i can fix my contour.  :|

    If i cant get this sorted tonight, i may outsource it.. is anybody interested in doing this, and if so can you send me a quote?

    cheers

  • Harald Ulriksen 207 posts 249 karma points
    Nov 17, 2010 @ 01:02
    Harald Ulriksen
    0

    Hi Greg,

    I can create a contour fieldtype for you with textbox and jQuery autocomplete. I suggest adding the script at least in the header if not by some framework. 

    Send me a mail at [email protected]

    Cheers,
    Harald

  • Greg Berlin 818 posts 634 karma points
    Nov 17, 2010 @ 06:25
    Greg Berlin
    0

    Thanks Harald.. I'm going to give it a bash tonight.. i think i may have it almost sorted, i'll know once i get my contour working again.  If i'm still having issues i'll be in touch about getting some help - under the pump to have this done ASAP.

    cheers

  • Greg Berlin 818 posts 634 karma points
    Nov 18, 2010 @ 00:23
    Greg Berlin
    0

    I've got an autocomplete box working in contour.. i'm not entirely sure how i'll validate it though... that's my next little challenge, but for now its looking good.

    I'm also injecting the javascript via the control to keep it all nice and portable (and reusable by adding some parameters on the fieldtype to set which web service to poll etc)... 

    I'll be in touch if i have any issues, but i think i have it under control for now... thanks for all the advice guys

  • Greg Berlin 818 posts 634 karma points
    Nov 18, 2010 @ 00:31
    Greg Berlin
    0

    Again i wrote a post and it dissappeared.  This forum is waaaaay wacky!

    [EDIT: sorry, seems i'm the wacky one... it sent me back to page 1, and i thought my post hadn't appeared.  sorry forum, didnt mean to offend you LOL]

    in summary.. i have it under control. need to get some validation happening, and hook up to my data store.. but seems all good.

    thanks for all the advice everyone

    gb

  • Greg Berlin 818 posts 634 karma points
    Nov 22, 2010 @ 10:25
    Greg Berlin
    0

    Hey all,

    I have the autocomplete box working perfectly now, picking values from a DB, selecting etc.  But when i try progress to the next step on the form (using 'next' button), i get this error:

    Object reference not set to an instance of an object.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

    Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

    Source Error: 

    An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

    Stack Trace: 

    [NullReferenceException: Object reference not set to an instance of an object.]
       Umbraco.Forms.Data.Storage.RecordFieldValueStorage.InsertRecordFieldValues(RecordField rf) in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.Core\Data\RecordStorage\RecordFieldValueStorage.cs:83
       Umbraco.Forms.Data.Storage.RecordFieldStorage.InsertRecordField(RecordField recordfield) in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.Core\Data\RecordStorage\RecordFieldStorage.cs:101
       Umbraco.Forms.Data.Storage.RecordStorage.InsertRecord(Record record, Form form) in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.Core\Data\RecordStorage\RecordStorage.cs:129
       Umbraco.Forms.Core.Services.RecordService.PersistRecord() in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.Core\Services\RecordService\RecordService.cs:413
       Umbraco.Forms.Core.Services.RecordService.NextPage() in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.Core\Services\RecordService\RecordService.cs:290
       Umbraco.Forms.UI.Usercontrols.RenderForm.nextPage(Object sender, EventArgs e) in d:\TeamCity\buildAgent\work\e4473b9ec9597356\Umbraco.Forms.UI\Usercontrols\RenderForm.ascx.cs:204
       System.Web.UI.WebControls.Button.OnClick(EventArgs e) +114
       System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +139
       System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +28
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2980

    The rendered code of the custom control is as follows:

    <!-- The data entry control -->
      <input name="ctl00$ctl00$ctl00$ContentPlaceHolderDefault$main_content$ctl01$RenderForm_5$rpFieldsets$ctl00$rpFields$ctl00$txtSearch" type="text" id="ctl00_ctl00_ctl00_ContentPlaceHolderDefault_main_content_ctl01_RenderForm_5_rpFieldsets_ctl00_rpFields_ctl00_txtSearch" /><span><script type="text/javascript" src="/scripts/KIND_TownLookup.js"></script><br/></span><span><span class="contourErrorMessage" style="display:block; clear:both; margin-top: 15px;">

    I notice it doesn't have a falue attribute... could that be why its failing? Since this is a custom control that inherits from TextBox control, i'm not sure how i'd force it to render the value attribute...  i could use javascript to add it when a value is selected, but not sure that'll solve the issue.

    Any suggestions?

  • Comment author was deleted

    Nov 22, 2010 @ 11:30

    Hi Greg,

    Could you post the code for the custom fieldtype? You might be missing something obvious.

    Cheers,
    Tim

  • Comment author was deleted

    Nov 22, 2010 @ 11:31

    DId you override the values property on your custom fieldtype class?

  • Greg Berlin 818 posts 634 karma points
    Nov 22, 2010 @ 11:37
    Greg Berlin
    0

    Here's the custom field type code

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Umbraco.Forms.Core;
    using System.Web.UI.WebControls;
    using System.Text;
    using umbraco.presentation.nodeFactory;
    
    namespace BerlinIT.KIND
    {
        public class Validated_AutoComplete : FieldType
        {
            private ValidatedAutoComplete dd;
            private List<Object> _value;
    
            #region -- SETTINGS --
    
            // Validation Fail Value
            [Umbraco.Forms.Core.Attributes.Setting("Not Found List Value",
                description = "What value should show in the dropdown for user to select if they cannot find their required value? This is the value that will fail validation.",
                control = "Umbraco.Forms.Core.FieldSetting.TextField")]
            public string NotFoundValue { get; set; }
    
            [Umbraco.Forms.Core.Attributes.Setting("Unselected Error Message",
                description = "Error message to display to user if they try to submit the form without selecting a value.",
                control = "Umbraco.Forms.Core.FieldSetting.TextField")]
            public string UnselectedErrorMessage { get; set; }
    
            [Umbraco.Forms.Core.Attributes.Setting("Success Message",
                description = "Message to show when a supported value is selected.",
                control = "Umbraco.Forms.Core.FieldSetting.TextField")]
            public string SuccessMessage { get; set; }
    
    
            #endregion -- SETTINGS --
    
            public Validated_AutoComplete()
            {
                // Set a default NotFoundValue just in case its not set via Contour
                if (String.IsNullOrEmpty(NotFoundValue))
                    NotFoundValue = "Please Select";
    
                this.Id = new Guid("132C8F92-C86D-11DF-8B53-5AF8DFD72085");
                this.Name = "Validated Autocomplete";
                this.Description = "Use this control to render an ajax autocomplete that must be validated based on rules supplied";
    
                this.Icon = "dropdownlist.png";
                this.DataType = FieldDataType.String;
                this.AssociatedField = new Field();
    
                dd = new ValidatedAutoComplete();
    
                _value = new List<object>();
            }
    
            public override System.Web.UI.WebControls.WebControl Editor
            {
                get
                {
                    short failId = 0;
                    string failMsg = "";
    
                    // Unselected Error Message
                    if (!Int16.TryParse(UnselectedErrorMessage, out failId))
                        failMsg = "Please select a town from the list above";
                    else
                    {
                        try
                        {
                            Node failNode = new Node(failId);
                            failMsg = String.Format("<h3>{0}</h3>{1}", failNode.GetProperty("intro_header").Value, failNode.GetProperty("intro_text").Value);
                        }
                        catch (Exception)
                        {
                            failMsg = "Please select a town from the list above";
                        }
                    }
                    dd.UnselectedErrorMessage = failMsg;
    
    
                    // Not Found Error Message
                    if (!Int16.TryParse(NotFoundValue, out failId))
                        failMsg = "The town you have entered is not supported by KIND";
                    else
                    {
                        try
                        {
                            Node failNode = new Node(failId);
                            failMsg = String.Format("<h3>{0}</h3>{1}", failNode.GetProperty("intro_header").Value, failNode.GetProperty("intro_text").Value);
                        }
                        catch (Exception)
                        {
                            failMsg = "The town you have entered is not supported by KIND";
                        }
                    }
                    dd.NotFoundValue = failMsg;
    
    
                    // Not Found Error Message
                    if (!Int16.TryParse(SuccessMessage, out failId))
                        failMsg = "Your town is supported by KIND. Please click the Next button to continue to the submission form.";
                    else
                    {
                        try
                        {
                            Node failNode = new Node(failId);
                            failMsg = String.Format("<h3>{0}</h3>{1}", failNode.GetProperty("intro_header").Value, failNode.GetProperty("intro_text").Value);
                        }
                        catch (Exception)
                        {
                            failMsg = "Your town is supported by KIND. Please click the Next button to continue to the submission form.";
                        }
                    }
                    dd.SuccessMessage = failMsg;
    
                    return dd;
                }
                set
                {
                    base.Editor = value;
                }
            }
    
            public override string RenderPreview()
            {
                return "<select class=\"dropdownlist\"></select>";
            }
    
            public override string RenderPreviewWithPrevalues(List<object> prevalues)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("<select class=\"dropdownlist\">");
    
                sb.Append("<option></option>");
    
                foreach (var prevalue in prevalues)
                {
                    sb.Append(string.Format("<option>{0}</option>", prevalue));
                }
    
                sb.Append("</select>");
    
                return sb.ToString();
            }
    
            public override bool SupportsPrevalues
            {
                get
                {
                    return false;
                }
            }
        }
    }

    And here's the AutoComplete textbox code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI.WebControls;
    using System.Web.UI;
    
    namespace BerlinIT.KIND
    {
        public class ValidatedAutoComplete : TextBox           
        {
            private Label lblUnselectedErrorMessage;
            private Label lblJqueryInitialisation;
            private Label lblNotFoundErrorMessage;
            private Label lblSuccessMessage;
    
            public string NotFoundValue;
            public string UnselectedErrorMessage;
            public string SuccessMessage;
            public string ClientScript = "true";
    
            public System.Web.UI.WebControls.TextBox tb;
    
            protected override void OnInit(EventArgs e)
            {
                base.OnInit(e);
                this.ID = "txtSearch";
    
                lblJqueryInitialisation = new Label();
                lblJqueryInitialisation.Text = "<script type=\"text/javascript\" src=\"/scripts/KIND_TownLookup.js\"></script><br/>";
    
                // Create custom error message lables
                lblSuccessMessage = new Label();
                lblSuccessMessage.Text = String.Format("<span class=\"contourSuccessMessage\" style=\"display:block; clear:both; margin-top: 15px;\">{0}</span>", SuccessMessage);
    
                lblUnselectedErrorMessage = new Label();
                lblUnselectedErrorMessage.Text = String.Format("<span class=\"contourErrorMessage\" style=\"display:block; clear:both; margin-top: 15px;\">{0}</span>", UnselectedErrorMessage);
    
                lblNotFoundErrorMessage = new Label();
                lblNotFoundErrorMessage.Text = String.Format("<span class=\"contourErrorMessage\" style=\"display:block; clear:both; margin-top: 15px;\">{0}</span>", NotFoundValue);
    
                Controls.Add(lblJqueryInitialisation);
                Controls.Add(lblSuccessMessage);
                Controls.Add(lblUnselectedErrorMessage);
                Controls.Add(lblNotFoundErrorMessage);
            }
    
            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);
            }
    
            protected override void Render(HtmlTextWriter w)
            {
                base.Render(w);
                base.RenderChildren(w);
            }
    
            /// <summary>
            /// Override the CreateControlCollection to overcome the "does not allow child controls".
            /// </summary>
            /// <returns></returns>
            protected override ControlCollection CreateControlCollection()
            {
                return new ControlCollection(this);   
            }
        }
    }

     

  • Comment author was deleted

    Nov 22, 2010 @ 11:41

    Simply add

     

            public override List<Object> Values

    {

    get

    {

    if (dd.Text != "")

    {

    Value.Clear();

    Value.Add(dd.Text);

    }

    return Value;

    }

    set { Value = value; }

    }

     

  • Greg Berlin 818 posts 634 karma points
    Nov 22, 2010 @ 11:43
    Greg Berlin
    0

    Nice.. thanks Tim.  I've shut down my VM for the night but will try it first thing tomorrow (about 7 hours time).

  • Greg Berlin 818 posts 634 karma points
    Nov 23, 2010 @ 11:47
    Greg Berlin
    0

    Thanks Tim, that worked a treat.  Everything working perfectly now.. yay!

  • Greg Berlin 818 posts 634 karma points
    Dec 01, 2010 @ 09:01
    Greg Berlin
    0

    Hey all,

    So my new field type is working nicely, except during UAT my client discovered the following behaviour:

    If they browse to the page with the form for the first time, the new field type displays (its set in a step on its own - the first step on a multi-step form)

    If they fill in and select a value and go next, it works fine

    if they then browse to that page again (via link, or removing the ? from the url and reloading), it takes them straight to step 2

    So essentially it seems Contour is treating that step of the form as filled in, and skipping it.  Any idea why Contour would do that, and how i can stop it from making said assumption?

Please Sign in or register to post replies

Write your reply to:

Draft