Trevor
Trevor

Reputation: 1604

select2 remote data: populate using Web API 2 endpoint

As per the title I am using the latest select2 select box on a HTML5 page and using their example I am making a call to a Web API 2 endpoint I created.

For testing purposes the endpoint simply builds a collection of Tags and returns the result as a HttpResponseMessage. Here is some code to visualise it all:

The Tag entity

public class TagData
{
    public string Id { get; set; }
    public string Text { get; set; }
}

The model:

public class TagsModel
{
    public IEnumerable<TagData> Tags { get; set; }
}

The controller action in the Web API 2 project:

    public IHttpActionResult Get()
    {
        var tags = new TagsModel()
        {
            Tags = new List<TagData>
            {
                new TagData()
                {
                    Id = "1",
                    Text = "Tag1"
                },
                new TagData()
                {
                    Id = "2",
                    Text = "Tag2"
                }
            }
        };
        return Ok(tags);
    }

I knocked up a unit test and it all works also running a test in Fiddler4 using composer returns the following JSON response:

{"Tags":[{"Id":"1","Text":"Tag1"},{"Id":"2","Text":"Tag2"},{"Id":"3","Text":"Tag3"}}]}

In the HTML5 page I dropped in the standard select2 html element:

    <select class='s2-search-box form-control' multiple='multiple'></select>

The idea here is to bind the select2 control to remote data then use the tagging feature to allow the user to find a tag, select it and repeat so this is done using this ajax call:

    $(document).ready(function () {

        $(".s2-search-box").select2({
            ajax: {
                url: "http://localhost:54850/api/s2demo",
                dataType: 'json',
                delay: 250,
                data: function (params) {
                    return {
                        q: params.term, // search term
                        page: params.page
                    };
                },
                processResults: function (data, page) {
                    // parse the results into the format expected by Select2.
                    // since we are using custom formatting functions we do not need to
                    // alter the remote JSON data
                    return {
                        results: data.Tags
                    };
                },
                cache: true
            }
        });
    });

So far so good, my unit tests work and when I used Firebug to inspect the ajax call I can see data being returned in the correct format and I am able to display the data.Tags collection so I know the data is there however the actual select2 dropdown displays a list of 'undefined' items and doesn't actually display the data I want or allow me to create the tags from those selections.

As a side note I tried this as well:

    $(".s2-search-box").select2({
        ajax: {
            url: "http://localhost:54850/api/s2demo",
            dataType: "json",
            type: "GET",
            data: function (params) {

                var queryParameters = {
                    term: params.term
                }
                return queryParameters;
            },
            processResults: function (data) {
                return {
                    results: $.map(data, function (item) {
                        return {
                            text: item.Text,
                            id: item.Id
                        }
                    }),
                    pagination: {
                        more: data.more
                    }
                };
            }
        }
    });

This works in that I can create the tags so they appear in the select2 control as :

tag1[x] tag2[x]

However the dropdown list is permanently displayed.

I am using the latest 4.0.2 version of the select2 library.

Thanks in advance.

T

Upvotes: 0

Views: 2027

Answers (1)

Trevor
Trevor

Reputation: 1604

Solved this finally after some serious hours burned mostly because the information was scattered and due to the new version of select2 not so widely used right now.

So I had to make a few changes first one was to the controller to allow me to make GET and POST requests (there are better ways to do this but for the purpose of prototyping this works):

    [AcceptVerbs("GET","POST")]
    public IHttpActionResult TagLookup([FromBody]string value)
    {
        var tags = TagsModel;

        var results = new TagsModel();
        var tagsList = new List<TagData>();
        var i = 0;

        foreach (var m in tags.Tags)
        {
            i++;
            if (m.Text.Contains(value))
            {
                tagsList.Add(new TagData()
                {
                    Id = i.ToString(),
                    Text = m.Text
                });
            }
        }

        results.Tags = tagsList;

        return Ok(results);
    }

The code block is simple enough, I am returning a model that contains a collection of tags (TagaData entity) however what is different is the decorator on the action, the AcceptVerbs attribute which accepts both GET and POST requests from any client.

Also I added the [FromBody] parameter attribute which forces Web API to read a simple type from the request body (for complex types you would use FromUri).

Then I modified the Ajax call in the HTML page like this:

    $(document).ready(function () {

        $(".s2-search-box").select2({
            ajax: {
                url: "http://localhost:54850/api/s2demo",
                contentType: "application/x-www-form-urlencoded; charset=UTF-8",
                type: 'POST',
                dataType: 'json',
                delay: 250,
                data: function(params) {
                    return '=' + params.term; // search term
                },
                processResults: function(data) {
                    return {
                        results: $.map(data.Tags, function(item) {
                            return {
                                text: item.Text,
                                id: item.Id
                            }
                        })
                    };
                },
                cache: true
            }
        });
    });

So the important thing to note here is the function that passes in a parameter to the data payload being sent but what is the gotcha here for Web API 2 is the '=' symbol that I had to bolt on to the return:

return '=' + params.term; 

This is required so Web API understands what to do with the parameter otherwise it gets ignored.

The 'processResults:' section tags the Tags array and binds the data to the select2 control giving me a populated dropdown list (if any data was returned that matched the term being passed in).

Also now I can pick a tag and it will appear in select2 as a tag in the input box, I did this by making sure the multiple attribute was included in select2 html control:

<select class="s2-search-box form-control" multiple="multiple"></select>

That sorted it for me hopefully will shed some light for anyone else scratching their head. On to the next part, taking the tags in the select2 box and sending them off to another Web API controller action.

Upvotes: 3

Related Questions