0

I have this object TrSet that takes in nodes from the dom. Sometimes those dom elements can be null, and I want to handle properties of the nodes, unfortunately, I can't figure out how to use null conditional assignment on undefined properties.

var TrSet = function(valid,stat,txt,dd){
     this.validateField = valid || "";
     this.statusDropDown = stat || "";
     this.txtValue = txt || "";
     this.ddValue = dd || "";
     this.txtValue.value = txt.value || ""; //Cannot read property value of undefined
     this.ddValue.style.visibility = dd.style.visibility || "hidden"; //same

     this.validate = function () {
         /// Even when attempting to access 
         /// the property with hasOwnProperty throws the error 
         ///"Cannot read property 'hasOwnProperty' of null"!
       if (this.statusDropDown.hasOwnProperty("value") && this.validateField.hasOwnProperty("style")) {

I don't understand how either of these is failing. If it's null or undefined, it should evaluate to true for the empty string and set it equal to that.

EDIT For Clarity

In the first example

var TrSet = function(txt) {
    this.txtValue = txt || "";
    this.txtValue.value = txt.value || "";
}

If txt is null set it equal to "", if txt.value is null set it equal to "". how does this fail?

Second example

var TrSet = function(stat) {
    this.statusDropDown = stat || "";

    if (this.statusDropDown.hasOwnProperty("value")) { }

If stat is null set statusDropDown equal to "". Then I check if if it hasOwnProperty. Why does this fail?

christopher clark
  • 1,666
  • 2
  • 22
  • 42
  • `this.statusDropDown.hasOwnProperty("value")` the `this` refers to the _function currently being executed_ - i.e., the one assigned to `this.validate` - you won't get the properties from outside. – VLAZ Oct 06 '16 at 16:22
  • After edit: same thing as my comment above. You aren't checking _that_ `statusDropDown`, you have an entirely new `this` which does not have a `statusDropDown` property. – VLAZ Oct 06 '16 at 16:33

2 Answers2

4

I don't understand how either of these is failing. If it's null or undefined, it should evaluate to true for the empty string and set it equal to that.

Not quite: the statement txt || "" will return the value of txt or "", whichever is truthy first. It does not assign that value anywhere, especially not to txt. You assign separately.

Therefore, in your example (reduced to the necessary parts for example):

var TrSet = function(valid,stat,txt,dd){
  this.txtValue = txt || "";
  console.log('txt will still be undefined but txtValue will not be', txt, this.txtValue);
  this.txtValue.value = txt.value || "";
}

To do what you expect, you would need to default out txt again, like so:

var TrSet = function(valid,stat,txt,dd){
  this.txtValue = txt || "";
  console.log('txt will still be undefined but txtValue will not be', txt, this.txtValue);
  this.txtValue.value = (txt || "").value || ""; // <- change here
}

Of course, that's largely unreadable (how does .value || '' group? do you know at a glance?), so we'd probably cache it:

var TrSet = function(valid,stat,txt,dd){
  var realTxt = txt || ""
  this.txtValue = realTxt;
  this.txtValue.value = realTxt.value || "";
}

Now, that works, but is less than ideal. ES6 added the ability to provide a default directly in the parameter list, which is much cleaner:

var TrSet = function(valid = "", stat = "", txt = "", dd = "") {
  this.txtValue = txt;
  this.txtValue.value = txt.value || "";
}

To directly address the two brief questions you added:

var TrSet = function(txt) {
  this.txtValue = txt || "";
  this.txtValue.value = txt.value || "";
}

If txt is null set it equal to "", if txt.value is null set it equal to "". how does this fail?

Because txt || "" does not assign to txt, it simply returns txt or "" and that return value is assigned to this.txtValue.

var TrSet = function(stat) {
  this.statusDropDown = stat || "";

if (this.statusDropDown.hasOwnProperty("value")) { }

If stat is null set statusDropDown equal to "". Then I check if if it hasOwnProperty. Why does this fail?

Probably because this is not bound, so it doesn't actually have a value for statusDropDown. In JavaScript, this can change depending on how a function is called. Check out this question for more details.

Community
  • 1
  • 1
ssube
  • 41,733
  • 6
  • 90
  • 131
  • That makes sense. `this.txtValue.value = this.txtValue.value || ""` also seemed to work. How about my second example then? I updated my question. – christopher clark Oct 06 '16 at 16:29
  • @christopherclark have you verified that the functions are being called the same way in your real code? In particular, try logging `this` to make sure it has the value you're expecting. – ssube Oct 06 '16 at 17:25
  • I found the issue, I was calling `new TrSet()` thinking that I'm still passing null values so they would evaluate to the `""` regardless. thank you. – christopher clark Oct 06 '16 at 17:27
1

On this line:

this.txtValue.value = txt.value || "";

txt is null, and the attempt to reference a property on a null object results in an error. You need to change it to:

this.txtValue.value = txt ? (txt.value || "") : "";

Test the variable for null before attempting to dereference it.