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;
Related
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});
}
I have a case in which I am iterating the List<DiscountClass> and need to compare the list value with another List<TypeCode>, based on satisfying the condition (when Discount.code equals TypeCode.code) I need to set Discount.setCodeDescr(). How to achieve this with nested forEach loop in java 8? (I am not able to set after comparing the values in java 8 forEach).
for (Discount dis : discountList) {
for (TypeCode code : typeCodeList) {
if (dis.getCode().equals(code.getCode())) {
dis.setCodeDesc(code.getCodeDesc());
}
}
}
A possible solution using java 8 lambdas could look like this:
discountList.forEach(dis -> {
typeCodeList
.stream()
.filter(code -> dis.getCode().equals(code.getCode()))
.findAny()
.ifPresent(code -> dis.setCodeDesc(code.getCodeDesc()));
});
For each discount you filter the TypeCodes according to the code and if you find any you set the desc poperty to the one of the found TypeCode.
The other answer showed how to convert a nested loop to a nested functional loop.
But instead of iterating over a list of TypeCode, it's better to use a HashMap to get random access, or an enum like this:
public enum TypeCode {
CODE_1("description of code 1"),
CODE_2("description of code 2");
private String desc;
TypeCode(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
public class Discount {
private String typeCode; //assuming you can't have the type as TypeCode
private String desc;
public Discount(String typeCode) {
this.typeCode = typeCode;
}
//getters/setters
}
Then your code will change to:
Discount d1 = new Discount("CODE_1");
Discount d2 = new Discount("CODE_2");
List<Discount> discounts = List.of(d1, d2);
discounts.forEach(discount ->
discount.setDesc(TypeCode.valueOf(discount.getTypeCode()).getDesc()));
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! :-)
Is there a generic way to retrieve PropertyInfo based on a string value alone, when deeper than one level.
I assume this is probably simple enough, but my search results are only as good as my search criteria, and I think I am having an issue articulating the proper keywords to get search results for what I am after.
I would like to be able to do something like the following (which works perfect if the key is for a direct property / one level - ie key = 'firstName'):
public static PropertyInfo (this HtmlHelper htmlHelper, string key) {
PropertyInfo pInfo = htmlHelper.ViewData.Model.GetType().GetProperty(key);
return pInfo;
}
But is there a way for me to return the PropertyInfo based on a string alone
when Key equals something more complex, such as nested classes, objects, lists, etc...:
key = "somelist[0].myproperty"
key = "Items[0].someotherlist[1].someproperty" (where Items is defined as List<Item> Items {get; set;}, someotherlist is defined similarly)
Can the method be generic enough to essentially drill down as many levels as needed (defined)?
So here is what I came up with... this is about to get wordy, and mostly 'stream of thought'
I have custom HtmlHelperExtension, and within it :
PropertyInfo[] pInfoArray = htmlHelper.ViewData.Model.GetType().GetProperties();
PropertyInfo pInfo = GetPropertyInfo(pInfoArray, key);
This GetPropertyInfo() method takes the key, and the PropertyInfo array, cycles through the properties, until the keypart (using regex to remove any indication of an array from the string, so I am left with only the property) matches the property name. On Match, determine if this is the first cycle in the loop, and if so assign the matched property to my Temp Type and PropertyInfo variables. If keyParts are remaining to loop through, subsequent loops now use previously set temp variables and the for loop index [i] to iterate / drill down the class structure. Each time setting the pInfoTemp variable, and then pTypeTemp so the next loop can use where it left off.
private static PropertyInfo GetPropertyInfo(PropertyInfo[] pInfoArray, string key)
{
PropertyInfo pInfo = null;
string[] keyParts = key.Split('.');
Regex arrayRgx = new Regex("\\[\\d*\\]");
PropertyInfo pInfoTemp = null;
Type pTypeTemp = null;
foreach (PropertyInfo prop in pInfoArray)
{
string keyPartsTrimmed = arrayRgx.Replace(keyParts[0], ""); // removes '[#]' from string
if (keyPartsTrimmed == prop.Name) // match property name
{
for (int i = 0; i < keyParts.Count(); i++)
{
if (i == 0) // initial item [0]
{
pTypeTemp = prop.PropertyType; // gets [0]'s type
pInfoTemp = prop; // assigns [0]'s property info
}
else
{
pInfoTemp = GetNestedPropertyInfo(pTypeTemp, arrayRgx.Replace(keyParts[i], "")); // gets [i]'s property info for return or next iteration
pTypeTemp = pInfoTemp.PropertyType; // gets [i]'s type for next iteration
}
}
pInfo = pInfoTemp;
break;
}
}
return pInfo;
}
This next method is invoked by the previous for grabbing nested property info, more importantly for detecting whether the passedItemType is a List (without this, it fails to work correctly as it is unable to find the property asked for in a List<> Type. I need to know what the List item Type is.
private static PropertyInfo GetNestedPropertyInfo(Type passedItemType, string passedProperty)
{
PropertyInfo pInfoOut = null;
if (passedItemType.IsGenericType && passedItemType.GetGenericTypeDefinition() == typeof(List<>))
{
Type itemType = passedItemType.GetGenericArguments()[0];
pInfoOut = itemType.GetProperty(passedProperty);
}
else
{
pInfoOut = passedItemType.GetProperty(passedProperty);
}
return pInfoOut;
}
This currently suits my requirements as they are today, and I have tested it with the following properties, lists, subclasses, subclasses with lists, etc.. to 4 levels deep, but should function properly no matter the depth:
firstName
lastName
Items[1].sprocket
subClass.subClassInt
subClass.mySubClassObj.sprocketObj
subClass.ItemsInMySubClass[1].sprocket
subClass.ItemsInMySubClass[0].mySubClassObj.widgetObj
subClass.ItemsInMySubClass[2].mySubClassObj.sprocketObj
If anyone has a better solution, or see any potential issues with what I have, I welcome the feedback.
The best way in your case is to make a parser that split that expression.
I'm getting this error:
ex = {"The binary operator Equal is not defined for the types 'MySite.Domain.DomainModel.EntityFramework.NickName' and 'System.Int32'."}
What I tried to do was do a select all where the NickNameId = someIntPassedIn... the problem is that the NickNameId is a foreign key, so when it compares the someIntPassedIn to the NickNameId it pulls the whole NickName object that the NickNameId refers to and tries to compare the int to that object.
I need a solution here to allow it to compare the int to the NickName object's Id... so
A) How can I define the binary operator Equal for comparing these two objects
OR
B) How can I compare it directly to the id instead of the whole object?
You don't have to read this, but here's the SelectAllByKey method incase it helps: (I passed in "NickNameId" and "1")
public IList<E> SelectAllByKey(string columnName, string key)
{
KeyProperty = columnName;
int id;
Expression rightExpr = null;
if (int.TryParse(key, out id))
{
rightExpr = Expression.Constant(id);
}
else
{
rightExpr = Expression.Constant(key);
}
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
int temp;
BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr = Expression.Lambda<Func<E, bool>>(binaryExpr, new ParameterExpression[] { xParam });
//Searching ....
IList<E> resultCollection = ((IRepository<E, C>)this).SelectAll(new Specification<E>(lambdaExpr));
if (null != resultCollection && resultCollection.Count() > 0)
{
//return valid single result
return resultCollection;
}//end if
return null;
}
Let me know if you need any more info.
Thanks,
Matt
You should call SelectAllByKey('NickName.ID','1').
Since ID is property of property, you could use this extension method:
public static MemberExpression PropertyOfProperty(this Expression expr,string propertyName)
{
var properties = propertyName.Split('.');
MemberExpression expression = null;
foreach (var property in properties)
{
if (expression == null)
expression = Expression.Property(expr, property);
else
expression = Expression.Property(expression, property);
}
return expression;
}
The accepted answer seems way too complicated for the problem at hand, if I'm reading this correctly.
If I understand you correctly, you're trying to run a query like:
var q = from e in Context.SomeEntities
where e.NickNameId == someIntPassedIn
select e;
...but this won't work, because e.NickNameId is an entity, not an integer.
To reference the Id property, you can just refer to it, like this:
var q = from e in Context.SomeEntities
where e.NickNameId.Id == someIntPassedIn
select e;
Update: If you can't use strong-typed properties due to your level of abstraction (per your comment), then use query builder methods:
var q = (ObjectQuery<T>)Repository.SelectSomething();
return q.Where("it.NickName.Id = " + someIntPassedIn.ToString());
You can adapt this as you see fit, but the general point is that the EF already knows how to translate strings to property members.