In an effort to better understand how Web AppBuilder works, I'm going through the samples provided under the Sample Code tab of Web AppBuilder for ArcGIS (Developer Edition). I'm working through Create a new controller widget, but I'm getting the following error when I reach step 5e: error:TypeError: this.createIconNode is not a function. Below is my code for the SidebarController\Widget.js. Any help would be appreciated. Thank you. William
define([
  'dojo/_base/declare',
  'jimu/PoolControllerMixin',
  'jimu/BaseWidget'
  ], function(
    declare,
    PoolControllerMixin,
    BaseWidget
    ) {
  //To create a widget, you need to derive from BaseWidget.
  return declare([BaseWidget, PoolControllerMixin], {
    // DemoWidget code goes here
    //please note that this property is be set by the framework when widget is loaded.
    //templateString: template,
    baseClass: 'jimu-widget-sidebar-controller jimu-main-background',
    
    allConfigs: [],
    
    postCreate: function() {
      this.inherited(arguments);
      console.log('postCreate');
      
      this.allConfigs = this.getAllConfigs();
      for(var i = 0; i < this.allConfigs.length; i++) {
        this._createIconNode(this.allConfigs);
      }
    },
    
    startup: function() {
      this.inherited(arguments);
      console.log('startup');
    },
    
    _createIconNode: function(iconConfig, targetNode) {
      var iconNode, iconImage;
      if(!targetNode) targetNode = this.containerNode;
      iconNode = document.createElement('DIV');
      iconNode.className = 'icon-node';
      if(iconConfig.icon) {
        iconImage = document.createElement('img');
        iconImage.src = iconConfig.icon;
      }
      if(iconConfig.label) {
        iconNode.title = iconConfig.label;
        iconImage.alt = iconConfig.label;
      }
      iconNode.appendChild(iconImage);
      targetNode.appendChild(iconNode);
      return iconNode;
    }
  });
});
					
				
			
			
				
			
			
				Solved! Go to Solution.
William,
Step 12c has some issues/typos.
it says:
_createIconNode: function(iconConfig, targetNode) {
  ...
  if (iconConfig.openAtStart) {...}
  // check if the icon is a group icon
  if(this._isGroupIcon(iconConfig)) {
    // if group's tooltip has not been created yet
    if(!this.groupTooltip[iconConfig.id]) {
      // create group tooltip and its content
     var groupTooltip = document.createElement('div');
      groupTooltip.className = 'group-tooltip';
      document.body.appendChild(groupTooltip);
      for(var i = 0; i < iconConfig.widgets.length; i++) {
        this._createIconNode(iconConfig.widgets, groupTooltip);
      }
      this.groupTooltip[iconConfig.id] = groupTooltip;
    }
  }
  ...
},and it should be: (note line 7 and 15 are this.groupTooltips... plural not singular)
_createIconNode: function(iconConfig, targetNode) {
  ...
  if (iconConfig.openAtStart) {...}
  // check if the icon is a group icon
  if(this._isGroupIcon(iconConfig)) {
    // if group's tooltip has not been created yet
    if(!this.groupTooltips[iconConfig.id]) {
      // create group tooltip and its content
     var groupTooltip = document.createElement('div');
      groupTooltip.className = 'group-tooltip';
      document.body.appendChild(groupTooltip);
      for(var i = 0; i < iconConfig.widgets.length; i++) {
        this._createIconNode(iconConfig.widgets, groupTooltip);
      }
      this.groupTooltips[iconConfig.id] = groupTooltip;
    }
  }
  ...
},
					
				
			
			
				
			
			
				
			
			
				
			
			
			
			
			
		William,
It looks like the doc has a typo. The functions name is _createIconNode so this.createIconNode is not a function in your code. But it looks like you have that corrected already in your posted code.
this._createIconNode(this.allConfigs);
Have you tried clearing your browser cache?
Hi Robert,
Thanks for the suggestion. After clearing my browser cache, I still had the problem. However your suggestion gave me the idea to start a new app, and now it works.
Gratefully, William
Now I'm having trouble with step 12 in the process. However, if I add the About widget to the SidebarController, the following warnings and error appear in the console:
Warning: no uploadUrl provided. FileUploader.js:32
this.movie.PercentLoaded() failed TypeError: this.movie.PercentLoaded is not a function(…) main.js:2867
x 17
Uncaught Error: Building SWF failed. main.js:2867
Even with these errors and warning, everything seems to be working through step 11 (code below).
define([
  'dojo/_base/declare',
  'dojo/on',
  'dojo/query',
  'dojo/dom-class',
  'jimu/PoolControllerMixin',
  'jimu/BaseWidget'
  ], function(
    declare,
    on,
    query,
    domClass,
    PoolControllerMixin,
    BaseWidget
    ) {
  //To create a widget, you need to derive from BaseWidget.
  return declare([BaseWidget, PoolControllerMixin], {
    // DemoWidget code goes here
    
    //please note that this property is be set by the framework when widget is loaded.
    //templateString: template,
    
    baseClass: 'jimu-widget-sidebar-controller jimu-main-background',
    
    allConfigs: [],
    
    openedWidgetId: '',
    
    activeIconNode: null,
    
    postCreate: function() {
      this.inherited(arguments);
      console.log('postCreate');
      
      this.allConfigs = this.getAllConfigs();
      for(var i = 0; i < this.allConfigs.length; i++) {
        this._createIconNode(this.allConfigs);
      }
    },
    
    startup: function() {
      this.inherited(arguments);
      console.log('startup');
    },
    
    _createIconNode: function(iconConfig, targetNode) {
      var iconNode, iconImage;
      if(!targetNode) targetNode = this.containerNode;
      iconNode = document.createElement('DIV');
      iconNode.className = 'icon-node';
      if(iconConfig.icon) {
        iconImage = document.createElement('img');
        iconImage.src = iconConfig.icon;
      }
      
      if(iconConfig.label) {
        iconNode.title = iconConfig.label;
        iconImage.alt = iconConfig.label;
      }
      iconNode.appendChild(iconImage);
      
      targetNode.appendChild(iconNode);
      // check if the widget is set to open at start
      if (iconConfig.openAtStart) {
         // check if the icon is a group icon
        this.activeIconNode = iconNode;
        domClass.add(iconNode, 'jimu-state-active');
        this._showWidgetContent(iconConfig);
      }
      
      var self = this;
      this.own(on(iconNode, 'click', function() {
        // remove active state from any icon node
        query('.jimu-state-active', self.domNode).removeClass('jimu-state-active');
        // close panel
        // close group tooltips
        // if clicked on an active icon node
        if(self.activeIconNode === this) {
          self.panelManager.closePanel(iconConfig.id + '_panel');
          self.activeIconNode = null;
          return;
        }
        
        // show panel
        domClass.add(this, 'jimu-state-active');
        self._showWidgetContent(iconConfig);
        self.activeIconNode = this;
      }));
      return iconNode;
    },
    
    _showWidgetContent: function(iconConfig) {
      if(this.openedWidgetId) {
        this.panelManager.closePanel(this.openedWidgetId + '_panel');
      }
      var self = this;
      this.panelManager.showPanel(iconConfig).then(function(widget) {
        // the panel displays successfully
        self.own(on.once(widget, 'close', function () {
          domClass.remove(self.activeIconNode, 'jimu-state-active');
          self.activeIconNode = null;
        }));
      }, function (err) {
        // the panel failed to display
      });
      this.openedWidgetId = iconConfig.id;
    }
  });
});After I add code for step 12 (code below), I get an error if I try to create a group (error:TypeError: Cannot read property '_29' of undefined at _createIconNode Line 79). Even if I don't create a group, the widgets will no longer close.
define([
  'dojo/_base/declare',
  'dojo/on',
  'dojo/query',
  'dojo/dom-class',
  'jimu/PoolControllerMixin',
  'jimu/BaseWidget'
  ], function(
    declare,
    on,
    query,
    domClass,
    PoolControllerMixin,
    BaseWidget
    ) {
  //To create a widget, you need to derive from BaseWidget.
  return declare([BaseWidget, PoolControllerMixin], {
    // DemoWidget code goes here
    
    //please note that this property is be set by the framework when widget is loaded.
    //templateString: template,
    
    baseClass: 'jimu-widget-sidebar-controller jimu-main-background',
    
    allConfigs: [],
    
    openedWidgetId: '',
    
    activeIconNode: null,
    
    groupTooltips: {},
    
    
    postCreate: function() {
      this.inherited(arguments);
      console.log('postCreate');
      
      this.allConfigs = this.getAllConfigs();
      for(var i = 0; i < this.allConfigs.length; i++) {
        this._createIconNode(this.allConfigs);
      }
    },
    
    startup: function() {
      this.inherited(arguments);
      console.log('startup');
    },
    
    _createIconNode: function(iconConfig, targetNode) {
      var iconNode, iconImage;
      if(!targetNode) targetNode = this.containerNode;
      iconNode = document.createElement('DIV');
      iconNode.className = 'icon-node';
      if(iconConfig.icon) {
        iconImage = document.createElement('img');
        iconImage.src = iconConfig.icon;
      }
      
      if(iconConfig.label) {
        iconNode.title = iconConfig.label;
        iconImage.alt = iconConfig.label;
      }
      iconNode.appendChild(iconImage);
      
      targetNode.appendChild(iconNode);
      // check if the widget is set to open at start
      if (iconConfig.openAtStart) {
         // check if the icon is a group icon
        this.activeIconNode = iconNode;
        domClass.add(iconNode, 'jimu-state-active');
        this._showWidgetContent(iconConfig);
      }
      
      // check if the icon is a group icon
      if(this._isGroupIcon(iconConfig)) {
        // if group's tooltip has not been created yet
        if(!this.groupTooltip[iconConfig.id]) {
          // create group tooltip and its content
         var groupTooltip = document.createElement('div');
          groupTooltip.className = 'group-tooltip';
          document.body.appendChild(groupTooltip);
          for(var i = 0; i < iconConfig.widgets.length; i++) {
            this._createIconNode(iconConfig.widgets, groupTooltip);
          }
          this.groupTooltip[iconConfig.id] = groupTooltip;
        }
      }
      
      var self = this;
      this.own(on(iconNode, 'click', function() {
        // remove active state from any icon node
        query('.jimu-state-active', self.domNode).removeClass('jimu-state-active');
        // close panel
        self.panelManager.closePanel(self.openedWidgetId + '_panel');
        // close group tooltips
        query('.group-tooltip').removeClass('show');
        // if clicked on an active icon node
        if(self.activeIconNode === this) {
          self.activeIconNode = null;
          return;
        }
        // clicking on a group icon
        if (self._isGroupIcon(iconConfig)) {
          self.openedWidgetId = null;
          self._positionTooltip(self.groupTooltips[iconConfig.id], this);
          domClass.add(self.groupTooltips[iconConfig.id], 'show');
        } else { // clicking on a widget icon
          // show panel
          self._showWidgetContent(iconConfig);
        }
          domClass.add(this, 'jimu-state-active');
          self.activeIconNode = this;
      }));
      return iconNode;
    },
    
    _showWidgetContent: function(iconConfig) {
      if(this.openedWidgetId) {
        this.panelManager.closePanel(this.openedWidgetId + '_panel');
      }
      var self = this;
      this.panelManager.showPanel(iconConfig).then(function(widget) {
        // the panel displays successfully
        self.own(on.once(widget, 'close', function () {
          domClass.remove(self.activeIconNode, 'jimu-state-active');
          self.activeIconNode = null;
        }));
      }, function (err) {
        // the panel failed to display
      });
      this.openedWidgetId = iconConfig.id;
    },
    
    _isGroupIcon: function(iconConfig) {
      return iconConfig.widgets && iconConfig.widgets.length > 1;
    },
    
    _positionTooltip: function(tooltip, iconNode) {
      var iconBoundingRect = iconNode.getBoundingClientRect();
      tooltip.style.top = iconBoundingRect.top + 'px';
      tooltip.style.left = (iconBoundingRect.width || iconNode.clientWidth) + 'px';
    }
  });
});Thank you for your time and help.
William
William,
Step 12c has some issues/typos.
it says:
_createIconNode: function(iconConfig, targetNode) {
  ...
  if (iconConfig.openAtStart) {...}
  // check if the icon is a group icon
  if(this._isGroupIcon(iconConfig)) {
    // if group's tooltip has not been created yet
    if(!this.groupTooltip[iconConfig.id]) {
      // create group tooltip and its content
     var groupTooltip = document.createElement('div');
      groupTooltip.className = 'group-tooltip';
      document.body.appendChild(groupTooltip);
      for(var i = 0; i < iconConfig.widgets.length; i++) {
        this._createIconNode(iconConfig.widgets, groupTooltip);
      }
      this.groupTooltip[iconConfig.id] = groupTooltip;
    }
  }
  ...
},and it should be: (note line 7 and 15 are this.groupTooltips... plural not singular)
_createIconNode: function(iconConfig, targetNode) {
  ...
  if (iconConfig.openAtStart) {...}
  // check if the icon is a group icon
  if(this._isGroupIcon(iconConfig)) {
    // if group's tooltip has not been created yet
    if(!this.groupTooltips[iconConfig.id]) {
      // create group tooltip and its content
     var groupTooltip = document.createElement('div');
      groupTooltip.className = 'group-tooltip';
      document.body.appendChild(groupTooltip);
      for(var i = 0; i < iconConfig.widgets.length; i++) {
        this._createIconNode(iconConfig.widgets, groupTooltip);
      }
      this.groupTooltips[iconConfig.id] = groupTooltip;
    }
  }
  ...
},
					
				
			
			
				
			
			
				
			
			
			
			
			
			
		Hi Robert,
Thanks for your help with the groupTooltips issue. That part is working. Unfortunately, while the group widget "closes", the solo widgets stay visible after a second click (the first click for opening them). The following snippet shows where I think the issue might be located in the code.
this.own(on(iconNode, 'click', function() {
  // remove active state from any icon node
  query('.jimu-state-active', self.domNode).removeClass('jimu-state-active');
  // close panel
////self.panelManager.closePanel(self.openedWidgetId + '_panel');            // uncomment this line and the group widget closes
  // close group tooltips
  query('.group-tooltip').removeClass('show');
  // if clicked on an active icon node
  if(self.activeIconNode === this) {
//////self.panelManager.closePanel(iconConfig.id + '_panel');               // uncomment this line and the solo widgets close
    self.activeIconNode = null;
    return;
  }Uncomment both lines 5 and 11 and only the group widget "closes."
Again, any help would be greatly appreciated.
William
William,
Here is my complete _createIconNode function that is working fine for me:
_createIconNode: function(iconConfig, targetNode) {
      var iconNode, iconImage;
      if(!targetNode) targetNode = this.containerNode;
      iconNode = document.createElement('DIV');
      iconNode.className = 'icon-node';
      if(iconConfig.icon) {
        iconImage = document.createElement('img');
        iconImage.src = iconConfig.icon;
      }
      if(iconConfig.label) {
        iconNode.title = iconConfig.label;
        iconImage.alt = iconConfig.label;
      }
      iconNode.appendChild(iconImage);
      targetNode.appendChild(iconNode);
      // check if the widget is set to open at start
      if (iconConfig.openAtStart) {
         // check if the icon is a group icon
        this.activeIconNode = iconNode;
        domClass.add(iconNode, 'jimu-state-active');
        this._showWidgetContent(iconConfig);
      }
      // check if the icon is a group icon
      if(this._isGroupIcon(iconConfig)) {
        // if group's tooltip has not been created yet
        if(!this.groupTooltips[iconConfig.id]) {
          // create group tooltip and its content
         var groupTooltip = document.createElement('div');
          groupTooltip.className = 'group-tooltip';
          document.body.appendChild(groupTooltip);
          for(var i = 0; i < iconConfig.widgets.length; i++) {
            this._createIconNode(iconConfig.widgets, groupTooltip);
          }
          this.groupTooltips[iconConfig.id] = groupTooltip;
        }
      }
      var self = this;
      this.own(on(iconNode, 'click', function() {
        // remove active state from any icon node
        query('.jimu-state-active', self.domNode).removeClass('jimu-state-active');
        // close panel
        self.panelManager.closePanel(self.openedWidgetId + '_panel');
        // close group tooltips
        query('.group-tooltip').removeClass('show');
        // if clicked on an active icon node
        if(self.activeIconNode === this) {
          self.activeIconNode = null;
          return;
        }
        // clicking on a group icon
        if (self._isGroupIcon(iconConfig)) {
          self.openedWidgetId = null;
          self._positionTooltip(self.groupTooltips[iconConfig.id], this);
          domClass.add(self.groupTooltips[iconConfig.id], 'show');
        } else { // clicking on a widget icon
          // show panel
          self._showWidgetContent(iconConfig);
        }
          domClass.add(this, 'jimu-state-active');
          self.activeIconNode = this;
      }));
      return iconNode;
    },
    _showWidgetContent: function(iconConfig) {
      if(this.openedWidgetId) {
        this.panelManager.closePanel(this.openedWidgetId + '_panel');
      }
      var self = this;
      this.panelManager.showPanel(iconConfig).then(function(widget) {
        // the panel displays successfully
        self.own(on.once(widget, 'close', function () {
          domClass.remove(self.activeIconNode, 'jimu-state-active');
          self.activeIconNode = null;
        }));
      }, function (err) {
        // the panel failed to display
      });
      this.openedWidgetId = iconConfig.id;
    },
					
				
			
			
				
			
			
				
			
			
			
			
			
			
		Hi Robert,
The code is are the same...
Are your panels Simple Border Panels: Or Foldable(?) Panels:


I'm having trouble with the Simple Border Panels.
Does it matter that I'm using Web AppBuilder 2.0 and not 2.1?
Thanks again and sorry to be such a bother.
William
Robert-- Thanks for the correction. The code update will be applied in the mid of August.
Jianxia
William,
I was using foldable panels. Did I miss some step about the simple border panels?
