1

I am building a configuration page that allows a user to reconcile unmatched data by selecting a match from a list of possibilities. The list of items needing to be matched is generated by the backend, then a JS function requests the list of possible matches and populates the corresponding element. I'm currently using D3.js to do the selecting and updating.

As it stands, it looks like everything is attaching correctly from the .data() function, but only the last select is receiving its options.

Here is the JS code that retrieves the JSON and populates the select elements. It runs after the window loads

function attach_options() {
    opts = d3.selectAll("select[data-opt-table]")
    opts.each(function(p,j) {
        obj = d3.select(this)
        opt_table = obj.attr('data-opt-table')
        opt_field = obj.attr('data-opt-field')
        opt_disp = obj.attr('data-opt-disp')
        opt_lookup = obj.attr('data-opt-lookup') 
        d3.json("{% url 'get-opts' %}" + opt_table + '/' + opt_field + '/' + opt_disp + '/' + opt_lookup + '/').then(
            function(data) {
                options = obj.selectAll('option').data(data.opt_list)
                options.join('option')
                    .attr('value', d => d.pk)
                    .text(d => d.disp_value)
            }
        )
    })
}

Here is a sample JSON response:

{
  "opt_list": [
    {
      "pk": "DE-001",
      "disp_value": "Kent"
    },
    {
      "pk": "DE-003",
      "disp_value": "New Castle"
    },
    {
      "pk": "DE-005",
      "disp_value": "Sussex"
    }
  ]
}

1 Answer 1

0

The function you pass to d3.json is executed async. By the time it is called, the variable obj has been over-written by a subsequent execution of the loop. The easiest fix is to wrap d3.json in an additional function where you pass in obj. Here's what that would look like with an anon function:

function attach_options() {
    opts = d3.selectAll("select[data-opt-table]");
    opts.each(function(p,j) {
        obj = d3.select(this);
        opt_table = obj.attr('data-opt-table');
        opt_field = obj.attr('data-opt-field');
        opt_disp = obj.attr('data-opt-disp');
        opt_lookup = obj.attr('data-opt-lookup'); 
        ((obj) => {
            d3.json("{% url 'get-opts' %}" + opt_table + '/' + opt_field + '/' + opt_disp + '/' + opt_lookup + '/').then(
                function(data) {
                    options = obj.selectAll('option').data(data.opt_list)
                    options.join('option')
                        .attr('value', d => d.pk)
                        .text(d => d.disp_value);
                }
            );
        })(obj);
    });    
}

Working example.

Also, remember to use semi-colons with JavaScript.

Sign up to request clarification or add additional context in comments.

1 Comment

That did the trick, and was what I suspected was the issue. But I use JavaScript so little that I struggle with some of these techniques with wrapper/anonymous functions. Incidentally, since posting the question, I was able to refactor things to generate the list from the backend, which is probably a better option since it doesn't require additional calls and doesn't require exposing as much information about the configuration as what I had implemented originally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.