Select to view content in your preferred language

Registry Error in Custom Widget

2308
26
Jump to solution
01-24-2018 07:53 AM
AbhinavSharma
New Contributor III

//Updated define //

//updated last line from fields to fieldNames// 

Hi everyone, I am attempting to expand my widget by adding in a renderer that is is available to the end user. I have been working my way through the following ESRI Sample. The sample is for a mapservice and not a feature layer but I do not see why the same idea can not be applied. The issue is in bold. It stops at both of those instances. I have used console.log to see if there is any difference in the object that is being passed. ( Looked at the object in the sample and compared to my own) and the only difference i can find is when i print out fieldname after the push: using (console.log("fieldnames", fieldNames)

the sample looks like this:

fieldnames

  1. {identifier: "value", label: "name", items: Array(2)}
    1. identifier:"value"
    2. items:Array(10)
      1. 0:
        1. name:["FIPS"]
        2. value:["FIPS"]
        3. _0:0
        4. _RI:true
        5. _S:{_arrayOfAllItems: Array(10), _arrayOfTopLevelItems: Array(10), _loadFinished: true, _jsonFileUrl: undefined, _ccUrl: undefined, …}
        6. __proto__:Object 

whereas from my widget it looks like this:

fieldnames

  1. {identifier: "value", label: "name", items: Array(1)}
    1. identifier:"value"
    2. items:Array(19)
      1. 0:
        1. name:"OBJECTID"
        2. value:"OBJECTID"

TypeError: Cannot read property 'on' of undefined

define(
['dojo/_base/declare',
'jimu/BaseWidget',
"esri/toolbars/draw",
"esri/toolbars/edit",
"esri/graphic",
"esri/request",
"esri/config",
"esri/tasks/ClassBreaksDefinition",
"esri/tasks/UniqueValueDefinition",
"esri/tasks/AlgorithmicColorRamp",
"esri/tasks/GenerateRendererParameters",
"esri/tasks/GenerateRendererTask",
"esri/layers/LayerDrawingOptions",
"esri/layers/FeatureLayer",
"esri/layers/Field",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/dijit/editing/TemplatePicker",
"dojo/_base/array",
"dojo/_base/event",
"dojo/_base/lang",
"dojo/parser",
"dijit/registry",
"dojo/data/ItemFileReadStore",
"dojox/grid/DataGrid",
"dijit/form/FilteringSelect",
"dojo/domReady!"],
function(declare, BaseWidget, Draw, Edit, Graphic, esriRequest, esriConfig,
 ClassBreaksDefinition, UniqueValueDefinition, AlgorithmicColorRamp,
 GenerateRendererParameters, GenerateRendererTask, LayerDrawingOptions,
 FeatureLayer,Field,
 SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol,
 TemplatePicker,
 arrayUtils, event, lang, parser, registry, ItemFileReadStore, DataGrid, FilteringSelect) {
 //To create a widget, you need to derive from BaseWidget.
 return declare([BaseWidget], {
 // DemoWidget code goes here
//please note that this property is be set by the framework when widget is loaded.
 //templateString: template,
baseClass: 'jimu-widget-ROP',
postCreate: function() {
 this.inherited(arguments);
 console.log('postCreate');
 },
startup: function() {
 ///INICIO/////
 parser.parse();
// refer to "Using the Proxy Page" for more information: https://developers.arcgis.com/javascript/3/jshelp/ags_proxy.html
 esriConfig.defaults.io.proxyUrl = "/proxy/";
 map= this.map;
map.on("layers-add-result", initEditing);

 var landusePolygonLayer = new FeatureLayer("MyLayer", {
 id: "canada_Risk",//confirm works
 mode: FeatureLayer.MODE_ONDEMAND,
 outFields: ["*"]
 });
 console.log("LayerInfo", landusePolygonLayer)
 map.addLayers([landusePolygonLayer]);
//fieldInfo
 var canadaFields = esriRequest({
 url:"https://services7.arcgis.com/Layer",
 content:{
 f: "json"
 },
 callbackParamName : "callback"
 });
 console.log("fields", canadaFields)
 canadaFields.then(function(resp){
 var fieldNames, fieldStore;
fieldNames = { identifier : "value", label: "name", items: []};
 console.log("fields", fieldNames)
 arrayUtils.forEach(resp.fields,function(f){
 fieldNames.items.push({"name": f.name, "value": f.name});
 console.log("fieldnames", fieldNames)
 });
fieldStore = new ItemFileReadStore({ data:fieldNames});
 console.log("store", fieldStore)
 registry.byId("fieldNames").set("store", fieldStore);
 }, function(err){
 console.log("failed to get field names: ", err);
 });
 //update when field name changes
 registry.byId("fieldNames").on("change", getData);
0 Kudos
26 Replies
AbhinavSharma
New Contributor III

That gives me from that line

Cannot read property 'store' of undefined
0 Kudos
KenBuja
MVP Esteemed Contributor

That's pretty odd. When I use this code in a Widget, I'm not getting that error at all.

define(['dojo/_base/declare', 'jimu/BaseWidget', 'dijit/_WidgetsInTemplateMixin', 'jimu/dijit/Message', 'dojo/on', 'dojo/_base/lang', 'dojo/store/Memory', 'dojo/data/ObjectStore', 'dijit/form/Select'], function (declare, BaseWidget, _WidgetsInTemplateMixin, Message, on, lang, Memory, ObjectStore) {
  return declare([BaseWidget, _WidgetsInTemplateMixin], {

    // Custom widget code goes here

    baseClass: 'testing',
    _featureLayer: null,

    postCreate: function postCreate() {
      this.inherited(arguments);
      console.log('Testing::postCreate');
      this._initDijits();
    },
    _initDijits: function _initDijits() {

      var data = [{
        value: 'Field_1',
        label: 'Field 1'
      }, {
        value: 'Field_2',
        label: 'Field 2'
      }, {
        value: 'Field_3',
        label: 'Field 3'
      }];
      this.sourceLayerFieldSelect.addOption(data);
      //var store = new Memory({
      //  data: data
      //});
      //var os = new ObjectStore({
      //  objectStore: store
      //});
      //this.sourceLayerFieldSelect.setStore(os);
      //this.sourceLayerFieldSelect.startup();

      this.sourceLayerFieldSelect.set('disabled', false);
      this.own(on(this.sourceLayerFieldSelect, 'change', lang.hitch(this, this._onFieldSelectChange)));
    },
    _onFieldSelectChange: function _onFieldSelectChange() {
      new Message({
        titleLabel: 'Field Selected',
        message: 'Selected ' + this.sourceLayerFieldSelect.get('value')
      });
    }
  });
});

When I comment out line 26 and uncomment lines 27-34, I still don't get that error, but I'm also not getting the "change" event to fire.

0 Kudos
KenBuja
MVP Esteemed Contributor

I suspect what's happening is you're calling a function to do all your work and calling this.sourceLayerFieldSelect within that function. You have to use lang.hitch to maintain the proper scope of the "this" variable. You can see it used in setting the on change event.

Here's another case where I have to use it after querying a FeatureLayer

this._layerSPGrid.queryFeatures(query, lang.hitch(this, function (featureSet) {
  //code
}));
‍‍‍‍
0 Kudos
AbhinavSharma
New Contributor III

I do not use "this" anywhere until the end of the block (when i need to change the render) so I do not think that could be it.

I played around with my code some more and I think it has to do with the way my data is being attached to the form. if i use your example or the one from the site it works fine (ie just hard code the options into the form). After adding my own data though it gives me an empty form (with enough rows for my data) and the chrome element has an empty table in it.

I will attach images to help explain what i mean.

This is the empty form

 

Empty table in chrome

widget.js

var canadaFields = esriRequest({
         url:"https://services7.arcgis.com/AGtHHrVGb3W75sdN/ArcGIS/rest/services/Canada/FeatureServer/0",
         content:{
           f: "json"
         },
         callbackParamName : "callback"
       });
       console.log("fields", canadaFields)
       var fieldNames, fieldStore;
       canadaFields.then(function(resp){

         fieldNames = { identifier : "value", label: "name", items: []};
         console.log("fields", fieldNames)
         arrayUtils.forEach(resp.fields,function(f){
           fieldNames.items.push({"name": f.name, "value": f.name});
           //console.log("fields_Sliced", fieldNames)
           //this.sourceLayerFieldSelect.set("value", f.name)
           //console.log("fieldnamesForStore", fieldNames.items)


        });
        var data = fieldNames.items;
        console.log("dvfdata", data)

        var store = new Memory({
          data: data
        });

        var os = new ObjectStore({objectStore: store});
        console.log("dsds",os)

        var s = new Select({
          store: os
        }, "sourceLayerFieldSelect");
        s.startup();
      //  this.sourceLayerFieldSelect.store(os);
        console.log("dsddssdsd",s)

        s.on("change", function(){
          lang.hitch(this, this.get("value"));
    console.log("my value: ", this.get("value"))
  })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Widget.html

<div class="jimu-widget-use-jquery">
Use ctrl or cmd + click on graphic to delete.  Double click on graphic to edit vertices.
<div id="templatePickerDiv"></div>
<label for="sourceLayerFieldSelect">Render based on: </label>
<div id="sourceLayerFieldSelect"></div>

</div>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
KenBuja
MVP Esteemed Contributor

This works properly

Widget.html

<div>
  <div data-dojo-attach-point="sourceLayerFieldSelect"></div>
</div>

Widget.js

define(['dojo/_base/declare', 'jimu/BaseWidget', 'dijit/_WidgetsInTemplateMixin', 'jimu/dijit/Message', 'dojo/on', 'dojo/_base/lang', 'dojo/store/Memory', 'dijit/form/Select', 'esri/request', 'dojo/_base/array'], function (declare, BaseWidget, _WidgetsInTemplateMixin, Message, on, lang, Memory, Select, esriRequest, arrayUtils) {
  return declare([BaseWidget, _WidgetsInTemplateMixin], {

    // Custom widget code goes here

    baseClass: 'testing',
    _featureLayer: null,

    postCreate: function postCreate() {
      this.inherited(arguments);
      console.log('Testing::postCreate');
      this._initDijits();
    },
    _initDijits: function _initDijits() {
      var fieldNames, fieldStore, data, store, os;
      var fields = esriRequest({
        url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3',
        content: {
          f: "json"
        },
        callbackParamName: 'callback'
      }).then(lang.hitch(this, function (response) {
        fieldNames = { identifier: 'value', label: 'name', items: [] };
        arrayUtils.forEach(response.fields, function (f) {
          // add some field names to the FS
          fieldNames.items.push({ 'name': f.name, 'value': f.name });
        });
        data = fieldNames.items;
        store = new Memory({
          idProperty: 'name',
          data: data
        });

        var select = new Select({
          store: store,
          labelAttr: 'name',
          onChange: this._onFieldSelectChange
        }, this.sourceLayerFieldSelect);
        select.startup();
      }));
    },
    _onFieldSelectChange: function _onFieldSelectChange(value) {
      new Message({
        titleLabel: 'Field Selected',
        message: 'Selected ' + value
      });
    }
  });
});
AbhinavSharma
New Contributor III

edit: I am now having a problem with my render but I will post a new question for that if i do not figure it out by the end of the day.

edit2: removed log statements from snippet

I actually just figured out my code and was typing a reply to the previous message, I will copy and paste it here instead.

It turns out that the way I was creating the Object data was incorrect. I am able to populate the list and handle the the on change with the following code :

var fieldNames, fieldStore;
       canadaFields.then(function(resp){

         fieldNames = { identifier : "id", label: "label", items: []};
         //console.log("fields", fieldNames)
         arrayUtils.forEach(resp.fields,function(f){
           fieldNames.items.push({"id": f.name, "label": f.name});
        });
        var data = fieldNames;

        var store = new Memory({
          data: data.items
        });

        var os = new ObjectStore({objectStore: store});

        var s = new Select({
          store: os
        }, "sourceLayerFieldSelect");
        s.startup();
)

        s.on("change", function(){
          console.log("my value: ", this.get("value"))
          currField = this.get("value");
          getData()
        })‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
KenBuja
MVP Esteemed Contributor

Please close out this discussion by clicking "Mark Correct" button on the post that best answered your question.

0 Kudos