The
Variant data type provides a flexible general purpose data type. It can hold basically anything. You can use variants when interacting with native javascript objects or external libraries.
We can also use variants in
external classes. The External classes are the glue between Smart Pascal and the javascript world. It's important to say these external classes are case sensitive, in fact, the external code is only interfaced – this is not a real class, but an external class. This means that we only need the definition while the implementation is done by the external JavaScript code.
The Variants are treated as
"unknown" by the smart pascal codegen. Variants are primarily used to transport native values and objects between methods dealing with javascript objects or browser objects. So Smart Mobile Studio compiler deals with variants as unknown values. Use it with care - there are potentials for run time errors and poor code clarity when using variants. For instance, the compiler does not check if a method of an object actually exists.
In this example, I'm going to define an external global variable. Note that, external variables must be
declared in global scope. We're going to use
this in global context. The
this keyword behaves differently in JavaScript compared to other language. In Object Oriented languages, like
smart pascal, the
this keyword
refers to the current instance of the class (aka
Self).
In
JavaScript the value of this is determined mostly by the
invocation context of function and where it is called. Se, we can interface with native javascript objects with object pascal and create powerful components.
We can declare this variable using two ways:
{ using external variable }
var this external "this" : variant;
{ or using external function }
function this: variant; external 'this' property;
then we can use this external variable, such as:
procedure HandleCallback(a: variant);
begin
console.log(a);
end;
procedure InvokeByVariant;
var mSrc: variant;
begin
mSrc := this.document;
(* invoke our method *)
HandleCallback(mSrc);
end;
Let's suppose we have a method which looks like:
procedure HandleCallback(data: variant);
begin
//
end;
In the sample code, it remains unclear the argument
data. I'd call them a better name because that gives you more of a chance of understanding at a glance what your code means. You can customize it, so the method looks like this now:
type
TJSONData = Variant;
procedure HandleCallback(data: TJSONData);
begin
//
end;
Literal object using the keyword Class
|
I'd like to create a JS literal object in smart pascal, like this:
var object1 = {
"child" : {
"Callback" : function () {
console.log(this);
return this;
}
}
};
As has been said: variants are treated as "unknown" by the smart pascal codegen. The smart compiler does no check if a method f.i. actually exists! Use it with care.
var object1 := CLASS
that: variant = class external;
child = class
Callback := function(): variant
begin
console.log(this);
Result := this;
end;
end;
END;
procedure CallMe(unknown: variant);
begin
unknown.Name := "MyName";
unknown.Age := 102;
unknown.Callback(unknown);
end;
CallMe(Object1.child);
CallMe(CLASS
Callback := lambda
console.log('this is a test');
end;
END);
JS Emitted:
function CallMe(unknown) {
unknown.Name = "MyName";
unknown.Age = 102;
unknown.Callback(unknown);
};
var object1 = {
"child" : {
"Callback" : function () {
var Result = undefined;
console.log(this);
Result = this;
return Result
}
}
,"that" : {
}
};
CallMe(object1.child);
CallMe({
"Callback" : function () {
console.log("this is a test");
}
});
Array of Array of Integer
|
Now I'd like an object such as:
{"values":[[0,100],[1000,200],[2000,300],[3000,400],[4000,500]] }
I've got weird result using this approach:
var preloaded = CLASS
values : array [0..4] of array [0..1] of Integer = [ [0, 100],[1000, 200],[2000, 300],[3000, 400],[4000, 500] ];
END;
console.log(JSON.stringify(preloaded));
{"values":[[4000,500],[1000,200],[2000,300],[3000,400],[4000,500]]}
an easy workaround would be defining a local variable like this:
var valor : array [0..4] of array [0..1] of Integer = [ [0, 100],[1000, 200],[2000, 300],[3000, 400],[4000, 500] ];
var preloaded2 = CLASS
values := valor;
END;
console.log(JSON.stringify(preloaded2));
{"values":[[0,100],[1000,200],[2000,300],[3000,400],[4000,500]]}
Now I would like to create a JSON object like this:
[{"id":1,"name":"Item#1"},{"id":2,"name":"Item#2"},{"id":3,"name":"Item#3"},{"id":4,"name":"Item#4"},{"id":5,"name":"Item#5"},{"id":6,"name":"Item#6"},{"id":7,"name":"Item#7"},{"id":8,"name":"Item#8"},{"id":9,"name":"Item#9"},{"id":10,"name":"Item#10"}]
var obj :=
CLASS
that: variant = class external;
child = class
Callback := function(): variant
begin
Result := this;
end;
end;
"JSONArray1": array of variant;
"JSONArray2": array of variant;
"JSONArray3": array of variant;
"JSArray": array of variant;
"name": string = "obj";
"f" : TFunc = function(): variant
begin
result := this.that;
end;
END;
for var x:=1 to 10 do
begin
obj.JSONArray1.add(CLASS
name := "Item#" + IntToStr(x);
id := x;
END);
end;
...and a simple javascript array list like:
["Item#1", "Item#2", "Item#3", "Item#4", "Item#5", "Item#6", "Item#7", "Item#8", "Item#9", "Item#10"]
for var x:=1 to 10 do
begin
obj.JSArray.add("Item#" + IntToStr(x));
end;
Playing more, with smart pascal, how about to create this weird array of array of objects?
[[{"age":1,"nome":"Nome#1"},{"cep":"35700-001","address":"Addr#1"},{"country":"Country 1","city":"City#1"}],[{"age":2,"nome":"Nome#2"},{"cep":"35700-002","address":"Addr#2"},{"country":"Country 2","city":"City#2"}],[{"age":3,"nome":"Nome#3"},{"cep":"35700-003","address":"Addr#3"},{"country":"Country 3","city":"City#3"}],[{"age":4,"nome":"Nome#4"},{"cep":"35700-004","address":"Addr#4"},{"country":"Country 4","city":"City#4"}],[{"age":5,"nome":"Nome#5"},{"cep":"35700-005","address":"Addr#5"},{"country":"Country 5","city":"City#5"}],[{"age":6,"nome":"Nome#6"},{"cep":"35700-006","address":"Addr#6"},{"country":"Country 6","city":"City#6"}],[{"age":7,"nome":"Nome#7"},{"cep":"35700-007","address":"Addr#7"},{"country":"Country 7","city":"City#7"}],[{"age":8,"nome":"Nome#8"},{"cep":"35700-008","address":"Addr#8"},{"country":"Country 8","city":"City#8"}],[{"age":9,"nome":"Nome#9"},{"cep":"35700-009","address":"Addr#9"},{"country":"Country 9","city":"City#9"}],[{"age":10,"nome":"Nome#10"},{"cep":"35700-0010","address":"Addr#10"},{"country":"Country 10","city":"City#10"}]]
In my experiments,
for var x:=1 to 10 do
begin
var objeto :=
[CLASS
nome : string = "Nome#" + IntToStr(x);
age : integer = + x;
END,
CLASS
address: string = "Addr#" + IntToStr(x);
cep : variant = '35700-00'+ IntToStr(x);
END,
CLASS
city : string = "City#" + IntToStr(x);
country: string = "Country " + IntToStr(x);
END];
obj.jsonarray2.push(objeto);
end;
The codegen generated weird
pusha javascript instruction. I had to replace to
push to get working.
var obj = null,
x$32 = 0;
var objeto = [null,null,null];
/// anonymous TClassSymbol
/// anonymous TClassSymbol
TApplication.InitApp(Self);
obj = {
"that" : {
}
,"name" : "obj"
,"JSONArray3" : []
,"JSONArray2" : []
,"JSONArray1" : []
,"JSArray" : []
,"f" : function () {
var Result = undefined;
Result = this.that;
return Result
}
,"child" : {
"Callback" : function () {
var Result = undefined;
Result = this;
return Result
}
}
};
for(x$32=1;x$32<=10;x$32++) {
/// anonymous TClassSymbol
/// anonymous TClassSymbol
/// anonymous TClassSymbol
objeto = [{
"age" : x$32
,"nome" : "Nome#"+x$32.toString()
}, {
"cep" : "35700-00"+x$32.toString()
,"address" : "Addr#"+x$32.toString()
}, {
"country" : "Country "+x$32.toString()
,"city" : "City#"+x$32.toString()
}];
obj.JSONArray2.pusha(objeto.slice(0));
}
It would be handy in SMS, in this instance, to have a kind of TDataModule - collection of data stores. How to use in-memory data stores in Smart. This TDataModule should have agnostic functions like findById, findByName, etc..
I ended up spending a decent amount of time trying different things to get this TW3DataSet as in memory dataStore. To save some headaches, I decided to create my own data store, it’s ugly, unfinished, I find it more flexible to use it through the units. I don't know if the right stack, anyway, stay tuned.