I understand there are a few similar questions (e.g. here), but they all seem to be in different context. This is what I see in my chrome console:
Unhandled rejection reasons (should be empty):
["TypeError: Cannot read property 'compound' of null…://localhost:1476/Scripts/breeze.debug.js:234:15)", joinBy: function, equals: function, indexByKey: function, getByKey: function, sortOn: function]
I could see this error in my debugger at failed(error):
return manager.executeQuery(query).then(succeeded).fail(failed);
function failed(error) {
logger.logError(error);
}
For a specific object, it happens most of time, but no always (maybe one out of 10 is okay). The query from server returns without exception. As I figured, it seems to be related to the query with a specific include of table. However, in fact, in this case, the included table doesn't have any related entry yet (if there is an entry, it doesn't seem to have any problem). Any idea?
Okay, I accidentally found out what has caused this error. It is metadatastore post-construction initializer like
manager.metadataStore.registerEntityTypeCtor("Result", Result, initialize);
// constructor
function Result() {
}
// post-construction initializer
function initialize(result) {
result.cmpName = result.cs.compound.name;
}
where result.cs could be null sometimes.
Too bad the error message didn't provide any clue.
Related
I have some code like this:
File("foo.txt").readAsString().catchError((e)=>print(e));
The compiler is complaining
info: The return type 'void' isn't assignable to 'FutureOr<T>', as required by 'Future.catchError'.
I can't seem to give it what it wants and can't find a single clear usage example in any of the docs (just a long issue in git about how many ways there are to mis-use this). If I take the docs at face value, I should be able to return a bool, or a future, neither make the analyzer happy.
How do I provide this FutureOr?
The documentation for Future.catchError could be a lot clearer, but the relevant part is:
onError is called with the error and possibly stack trace, and the returned future is completed with the result of this call in exactly the same way as for then's onError.
Cross-referencing to the documentation for Future.then, the relevant portion is:
The onError callback must return a value or future that can be used to complete the returned future, so it must be something assignable to FutureOr<R>.
Since File.readAsString returns a Future<String>, your catchError callback also must return a Future<String>. Examples of doing that:
File("foo.txt").readAsString().catchError((e) {
print(e);
return Future.value('');
});
File("foo.txt").readAsString().catchError((e) async {
print(e);
return '';
});
Logically, this makes sense; because given:
String value = await File("foo.txt").readAsString().catchError(...);
then if readAsString succeeds, value should be assigned a String. If it fails, since you catch the exception without rethrowing it, value still needs to be assigned a String.
Put another way, your code is equivalent to:
Future<String> readFoo() async {
try {
return await File("foo.txt").readAsString();
} catch (e) {
print(e);
}
// Oops, missing return value.
}
In general, I strongly recommend using async/await with try-catch instead of using .catchError, which would avoid this confusion.
I have tried to use #BindUsing for a property (amt100) of a domain class (TcTransaction). The purpose is to convert a display format to a database format. Out of a dozen variants, here is one:
#BindUsing({tcTransaction, source ->
def result = tcTransaction.amt100
def amt = Amount100.of(source['amt100']) //parse and convert input
if (amt) {
result = amt.cents
} else {
tcTransaction.errors.rejectValue('amt100', 'tcTransaction.invalid.amount')
}
return result
})
Integer amt100
It works beautifully in the absence of errors. The problem is error management. This particular version returns the original value if the new value is invalid. It also adds an error to the domain object.
It seems adding to errors has no effect. The user is not warned.
I also tried to throw an exception. It seems whatever exception you throw you end up with a standard error code saying that the property must not be left empty. A previous post indicated this behaviour. (It has gone unanswered for two years.) I'm on Grails 3.2.8.
So, is there any theory of error management in #BindUsing? It's such a nifty mechanism.
When I define a spinner in ScalaJS and handle the spin value I am not able to get the new spin value in the event as I would have expected. According to the JQuery UI documentation the second parameter to the spin event is the ui object that contains a value attribute. So I defined a trait:
trait Number extends js.Object {
val value: Int = js.native
}
And then handle my spin event thus:
jQuery("#mySpinner").spinner(js.Dynamic.literal(spin = { (e: HTMLInputElement, ui: Number) =>
log("Change: " + ui.value)
}: js.ThisFunction1[HTMLInputElement, Number, Any]))
But the "value" attribute does not seem to be a member of the ui object as I get the exception below in my log statement. Can someone tell me what I am doing wrong?
uncaught exception: scala.scalajs.runtime.UndefinedBehaviorError: An
undefined behavior was detected: undefined is not an instance of
java.lang.Integer
You say e: HTMLInputElement but it should be e: Event
I suspect the problem is a combination of the previous comments. You are correct that, since you're using ThisFunction, the first element should be an Element of some sort. (Although, is it really an HTMLInputElement? That's a slightly unusual element type to put a spinner on.)
But that Element gets prepended to the function parameters, whereas you've got it replacing one.
In other words, you have
(e: HTMLInputElement, ui: Number)
but it needs to be
(elem: HTMLInputElement, e:Event, ui: Number)
in order to match the expected signature. So in practice, the system is trying to cast the value member of an Event, which of course doesn't exist, to Integer. It finds that value is undefined, tries to cast it to Integer, and boom.
I can't say I'm 100% certain (and IMO that ui parameter is just plain weird to begin with -- I'm a little suspicious of the jQueryUI documentation there), but that's my guess. Try fixing the signature of your call, and see if the error goes away...
A friend's query was failing. Fortunately, he was catching it in his fail callback (you DO have a fail callback for every server call, right?). Here's kind of what he had:
var getPersons = function(personsObservable) {
return EntityQuery.from('Person')
.using(manager).execute()
.then(querySucceeded).fail(queryFailed);
}
function queryFailed(error) {
var msg = 'Error retreiving data. ' + error.message;
logError(msg, error);
throw error;
}
The error.message simply showed the JSON data ... which looked a bit like this:
"[{"$id":"1","$type":"Person, ProjectName","Id":12,"FirstName":"Bob","LastName":"Smith","Email":"bs#contoso.com","Blog":"http://bs.contoso.com","Twitter": ..."
WAT?
He examined the error.XHR which provides the full AJAX XHR object used for this query. He could see that the HTTP Status Code was a 200 ... meaning that everything was cool from the server. The fact that he had real data pretty much said the same thing.
So why is Breeze failing? How does he diagnose the problem?
Breeze might be failing. But there's a good chance that the problem lies elsewhere. Usually if Breeze fails, there is a meaningful error message. This error message is not meaningful. But it does provide clues.
Check your success callback first
The fail callback can be invoked (1) if the operation fails or (2) if the success callback fails. If the operation fails, you've got a Breeze-related problem. If the success callback fails, you probably have an application code problem.
To determine which, put a breakpoint on the first line of the success callback (in his case, the first line of querySucceeded). If you hit the breakpoint, you know Breeze has done its bit and has handed off to you. Step through your callback to find the mistakes which are most likely yours and, therefore, easy to fix.
Check your custom EntityType constructors and initializers
In his case it did not get to the success callback. So something went wrong as Breeze tried to make cached entities out of the JSON data from the server. What could that be?
There are many potential causes. Could be a Breeze bug. Always best, though, to eliminate pilot error first. Did you write a custom constructor or initializer for this EntityType?
He did. He had an initializer that added a fullName calculated property to his Person. It looked sort of like this:
metadataStore.registerEntityTypeCtor('Person', null, personInitializer);
function personInitializer(person) {
person.fullName = ko.computed(function () {
return entity.firstName() + ' ' + person.lastName();
});
}
He didn't see a problem. But following diagnostic procedure, he put a breakpoint on the initializer.
Sure enough ... he had a typo ...
// "entity" does not exist. Null object error
return entity.firstName() + ' ' + person.lastName();
As soon as he changed entity to person, all was well.
I can't explain at the moment why the null object reference manifested as a Q promise fail error with the JSON Person data in the message. Strange stuff happens in JavaScript. But the clues were there:
server delivered the data
failed before getting to the success callback
data are about Person
have a Person initializer (or constructor)
Read the clues and you'll know where to look.
Hope this tip saves you from gray hair and a bald head.
In a controller I have this finder
User.findByEmail('test#test.com')
And works.
Works even if I write
User.findByEmail(null)
But if i write
User.findByEmail(session.email)
and session.email is not defined (ergo is null) it throw exception
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
Is this behavior right?
If i evaluate "session.email" it give me null so I think it must work as it do when I write
User.findByEmail(null)
Even more strange....
If I run this code in groovy console:
import myapp.User
User.findByEmail(null)
It return a user that has null email but if I run the same code a second time it return
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
You can't use standard findBySomething dynamic finders to search for null values, you need to use the findBySomethingIsNull version instead. Try
def user = (session.email ? User.findByEmail(session.email)
: User.findByEmailIsNull())
Note that even if User.findByEmail(null) worked correctly every time, it would not necessarily give you the correct results on all databases as a findBySomething(null) would translate to
WHERE something = null
in the underlying SQL query, and according to the SQL spec null is not equal to anything else (not even to null). You have to use something is null in SQL to match null values, which is what findBySomethingIsNull() translates to.
You could write a static utility method in the User class to gather this check into one place
public static User byOptEmail(val) {
if(val == null) {
return User.findByEmailIsNull()
}
User.findByEmail(val)
}
and then use User.byOptEmail(session.email) in your controllers.
Jeff Brown from grails nabble forum has identified my problem. It's a GORM bug. see jira
More info on this thread
This jira too
I tried with debugger and it looks it should be working, as you write. Maybe the groovy itself is a little bit confused here, try to help it this way:
User.findByEmail( session['email'] )