• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Populate dropdownlist based on another ddl selection

Enthusiast ,
Mar 05, 2019 Mar 05, 2019

Copy link to clipboard

Copied

Hello, there.

I am creating a script that will create a group with 3 columns: name (statictext), template (dropdownlist) and masterpages (dropdownlist).

If the name's array has 4 items, the group will be populated with 4 lines (each one with name, template and masterpages).

The dropdownlists on column 2 (template) I filled with files found in selected folder.

The dropdownlists on column 3 (masterpages) I want to fill with the masterpages in each document, based on the selection of previous dropdownlist.

Is it possible?

To create my window, I'm using this:

for (var i=0; i<myEditorias.length; i++) {

    g1p1_c1.add("statictext" , undefined , myEditorias);

    g2p1_c2.add("dropdownlist" , [0,0,180,20] , myTemplateFiles);

    g3p1_c3.add("dropdownlist" , [0,0,180,20] , "" , {title: myEditorias});

    }

for (var y=0; y<g2p1_c2.children.length; y++) {

    g2p1_c2.children.onChange = function () {

        alert(y);

        for (var i=0; i<myMasters.length; i++) {

            if (this.selection.text == myMasters[0]) {

                //POPULATE g3p1_c3.children(y) BASED ON g2p1_c2 SELECTION

                }

            }

        }

    }

The alert on line 12 results as undefined, because the y is outside the function.

I tried to put y inside the function's parenthesis, but it didn't work.

Any help will be appreciated.

TOPICS
Scripting

Views

1.3K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Mar 06, 2019 Mar 06, 2019

On afterthought i think this can also be solved by making a closure, something like the below

for (var y=0; y<g2p1_c2.children.length; y++)

{

    g2p1_c2.children.onChange = getCallBack(y)

}

function getCallBack(y)

{

    return function()

    {

          alert(y);

          for (var i=0; i<myMasters.length; i++)

          {

              if (this.selection.text == myMasters[0])

              {

                    //POPULATE g3p1_c3.children(y) BASED ON g2p1_c2 SELECTION

              }

          }

    }

}

-M

...

Votes

Translate

Translate
Community Expert ,
Mar 05, 2019 Mar 05, 2019

Copy link to clipboard

Copied

Hi,

Could you post the whole code that creates the UI and some instructions on how to set it up to see the problem first hand. Off hand i could say you could add a property to the children which will hold the value of y and that will be accessible inside the OnChange method. Something like

g2p1_c2.children.myProperty = y

One thing to note is that you are using OnChange method, i think in this method you would not have "this" set to the object on which the event was fired, so you will either have to either use "AddEventListener" which many times does not work due to other issues or else will have to find a way to identify as to which object was the event called for and then access it property value that you added in the loop

If you could share your code i could try and make it work. Hope this did not get too confusing.

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 06, 2019 Mar 06, 2019

Copy link to clipboard

Copied

On afterthought i think this can also be solved by making a closure, something like the below

for (var y=0; y<g2p1_c2.children.length; y++)

{

    g2p1_c2.children.onChange = getCallBack(y)

}

function getCallBack(y)

{

    return function()

    {

          alert(y);

          for (var i=0; i<myMasters.length; i++)

          {

              if (this.selection.text == myMasters[0])

              {

                    //POPULATE g3p1_c3.children(y) BASED ON g2p1_c2 SELECTION

              }

          }

    }

}

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 06, 2019 Mar 06, 2019

Copy link to clipboard

Copied

This last worked fine. The alerts show me the right index.

And the changing the code to populate the column 3 childrens... works fine!!!!!

Thank you so much.

Just to understand: Before to post here at the Forum, I tried an approach like this, but just calling a new function.

Could you help me to understand what the getCallBack function is doing?

I appreciate a LOT your help, thank you so much Manan Joshi​

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Mar 06, 2019 Mar 06, 2019

Copy link to clipboard

Copied

Hi,

There is another possible approach, avoiding closure and multiple event listeners.

What is not clearly stated in your original question is that you have a 1-to-N map structure from file name to master templates. So you can build your dialog by just passing your titles (static texts) and that map.

I don't think it's a good design to build three independent columns managed separately, while the actual data structure is a set of rows, each having 3 interconnected fields. In this context, better is to use the scheme

+----------------------------------+

¦ +------+ +---------+ +---------+ ¦

¦ ¦      ¦ ¦         ¦ ¦         ¦ ¦

¦ +------+ +---------+ +---------+ ¦

+----------------------------------+

+----------------------------------+

¦ +------+ +---------+ +---------+ ¦

¦ ¦      ¦ ¦         ¦ ¦         ¦ ¦

¦ +------+ +---------+ +---------+ ¦

+----------------------------------+

+----------------------------------+

¦ +------+ +---------+ +---------+ ¦

¦ ¦      ¦ ¦         ¦ ¦         ¦ ¦

¦ +------+ +---------+ +---------+ ¦

+----------------------------------+

Finally, a great mechanism in ScriptUI is the option to address various events from a single place, using a single event listener that properly identifies the target (child component) that is sending a command.

Here is a full implementation to illustrate these ideas:

const myDialog = function myDialog(/*str[]*/names,/*{file=>str[]}*/map,  ff,k,w,p,i,g,f)

//----------------------------------

{

    // Checkpoints.

    // ---

    if( !(names instanceof Array) ) throw "Invalid `names` arg. Should be an array.";

    if( !(map===Object(map)) ) throw "Invalid `map` arg. Should be an object.";

   

    // Extract the file array from `map` (for feeding the 2nd dropdown.)

    // ---

    ff = [];

    for( k in map )

    {

        if( !map.hasOwnProperty(k) ) continue;

        if( !(map instanceof Array) ) throw "Invalid `map` key. Should refer to an array.";

        ff[ff.length] = k;

    }

    // Create the dialog and a convenient root container.

    // ---

    w = new Window('dialog',"My Dialog");

    (p = w.add('panel')).orientation = 'column';

    p.margins = 20;

    // Create the rows (one for each name ; group of 3 columns.)

    // ---

    for( i=-1 ; ++i < names.length ; )

    {

        // Group container (row #i).

        // ---

        (g=p.add('group')).orientation = 'row';

        g.spacing = 20;

        g.alignChildren = ['left','center'];

       

        // Row fields.

        // ---

        k = names;

        g.add('statictext',   void 0, k, { idx: i }).preferredSize = [150,-1];

        g.add('dropdownlist', void 0, ff, { kind:'FILE' }).preferredSize = [180,-1];

        g.add('dropdownlist', void 0, '', { name:'TPL', title:k }).preferredSize = [180,-1];

    }

   

    // The root container is the best place to manage the

    // change 'event'. You then have a unique listener.

    // ---

    (f=callee.ON_CHANGE).Q = map;     // Cache the map.

    p.addEventListener('change', f);  // Register the handler.

    w.add('button', void 0, "Done", { name:'ok' });

    return w;

};

myDialog.ON_CHANGE = function onChange(/*Event*/ev,  src,dst,s,a,i)

//----------------------------------

// this :: 'panel' ; ev.type == 'change'

// [REM] callee.Q maps a source keystring to a dest array.

{

    // Checkpoint and cleanup.

    // ---

    if( 'change' != ev.type ) return;

    src = ev.target;

    if( 'FILE' != (src.properties||0).kind ) return;

    dst = src.parent['TPL'];

    if( (!dst) || 'dropdownlist' != dst.type ) return;

    dst.items.length && dst.removeAll();

   

    // Get the mapped array from the current `src` selection.

    // ---

    if( !(s=src.selection) ) return;

    if( !(a=(callee.Q||0)[s.text]) || (!a.length) ) return;

   

    // Load a in `dst`.

    // ---

    for( i=-1 ; ++i < a.length ; dst.add('item',a) );

    dst.selection = 0; // Maybe we can select the 1st item (?)

};

// =========================================================

// YOUR DATA

// =========================================================

const NAMES = ["aaaa", "bbb", "cccc", "dddddd"];

const MAP =

    {

    "File1":      ["Master1-A", "HelpMaster", "Summary"],

    "MyFileNo2":  ["Master2-A", "SubMaster"],

    "Other_File": ["Master3-A", "ChapterIntro", "IndexPages"],

    "AnotherOne": ["Master4-A", "ThatsIt"],

    };

var dlg = myDialog(NAMES, MAP);

dlg && dlg.show();

Best,

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 06, 2019 Mar 06, 2019

Copy link to clipboard

Copied

Awesome too. That's what I'm looking for in this code. An array with masterpages for each file.

As I can't deal with it, I found this way I did.

Your code is a little complex to me. I'll study it to figure out what each variable means, etc.

As I saw, a map is an object of arrays formed by two values.

What is the difference between a map and myArray.push([value1 , value2])?

How to set a Map in ESTK?

Thank you so much, Marc!!!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 06, 2019 Mar 06, 2019

Copy link to clipboard

Copied

In Javascript a function has access to the variables in the parent scope even after the parent has finished its execution. In the getCallBack method what i am doing is binding the state of y with the function that is being returned. Each time getCallBack is called with a different value of y which is accessible to the inner function which is returned and whose reference is bound to the callback method. Now whenever this callback method is called, it will have access to the value of y and hence we use closure to our rescue. I hope this makes sense.

A simple start to closures can be read from the following link, then you can explore ahead as it suits you

Demystifying JavaScript Closures, Callbacks and IIFEs

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Mar 07, 2019 Mar 07, 2019

Copy link to clipboard

Copied

Great, thank you so much again, Manan!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 22, 2019 May 22, 2019

Copy link to clipboard

Copied

Hello, Manan Joshi​.

I'm getting blind about a very similar situation of this original post.

But, this time, I am adding new groups to my window with + buttons, as in Peter Kahrel's UI manual (page 80).

    plusBtn.onClick = function () {

        g1 = g1g0p1.add("group");

        g1.alignment = "left";

        g1.orientation = "row";

        g1.add("statictext" , undefined , "A página");

        g1.add("dropdownlist" , undefined , myPares);

        g1.add("statictext" , undefined , "deve casar com a página");

        g1.add("statictext" , undefined , "[...]");

        if (g1g0p1.children.length > 1) {

            minusBtn.enabled = true;

            }

        if (g1g0p1.children.length == myPares.length) {

            plusBtn.enabled = false;

            }

        g1.index = g1g0p1.children.length-1;

        w.layout.layout(true);

        }

In the new g1 element, I want to fill the children[3] based on children[1] selection.

So, I tried to use yout getCallBack suggestion.

    for (var x=0; x<g1g0p1.children.length; x++) {

        g1g0p1.children.children[1].onChange = populate(x);

        }

    function populate(x) {

        return function () {

            var myNumero = Number(this.selection.text)+1;

            g1g0p1.children.children[3].text = "\[" + String(myNumero) + "\]";

            plusBtn.enabled = true;

            }

        }

But, even if I try an alert inside the return function () {} it's just working in the first g1 (the one created in the dialog main code).

Any suggestion?

Here is the complete fragment I am having problems.

if (!app.documents.length || (app.documents.length && !app.documents[0].visible)) {

    var w = new Window("dialog");

    w.orientation = "column";

   

    var myQtdPages = 8;

   

    var p1 = w.add("panel" , undefined , "Páginas duplas");

    p1.preferredSize.width = 312;

    p1.margins.top = 20;

    p1.orientation = "column";

    var g1g0p1 = p1.add("group");

    g1g0p1.orientation = "column";

    var g1 = g1g0p1.add("group");

    g1.alignment = "left";

    g1.orientation = "row";

    var myText_1 = g1.add("statictext" , undefined , "A página");

    var myPares = [];

   

    var myPagePar = g1.add("dropdownlist" , undefined , myPares);

    var myText_2 = g1.add("statictext" , undefined , "deve casar com a página");

    var myPageImpar = g1.add("statictext" , undefined , "[...]");

   

    if (myQtdPages > 0) {

        for (var i=0; i<myQtdPages-1; i++) {

            if (i<myQtdPages-1 && (i+1) % 2 == 0) {

                myPares.push(i+1);

                myPagePar.add("item" , i+1);

                }

            }

        }

   

    var myControls = p1.add("group");

    myControls.margins.top = 10;

    myControls.orientation = "row";

    myControls.alignment = "right";

    myControls.alignChildren = "right";

    var plusBtn = myControls.add("button" , [0,0,24,24] , "+");

    var minusBtn = myControls.add("button" , [0,0,24,24] , "-");

    plusBtn.enabled = false;

    minusBtn.enabled = false;

   

    plusBtn.onClick = function () {

        g1 = g1g0p1.add("group");

        g1.alignment = "left";

        g1.orientation = "row";

        g1.add("statictext" , undefined , "A página");

        g1.add("dropdownlist" , undefined , myPares);

        g1.add("statictext" , undefined , "deve casar com a página");

        g1.add("statictext" , undefined , "[...]");

        if (g1g0p1.children.length > 1) {

            minusBtn.enabled = true;

            }

        if (g1g0p1.children.length == myPares.length) {

            plusBtn.enabled = false;

            }

        g1.index = g1g0p1.children.length-1;

        w.layout.layout(true);

        }

    minusBtn.onClick = function () {

        var del = g1g0p1.children.length-1;

        if (del > 0) {

            g1g0p1.remove(g1g0p1.children[del]);

            if (g1g0p1.children.length == 1) {

                minusBtn.enabled = false;

                }

            }

        w.layout.layout(true);

        }

    for (var x=0; x<g1g0p1.children.length; x++) {

        g1g0p1.children.children[1].onChange = populate(x);

        }

    function populate(x) {

        return function () {

            alert(x);

            var myNumero = Number(this.selection.text)+1;

            g1g0p1.children.children[3].text = "\[" + String(myNumero) + "\]";

            plusBtn.enabled = true;

            }

        }

    w.show();

    }

Thanks in advance.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 22, 2019 May 22, 2019

Copy link to clipboard

Copied

LATEST

Hi lf.corullon​,

Provided i understood the problem correctly, the issue is that you will need to add you event handler when you are adding control on click of the plus button. Initially the code runs from top to bottom and the event handler is attached on the first dropdown on the window, but after that whenever you click on the + sign the code only inside the onClick event handler is run and hence no event handlers are added to these new dropdowns and hence you see the issue. If you make the following change to your code it should work

plusBtn.onClick = function ()

{

        g1 = g1g0p1.add("group");

        g1.alignment = "left";

        g1.orientation = "row";

        g1.add("statictext" , undefined , "A página");

        g1.add("dropdownlist" , undefined , myPares);

        g1.add("statictext" , undefined , "deve casar com a página");

        g1.add("statictext" , undefined , "[...]");

        if (g1g0p1.children.length > 1)

        {

            minusBtn.enabled = true;

        }

        if (g1g0p1.children.length == myPares.length)

        {

            plusBtn.enabled = false;

        }

        g1.index = g1g0p1.children.length-1;

        w.layout.layout(true);

        for (var x=0; x<g1g0p1.children.length; x++)

        {

              g1g0p1.children.children[1].onChange = populate(x);

          }

}

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines