Do something when a variable changes

There are times you might want to do something when a particular dynamic variable changes. For instance, you could imagine that if the user interacted with a certain widget in the QuickApp, you would want to set the value of one or more other dynamic variables.

In Display a widget conditionally, the list widget is only displayed if the user has clicked an item in the grid widget. After that, the widget remains visible. However, if the user changes a value in the Department or Aggregate by drop-down widgets, you might want to hide the list widget again until the user makes a new selection in the grid widget.

This can be accomplished by adding a <do> clause that is triggered when either the aggregate_by or selection dynamic variables change. The <do> clause will set clicked_value to the empty string. When clicked_value is equal to the empty string, the list widget is invalidated and subsequently hidden.

To do something when a variable changes:

  1. Add a <do> clause, which is triggered when either the aggregate_by or selection dynamic variables change. This <do> clause will set the value of the clicked_value dynamic variable to the empty string.
    
      ...
    
    <dynamic selection="19" product_master="pub.doc.retail.product" 
     sales_detail="pub.doc.retail.salesdetail" aggregate_by="groupdesc_prod"
     mode_="auto" display_chart="1" clicked_value="">
      <do onchange_="@aggregate_by,@selection">
        <set clicked_value=""/>
      </do>
      <layout background_="lightblue" border_="10">
        <widget class_="dropdown" base_="{@product_master}" 
         inputwidth_="250" value_="@selection"
         label_="Department:" labelwidth_="75">
    
      ...
    
    

    The onchange_ attribute in the opening <do> tag allows you to specify a comma-separated list of dynamic variables. When the value of any of these variables change, the Macro Language code inside the <do> clause is run.

    Note: Typically, <do> clauses are located at the top of the <dynamic> before any <widget> elements.
  2. Click Apply.
  3. Click an item in the grid widget (e.g., SOFT DRINKS).

    The list widget appears as expected.

  4. Select Brand from the Aggregate by drop-down menu.

    The grid and graphics widget are both invalidated and blocked, and the list widget is hidden.

  5. Click Run.

    The grid widget displays a list of all the brands for the specified department.

  6. Click an item in the grid widget (e.g., PEPSI).

    The list widget appears and contains the item descriptions for the SKUs matching the selected brand.

Since the value of the brand or group description that the user clicked in the grid widget is stored in the clicked_value dynamic variable, that value can be used to create a label for the list widget, which could be used to identify the contents of the list. The value of this label can be set in a different <do> clause, which is triggered when clicked_value changes.

  1. Create a <do> clause, which is triggered when clicked_value changes, that sets the label of the list widget using a new dynamic variable named list_title.
    
      ...
    
    <dynamic selection="19" product_master="pub.doc.retail.product" 
     sales_detail="pub.doc.retail.salesdetail" aggregate_by="groupdesc_prod"
     mode_="auto" display_chart="1" clicked_value="" list_title="PRODUCT LIST">
      <do onchange_="@aggregate_by,@selection">
        <set clicked_value=""/>
      </do>
      <do onchange_="@clicked_value">
        <set list_title="{@clicked_value} PRODUCT LIST"/>
      </do>
      <layout background_="lightblue" border_="10">
        <widget class_="dropdown" base_="{@product_master}" 
         inputwidth_="250" value_="@selection"
         label_="Department:" labelwidth_="75">
    
      ...
    
        <layout>
          <widget class_="list" base_="{@product_master}" 
           width_="500" maxheight_="600"
           require_="{@clicked_value <> ''}" invmode_="hide"
           label_="{@list_title}">
            <if test="{@aggregate_by = 'brand_prod'}">
              <then>
                <sel value="(brand='{@clicked_value}')"/>
              </then>
              <else>
                <sel value="(groupdesc='{@clicked_value}')"/>
              </else>
            </if>
            <colord cols="sku,description"/>
            <sort col="description" dir="up"/>
          </widget>
        </layout>
      </layout>
    </dynamic>
    

    A new dynamic variable, list_title, has been added to the <dynamic> with an initial value of PRODUCT LIST.

    A <do> clause has been added, which is triggered when the value of clicked_value changes. This <do> clause sets the list_title dynamic variable to the value of clicked_value followed by the words PRODUCT LIST. For instance, if the user clicks SOFT DRINKS in the grid widget, the title of the list widget will be SOFT DRINKS PRODUCT LIST.

    A label_ attribute, which is set to the value of list_title, has been added to the list widget, which will display the label above the widget.

    Note: If there are multiple <do> clauses, they are executed in the order in which they appear in the <dynamic>.
  2. Click Apply.
  3. Click an item in the grid widget (e.g., ENERGY DRINKS).

    The text of the list_title dynamic variable now appears above the list widget.

Cumulative QuickApp code

The Macro Language code for the QuickApp up to this point is:

<defblock name="sales_by_date" prod_table="" department="" group_by="">
  <link table2="{@prod_table}" col="sku" col2="sku" 
   suffix="_prod" type="select">
    <sel value="dept={@department}"/>
  </link>
  <tabu label="Tabulation on Sales Detail" breaks="{@group_by}">
    <tcol source="xsales" fun="sum" name="tot_sales" 
     label="Sum of`Extended Sales" format="type:currency"/>
  </tabu>
  <sort col="tot_sales" dir="down"/>
  <sel value="({@group_by} <> '')"/>
</defblock>
<dynamic selection="19" product_master="pub.doc.retail.product" 
 sales_detail="pub.doc.retail.salesdetail" aggregate_by="groupdesc_prod" 
 mode_="auto" display_chart="1" clicked_value="" list_title="PRODUCT LIST">
  <do onchange_="@aggregate_by,@selection">
    <set clicked_value=""/>
  </do>
  <do onchange_="@clicked_value">
    <set list_title="{@clicked_value} PRODUCT LIST"/>
  </do>
  <layout background_="lightblue" border_="10">
    <widget class_="dropdown" base_="{@product_master}" 
     inputwidth_="250" value_="@selection" 
     label_="Department:" labelwidth_="75">
      <tabu label="Tabulation on Product Master" breaks="deptdesc">
        <break col="deptdesc" sort="up"/>
        <tcol source="dept" name="dept" fun="first" label="First`Department"/>
      </tabu>
      <colord cols="dept,deptdesc"/>
    </widget>
    <widget class_="dropdown" value_="@aggregate_by" 
     label_="Aggregate by:" labelwidth_="75" inputwidth_="250">
      <table>groupdesc_prod,Group;brand_prod,Brand
      </table>
    </widget>
    <widget class_="checkbox" label_="Display Chart" 
     value_="@display_chart"/>
    <widget class_="button" text_="Run" type_="submit"/>
    <ignore>
      <widget class_="text" text_="Current selection: {@selection}"/>
    </ignore>
  </layout>
  <layout>
    <layout>
      <widget class_="grid" base_="{@sales_detail}" 
       insert_="sales_by_date" prod_table="{@product_master}" 
       department="{@selection}" group_by="{@aggregate_by}" 
       invmsg_="Click Run for changes to take effect" 
       holdfor_="@aggregate_by,@selection" 
       clickable_="{@aggregate_by}" value_="@clicked_value"/>
      <widget class_="graphics" base_="{@sales_detail}" width_="800" 
       insert_="sales_by_date" prod_table="{@product_master}" 
       department="{@selection}" group_by="{@aggregate_by}" 
       invmsg_="Click Run for changes to take effect" 
       visible_="{@display_chart}" holdfor_="@aggregate_by,@selection">
        <graphspec>
          <chart type="bar">
            <data x="{@aggregate_by}" y="tot_sales"/>
            <ticks xrot="45"/>
            <style xaxissize="10" yaxissize="10"/>
          </chart>
        </graphspec>
      </widget>
    </layout>
    <layout>
      <widget class_="list" base_="{@product_master}" 
       width_="500" maxheight_="600" 
       require_="{@clicked_value <> ''}" invmode_="hide" 
       label_="{@list_title}">
        <if test="{@aggregate_by = 'brand_prod'}">
          <then>
            <sel value="(brand='{@clicked_value}')"/>
          </then>
          <else>
            <sel value="(groupdesc='{@clicked_value}')"/>
          </else>
        </if>
        <colord cols="sku,description"/>
        <sort col="description" dir="up"/>
      </widget>
    </layout>
  </layout>
</dynamic>