computedAttr.compute is not a function after upgrade

First, let me say that I am very new to CanJS. I have been upgrading our various javascript libraries. After upgrading from CanJS 2.0.7 to 2.3.22 I have the following issue. We have a section of code in a model that has a bind on a attribute. This section of code used to work fine, but now fails with the following error: computedAttr.compute is not a function. I noticed that the value of the attribute was an object in 2.0.7, and is now an Constructor, with many more attributes in 2.3.22.

Here is the portion of the model.js file. The failure occurs after the else clause.

DiagnosisSelectionViewModel: can.Map.extend({
        init: function() {
        	selectionMap = this;
        	this.bind("selection", this.onSelectedDiagnosis);
            this.clearSelection();
          },
          onSelectedDiagnosis: function(evt, data) {
            if (_.isEmpty(data)) {
              var errorMessage = "Please make sure to select from the list shown after entering the problem.";
              var emptyDiagnosis = _.isEmpty(this.attr("description"));
              this.attr({
                stateClass: "ui-state-error",
                stateIcon: "ui-icon-alert",
                identifierLabel: emptyDiagnosis ? "" : errorMessage,
                icd10:{id: ""},
                icd9:{id: ""},
                snomed:{id: ""}
              });
            }
            else {
            	this.attr(_.extend({
                stateClass: "ui-state-highlight",
                stateIcon: "ui-icon-circle-check"
              }, data));
            }
          },
          clearSelection: function() {
            this.attr("selection", "");
            this.attr("description", "");
          }
        })

Sample of “data” in CanJS 2.3.22:

data: Constructor
	_backupStore: function(val)
	_cid: ".map456"
	_computedAttrs: Object
	_data: Object
	description: "Nontraffic accident involving motor-driven snow vehicle injuring other specified person"
	icd9:  Constructor
	icd10: Constructor
	identifierLabel: " [ICD9: E82.08]"
	label: "Nontraffic accident involving motor-driven snow vehicle injuring other specified person [ICD9: E82.08]"
	snomed: Constructor
	value: "Nontraffic accident involving motor-driven snow vehicle injuring other specified person [ICD9: E82.08]"
	__proto__:can.Construct

Sample of “data” from 2.0.7:

data: Object
	description: "Nontraffic accident involving motor-driven snow vehicle injuring motorcyclist"
	icd9: Object
	icd10: Object
	identifierLabel: " [ICD9: E82.02]"
	label: "Nontraffic accident involving motor-driven snow vehicle injuring motorcyclist [ICD9: E82.02]"
	snomed: Object
	value: "Nontraffic accident involving motor-driven snow vehicle injuring motorcyclist [ICD9: E82.02]"
	__proto__: Object

As you can see, the Objects are now Constructors and there are several fields added by CanJS. Even what used to to bbe simple objects, like icd9 and icd10 fields are now Constructors with the additional fields added. Tracing through the code show that it is elements of these new fields that are generating the error.

Like I said, I am new to CanJS, and I didn’t write this originally. I’m hoping someone can at least point me in the right direction.

Thanks.

Anyway you could point me to where that error is created within CanJS’s code? I’m unable to provide any suggestions from is presented here.

The error occurs in the can/map/map module at ___set.

 ___set: function (prop, val) {
            var computedAttr = this._computedAttrs[prop];
            if (computedAttr) {
                computedAttr.compute(val);
            } else {
                this._data[prop] = val;
            }
            if (typeof this.constructor.prototype[prop] !== 'function' && !computedAttr) {
                this[prop] = val;
            }
        },

Specifically, the error is in the line “computedAttr.compute(val)”.

The new version of canjs does different things to a JavaScript object when the attr() method is called. In 2.0.7 it is pretty much left as a JavaScript object. The later version turns the object into a canjs Constructor, also any child JavaScript objects are converted into a canjs Constructor. The Constructor objects include new additional methods. This seems to be the source of the error.

While not the best approach, I was able to get around the error by converting the data object passed into the OnSelectedDiagnosis event back into a standard JavaScript object before passing it to the attr() method.

DiagnosisSelectionViewModel: can.Map.extend({
      init: function() {
        this.bind("selection", this.onSelectedDiagnosis);
        this.clearSelection();
      },
      onSelectedDiagnosis: function(evt, data) {
        if (_.isEmpty(data)) {
          var errorMessage = "Please make sure to select from the list shown after entering the problem.";
          var emptyDiagnosis = _.isEmpty(this.attr("description"));

          this.attr({
            stateClass: "ui-state-error",
            stateIcon: "ui-icon-alert",
            identifierLabel: emptyDiagnosis ? "" : errorMessage,
            icd10:{id: ""},
            icd9:{id: ""},
            snomed:{id: ""}
          });
        }
        else {
        	// CanJs has converted "data" into a CanJs Constructor object and blows up on the attr call.
        	// Converting "data" back into a simple JavaScript object.
        	var newObject = {
    	        label:data.label,
                description:data.description,
                icd10:{id:data.icd10.id},
                icd9:{id:data.icd9.id},
                snomed:{id:data.snomed.id},
                identifierLabel:data.identifierLabel,
                stateClass: "ui-state-highlight",
                stateIcon: "ui-icon-circle-check"
        	};
        	
        	this.attr(newObject);
        }
      },
      clearSelection: function() {
        this.attr("selection", "");
        this.attr("description", "");
      }
    })

While I have canjs working properly again in our main code, the new version of the attr() method has broken almost all of our tests in phantomjs. I’m currently working on figuring out what is going on there.