My application has a landing page with two components in two separate tabs.
The code from the first component that is causing the crash looks like this:
for (let key in linegraphdata) {
linegraphdata[key].price = Number(
linegraphdata[key].price.trim().slice(1)
);
linegraphdata[key].month = parseDate(linegraphdata[key].month);
}
When I load into my application initially it doesn't crash, loads the data from the first tab fine. I'll click into the second tab and when I eventually click back the whole application crashes and the log gives me this error:
Uncaught TypeError: linegraphdata[key].price.trim is not a function
It must have something to do with how React handles refreshing components once already rendered, could anyone help me figure it out please :)
You're setting what was a string to a number, and numbers don't have the trim() method on them. That's why it works the first time (when it's a string) and not the second time around:
array[key] = Number(array[key].trim());
So that code must be executing more than once.
linegraphdata[key].price is either null or not a string.
If there is a value, you can try using linegraphdata[key].price.toString().trim().slice(1)
You can check that price is a string with this ternary. If it's not a string it will set the value to -1
linegraphdata[key].price = Number(
typeof linegraphdata[key].price == 'string' ? linegraphdata[key].price.trim().slice(1) : -1
);
Related
I've created a script (my first) that accepts input text and then runs about 30 regular expressions on the text, outputting discovered values into an HTML table. The purpose is for me to be able to paste in text containing specification information for products and have them uniformly outputted so I can paste them into a spreadsheet.
I've had it working really well and I've been tuning the regexes as I've pasted data with different variations/layouts in. However, I've hit an impasse and need some assistance:
One of the regular expressions searches for the product part number (sku) and returns the value in a column. Some of the source data includes more than one match because there are regional variations to the products. In all cases the first match is the only one that I want. I've tested the RegEx on RegEx101 and it returns the first match only with the 'global' flag switched off. However, the same RegEx running in my script causes it to return console messages infinitely before crashing. It's immediately unresponsive so I can't see any error messages.
Here's a sample of the regex section in my script. sku being the one that's causing problems:
let wallMountable = /(?<wallmountable>[Ww]all [Mm]ount)/mg;
let sku = /^.*\((?<sku>\d\d\w\w[\d\w]+?).+?\).*$/m;
function parseData() {
// Execute the Regular Expressions on the text in 'specSheet'
let specSheet = document.getElementById("specSheet").value;
let wallMountableToken = wallMountable.exec(specSheet);
let skuToken = sku.exec(specSheet);
do {
// If Token isn't null, then test to see if the regex group value is undefined. If either are true, do nothing, otherwise write the value to the document.
if (wallMountableToken !== null) {
if (wallMountableToken.groups.wallmountable !== undefined)
{document.write(`${wallMountableToken.groups.wallmountable}`);}
else {}
}
else {}
if (skuToken !== null) {
if (skuToken.groups.sku !== undefined)
{document.write(`${skuToken.groups.sku}`);}
else {}
}
else {}
}
// Loop through the script until a condition is met.
while (
(wallMountableToken = wallMountable.exec(specSheet)) !== null,
(skuToken = sku.exec(specSheet)) !== null
);
}
The while loop may not be necessary and in truth I'm not entirely sure what purpose it serves here, but it seemed to consistently appear in the reference material I was studying. I have included it here because it's part of the script, but please note that the script works if I change the second regex to /mg instead of /m, however, it returns multiple values and I only want it to return the first capture.
I know there's a lot wrong with the script, but this particular question is about why the regex is causing an infinite loop, so any help toward that goal is much appreciated.
I'm trying to check if a number is present in an array (which I've done a thousand times before using .indexOf()) but I seem to be missing something now.
Vue method
showSeat(seat) {
if( !this.selectedSeats.length ) {
this.selectedSeats.push(seat)
} else {
let index = this.selectedSeats.indexOf(seat)
( index >= 0 ) ? this.selectedSeats.splice(index,1) : this.selectedSeats.push(seat)
}
}
Initially, this.selectedSeats is equal to [], and the first condition runs perfectly. However, when I try to add another seat, I get [Vue warn]: Error in event handler for "showSeat": "TypeError: this.selectedSeats.indexOf(...) is not a function". What am I missing?
This is one of those rare cases in JavaScript where leaving off a semicolon can cause huge problems. These two lines are being evaluated as a single expression:
let index = this.selectedSeats.indexOf(seat)
( index >= 0 ) ? this.selectedSeats.splice(index,1) : this.selectedSeats.push(seat)
It's trying to execute this.selectedSeats.indexOf(seat)(index>=0)
Add a semicolon at the end of your indexOf(seat); and you should be fine.
I'm creating a conditionalPanel in a Shiny app. I'm attempting to debug the JavaScript condition, but without inspecting it, I'm just guessing random JavaScript bits. Is there a way to inspect the condition directly?
selectizeInput('groups','Groups:',
choices = list('Choice1' = 'choice1','Choice2' = 'choice2'),
multiple = TRUE,selected = NULL
)
conditionalPanel(
print("input.groups.indexOf('choice2') >= 0"), # desired output
condition = "input.groups.indexOf('choice2') >= 0",
selectInput("statusfilter", "StatusFilter",
list("NewChoice1" = "nc1","NewChoice2" = "nc2"))
)
The above code (generalized from my production code) is showing the conditionalPanel at start up before I even click a choice. Once I do, it will disappear until I choose 'choice2'. If I could inspect the condition, I could fix this quickly.
After commenting our the print statement so that the code runs, if you open your javascript console, you'll see an error:
Error parsing expression: input.groups.indexOf('choice2') >= 0
This suggests your JS code breaks. This is because input.groups is null , and doing .indexOf on a null value causes an error.
So what you want in the condition is:
condition = "input.groups !== null && input.groups.indexOf('choice2') >= 0"
i've spent ages trying to understand bootstrap's navigation bar, mainly by spending 4-5 days reading stackoverflow posts
& finally i think i've found an answer that helps!!!
trouble is, i can't understand the accompanying javascript/jquery code. i'm guessing its a shorthand version of js or something but just what it means i cannot decipher
basically, its the javascript code that appears on this jsfiddle page
$('.navbar').on('show', function () {
var actives = $(this).find('.collapse.in'),
hasData;
if (actives && actives.length) {
hasData = actives.data('collapse')
if (hasData && hasData.transitioning) return
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
});
so, if anyone can explain to me what the code is doing on a line by line basis it'd be really cool
the first line i understand. its the weird-ass syntax in the next 6 lines that have me mystified
var actives = $(this).find('.collapse.in'),
hasData;
This creates two variables. One with elements picked from current scope that match the selector .collapse.in, and one empty variable.
if (actives && actives.length)
If actives exists and contains more than zero elements, do the following...
hasData = actives.data('collapse')
Retrieve arbitrary data stored under the key collapse. See https://api.jquery.com/jquery.data/ for more info.
if (hasData && hasData.transitioning) return
If hasData exists and hasData.transitioning is truthy, stop function execution.
actives.collapse('hide')
Call the collapse function on actives. This is not a native jQuery function, so you'll have to look up whatever plugin it comes from to make sense of the argument being passed in.
hasData || actives.data('collapse', null)
If hasData is truthy, skip this line. Otherwise, set the arbitrary data in actives variable to null.
Update: A Solution below!
I'm fairly new to website development but I've been tasked with developing e2e (end-to-end) tests for a developing website that uses AngularJS. As a result I've been looking down the road of using AngularJS's karma-run ngScenario testing wrapper.
Anyway, just getting started I want to make sure that a simple hyperlink's text matches part of its href address. There isn't need to know the structure of this code snippet, but these are thumbnails for user profiles, and you can click the thumbnail object completely (the first 'a') or you can click a link displaying their name (the second 'a').
There dozens of these on a page.
Here is what a part of the page looks like loaded with a user "PurplePenguin".
<div class="thumbnail__section">
<a href="/profile/PurplePenguin">
<div class="thumbnail__bottom">
<div class="thumbnail__rating ng-binding"> </div>
<div class="thumbnail__info">
<div class="thumbnail__name">
<a class="ng-binding" href="/profile/PurplePenguin">PurplePenguin</a>
</div>
[...]
Essentially I want a test that will take the text of the second 'a' element and check it against the href attribute: "assert that href '/profile/PurplePenguin' equals '/profile/' + 'PurplePenguin'"
This is what I've made just wanting to test the first thumbnail's 'a', (in my time writing "PurplePengiun" is the first user every time so I could hard code it).
it('should have the performer\'s name match the link', function() {
// "eq(n)" gets the nth instance of the element;
// "> a:eq(0)" grabs its first child a
var nameElement = element('.thumbnail__name:eq(0) > a:eq(0)');
// These work and pass
expect(nameElement.text()).toBe('PurplePenguin');
expect(nameElement.attr('href')).toBe('/profile/PurplePenguin');
// This does not
var textString = nameElement.text(); // textString == "[object Object]"
expect(nameElement.attr('href')).toBe('/profile/' + textString);
This gets returned:
expect element '.thumbnail__name:eq(0) > a:eq(0)' get attr 'href' toEqual "/profile/[object Object]"
expected "/profile/[object Object]" but was "/profile/PurplePenguin"
So I've figured out how to find the particular element on the page I need, but trying to manipulate the text of a simple 'a' element only gives me [object Object].
Some things I've tried with my basic knowledge of JS:
nameElement.text().toString(), nameElement.text().value(), nameElement[0].text()
Trying things other than text()
nameElement.html() and nameElement.val()
They too return [object Object] when trying to use them as strings.
It seems that looking at the values and attributes of these elements only works when using the very specific API functions like .toBe or .toEqual, but I want to assert that a specifically formatted string is made.
Thank you for any help!
Solution
Thanks for that bit of insight Andyrooger, I had actually taken a stab at the query function before posting my question but gave up on it too quick. Your explanation gave me the idea to start looking deeper into the samples that have been posted in the official docs. I ended up taking a hint from a Adi Roiban's post to another Angular e2e writer's question talking about query, done() messages, and promises. This ended up leading me to my eventual solution.
So I've made myself a solution and in the spirit of cooperation made a set of examples for others to learn by. There are four examples, the first two are just getting the text and the href and comparing them against hard-coded values. The third one uses indexOf to do dirt-simple comparisons. The fourth one shows how you can make your own more specific pass/fail conditions (more than what Jasmine provides with its matchers).
Number 1: User name text vs hard coded value
it('should have the user\'s name be \'PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text(); // Can finally access this guy!
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, text);
});
// Passes
expect(textPromise).toBe('PurplePenguin')
});
Number 2: Profile href value vs hard coded value
it('should have the user\'s link be \'/profile/PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var href = nameElement.attr('href');
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, href);
});
// Passes
expect(textPromise).toBe('/profile/PurplePenguin')
});
Number 3: Simple string comparison
it('should have the user\'s name match the link', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
// The first param null indicates a clean pass, the second param is a return of sorts
done(null, href.indexOf(text)); // indexOf returns -1 if text is _not_ a sub-string of href
});
expect(textPromise).toBeGreaterThan(-1);
// In the case of failure reports this in the console (if using Karma):
// expect element .thumbnail__name:eq(0) > a:eq(0) custom query toBeGreaterThan -1
// expected -1 but was -1
// In the runner.html page for the test simply reports this useless/misleading log:
// expected -1 but was -1
});
Number 4: More detailed string comparison doing it your own way plus a better error message
it('should have the user\'s name match the link', function() {
var nameStringPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
var message;
if (href.indexOf(text) == -1)
message = 'Did not find "' + text + '" in "' + href + '"';
done(message, href.indexOf(text));
});
expect(nameStringPromise);
// An expect() with no Jasmine matcher means that it only uses the done message to determine
// a pass or fail for the test. So I wrote that if my conditions weren't met it would print
// an informative message in the case of failure
// Example failure output with a broken url generator:
// Did not find "PurplePenguin" in "/profile/"
});
I'm struggling to find references to back this up or explain a little more nicely, other than these docs which don't have much info. Therefore this answer is mainly from memory of reading the relevant code.
When you write a test in ngScenario, you are not writing a series of immediate actions as you would do in a unit test. What you are actually doing queuing up a list of commands to execute when the test starts.
Methods that looks like normal jQuery functions, such as text() or attr() are actually part of ngScenario's DSL and return future objects (meaning they will at some point execute and have the result of the call you wanted.)
expect() already knows this so waits for the result before comparing to your expected value. The code underneath, in your example, is reading directly and immediately so will see the future object.
If you do need to read the element in this way I suggest looking at the query function which should allow it. On the other hand if you need to just check the formatting of the string you can use toMatch which has been implemented in ngScenario too.