ng-if statement works one way but not another, why? - javascript

When I write my ng-if statement like this...
ng-if="$state.params.Step != vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 2 && vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 1"
it doesn't work at all. I thought the above was just a shorter form of...
ng-if="$state.params.Step != vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 2 && $state.params.Step != vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 1"
What am I doing wrong? I don't want to repeat myself if I can help it.

ng-if="$state.params.Step != vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 2 && vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 1"
This is basically saying:
($state.params.Step != vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 2) &&
vm.$localStorage.builder[$state.params.ID].Data.Steps.length - 1
This compares then &&s the result with the last number. Valid; almost certainly not what you want.
This logic should almost certainly be moved out of the ng-if where it's almost impossible to read.

What you're doing wrong is not repeating yourself. :-)
Angular evaluates expressions like JavaScript does (I think it actually uses JavaScript to do it, but I haven't checked under the covesr). JavaScript, like nearly all the languages with syntax derived from B (C, C++, C#, Java, JavaScript, ...) don't have a x != a && b or x != a || b, you have to spell it out: x != a && x != b or x != a || x != b. That's just how it is.
The only reason you don't get an error is that JavaScript has type coercion, and so x != a && b evaluates to (x != a) && b, which ends up being true && b or false && b. b is then coerced to boolean according to the usual rules (anything falsy ends up being false, everything else ends up true). (The falsy values are 0, "", NaN, null, undefined, and of course, false.)

Related

"Do nothings" as statment in Javascript Condition

I want a ternary operator in JavaScript to return nothing if the statment is false
I have tried this:
1 == 1 ? alert('YES') : '';
But I want to know if this is the right way to make a statments "DO NOTHING" depending on the condition.
No, use if.
if (1 == 1) {
alert('YES');
}
Don't abuse the conditional operator as a replacement for if/else - it's confusing to read. I'd also recommend always using === instead of ==.
If you really want to do it, && will be one way.
1 == 1 && alert('ALERTED');
1 == 2 && alert('NOT ALERTED');
It is single statement.
A condition after an AND operator is only run if the first is true. So it is like an if statement.
did you try a single line if statement without brackets?
if(1 == 1) alert('Yes');

Using Both AND and OR in IF Statement

I'm working on a Google Spreadsheet adding scripts (Not formulas) and I am stuck on a problem.
I need to find a way to use something like this:
If (Product == "Shampoo" && (Box == "15" OR Box == "17"))
{
//Do Something...
}
Basically IF the product is a shampoo and the box it belongs to is either 15 or 17 then do something. I know doing a If (Product == "Shampoo" && Box == "15" OR Box == "17") will produce unexpected or bad results. How do we go about using an OR with AND in google scripting?
From what I understand Google Scripts are based on Javascript but I can't seem to find help posts online or here in SO, all I get are solutions in formula not script.
First a note about the OP code
JavaScript methods are case sensitive.
Instead of IF the correct syntax is if. By the other hand the Logical OR operator is ||.
Considering the above, the OP code could be replaced by
if(Product == "Shampoo" && (Box == "15" || Box == "17"))
{
//Do Something...
}
and
if(Product == "Shampoo" && Box == "15" || Box == "17")
Regarding the question, if (Product == "Shampoo" && Box == "15" || Box == "17") returns unexpected or bad results, this could be hard to read and lead to confusions for humans but JavaScript have very specific rules regarding how operations should be made by the engine, in this case Rhino which is used by Google Apps Script.
As was mentioned in a previous answer in this case the rule is called operator precedence but in order to make the code easier to read and to prevent confusions a good practice is to enclose each comparison in parenthesis.
Considering that the Logical AND has a higher precedence than Logical OR
(Product == "Shampoo" && Box == "15" || Box == "17")
is the same as
((Product == "Shampoo" && Box == "15") || Box == "17")
References
if..else
Logical OR
What are you looking for is the operator precedence of the two operators logical AND && and logical OR ||.
Part of the table:
Precedence Operator type Associativity Individual operators
---------- ------------- --------------- --------------------
6 Logical AND left-to-right … && …
5 Logical OR left-to-right … || …
You see a higher operator precendece of logical AND over logical OR. That means you need some parenthesis for the OR statement.

Best way to refactor complex if expression [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I have just come on board to a big e-commerce project that has an angularJS front-end. I have been tasked with adding a lot of complex features to the checkout pages... which already have a lot of complex logic.
To make things harder I keep coming across lots of if statement expressions like the one below which are making it hard to understand and it is a slow process going through this code with many of these type of if expressions.
Some of these expressions are pretty critical and are sometimes even longer... There are no unit tests and when I ask other devs what this is checking for and why (just to be sure I understand) I usually get pointed to someone else rather than an explanation.
if ((!previousBillingAddress) || (previousBillingAddress && previousBillingAddress.id !== bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
Does anyone have a good tip for refactoring these types of expressions?
I thought of breaking them down into functions that have descriptive names and the functions could return true or false but Im not sure if there is a better way.
Let there be
A = previousBillingAddress
B = previousBillingAddress.id !== bag.paymentAddress.id
then your expression is:
if (!A || (A && B)) {
log1
} else {
log2
}
What we can do we with !A || (A && B)? It's equal to !A || B:
A | B | !A | A && B | !A || (A && B) | !A || B
==========================================================
1 | 1 | 0 | 1 | 1 | 1
1 | 0 | 0 | 0 | 0 | 0
0 | 1 | 1 | 0 | 1 | 1
0 | 0 | 1 | 0 | 1 | 1
That's why your expression is equal to:
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
TL;DR
Above table is only check if !A || (A && B) is equal to !A || B. How to guess !A || B? In case of such expressions it's good to play with following rules:
A == !(!(A)) (rule 1)
!(A && B) == !A || !B (rule 2)
!(A || B) == !A && !B (rule 3)
A && (B || C) == A && B || A && C (rule 4)
So we have !A || (A && B), let's play. Due to rule 1 it's equal to
!(!(!A || (A && B)))
Now we use rule 3:
!(!(!A || (A && B))) == !(A && !( A && B))
Rule 2:
!(A && !( A && B)) == !(A && (!A || !B)) (*)
Due to rule 4:
A && (!A || !B) == (A && !A) || (A && !B)
We have (A && !A) || (A && !B) and it can be reduce to (A && !B). Now we can back to (*) and we have:
!(A && (!A || !B)) == !((A && !A) || (A && !B)) == !(A && !B)
With rule 2 we got:
!(A && !B) == !A || B
You can drop the previousBillingAddress && part - in the second operand of the || you already have estblished that previousBillingAddress is not falsy. That would make the overall condition
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
which seems short enough for me. If not, make an appropriately named helper function to which you pass the previousBillingAddress and the bag.
In my personal opinion, i think you can refactor and encapsulate the validation/check inside of a function, then if the same validation applies to other part of your code (file/module/etc..), you can reuse it. In this case you are using angular, so might be good to use an angular custom service for this type of business validations, since they also can manipulate the scope
Comments are our best friend. You want to make sure that the next person to take a look at your code knows exactly what it is supposed to do and from that, it will be quicker and easier to see exactly what it is doing.
In this case the expression in the if statement can be shortened. This is how I would comment it with almost a pseudocode approach:
// we need to check if the user has, and is using, a previous billing address
if (previousBillingAddress && previousBillingAddress.id === bag.paymentAddress.id)){
// the user is using a previous billing address
// do some work with the previous billing address
} else {
// the user is using a new address
// do some work with the new address
// save the new billing address
}
Note that previousBillingAddress is the main subject of this if statement. If we are checking for previousBillingAddress then in our first block we want to make sure we deal with what happens if we have a previous billing address. It's more logical that way.
It makes a lot less sense to check if we don't have a previous billing address and then what do we do if we have one.
Look how clean and logical it is! <3
I recommend hoisting the complex conditionals into Boolean variables (or constants, if your supported level of JS allows you to use const). For example:
var weakPrevBillingAddress =
!previousBillingAddress ||
(previousBillingAddress &&
(previousBillingAddress.id !== bag.paymentAddress.id));
if (weakPrevBillingAddress) {
console.log('judged weak previousBillingAddress');
// other processing expected here
} else {
console.log('judged strong previousBillingAddress');
// other processing expected here
}
As you and philip yoo suggest, you could hoist the conditional into a Boolean helper function. But most of the ways of defining such functions will put the logic farther away from the point of use, making comprehension harder. Boolean variables (a.k.a. "predicates") can be easily defined close to their use (e.g. right before your if statement). So there isn't much additional distance, but they still separate the computation of the Boolean value from its use. Your ability to focus on the computation separately can make things simpler and more comprehensible. As shown above, it also allows you to split long
lines and use indentation for better clarity.
I don't know if you have the flexibility to rename existing variable names. If you do, the ones you're using are exceedingly long. Long names can help increase understanding, but a 22-character name repeated several times per line is distracting, not clarifying. Shorter names, either for the source values, or just for the predicates you compute, can help. E.g.:
var weakPrevBA = !prevBA ||
(prevBA.id !== bag.paymentAddress.id);
Choosing good variable names is an art, and developer tastes about "how much description is enough" vary. But in an app that deals with billing addresses all the time, line after line after line, spelling out billingAddress every time doesn't necessarily help. If you're reading the code and comments, you probably are highly aware you're dealing with billing addresses. If not, add a quick comment, rather than spell out the full concept multiple times per line.
Note that I've also simplified the Boolean expression in this shorter example. (This was also suggested by Bergi) That can be a good idea--as long you're sure it can be simplified correctly. However, do this only with great caution. You don't have unit tests, so you have no easy way to test if simplifications are truly identical. And you're dealing with complex code. I recommend keeping the original Boolean expressions, at least to start (possibly with shortened names). That reduces one place bugs can creep in, at least until you understand the code more fully. The better you understand the code, and the simpler you have made it (e.g. by adding computed predicates, rather than long, complex expressions inside conditionals), the more freedom and safety you have in rewriting expressions.
Something like this
var billingId = previousBillingAddress && previousBillingAddress.id;
if (billingId || billingId === bag.paymentAddress.id){
console.log('all of the above was false'); // just a dummy log
} else {
console.log('all of the above was true'); // just a dummy log
}
what i did
since you are using if and else its not a good practice to use negation in the if statement. so i removed that
if you only want the negated part (without an else)
if (!(billingId || billingId === bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
}
the && will return the first false, or the last true value. so billingId will equal false or the id. building upon that, your first if is checking if its "falsey" continue, so you can use the same variable as well.

How to make true && false equal to true in angularjs expression?

I'm trying to check if an input has been touched and is empty (without the built in functions in angular).
<form-input is-invalid-on="isTouched && !gs.data.name || gs.data.name.length > 50"></form-input>
If isTouched && !gs.data.name evaluates to true && false then that side of the expression becomes false
So my question is quite simple, how do I make the entire expression evaluate to true if the input has been touched and if it's empty or has a length greather than 50?
I believe it is used as attribute directive.
is-invalid-on="(isTouched && gs.data.name.length) || gs.data.name.length > 50"
Reason? I assumed your gs.data.name is a string. Empty string when evaluated in javascript is still a truthy value. So you must evaluate it to length.
<form-input is-invalid-on="(isTouched && !gs.data.name) || (gs.data.name.length > 50)"</form-input>
can try using () and also check gs.data like isTouched && (!gs.data || !gs.data.name || !gs.data.name.length || gs.data.name.length > 50)
<form-input is-invalid-on="isTouched && (!gs.data || !gs.data.name || !gs.data.name.length || gs.data.name.length > 50)"></form-input>
Angular expression does not work exactly the same than javascript from what i got try this one :
<form-input is-invalid-on="isTouched && gs.data.name.length==0 || gs.data.name.length > 50"></form-input>
Assuming you properly initialized gs.data.name to empty string.
By the way you forgot the > on your tag.
I finally found the reason as to why it was behaving so strange, and as this question has many answers I could not delete it. So I might as well explain what happened.
It turned out that isTouched was always undefined because I was using it outside of the directive (even if it was used in an attribute of the directive) which made the expression undefined && false, resulting in isInvalidOn always being false.
Instead I made it so that I used isTouched later in the actual form-input template as ng-class={invalid: isInvalidOn && isTouched}, resulting in the desired behavior.

&& / || operator strangeness in JavaScript

So, I was working on a project of mine, when I came across a problem like this:
var one = 1;
var two = 2;
var three = 7;
if (one === 1 || two === 2 && three === 3) {
console.log("ok"); // prints out ok
}
I'm pretty confused with this since I don't think it should print out "ok". I thought that since the condition after the && operator was false, it shouldn't run, but I'm evidently wrong. Can anyone clarify why it's working?
In Javascript, operators are not just evaluated left-to-right, certain operators have more precedence than others. Those with higher precedence (in this case, the && operator of precedence 13) will be evaluated before others (||, precedence 14).
For your particular case, you need to group your conditionals in order to achieve the functionality you want:
if ((one === 1 || two === 2) && three === 3) {
console.log("ok"); // prints out ok
}
JSFiddle

Categories

Resources