ldlpdx ldlpdx - 5 months ago 25
Javascript Question

Getting TypeError when trying to call multiline string in p5.js

I've been trying to work out a solution for taking single-line sentences (which I call "statements" in my code) and turning them into multiline sentences on my sketch. This question relates directly to another question I posted on SO, and I have been asked to post this new issue as a new question.
The solution from @KevinWorkman got me the to this point. What's happening, though, is that I'm getting a

TypeError: Cannot read property 'display' of undefined
when I try to run the program. The sketch loads, but when I click within the sketch to start the animation, I get that error. I think it's because my original
statements[]
array is now no longer of a type that can be called in my
draw()
function as it is. But I'm not knowledgeable enough to know what the issue is and I've looked through all of my JS and p5.js references and can't find a solution.

I'm posting my complete code in an effort to provide a minimal, complete, verifiable example. Despite the fact, as a writer, I find "minimal" and "complete" to be contradictory terms and, therefore, very confusing, here goes:

var clContext;
var speed = 0.8;
var statements = [];
var canvas;

//load the table of Clinton's statements and their polarity
function preload() {
clContext = loadTable("cl_context_rev.csv", "header");
}

function setup() {
canvas = createCanvas(680, 420);
canvas.mousePressed(inWidth);
background(51);
noStroke();
// iterate over the table rows called in 'preload()' from .csv file
for (var i = 0; i < clContext.getRowCount(); i++) {
var statement = clContext.get(i, "statement");
var polarity = clContext.get(i, "polarity");
}
statements[i] = new Statement(polarity, statement);
}

function draw() {
if (mouseIsPressed) {
background(51);
for (var i = 0; i < statements.length; i++) {
statements[i].display();
}
}
}

// Function to align statements, categories, and polarity
function Statement(polarity, statement) {
// Break up single-line statements in order to display as multiline
this.statement = split(statement, "<br>");
this.polarity = polarity;
this.x = random(width);
this.y = random(height);
this.dx = random(-speed, speed);
this.dy = random(-speed, speed);
}
// Attach pseudo-class methods to prototype;
// Maps polarity to color and x,y to random placement on canvas
Statement.prototype.display = function() {
this.x += this.dx;
this.y += this.dy;

// Make statements reappear if they move off of the sketch display
if(this.x > width+10){
this.x = -10
}
if(this.y > height+10) {
this.y = -10
}
// Map positive/negative statements to colors
if(this.polarity == -1){
fill(229,121,59);
}
else if(this.polarity == 1){
fill(97,93,178);
}
textSize(14);
// Was directed to add both 'text' statements
text(this.statement[0], this.x, this.y);
text(this.statement[1], this.x, this.y+25);
}

// Create functions for hiding and showing statements
function inWidth() {
width = width+5;
};


Note:

console.log(typeof this.statement[0]) // returns 'undefined'
console.log(typeof this.statement[1]) // returns 'undefined'
console.log(split(statement, "<br>")) // returns 'undefined'

console.log(statements) // returns 'object'
console.log(statements.length) // returns 20

Answer
 for (var i = 0; i < clContext.getRowCount(); i++) {
  var statement = clContext.get(i, "statement");
  var polarity = clContext.get(i, "polarity");
 }
 statements[i] = new Statement(polarity, statement);

I think the last line here should be inside the for loop for this to do what you want.

Your error means that you are trying to call the display() method on a value that's undefined. The only place you are calling the display() method is inside your draw() function, when you loop through the statements array. This means that at one point your statements array contains an undefined value. You can verify this, by console logging the array.

I suspect the reason why you have undefined values, is because when you attempt to fill up the statements array in the setup() function, you only call statements[i] = once, after your for loop has finished. At this point the value of i is whatever it was at the end of the for loop, therefore your statements array becomes an array, where the i-th element is whatever you've set it, all the previous elements are undefined.

correct code:

 for (var i = 0; i < clContext.getRowCount(); i++) {
  var statement = clContext.get(i, "statement");
  var polarity = clContext.get(i, "polarity");
  statements[i] = new Statement(polarity, statement);
  // OR statements.push(new Statement(polarity, statement));
 }

As a generic JavaScript coding style principle it's also a good idea to Array.push() instead of direct assignment to add items to an array.