The domain looks like
class EventDonation implements Serializable{
String title
String body
Integer customDonationMin
Integer customDonationMax
EntityStatus status
List denominations
static hasMany = [denominations: Integer]
}
Please note that the denominations is a list of Integers. Now during binding
List<String> whiteList = ['title', 'body', 'customDonationMin', 'customDonationMax', 'denominations']
bindData(don, params, ['include': whiteList])
if(params.status){
don.status = EntityStatus.ACTIVE
}
else{
don.status = EntityStatus.INACTIVE
}
don.validate()
if(don.hasErrors()){
render(view: "editdonation", model: [id:id, donation:don])
return
}
The following error is displayed
Message: java.lang.String cannot be cast to java.lang.Integer
In the view page there are 4 textboxes for entering denominations. I was thinking that the strings i.e '10', '20', '30', '40' will automatically be converted to integers during binding. I think the error is associated with binding this list. I appreciate any help. Thanks!
Interesting thing is when i change the type of the list to String as follows:
class EventDonation implements Serializable{
String title
String body
Integer customDonationMin
Integer customDonationMax
EntityStatus status
List denominations
static hasMany = [denominations: String]
}
The following binding works fine i.e no error is thrown.
List<String> whiteList = ['title', 'body', 'customDonationMin', 'customDonationMax', 'denominations']
bindData(don, params, ['include': whiteList])
if(params.status){
don.status = EntityStatus.ACTIVE
}
else{
don.status = EntityStatus.INACTIVE
}
don.validate()
if(don.hasErrors()){
render(view: "editdonation", model: [id:id, donation:don])
return
}
So, i am confident that the type Integer for the list is the source of the problem. Since denominations are integers i cannot think of any other type. I appreciate any help on this. Thank you!
Related
Is there any difference between .toString and as String in Dart?
toString() is a method on Object and is therefore available on every object. The method is used to get a string representation of the object:
A string representation of this object.
Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string represetion.
Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.
https://api.dart.dev/stable/2.13.4/dart-core/Object/toString.html
as String is a typecast in Dart and is used to tell the analyzer/compiler that whatever it assumes, you are now going to tell it that your object is in fact a String at runtime. You can hereafter use the object like a String.
But the compiler will add a check at runtime and if the object is not compatible with the interface of String, your application will crash because you have lied to the compiler.
It is therefore two entire different things and is used for different purposes. You can e.g. not use as String on an object which is not already a String.
The safest you can do is just call toString() since toString() on String will just return itself.
They are completely different!
.toString() is a method to represent data of a object but as String is a type cast which tries to convert the object itself to a String .
Imagine you have a class named Person
class Person {
String firstName;
String lastName;
Person(
this.firstName,
this.lastName,
);
}
now casting person to a String will lead to an _CastError error since Person is not a String or a subtype of String(inherited classes from String class)
final person = Person('sajad', 'abd');
final personAsString = person as String;
Meanwhile, the method .toString() will represent you object in a String.
final person = Person('sajad', 'abd');
final personToString = person.toString();
print(personToString); // result: Instance of 'Person'
.toString() is defined for every class in dart and you can override it in you custom classes
for example you can override it in Person class to represent firstname and lastname of person
class Person {
String firstName;
String lastName;
Person(
this.firstName,
this.lastName,
);
#override
String toString() => 'Person(firstName: $firstName, lastName: $lastName)';
}
And now the result of
final person = Person('sajad', 'abd');
final personToString = person.toString();
print(personToString);
would be
Person(firstName: sajad, lastName: abd)
I've created my class in Dart this way, but I'm getting the Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. I would like to know if there's a way to do it in a 'Python' style where this kind of class creation is possible, thank you in advance.
class Lexer {
String _text;
int _pos;
String _current_char;
Lexer(String text) {
this._text = text;
this._pos = -1;
this._current_char = '';
this.advance();
}
void advance() {
this._pos++;
this._current_char = this._pos < this._text.length ? this._text[this._pos] : '';
}
}
class Lexer {
String _text;
int _pos;
String _current_char;
This declares several members with type String. Since they are declared as String and not as String?, these members are non-nullable; they are not allowed to ever be null. (This is part of the new null-safety feature from Dart 2.12.)
Dart initializes objects in two phases. When the constructor's body runs, Dart expects all member variables to already be initialized. Because your members are non-nullable and haven't been initialized to non-null values yet, this is an error. The error message explains what you can do:
Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Use initializer expressions. This means using an initializer list:
Lexer(String text)
: _text = text,
_pos = -1,
_current_char = '' {
advance();
}
Note that if you're initializing members with a construction parameter of the same name, you can use shorthand:
Lexer(this._text)
: _pos = -1,
_current_char = '' {
advance();
}
Adding field initializers. This means initializing members inline in the class declaration.
class Lexer {
String _text = '';
int _pos = -1,
String _current_char = '';
Marking your members as late. This means that you promise that the variables will be initialized before anything attempts to use them.
class Lexer {
late String _text;
late int _pos,
late String _current_char;
Making your members nullable, which allows them to be implicitly null by default:
class Lexer {
String? _text;
int? _pos,
String? _current_char;
However, that will require that all accesses explicitly check that the members aren't null before using them.
You also might want to read: Dart assigning to variable right away or in constructor?
Assuming I have this Dart class:
class Stock {
int id;
String externalCode;
String internalCode;
String name;
double quantity;
}
When I create a new instance of this object like Stock item = new Stock(); all the properties are null.
I know this is a Dart specific behavior, but when sending such objects to an API, since most backend languages like C#, Java etc. don't have nullable primitives exceptions occur when parsing to a corresponding model class.
What is the simplest approach to prevent int, double and bool properties of being null (set them to 0, 0.0 and false respectively) when instantiating a Dart class?
Since many classes might have a lot of properties, a hardwired instantiation like Stock item = new Stock(id: 0, quantity: 0 /*...and so on... */); it's out of the question.
Many thanks!
If you want a default value for members in a class you can just assign each member to a value in the class definition:
class Stock {
int id = 0;
String externalCode = "";
String internalCode = "";
String name = "";
double quantity = 0.0;
}
Alternative, you can also give default values to optional parameters like:
class Stock {
int id;
String externalCode;
String internalCode;
String name;
double quantity;
Stock(
{this.id = 0,
this.externalCode = '',
this.internalCode = '',
this.name = '',
this.quantity = 0.0});
}
When I have the following code:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah";
The compiler complains that "A value must be mutable in order to mutate the contents". However when I remove the Struct attribute it works fine. Why is that so ?
This protects you from a gotcha that used to plague the C# world a few years back: structs are passed by value.
Note that the red squiggly (if you're in IDE) appears not under FirstName, but under john. The compiler complains not about changing the value of john.FirstName, but about changing the value of john itself.
With non-structs, there is an important distinction between the reference and the referenced object:
Both the reference and the object itself can be mutable. So that you can either mutate the reference (i.e. make it point to a different object), or you can mutate the object (i.e. change the contents of its fields).
With structs, however, this distinction does not exist, because there is no reference:
This means that when you mutate john.FirstName, you also mutate john itself. They are one and the same.
Therefore, in order to perform this mutation, you need to declare john itself as mutable too:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let mutable john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah" // <-- works fine now
For further illustration, try this in C#:
struct Person
{
public string FirstName;
public string LastName;
}
class SomeClass
{
public Person Person { get; } = new Person { FirstName = "John", LastName = "Smith" };
}
class Program
{
static void Main( string[] args )
{
var c = new SomeClass();
c.Person.FirstName = "Jack";
}
}
The IDE will helpfully underline c.Person and tell you that you "Cannot modify the return value of 'SomeClass.Person' because it is not a variable".
Why is that? Every time you write c.Person, that is translated into calling the property getter, which is just like another method that returns you a Person. But because Person is passed by value, that returned Person is going to be a different Person every time. The getter cannot return you references to the same object, because there can be no references to a struct. And therefore, any changes you make to this returned value will not be reflected in the original Person that lives inside SomeClass.
Before this helpful compiler error existed, a lot of people would do this:
c.Person.FirstName = "Jack"; // Why the F doesn't it change? Must be compiler bug!
I clearly remember answering this question almost daily. Those were the days! :-)
What is the best practice in Dart when dealing with classes as data records?
To Elaborate: When writing an app, it is likely that a class for a table row will be created. As in
class Item { int itemid, String itemName, double score }
Item item = new Item();
This allows compile time catching of any typos etc. in Dart. (Unlike using a class that relies on NoSuchMethod.)
It will also need a corresponding string structure to bind to the HTML such as
<input id="itemname" type="text" bind-value="itemEdit.itemName">
So the Dart would be:
class ItemEdit { String itemId, String itemName, String score }
ItemEdit itemEdit = new ItemEdit();
Next we need a way to get from one to the other, so we add a method to Item
fromStrings(ItemEdit ie) {
itemid = ie.itemId == null ? null : int.parse(ie.itemId);
itemName = ie.ItemName;
score = ie.score == null ? null : double.parse(ie.score);
}
And the other way around:
toStrings(ItemEdit ie) {
ie.itemid = itemId == null ? '' : ie.itemId.toString();
ie. itemName = itemName == null ? '' : itemname; // Web_ui objects to nulls
ie.score = score == null ? null : score.toStringAsFixed(2);
}
Also, we get jason data from a database, so we need to add another method to Item:
fromJson(final String j) {
Map v = JSON.parse(j);
itemid = v['itemid'];
itemname = v['itemname'];
score = v['score'];
}
And we need to be able to revert to default values:
setDefaults() { itemId = 0; itemName = "New item"; score = 0; }
This verbosity gets me feeling like I am writing COBOL again!
There is something fundamental missing here - either in my understanding, or in the Dart/WebUI libraries.
What I would like to write is something like
class Item extends DataRecord {
int itemid = 0,
String itemName = 'New item',
double score = 0.0;
}
Then, without further coding, to be able to write code such as
item.toStrings();
...
item.fromStrings();
...
item.fromJson(json);
...
item.setDefaults(); // results in {0,'New item',0.0}
And to be able to write in the HTML:
value="{{item.strings.score}}"
If this was possible, it would be quicker, simpler, clearer, and less error prone than the code I am writing at the moment.
(Full disclosure, this answer is written with the assumption that at least one bug will be fixed. See below)
Three suggestions that might help.
Use named constructors to parse and create objects.
Take advantage of toJson() when encoding to JSON.
Use bind-value-as-number from Web UI
1) Named constructors
import 'dart:json' as json;
class Item {
int itemid;
String itemName;
double score;
Item.fromJson(String json) {
Map data = json.parse(json);
itemid = data['itemid'];
itemName = data['itemName'];
score = data['score'];
}
}
2) Encoding to JSON
The dart:json library has a stringify function to turn an object into a JSON string. If the algorithm encounters an object that is not a string, null, number, boolean, or collection of those, it will call toJson() on that object and expect something that is JSON-encodable.
class Item {
int itemid;
String itemName;
double score;
Map toJson() {
return {'itemid':itemid, 'itemName':itemName, 'score':score};
}
}
3) Now, having said that, sounds like you want to easily bind to HTML fields and get primitive values back, not just strings. Try this:
<input type="number" min="1" bind-value-as-number="myInt" />
(Note, there seems to be a bug with this functionality. See https://github.com/dart-lang/web-ui/issues/317)
(from https://groups.google.com/a/dartlang.org/forum/#!topic/web-ui/8JEAA5OxJOc)
Just found a way to perhaps help a little in the this situation:
class obj {
int gapX;
String get gapXStr => gapX.toString();
set gapXStr(String s) => gapX = int.Parse(s);
...
Now, from the HTML you can use, for example
bind-value="gapXStr"
and in code you can use
x += ob.gapX;