I have a function that's checking to see if a certain field in the API-revealed object has a value. The function returns a boolean. I am then styling some UI based on the result. The original function from the model looks like this:
public hasBillingCoverage(): boolean {
if (this.coverage[0].payer) {
return true;
} else if (!this.coverage[0].payer || this.coverage[0].payer === undefined) {
return false;
}
}
And I am using that in the component like this:
private hasCoverageBillingInfo() {
if (this.currentCustomer.hasFullName()) {
if (this.currentCustomer.hasBillingCoverage()) {
return true;
} else {
console.log('Coverage info not provided...');
return false;
}
}
}
The problem is, currently I'm getting an error in the console, that reads:
inline template:13:16 caused by: Cannot read property 'payer' of
undefined
That error points to this line in my component view:
<div class="page-content-header-item" [class.selected]="isSection('billing-and-coverage')" (click)="navigateTo('billing-and-coverage')"
[ngClass]="{'attention-needed': !hasCoverageBillingInfo()}">Billing and Coverage</div>
The above code in the view is designed to add some styling (the class "attention-needed') if the function "hasCoverageBillingInfo()" evaluates to false.
I thought I was already handling an undefined result, but apparently not. How can I edit this function so as to prevent the console error?
Related
I want to build a table in Raect with a sorted list of watches of a certain ebay listing.I figured out that the problem is this line:
entriesObj[value][1][0].listingInfo[0].watchCount[0]
because sometimes listing don't have any watches at all and in this case value watchCount doesn't exist at all so I can't loop through it, although I tried to use conditional operator (and if else statements in many different ways) it still throws an error. First I created an object:
watcherCount = () => {
return (
this.state.itemList.reduce((watcherObject,item) => {
const watcherKey = item.itemId;
if (!watcherObject[watcherKey]) {
watcherObject[watcherKey] = [item];
} else {
watcherObject[watcherKey].push(item);
}
return watcherObject;
},{})
);
}
and now I am trying to move them to an array ([number of watches, title of listing, item id]) in order to sort them:
import React from 'react';
class Watches extends React.Component {
render () {
var entriesObj = Object.entries(this.props.watcherCount);
var sortable = [];
for (var value in entriesObj){
for (var value in entriesObj){
sortable.push([typeof entriesObj[value][1][0].listingInfo[0].watchCount[0] === "undefined" ? "-" : entriesObj[value][1][0].listingInfo[0].watchCount[0], entriesObj[value][1][0].title[0], entriesObj[value][0]]);
}
}
sortable.sort(function(a, b) {
return b[0] - a[0];
});
console.log(sortable);
//Output: Uncaught (in promise) TypeError: Cannot read property '0' of undefined
return <table></table>
}
}
export default Watches;
Do you know any other way to build this exact kind of array or how to solve the problem with missing property?
I Don't know if I fully understood the problem.
In cases with deep references, if I don't want or can't use any conditional checks I simply put the object path reference in a try catch (finally) block.
e.g. (untested though)
for (var value in entriesObj){
var val;
try {
// just make sure the only error that might be occuring
// here is an object reference error
// therefore the array push happens after the try catch block
val = entriesObj[value][1][0].listingInfo[0].watchCount[0], entriesObj[value][1][0].title[0], entriesObj[value][0]];
} catch(err) {
val = "-";
// log the error or maybe issue a warning since missing items
// is actually expected behaviour
} finally {
sortable.push(val);
}
}
Maybe it solves your problem.
flow 0.67.1 (but behavior continues to exist in 0.73.1)
Example:
type PropOptional = {
prop?: ComplexType
};
type ComplexType = {
callable: () => void,
anotherCallable: () => void
};
function usePropOptional(arg1: PropOptional) {
if (arg1.prop) {
arg1.prop.callable();
arg1.prop.anotherCallable();
arg1.prop.callable();
}
};
The function checks for the presence of arg1.prop before accessing any properties on arg1.prop. This should be sufficient to verify that arg1.prop is defined.
Flow is fine with the first time an arg1.prop property is accessed, which is the call to arg1.prop.callable() on the first line inside the if block. However, flow generates errors on subsequent attempts to access arg1.prop properties in the exact same if block:
arg1.prop.anotherCallable();
arg1.prop.callable();
I am forced to either prepend each line with a rote arg1.prop && truthy check, or reassign arg1.prop to a local variable inside the if block:
function usePropOptional(arg1: PropOptional) {
if (arg1.prop) {
const reallyExists = arg1.prop;
reallyExists.callable();
reallyExists.anotherCallable();
reallyExists.callable();
}
};
This doesn't feel right. What am I doing wrong or missing?
You can check this in a flow repl here on flow.org.
This is documented in FlowType's Type Refinement section:
Refinement Invalidations
It is also possible to invalidate refinements, for example:
// #flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
otherMethod();
// $ExpectError
value.prop.charAt(0);
}
}
The reason for this is that we don’t know that otherMethod() hasn’t
done something to our value.
...
There’s a straightforward way to get around this. Store the value
before calling another method and use the stored value instead. This
way you can prevent the refinement from invalidating.
// #flow
function otherMethod() { /* ... */ }
function method(value: { prop?: string }) {
if (value.prop) {
var prop = value.prop;
otherMethod();
prop.charAt(0);
}
}
So the workaround in your final case appears to be the suggested way to avoid this problem.
I'm sure this is a rookie error so forgive me since I'm new (this week) to AngularJS.
I've got an input which is a checkbox like below and it's hooked up to an ng-change event.
<input type="checkbox" ng-model="vm.hasCondoKey" ng-change="vm.onKeyCheckboxChange()" />
That^ event fires and runs the function "onKeyCheckBoxChange" in the controller.
export class CondoKeyController {
public condoKey: AdditionalItemModel;
public additionalItems: AdditionalItemModel[];
public hasCondoKey: boolean = false;
static $inject = ["$scope","CondoKeyService"];
constructor($scope: any, CondoKeyService: ICondoKeyService) {
$scope.additionalItems.forEach((addOn: AdditionalItemModel, index: number, addOnArray: AdditionalItemModel[]) => {
if (addOn.typeId === Models.AdditionalItemType.CONDO_KEY && addOn.quantity > 0) {
this.condoKey = addOn;
this.hasCondoKey = true;
}
});
}
public onKeyCheckboxChange(): void {
console.log("Checkbox changed.");
if(this.hasCondoKey === true) {
this.condoKey.typeId = AdditionalItemType.CONDO_KEY;
this.condoKey.quantity = 1;
if(!this.addOnsContainCondoKey()) {
this.additionalItems.push(_this.condoKey);
}
} else {
this.condoKey.quantity = 0;
}
}
}
The "CondoKeyController" is nested in a parent which passes the array "additionalItems" to this controller via a directive. I can see these other variables in other functions and when constructing the controller so they make it into this controller fine.
My problem is that in the function "onKeyCheckBoxChange" I can access "this.hasCondoKey" but I cannot access any of the other values such as "this.condoKey" or "this.additionalItems".
I was thinking that this had to do with the scope and context of my function since it came from a checkbox event but then I reasoned that I should not have access to the "this.hasCondoKey" value. This value reads as "true" from my breakpoints so it's been changed from its initialization value.
Does anyone know why I can access some variables and not others? Also how do I access the other variables?
Angularjs 1.6.6
Typescript ~2.3.1
Add some debug helping log to the method:
public onKeyCheckboxChange(): void {
console.log("Checkbox changed.");
console.log(this.condoKey);
console.log(this.additionalItems);
// ...
}
Check the result. It could be possible that condoKey and additionalItems are undefined, meaning they are never set.
According to https://github.com/jquery/jquery/blob/9434e03193c45d51bbd063a0edd1a07a6178d33f/src/event.js#L21-L27
There are two functions in event.js in jquery that return true and false:
from events.js
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
I know they are good. But I don't understand the reasoning for this.
Did you look and see where they are used?
They are used as a stubs for assignments that need a function that returns a boolean.
For example on line 670 of the same document:
this.isDefaultPrevented = returnTrue;
isDefaultPrevented is a function. Thus it needs a function that returns true as the default functionality.
Consider their usage:
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = src.defaultPrevented ||
// Support: Android < 4.0
src.defaultPrevented === undefined &&
src.getPreventDefault && src.getPreventDefault() ?
returnTrue :
returnFalse;
Do this or something similar across several functions. It's easier to type returnTrue, than have to spell out function() { return true; } every time, isn't it? Code reuse and readability.
I'm having trouble adding proper exception handling to existing code that makes heavy use of Silverlight - JavaScript interoperability. In this case, my JavaScript can throw an exception that I want to handle meaningfully in Silverlight.
From Silverlight, I'm creating an instance of a JavaScript object, then later I'm calling a method on that object:
public class MyWrapper
{
dynamic _myJSObject;
public MyWrapper()
{
_myJSObject = HtmlPage.Window.CreateInstance("MyJSObject");
}
public int MyMethod()
{
try
{
int result = (int)_myJSObject.MyMethod();
}
catch (Exception ex)
{
// I want to add meaningful exception handling here
}
}
}
Whenever MyJSObject.MyMethod throws an exception, there are two problems:
The browser shows a message that an exception has occurred.
Information about the exception is not passed to my managed code. Instead I get a RuntimeBinderException which just says "Cannot invoke a non-delegate type" and contains no other information whatsoever. This does not seem to match what is described here; I'd expect an InvalidOperationException.
I've tried avoiding to cast the returned value of the method:
object tmp= _myJSObject.MyMethod();
This makes no difference. Changing the type of exception thrown on the JavaScript side has no effect either.
MyJSObject.prototype.MyMethod = function ()
{
throw "Hello Silverlight!";
}
The only solution I can think of right now is abusing the function's return value to pass information about the exception, but that will make my code a whole lot uglier... so:
Why is the behavior I'm seeing different from what is described in documentation? Does it have to do with my use of dynamic somehow? How can I properly handle exceptions that occur in JavaScript in my managed code?
After quite a bit of experimentation, I concluded that there is no way to directly handle the JavaScript exception from Silverlight. In order to be able to process the exception, the JavaScript code needs to be changed slightly.
Instead of throwing the error, I return it:
function MyMethod()
{
try
{
// Possible exception here
}
catch (ex)
{
return new Error(ex);
}
}
Then on the Silverlight side, I use a wrapper around ScriptObject to turn the return value into an exception again. The key here is the TryInvokeMember method:
public class ScriptObjectWrapper : DynamicObject
{
private ScriptObject _scriptObject;
public ScriptObjectWrapper(ScriptObject scriptObject)
{
_scriptObject = scriptObject;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = _scriptObject.Invoke(binder.Name, args);
ScriptObject s = result as ScriptObject;
if (s != null)
{
// The JavaScript Error object defines name and message properties.
string name = s.GetProperty("name") as string;
string message = s.GetProperty("message") as string;
if (name != null && message != null && name.EndsWith("Error"))
{
// Customize this to throw a more specific exception type
// that also exposed the name property.
throw new Exception(message);
}
}
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
try
{
_scriptObject.SetProperty(binder.Name, value);
return true;
}
catch
{
return false;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
try
{
result = _scriptObject.GetProperty(binder.Name);
return true;
}
catch
{
result = null;
return false;
}
}
}
Potentially you could improve this wrapper so it actually injects the JavaScript try-catch mechanism transparently, however in my case I had direct control over the JavaScript source code, so there was no need to do this.
Instead of using the built in JavaScript Error object, it's possible to use your custom objects, as long as the name property ends with Error.
To use the wrapper, the original code would change to:
public MyWrapper()
{
_myJSObject = new ScriptObjectWrapper(
HtmlPage.Window.CreateInstance("MyJSObject"));
}