Quantcast
Channel: TMS Software
Viewing all 1006 articles
Browse latest View live

JSON backstage pass for TMS WebCore developers

$
0
0

TMS Software Delphi  Components

Guest post about web wizardry with TMS WEB Core

With pleasure, we present today a new guest post. We might go as far as saying that it might change your experience with dealing with JSON in TMS WEB Core apps as Object Pascal developer radically. This wizardry is presented today in this blog by Martin Suer. Martin was already present and enthusiast about TMS WEB Core when we first started showing the product early 2018. Since then, Martin initially started working and doing projects with TMS WEB Core for Delphi and for some time now, he and his colleagues at SymProject GmbH are doing web projects with TMS WEB Core for Visual Studio Code (btw, there is a webinar if you want to learn about it on June 3).
I give the word to Martin. Bookmark this article, it will make your TMS WEB Core application code so much better!


Introduction

Every web frontend developer sooner or later has the pleasure of processing or creating JavaScript objects. In today’s world of web services and microservice architectures, results of function calls are often represented in JSON. To access this as a TMS WEB Core developer, the RTL packages JS and WEBLib.JSON contain many useful classes and functions. With the help of these packages it is possible to convert JSON into JavaScript objects and vice versa, to access single attributes of a JavaScript object or to create JavaScript objects. Dr. Holger Flick has described in his book "TMS WEB Core" in a whole chapter how these RTL packages can be used. (Teaser: The book is worth every cent) Today we will look a little behind the scene and learn about new possibilities beyond that, where we will use the advantages of TMS WEB Core to access JavaScript objects very elegantly and thus combine the advantages of both worlds. As object Pascal developers we are used to work with static type safety. Our data structures contain attributes where the compiler detects errors before runtime. If we try to assign a number to a variable of type string it will be detected by the compiler upfront. In the JavaScript world this is not possible. This gives a JavaScript developer more freedom and flexibility in using variables but also a bigger potential for errors if typos or assumptions about the types of values contained in a variable are incorrect. There errors occur only at runtime. No problem at all, you might think. We use TMS WEB Core and are type safe. But if we access JavaScript objects, we leave the nice guys and visit the dark side…


2. First things first

In the previous section we used the terms JSON and JavaScript object. But what is it exactly and what are the differences? In JavaScript, a variable can contain a simple value as well as an object or an array.

// JavaScript
let i = 5;
let s = 'some text';
let o = {
  a = 'text',
  b = i
}

In this example the variable o contains a JavaScript object with the two attributes a and b where a contains a text and b contains a number.

In TMS WEB Core there is a type JSVALUE for the value of a variable from the JavaScript world. Whenever we want to access the value of a JavaScript variable, we can use the JSVALUE type for it.

// WEBCore

function sampleJavascriptVariable : JSVALUE; assembler;
asm
  let i = 5;
  return i;
end;

procedure AccessJsvalue;
var
  javascriptvalue: JSVALUE;
  i: integer;

begin
  javascriptvalue := sampleJavascriptVariable;
  // value will now contain the JavaScript VALUE (hence JSVALUE) 5
  i := integer(javascriptvalue);
  // we needed to typecast javascriptvalue to integer because the compiler
  // doesn't know the type of the value actually stored in that variable
end;

So whenever we want to access JavaScript values directly from TMS WEB Core, we need a typecast to tell the compiler which type the value in the variable with the type JSVALUE contains. This is already cumbersome with simple types and also error-prone and it gets even more complex when we want to access JavaScript objects.

Let’s summarize: A JavaScript object is an object in the JavaScript world that is stored in an internal representation in a JavaScript variable. To store such a JavaScript object in an Object Pascal variable in TMS WEB Core, the object pascal type JSVALUE is available.

And what has all this to do with JSON ?

JSON (JavaScript Object Notation) is a string that represents a JavaScript object. A JavaScript object can be converted to JSON and a JSON string can be converted to a JavaScript object. More information about the syntax definition of JSON can be found here: https://www.json.org/json-en.html.

Whenever we talk about JSON, we mean a string from an Object Pascal point of view. Whenever we talk about a JavaScript object we mean a JSVALUE from an Object Pascal point of view.

// WEBCore

var
  json: String;
  javascriptobject: JSValue;

begin
  json := '{ "x" : 5, "y" : 7 }';
  javascriptobject := TJSJSON.parse(json);
end;

In this example the JSON variable contains a JSON representation (data type String) of an object with the members x and y, both containing numbers. This string is parsed into a JavaScript object using the static method TJSJSON.parse() and stored in the variable javascriptobject.

Note: All sample sources use only the units that are already automatically included in the uses list of a standard TMS WEB Core web project. So you can try and reproduce everything directly in TMS WEB Core when you include the source texts in the unit1.pas of a newly created project. To keep the example source code short, only necessary code is shown, in real world code you would need to add some exception handling here and there…

All well and good, now we have a JavaScript object in an Object Pascal JSVALUE variable in TMS WEB Core. But how to access it and the individual elements?


3. Practical uses

This is where the backstage pass comes in handy: backstage we learn what the pas2js compiler actually does with the TMS WEB Core source code when our source code is compiled.

type
  TCoordinate = record
    x: double;
    y: double;
  end;

var
  javascriptobject: JSValue;
  json: string;
  coordinate: TCoordinate;

begin
  json := '{ "x" : 5, "y" : 7 }';
  javascriptobject := TJSJSON.parse(json);
  coordinate := TCoordinate(javascriptobject);
  ShowMessage(Format('The coordinate is (%f,%f).',[coordinate.x, coordinate.y]));
end;

Backstage information: The pas2js compiler creates JavaScript code from our Object Pascal source code and finally there is (almost) no difference between a record and a JavaScript object. A record is a JavaScript object in the resulting JavaScript code.

As in the previous example with the typecast from JSVALUE to integer, we can tell the compiler with a typecast from JSVALUE to TCoordinate that we expect an object in the javascriptobject variable that corresponds to the structure of the TCoordinate record type we defined.

In the following ShowMessage() we can then access the values coordinate.x and coordinate.y and it even works with auto code completion.

Let that sink in.

If we tell the compiler what the structure of the JavaScript object looks like by typecasting JSVALUE to an arbitrary record type, we only need one typecast and can then continue programming exactly as if the variable would contain an Object Pascal record. Code completion works and error detection already at compile time. No more cumbersome single accesses with potential typos.

What can we do with this new bag of tricks now?

To clarify this and to understand the following sample code let’s have a quick look at the service https://jsonplaceholder.typicode.com. Here we can retrieve various JSON object representations of sample data through a simple HTTPRequest and use them for testing purposes.

For example, the call https://jsonplaceholder.typicode.com/users/1 returns the following object as JSON:

{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
  "street": "Kulas Light",
  "suite": "Apt. 556",
  "city": "Gwenborough",
  "zipcode": "92998-3874",
  "geo": {
    "lat": "-37.3159",
    "lng": "81.1496"
    }
  },
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
  "name": "Romaguera-Crona",
  "catchPhrase": "Multi-layered client-server neural-net",
  "bs": "harness real-time e-markets"
  }
}

Accessing this data with our new knowledge is now pretty simple:

procedure TMainForm.BtnFetchUserClick(Sender: TObject);
type
  TUser = record
    id: integer;
    name: string;
    email: string;
  end;

var
  request: TJSXMLHttpRequest;
  json: string;
  javascriptobject: JSValue;
  user: TUser;

begin
  WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/1';
  request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
  json := string(request.response);
  javascriptobject := TJSJSON.parse(json);
  user := TUser(javascriptobject);
  ShowMessage(Format('User %d''s name is %s. You can reach him/her at %s', [user.id, user.name, user.email]));
end;

Note: For this code example to work, we first place a non-visual component TWebHttpRequest on the main form and change its name to WebHttpRequest in the Object Inspector. Additionally, in the declaration of the form it is necessary to mark the BtnFetchUserClick method with the [async] attribute:

type
  TMainForm = class(TWebForm)
    WebHTTPRequest: TWebHTTPRequest;
    [async] 
    procedure BtnFetchUserClick(Sender: TObject);
  end;

In this example we have declared a record type TUser with the fields id, name and email. Using the WebHttpRequest component, we fetch the JSON for a user from the typicode.com web service and store the result in a JSON string variable. Then we convert the obtained JSON to a javascriptobject using TJSJSON.parse(json) and cast it to our TUser type and store it in the user variable.

The variables user and javascriptobject then contain the same values. Once as type TUser and once as JSVALUE. But both reference the same object in memory.

Using the variable user we can now access the individual members of the user and display them in the ShowMessage() method as in this example. When entering the code we are supported by the editor with Code Completion.

The question arises what happened to the rest of the values of the JavaScript object for which we did not define any members in our TUser type declaration?

Well, they are still there, we just can’t access them with our TUser type because the compiler doesn’t know about their existence. With our TUser type declaration we have not changed any data. We only told the compiler how we imagine the representation of the JavaScript object and how we want to access it. A typecast does not result in additional JavaScript code. It is merely information for the compiler that enables it to perform static type checks at compile time.

If we want to access more details of the JavaScript object from the example, we can simply extend our TUser type and get full access to all details:

procedure TMainForm.BtnFetchComplexUserClick(Sender: TObject);
type
  TGeo = record
    lat, lng: string;
  end;

  TAddress = record
    street, suite, city, zipcode: string;
    geo: TGeo;
  end;

  TCompany = record
    name, catchPhrase, bs: string;
  end;

  TUser = record
    id: integer;
    name: string;
    email: string;
    address: TAddress;
    company: TCompany;
    nonExistentField: string;
  end;

var
  request: TJSXMLHttpRequest;
  json: string;
  javascriptobject: JSValue;
  user: TUser;
  doesOrDoesNot, msg: string;

begin
  WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/3';
  request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
  json := string(request.response);

  javascriptobject := TJSJSON.parse(json);

  user := TUser(javascriptobject);

  if isDefined(user.nonExistentField) then
    doesOrDoesNot := 'does'
  else
    doesOrDoesNot := 'does not';

  ShowMessage( Format('%s lives in %s (Geolocation [%s,%s]) and works for %s. '
    + 'The field "nonExistentField" %s exist.',
    [ user.name, user.address.city, user.address.geo.lat, user.address.geo.lng, user.company.name, doesOrDoesNot ]) );

  ShowMessage('Value of user.nonExistentField is ' + user.nonExistentField);
end;

In the extended example we use a modified TUser type that contains all the details of the JSON supplied by typicode.com and even defines an additional field.

The rest of the example code is the same for now and in the ShowMessage() we can access all the details of the user, including the sub objects. Like such as using user.address.geo.lat to access the coordinate of the address.

Since we have defined a field nonExistendField in our record type, which does not occur in the JSON string at all, the typecast itself won’t be a problem. However, we would get a runtime error if we access such field in the program code assuming that the field exists and contains a valid value. The good news is: with the isDefined function, it is possible to check for the presence of a field value.

Again, no additional code is generated with our typecast. It only tells the compiler which structure of the JavaScript object we expect and how we want to access it. A JavaScript object is a JavaScript object and it is still a JavaScript object after the typecast. BUT we can use it after the typecast as if it were an Object Pascal record.

Of course, this can go wrong if the structure of the JavaScript object does not match the type definition. Is that a problem? It depends…

You usually know what data you expect from a service or in an object. If the assumptions are not correct, you may get a runtime error, but it can be dealt with with exception handling.

Is this an additional new problem I’m introducing with TypeCasts? No, the same problem exists in the pure JavaScript world. (But there it exists even without typecasts) There too, as a developer, you make assumptions about the contents of my data structures. There too, a possible runtime error has to be handled. In the JavaScript world, however, there is no compiler that helps you out with type checks.


4. Going even further

As we have seen, a JavaScript object is easy to use in TMS WEB Core if we have defined a suitable record type for it.

The pas2js compiler (or better transpiler) converts Object Pascal to JavaScript and records become JavaScript objects. But not only that, Object Pascal arrays become JavaScript arrays. Better said: Records are JavaScript objects, Arrays are JavaScript arrays. And since JavaScript arrays are just a special form of JavaScript objects we can take advantage of this and access JavaScript arrays in the same way we access JavaScript objects.

This is illustrated by the following example, where we retrieve a whole list of users via https://jsonplaceholder.typicode.com/users. The corresponding code looks like this:

procedure TMainForm.BtnFetchMultipleUsersClick(Sender: TObject);
type
  TUser = record
    id: integer;
    name: string;
    email: string;
  end;

  TUserArray = array of TUser;

var
  request: TJSXMLHttpRequest;
  json: String;
  javascriptarray: JSValue;
  users: TUserArray;
  user: TUser;

begin
  WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users';
  request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
  json := string(request.response);
  javascriptarray := TJSJSON.parse(json);
  users := TUserArray(javascriptarray);

  user := users[4];

  ShowMessage(Format('Received %d users. User %d''s name is %s. You can reach him/her at %s',
   [Length(users), user.id, user.name, user.email]));
end;

Here we convert the result of the web request using a type cast into a standard array and can thus access all contained user records of the array in a type-safe manner as shown in the ShowMessage() method.


5. The other way round

In the previous examples we always cast JavaScript objects into records to be able to access JavaScript objects easily and comfortably from TMS WEB Core. Often we need to go the other way. If we need to pass more complex data structures to web services, we need to serialize records or objects to pass them to JavaScript functions. Our new principle works fine in this direction as well. Wherever a JSVALUE is expected, we can easily pass a record and this time we don’t even need a typecast, because the receiving side (JavaScript) only knows universal variables, in which arbitrary values or objects can be stored.

Here is a small example:

function AddAToB(value : JSValue): string; assembler;
asm
  value.result = value.a + value.b;
  value.s = 'The sum is ' + value.result;
  return JSON.stringify(value);
end;

procedure TMainForm.BtnTheOtherWayRoundClick(Sender: TObject);
type
  TSampleRecord = record
    a, b, c, result : double;
    s : string;
  end;

var
  sample: TSampleRecord;
  json: string;

begin
  sample.a := 4.5;
  sample.b := 3.2;
  json := AddAToB(sample);

  ShowMessage (json);
end;

In this example, we use the assembler directive to define a JavaScript function AddAToB() that expects a JavaScript object as a parameter. This function does not know the type of the passed object, but it makes assumptions about it in its code (welcome to the dark side). And so it performs an addition of the values value.a and value.b and stores the result in value.result. Also, the result is stored as a string in value.s and the entire value object in JSON representation is returned as the result.

To call this dark side function AddAToB(), we define a TSampleRecord with the values a and b, which we assign numbers to and then use that record as parameter for the function call.

Because of the type declaration, we as callers are not able to preset the values a and b with strings. The type check of the compiler will prevent that.

We can get another interesting information from the function result: The member c is contained in the result and has the value 0. Although the function AddAToB neither knows about the existence of c nor has set the value. This is because we passed our record as a parameter to the function and there the c is defined and during the initialization of the record in the Object Pascal world all members were preset with default values.

So if we use records for values passed to JavaScript functions, we can’t forget to create necessary members in the code, because they are always pre-allocated by the record usage.

We hope that this blog article gave you some interesting insights into the backstage of JSON and JavaScript objects in the TMS WEB Core concert.

Author: Martin Suer

Symproject GmbH https://www.symproject.com





Keep up with the latest developments!

$
0
0

TMS Software Delphi  Components

Everything evolves so incredibly fast in the software world!
So our team is also trying really hard to keep up with the latest developments. And to give our customers access to all our latest developments we have created our no-nonsense subscription TMS ALL-ACCESS! Because we want our customers to be able to use all our products with no restrictions or limitations.
And when we say ALL we really mean ALL our products we offer now and also all new products we'll release in the year of the subscription. Moreover, our all-access users are the first to receive the previews and betas of our non-released products!

Here is a small overview of the new products we added in 2021:

January 2021:

  • TMS VCL UI Pack v10.4: Powerful, extensive & flexible component suite for native Excel report & file generation & manipulation for VCL & FireMonkey
  • TMS FNC Cloud Pack v1.3: Seamless access to cloud services from Windows, cross-platform and the web
  • TMS WEB Core v1.6: Framework for creating modern web applications

February 2021:

  • TMS FNC Maps v1.3: Hold the full power of mapping in your hand! Cross-framework, cross-platform, cross-service mapping component library
  • TMS Web Academy launch

March 2021: 

April 2021: 

May 2021: 



Get full access with: 

  • Easy plan: All current and new products for 1 price!
  • Easy renewal: Yearly renewal only for 495 EUR!
  • Easy install: all products accessible via TMS Subscription Manager tool
  • Easy support: Full online support center access to all areas
  • Easy up-to-date: Early access to product betas
  • Easy learning: Access to TMS WEB Academy


Act now:

So what are you waiting for?

Find out more about our products included in TMS ALL-ACCESS.

NOTE: Contact sales for special upgrading pricing for existing customers of other products.



Another webinar with another first ...

$
0
0

TMS Software Delphi  Components

If any of these technologies are of interest to you, we have a new webinar coming up with another first!
Reserve your first class seat now while it can here

We will show you for the first time in public, the latest state of project Miletus in TMS WEB Core

To give you a hint, we only show today this screenshot:

TMS Software Delphi  Components

First time you hear about the word Miletus? Read up here and then reserve your seat! 

TMS Software Delphi  Components
With your registration, you'll see & discover it first-hand in our online webinar running in our own fully web-based TMS Web Academy platform developed here using TMS WEB Core for Visual Studio.


We look forward to meet you in the webinar and discuss via the platform the many new capabilities that open up for Delphi developers.  See you!




Discover our wide range of TMS FNC Components

$
0
0


TMS Software Delphi  Components

FNC stands for Framework Neutral Components. This means that the components can be used cross-framework and cross-platform. With our TMS FNC Component studio bundle you will get access to all our current FNC components!

That is not all! You will save over 45% now and even more with new free FNC component releases!

All products come with perpetual licenses and 1 year of FREE updates & support. Subscriptions can be extended for another year at 70% discount!

Products included in the bundle:


    Get full access to FNC components:

    • Components for Delphi & C++Builder that can be used in all project types.
    • Only one learning curve: VCL, FMX, LCL and WEB.
    • Target Windows, macOS, iOS, Android, Linux and WEB.
    • Growing range of components, new products on the way.
    • Yearly renewal only 145 EUR* instead of regular 495 EUR.
    • All products accessible via TMS Subscription Manager tool.
    • Full online support through TMS Support Center.

    *The price is valid for this moment & depends on the price of the pack at the moment of renewal.


    Learn More:

    Want to learn more about FNC and how you can benefit from it? Join us on our upcoming webinar: "What is FNC : Introduction to the growing family of FNC technology based components", on June 22, 2021.


    Act now:

    Find out more about our products included in TMS FNC Component Studio and get your license today!

    NOTE: Contact sales for special upgrading pricing for existing customers of other products.



    TMS WEB Core & FNC trial support

    $
    0
    0

    A small update with a big impact

    We have recently changed the way the trial version is being built and now works together with other TMS WEB Core enabled products. Evaluating a product before considering a purchase is important and therefore we have bundled all of our resources and investigated a solution for the ongoing issues trying out the combination of trial FNC & trial TMS WEB Core. Today we can announce there are no issues compiling FNC trial & TMS WEB core trial together. Enjoy and test the trial version of the following products in combination with TMS WEB Core trial today!


    In case you didn't notice

    This week is also FNC week. Please read the following blog post to know more about FNC & FNC Studio and the benefits it has to offer: https://tmssoftware.com/site/blog.asp?post=806

    TMS Software Delphi  Components




    Google Maps heat maps

    $
    0
    0

    We recently received a question whether TMS FNC Maps supports heat maps. Heat maps are supported in Google Maps and we immediately went exploring the capabilities upon this request. More info and a sample about heat maps can be found here: https://developers.google.com/maps/documentation/javascript/examples/layer-heatmapTMS FNC Maps comes with a lot of great functionality as well as a (lesser visible) customization events and methods to add your own extensions. To add support for heat maps, we had to override 2 virtuals and we can do this by inheriting from the TTMSFNCGoogleMaps class.


    Customizing the API URL

    TTMSFNCMaps (and TTMSFNCGoogleMaps), expose a lot of virtuals to extend and customize existing or new functionality. To support heat maps, we needed to add an extra parameter to the URL that is required to load the Google Maps JavaScript API. This can be done by overriding the GetHeadLinks procedure.

    procedure GetHeadLinks(AList: TTMSFNCMapsLinksList; ACheckReady: Boolean = True); override;
    Additionally, we need to change the link to include an additional parameter: "libraries=visualization" to enable the library required to load the heat map layer.

    procedure TTMSFNCGoogleMapsEx.GetHeadLinks(AList: TTMSFNCMapsLinksList;
      ACheckReady: Boolean);
    begin
      AList.Add(TTMSFNCMapsLink.CreateScript('https://maps.googleapis.com/maps/api/js?key=' + MapsProperties.GetAPIKey + '&language=' +
        MapsProperties.GetLocale + '&libraries=visualization', 'text/javascript', 'utf-8', '', True, True));
    end;


    Adding additional JavaScript functionality

    After extending the API URL to load the required libraries we need to add custom JavaScript to visualize the heat map. According to the sample (following the link at the top), we need to add code to visualize heat maps in San Francisco. To add custom JavaScript functionality, override the DoCustomizeMap virtual.

    procedure DoCustomizeMap(var ACustomizeMap: string); override;

    The code specified in the sample translated to Delphi code is shown below.

    procedure TTMSFNCGoogleMapsEx.DoCustomizeMap(var ACustomizeMap: string);
    var
      h: string;
    begin
      h := 'var heatMapData = [' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.447), weight: 0.5},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.445),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.443), weight: 2},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.441), weight: 3},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.439), weight: 2},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.437),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.435), weight: 0.5},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.447), weight: 3},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.445), weight: 2},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.443),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.441), weight: 0.5},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.439),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.437), weight: 2},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.435), weight: 3}' + LB +
      '];' + LB +
    
      'var heatmap = new ' + MAPSERVICEVAR + '.visualization.HeatmapLayer({' + LB +
      '  data: heatMapData' + LB +
      '});' + LB +
    
      'heatmap.setMap(' + MAPVAR + ');';
    
      ACustomizeMap := h;
    
      inherited;
    end;

    Bundling this all together produces the following result.

    TMS Software Delphi  Components


    The complete code snippet (written in FMX, but can be used in any framework)

    unit UGoogleMapsHeatMap;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TMSFNCGoogleMaps, FMX.TMSFNCMaps;
    
    type
      TTMSFNCGoogleMapsEx = class(TTMSFNCGoogleMaps)
      protected
        procedure GetHeadLinks(AList: TTMSFNCMapsLinksList; ACheckReady: Boolean = True); override;
        procedure DoCustomizeMap(var ACustomizeMap: string); override;
      end;
    
      TFormHeatMap = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        g: TTMSFNCGoogleMapsEx;
        { Private declarations }
        procedure DoMapInitialized(Sender: TObject);
      public
        { Public declarations }
      end;
    
    var
      FormHeatMap: TFormHeatMap;
    
    implementation
    
    uses
      FMX.TMSFNCMaps.GoogleMaps, FMX.TMSFNCMapsCommonTypes;
    
    {$R *.fmx}
    
    procedure TFormHeatMap.DoMapInitialized(Sender: TObject);
    begin
      g.SetCenterCoordinate(CreateCoordinate(37.774546, -122.433523)); //San Francisco
    end;
    
    procedure TFormHeatMap.FormCreate(Sender: TObject);
    begin
      g := TTMSFNCGoogleMapsEx.Create(Self);
      g.Parent := Self;
      g.APIKey := MY API KEY;
      g.OnMapInitialized := DoMapInitialized;
    end;
    
    { TTMSFNCGoogleMapsEx }
    
    procedure TTMSFNCGoogleMapsEx.DoCustomizeMap(var ACustomizeMap: string);
    var
      h: string;
    begin
      h := 'var heatMapData = [' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.447), weight: 0.5},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.445),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.443), weight: 2},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.441), weight: 3},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.439), weight: 2},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.437),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.435), weight: 0.5},' + LB +
    
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.447), weight: 3},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.445), weight: 2},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.443),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.441), weight: 0.5},' + LB +
      'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.439),' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.437), weight: 2},' + LB +
      '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.435), weight: 3}' + LB +
      '];' + LB +
    
      'var heatmap = new ' + MAPSERVICEVAR + '.visualization.HeatmapLayer({' + LB +
      '  data: heatMapData' + LB +
      '});' + LB +
      'heatmap.setMap(' + MAPVAR + ');';
    
      ACustomizeMap := h;
    
      inherited;
    end;
    
    procedure TTMSFNCGoogleMapsEx.GetHeadLinks(AList: TTMSFNCMapsLinksList;
      ACheckReady: Boolean);
    begin
      AList.Add(TTMSFNCMapsLink.CreateScript('https://maps.googleapis.com/maps/api/js?key=' + MapsProperties.GetAPIKey + '&language=' +
        MapsProperties.GetLocale + '&libraries=visualization', 'text/javascript', 'utf-8', '', True, True));
    end;
    
    end.

    Explore!

    Want to rebuild this sample? Download TMS FNC Maps today. Already have TMS FNC Maps installed? There are no additional requirements, the above code can be applied as-is on the TTMSFNCGoogleMaps class.




    One subscription for all current VCL components!

    $
    0
    0

    TMS Software Delphi  Components

    TMS VCL Subscription is our two year subscription to all our current & future VCL products for Windows application development.

    With this bundle you will save over 60% now and even more with new free VCL component releases! This bundle includes many unique products like TMS VCL UI Pack with over 600 VCL  components, grids, flexcel, charts, ...

    All products come with perpetual licenses and 2 years of FREE updates & support. After 2 years the subscription can be extended at 50% discount!

    Products included in the bundle:

    • TMS Logging
    • TMS VCL UI Pack
    • TMS VCL Cloud Pack
    • TMS VCL Chart
    • TMS VCL WebGMaps
    • TMS PassKit
    • TMS FlexCel Component Suite for VCL
    • TMS Scripter
    • TMS Query Studio
    • TMS Unicode Component Pack
    • TMS GUIMotions
    • TMS Async
    • TMS VCL Plugin Framework
    • TMS VCL Security System
    • TMS VCL Instrumentation Workshop
    • TMS Workflow Studio
    • TMS Diagram Studio
    • TMS MultiTouch SDK


    Get full access to VCL components:

    • Best bundle for Delphi / C++Builder VCL Win32 or Win64 application developer.
    •  2 years free updates & free support.
    •  Renewal after 2 years at 50% discount instead of regular 795 EUR*.
    • Always current with latest Delphi & C++Builder releases.
    • All products accessible via TMS Subscription Manager tool.
    • Full online support through TMS Support Center.


    *The price is valid for this moment & depends on the price of the pack at the moment of renewal.


    Act now:

    Find out more about our products included in TMS VCL Subscription and get your license today!

    NOTE: Contact sales for special upgrading pricing for existing customers of other products.



    TMS WEB Core for Visual Studio Code v1.3 released

    $
    0
    0

    Today is an exciting day as we reached yet another milestone, this time the release of TMS WEB Core for Visual Studio Code v1.3.

    You are an Object Pascal expert, your customers look out to access your applications everywhere via the web and without any install hassle, you love the RAD component based development methodology, you can do this software development from the operating system you love ... then having a look at TMS WEB Core for Visual Studio Code v1.3 will open a whole new world!

    TMS Software Delphi  Components  What brings TMS WEB Core for Visual Studio Code


    • Fully Object Pascal based web development
    • RAD component methodology driven fast & productive development 
    • Hosted in a free hugely popular IDE for web developers
    • Running on Windows, macOS and Linux
    • Using a WYSIWYG form designer
    • A framework that has a similarities with the familiar VCL framework where possible

    TMS Software Delphi  Components  What is on the table in v1.3


    This major update is packed with significant new features:

    Support for packages
    Yes, from now on, it is easy to create your own custom components and compile & install these for use at design-time. The package system is similar to how you can use packages to install components in the Delphi IDE or Lazarus

    Support for workspaces
    With workspaces, now you can have multiple projects in a folder and activate and select which project to compile/run/debug.

    Support for using the entire FNC component suite
    As our entire set of FNC components is ready for use in web client applications, the new package support in TMS WEB Core for Visual Studio Code enables to install all TMS FNC components in the TMS WEB Core for Visual Studio Code IDE. The latest FNC registered version updates released last week contain all the required packages for this.

    Brings the framework up-to-date with latest version as available in TMS WEB Core for Delphi/Lazarus v1.7
    The TMS WEB Core framework that comes with TMS WEB Core for Visual Studio Code is now in sync and up-to-date with the latest release of TMS WEB Core for Delphi. This means that all new components and component improvements are now also available for Visual Studio Code users. This includes the new USB components, local file access components, new Firestore dataset capabilities, extensions to grids, web socket connection improvements and much more...  One important note though, the Miletus application type is not yet in our release for Visual Studio Code release but scheduled for the next release.


    TMS Software Delphi  Components  Get started!


    There are several ways to get started with TMS WEB Core for Visual Studio Code

    • TMS ALL-ACCESS users have immediate & free access to TMS WEB Core for Visual Studio Code
    • TMS WEB Core for Visual Studio Code registered users will find the new release now on your "My Products" page
    • A fully functional trial version for Windows, macOS or Linux is available
    • We have a webinar coming up that will guide you around the IDE, the framework, the possibilities
    • Check more details about FNC and using FNC components from Visual Studio code in this blog

    TMS Software Delphi  Components  Learn how to use packages, write your own components or use FNC components

    We have already two videos for you and more are coming.

    Chief architect José Leon Serna explains how to start using packages:


    José Leon Serna and Bruno Fierens host a webinar giving a sneak preview of v1.3


    TMS Software Delphi  Components Get in touch

    We have our Support Center to help you getting started, contact our sales team to assist in any licensing queries, our let us know what you think about TMS WEB Core for Visual Studio Code and in what direction you wish to see things being developed further! We are eager to hear your feedback and further shape the product to your needs & wishes!






    Custom control development with TMS WEB Core for Visual Studio Code, an intern reports

    $
    0
    0

    TMS Software Delphi  Components
    At tmssoftware.com we love to have interns. I believe it is a fantastic experience to learn young aspiring software developers about Delphi, its eco-system, its amazing productivity and at the same time, absorb a lot of energy, get new fresh ideas and get to know what drives and motivates the next generation of software developers.
    So, also this school year, we had 2 interns learning all about Delphi, TMS WEB Core, Object Pascal, components and much more here at tmssoftware.com.
    Today, we want to give the word to our intern, and very soon colleague, Bradley who did a lot of stunning work this school year and tells in his first blog post about building his first custom control for TMS WEB Core. Nothing but an amazing and exciting story! So, with pleasure, I give the word to Bradley:

    A first custom web control

    Having only one year experience using Delphi and TMS WEB Core, I was assigned the (at first sight) daunting task of developing my first ever custom web control, namely a chat box control. I had only been developing web applications since I joined tmssoftware.com as an intern, so clearly writing a component was something completely new. Using the available documentation and the experience of my mentor Bruno I quickly obtained the necessary skills for developing a TMS WEB Core custom control, the TWebChatBox control.
    TMS Software Delphi  Components

    First steps

    To gain some experience, I first developed another very small control, a custom toggle button control to switch a web application between full-screen and normal mode. I went for developing the custom control directly from TMS WEB Core for Visual Studio Code because I just feel more comfortable for web projects to use this IDE. I had the "privilege" to use first the alpha and then the beta version of TMS WEB Core for Visual Studio Code v1.3 (released yesterday btw) as this introduced component package support. Using the new package support for installing custom controls made it very easy to test the control, also at design-time. The development of this first small control allowed me to get used to the concept of custom controls as well as the steps involved to get it installed in the IDE.

    Starting the chatbox implementation

    After I was comfortable enough with the principle of custom controls. I’ve started building the chat box component. As there were a ton of features on the requirements list, I had actually no idea where to start. I’ve focused first of all on a working way of sending/receiving messages. When I had that going, I started focusing on adding a basic design, followed up by adding the necessary properties for manipulating said chat box. 
    I think implementing collections as a way of managing messages was the only thing I really struggled with. Having never implemented anything like this, figuring out how to implement the chat message bubbles at collection item level was a challenge. If you add a message to your collection, a chat message bubble will be generated and displayed in the chat control. The collection item will also keep a reference of the chat message bubble so that you can manipulate it later on.
    A chat bubble contains several HTML elements. We have a SPAN for the message. For the user avatar there is an IMG element. And both the sender name and time are in P elements. The other elements are mostly used for positioning all the elements. The IMG element will not be shown when you choose to disable user images.

    FChatMessage := TJSHTMLElement(document.createElement('SPAN'));
    FMessageInfo := TJSHTMLElement(document.createElement('DIV'));
    FTimeStamp := TJSHTMLElement(document.createElement('P'));
    FChatSender := TJSHTMLElement(document.createElement('P'));
    FImageArea := TJSHTMLElement(document.createElement('DIV'));
    FMessageArea := TJSHTMLElement(document.createElement('DIV'));
    FImage := TJSHTMLElement(document.createElement('IMG'));
    These chat bubbles are, after being made, displayed on the chat display which is nothing more than a flexbox DIV. 
    Underneath the chat display we have our input area. This area contains several elements like an input for the message, a send button and an emoji picker.
    FArea := TJSHTMLElement(document.createElement('DIV'));
    FInputDisplay := TJSHTMLElement(document.createElement('DIV'));
    FInputTextBox := TJSHtmlElement(document.createElement('TEXTAREA'));
    FSendButton := TJSHTMLElement(document.createElement('BUTTON'));
    FInputDisplay.appendChild(FInputTextBox);
    FArea.appendChild(FInputDisplay);
    FArea.appendChild(FSendButton);
    ElementHandle.appendChild(FArea);
    FSendButton.addEventListener('click', @HandleSendMessageButtonClick);
    FInputTextBox.addEventListener('keyup', @HandleSendMessageInputEnter);
    There are 2 event listeners. You can press the send button for sending messages. But pressing enter will also send your message. When sending/receiving messages the chat box will auto scroll with all messages except if you scroll up.
    Another feature that the chat box has, is an auto scroll. when you send a message the chat box will automatically scroll down as long as the user hasn’t scrolled up. To check if a user has scrolled up is nothing more than using this function:
    function TChatBox.isScrolledToBottom: Boolean;
    begin
      Result := (FChatDisplay.scrollHeight – 
    FChatDisplay.clientHeight <= FChatDisplay.scrollTop + 1);
    end;
    To effectively scroll down you use this procedure:
    procedure TChatBox.scrollToBottom;
    begin
      FChatDisplay.scrollTop := FChatDisplay.scrollHeight;
    end;
    The Emoji Picker is an external JavaScript library. The emoji picker is added by instantiating the EmojiPicker class and then use the generateElements function and giving the input textbox as a parameter.    
     asm 
       (()=> {
         var picker = new EmojiPicker();
         picker.generateElements(element);
       })();
     end;
    
    This library will add a dropdown button linked to your input textbox and will allow you to choose an emoji.

    TMS Software Delphi  Components

    Typing some of the mostly used emoji shortcuts in the textbox will also change to an emoji when sending the message. The same applies for hyperlinks that are converted into clickable links in the chat bubble.

    Conclusion

    While web custom controls appear like complex magic black boxes at first sight, after some effort to learn their architecture and how to build them mapping to HTML elements, it turns out to be actually relatively straight-forward and doable. The advantage of TMS WEB Core certainly is that the built-in controls all come with full source code, so this is also a great resource to learn from. Using the Visual Studio Code IDE made it easy to immediately see the result on the form designer. Overall, I’m happy with the result. The chatbox will be integrated in the TMS WEB Academy application and will also become standard part of TMS WEB Core. I look forward to develop many more exciting web custom controls for TMS WEB Core! 


    Thank you

    Thank you Bradley for a great collaboration during the internship. We look forward to go to the next level as a colleague in the tms family in July! The year went by fast and let's see who will take on the challenge to become an intern at tmssoftware.com in the next school year!  



    JSON backstage pass for TMS WebCore developers

    $
    0
    0

    TMS Software Delphi  Components

    Guest post about web wizardry with TMS WEB Core

    With pleasure, we present today a new guest post. We might go as far as saying that it might change your experience with dealing with JSON in TMS WEB Core apps as Object Pascal developer radically. This wizardry is presented today in this blog by Martin Suer. Martin was already present and enthusiast about TMS WEB Core when we first started showing the product early 2018. Since then, Martin initially started working and doing projects with TMS WEB Core for Delphi and for some time now, he and his colleagues at SymProject GmbH are doing web projects with TMS WEB Core for Visual Studio Code (btw, there is a webinar if you want to learn about it on June 3).
    I give the word to Martin. Bookmark this article, it will make your TMS WEB Core application code so much better!


    Introduction

    Every web frontend developer sooner or later has the pleasure of processing or creating JavaScript objects. In today’s world of web services and microservice architectures, results of function calls are often represented in JSON. To access this as a TMS WEB Core developer, the RTL packages JS and WEBLib.JSON contain many useful classes and functions. With the help of these packages it is possible to convert JSON into JavaScript objects and vice versa, to access single attributes of a JavaScript object or to create JavaScript objects. Dr. Holger Flick has described in his book "TMS WEB Core" in a whole chapter how these RTL packages can be used. (Teaser: The book is worth every cent) Today we will look a little behind the scene and learn about new possibilities beyond that, where we will use the advantages of TMS WEB Core to access JavaScript objects very elegantly and thus combine the advantages of both worlds. As object Pascal developers we are used to work with static type safety. Our data structures contain attributes where the compiler detects errors before runtime. If we try to assign a number to a variable of type string it will be detected by the compiler upfront. In the JavaScript world this is not possible. This gives a JavaScript developer more freedom and flexibility in using variables but also a bigger potential for errors if typos or assumptions about the types of values contained in a variable are incorrect. There errors occur only at runtime. No problem at all, you might think. We use TMS WEB Core and are type safe. But if we access JavaScript objects, we leave the nice guys and visit the dark side…


    2. First things first

    In the previous section we used the terms JSON and JavaScript object. But what is it exactly and what are the differences? In JavaScript, a variable can contain a simple value as well as an object or an array.

    // JavaScript
    let i = 5;
    let s = 'some text';
    let o = {
      a = 'text',
      b = i
    }

    In this example the variable o contains a JavaScript object with the two attributes a and b where a contains a text and b contains a number.

    In TMS WEB Core there is a type JSVALUE for the value of a variable from the JavaScript world. Whenever we want to access the value of a JavaScript variable, we can use the JSVALUE type for it.

    // WEBCore
    
    function sampleJavascriptVariable : JSVALUE; assembler;
    asm
      let i = 5;
      return i;
    end;
    
    procedure AccessJsvalue;
    var
      javascriptvalue: JSVALUE;
      i: integer;
    
    begin
      javascriptvalue := sampleJavascriptVariable;
      // value will now contain the JavaScript VALUE (hence JSVALUE) 5
      i := integer(javascriptvalue);
      // we needed to typecast javascriptvalue to integer because the compiler
      // doesn't know the type of the value actually stored in that variable
    end;

    So whenever we want to access JavaScript values directly from TMS WEB Core, we need a typecast to tell the compiler which type the value in the variable with the type JSVALUE contains. This is already cumbersome with simple types and also error-prone and it gets even more complex when we want to access JavaScript objects.

    Let’s summarize: A JavaScript object is an object in the JavaScript world that is stored in an internal representation in a JavaScript variable. To store such a JavaScript object in an Object Pascal variable in TMS WEB Core, the object pascal type JSVALUE is available.

    And what has all this to do with JSON ?

    JSON (JavaScript Object Notation) is a string that represents a JavaScript object. A JavaScript object can be converted to JSON and a JSON string can be converted to a JavaScript object. More information about the syntax definition of JSON can be found here: https://www.json.org/json-en.html.

    Whenever we talk about JSON, we mean a string from an Object Pascal point of view. Whenever we talk about a JavaScript object we mean a JSVALUE from an Object Pascal point of view.

    // WEBCore
    
    var
      json: String;
      javascriptobject: JSValue;
    
    begin
      json := '{ "x" : 5, "y" : 7 }';
      javascriptobject := TJSJSON.parse(json);
    end;

    In this example the JSON variable contains a JSON representation (data type String) of an object with the members x and y, both containing numbers. This string is parsed into a JavaScript object using the static method TJSJSON.parse() and stored in the variable javascriptobject.

    Note: All sample sources use only the units that are already automatically included in the uses list of a standard TMS WEB Core web project. So you can try and reproduce everything directly in TMS WEB Core when you include the source texts in the unit1.pas of a newly created project. To keep the example source code short, only necessary code is shown, in real world code you would need to add some exception handling here and there…

    All well and good, now we have a JavaScript object in an Object Pascal JSVALUE variable in TMS WEB Core. But how to access it and the individual elements?


    3. Practical uses

    This is where the backstage pass comes in handy: backstage we learn what the pas2js compiler actually does with the TMS WEB Core source code when our source code is compiled.

    type
      TCoordinate = record
        x: double;
        y: double;
      end;
    
    var
      javascriptobject: JSValue;
      json: string;
      coordinate: TCoordinate;
    
    begin
      json := '{ "x" : 5, "y" : 7 }';
      javascriptobject := TJSJSON.parse(json);
      coordinate := TCoordinate(javascriptobject);
      ShowMessage(Format('The coordinate is (%f,%f).',[coordinate.x, coordinate.y]));
    end;

    Backstage information: The pas2js compiler creates JavaScript code from our Object Pascal source code and finally there is (almost) no difference between a record and a JavaScript object. A record is a JavaScript object in the resulting JavaScript code.

    As in the previous example with the typecast from JSVALUE to integer, we can tell the compiler with a typecast from JSVALUE to TCoordinate that we expect an object in the javascriptobject variable that corresponds to the structure of the TCoordinate record type we defined.

    In the following ShowMessage() we can then access the values coordinate.x and coordinate.y and it even works with auto code completion.

    Let that sink in.

    If we tell the compiler what the structure of the JavaScript object looks like by typecasting JSVALUE to an arbitrary record type, we only need one typecast and can then continue programming exactly as if the variable would contain an Object Pascal record. Code completion works and error detection already at compile time. No more cumbersome single accesses with potential typos.

    What can we do with this new bag of tricks now?

    To clarify this and to understand the following sample code let’s have a quick look at the service https://jsonplaceholder.typicode.com. Here we can retrieve various JSON object representations of sample data through a simple HTTPRequest and use them for testing purposes.

    For example, the call https://jsonplaceholder.typicode.com/users/1 returns the following object as JSON:

    {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
        }
      },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
      }
    }

    Accessing this data with our new knowledge is now pretty simple:

    procedure TMainForm.BtnFetchUserClick(Sender: TObject);
    type
      TUser = record
        id: integer;
        name: string;
        email: string;
      end;
    
    var
      request: TJSXMLHttpRequest;
      json: string;
      javascriptobject: JSValue;
      user: TUser;
    
    begin
      WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/1';
      request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
      json := string(request.response);
      javascriptobject := TJSJSON.parse(json);
      user := TUser(javascriptobject);
      ShowMessage(Format('User %d''s name is %s. You can reach him/her at %s', [user.id, user.name, user.email]));
    end;

    Note: For this code example to work, we first place a non-visual component TWebHttpRequest on the main form and change its name to WebHttpRequest in the Object Inspector. Additionally, in the declaration of the form it is necessary to mark the BtnFetchUserClick method with the [async] attribute:

    type
      TMainForm = class(TWebForm)
        WebHTTPRequest: TWebHTTPRequest;
        [async] 
        procedure BtnFetchUserClick(Sender: TObject);
      end;

    In this example we have declared a record type TUser with the fields id, name and email. Using the WebHttpRequest component, we fetch the JSON for a user from the typicode.com web service and store the result in a JSON string variable. Then we convert the obtained JSON to a javascriptobject using TJSJSON.parse(json) and cast it to our TUser type and store it in the user variable.

    The variables user and javascriptobject then contain the same values. Once as type TUser and once as JSVALUE. But both reference the same object in memory.

    Using the variable user we can now access the individual members of the user and display them in the ShowMessage() method as in this example. When entering the code we are supported by the editor with Code Completion.

    The question arises what happened to the rest of the values of the JavaScript object for which we did not define any members in our TUser type declaration?

    Well, they are still there, we just can’t access them with our TUser type because the compiler doesn’t know about their existence. With our TUser type declaration we have not changed any data. We only told the compiler how we imagine the representation of the JavaScript object and how we want to access it. A typecast does not result in additional JavaScript code. It is merely information for the compiler that enables it to perform static type checks at compile time.

    If we want to access more details of the JavaScript object from the example, we can simply extend our TUser type and get full access to all details:

    procedure TMainForm.BtnFetchComplexUserClick(Sender: TObject);
    type
      TGeo = record
        lat, lng: string;
      end;
    
      TAddress = record
        street, suite, city, zipcode: string;
        geo: TGeo;
      end;
    
      TCompany = record
        name, catchPhrase, bs: string;
      end;
    
      TUser = record
        id: integer;
        name: string;
        email: string;
        address: TAddress;
        company: TCompany;
        nonExistentField: string;
      end;
    
    var
      request: TJSXMLHttpRequest;
      json: string;
      javascriptobject: JSValue;
      user: TUser;
      doesOrDoesNot, msg: string;
    
    begin
      WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/3';
      request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
      json := string(request.response);
    
      javascriptobject := TJSJSON.parse(json);
    
      user := TUser(javascriptobject);
    
      if isDefined(user.nonExistentField) then
        doesOrDoesNot := 'does'
      else
        doesOrDoesNot := 'does not';
    
      ShowMessage( Format('%s lives in %s (Geolocation [%s,%s]) and works for %s. '
        + 'The field "nonExistentField" %s exist.',
        [ user.name, user.address.city, user.address.geo.lat, user.address.geo.lng, user.company.name, doesOrDoesNot ]) );
    
      ShowMessage('Value of user.nonExistentField is ' + user.nonExistentField);
    end;

    In the extended example we use a modified TUser type that contains all the details of the JSON supplied by typicode.com and even defines an additional field.

    The rest of the example code is the same for now and in the ShowMessage() we can access all the details of the user, including the sub objects. Like such as using user.address.geo.lat to access the coordinate of the address.

    Since we have defined a field nonExistendField in our record type, which does not occur in the JSON string at all, the typecast itself won’t be a problem. However, we would get a runtime error if we access such field in the program code assuming that the field exists and contains a valid value. The good news is: with the isDefined function, it is possible to check for the presence of a field value.

    Again, no additional code is generated with our typecast. It only tells the compiler which structure of the JavaScript object we expect and how we want to access it. A JavaScript object is a JavaScript object and it is still a JavaScript object after the typecast. BUT we can use it after the typecast as if it were an Object Pascal record.

    Of course, this can go wrong if the structure of the JavaScript object does not match the type definition. Is that a problem? It depends…

    You usually know what data you expect from a service or in an object. If the assumptions are not correct, you may get a runtime error, but it can be dealt with with exception handling.

    Is this an additional new problem I’m introducing with TypeCasts? No, the same problem exists in the pure JavaScript world. (But there it exists even without typecasts) There too, as a developer, you make assumptions about the contents of my data structures. There too, a possible runtime error has to be handled. In the JavaScript world, however, there is no compiler that helps you out with type checks.


    4. Going even further

    As we have seen, a JavaScript object is easy to use in TMS WEB Core if we have defined a suitable record type for it.

    The pas2js compiler (or better transpiler) converts Object Pascal to JavaScript and records become JavaScript objects. But not only that, Object Pascal arrays become JavaScript arrays. Better said: Records are JavaScript objects, Arrays are JavaScript arrays. And since JavaScript arrays are just a special form of JavaScript objects we can take advantage of this and access JavaScript arrays in the same way we access JavaScript objects.

    This is illustrated by the following example, where we retrieve a whole list of users via https://jsonplaceholder.typicode.com/users. The corresponding code looks like this:

    procedure TMainForm.BtnFetchMultipleUsersClick(Sender: TObject);
    type
      TUser = record
        id: integer;
        name: string;
        email: string;
      end;
    
      TUserArray = array of TUser;
    
    var
      request: TJSXMLHttpRequest;
      json: String;
      javascriptarray: JSValue;
      users: TUserArray;
      user: TUser;
    
    begin
      WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users';
      request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
      json := string(request.response);
      javascriptarray := TJSJSON.parse(json);
      users := TUserArray(javascriptarray);
    
      user := users[4];
    
      ShowMessage(Format('Received %d users. User %d''s name is %s. You can reach him/her at %s',
       [Length(users), user.id, user.name, user.email]));
    end;

    Here we convert the result of the web request using a type cast into a standard array and can thus access all contained user records of the array in a type-safe manner as shown in the ShowMessage() method.


    5. The other way round

    In the previous examples we always cast JavaScript objects into records to be able to access JavaScript objects easily and comfortably from TMS WEB Core. Often we need to go the other way. If we need to pass more complex data structures to web services, we need to serialize records or objects to pass them to JavaScript functions. Our new principle works fine in this direction as well. Wherever a JSVALUE is expected, we can easily pass a record and this time we don’t even need a typecast, because the receiving side (JavaScript) only knows universal variables, in which arbitrary values or objects can be stored.

    Here is a small example:

    function AddAToB(value : JSValue): string; assembler;
    asm
      value.result = value.a + value.b;
      value.s = 'The sum is ' + value.result;
      return JSON.stringify(value);
    end;
    
    procedure TMainForm.BtnTheOtherWayRoundClick(Sender: TObject);
    type
      TSampleRecord = record
        a, b, c, result : double;
        s : string;
      end;
    
    var
      sample: TSampleRecord;
      json: string;
    
    begin
      sample.a := 4.5;
      sample.b := 3.2;
      json := AddAToB(sample);
    
      ShowMessage (json);
    end;

    In this example, we use the assembler directive to define a JavaScript function AddAToB() that expects a JavaScript object as a parameter. This function does not know the type of the passed object, but it makes assumptions about it in its code (welcome to the dark side). And so it performs an addition of the values value.a and value.b and stores the result in value.result. Also, the result is stored as a string in value.s and the entire value object in JSON representation is returned as the result.

    To call this dark side function AddAToB(), we define a TSampleRecord with the values a and b, which we assign numbers to and then use that record as parameter for the function call.

    Because of the type declaration, we as callers are not able to preset the values a and b with strings. The type check of the compiler will prevent that.

    We can get another interesting information from the function result: The member c is contained in the result and has the value 0. Although the function AddAToB neither knows about the existence of c nor has set the value. This is because we passed our record as a parameter to the function and there the c is defined and during the initialization of the record in the Object Pascal world all members were preset with default values.

    So if we use records for values passed to JavaScript functions, we can’t forget to create necessary members in the code, because they are always pre-allocated by the record usage.

    We hope that this blog article gave you some interesting insights into the backstage of JSON and JavaScript objects in the TMS WEB Core concert.

    Author: Martin Suer

    Symproject GmbH https://www.symproject.com




    Keep up with the latest developments!

    $
    0
    0

    TMS Software Delphi  Components

    Everything evolves so incredibly fast in the software world!
    So our team is also trying really hard to keep up with the latest developments. And to give our customers access to all our latest developments we have created our no-nonsense subscription TMS ALL-ACCESS! Because we want our customers to be able to use all our products with no restrictions or limitations.
    And when we say ALL we really mean ALL our products we offer now and also all new products we'll release in the year of the subscription. Moreover, our all-access users are the first to receive the previews and betas of our non-released products!

    Here is a small overview of the new products we added in 2021:

    January 2021:

    • TMS VCL UI Pack v10.4: Powerful, extensive & flexible component suite for native Excel report & file generation & manipulation for VCL & FireMonkey
    • TMS FNC Cloud Pack v1.3: Seamless access to cloud services from Windows, cross-platform and the web
    • TMS WEB Core v1.6: Framework for creating modern web applications

    February 2021:

    • TMS FNC Maps v1.3: Hold the full power of mapping in your hand! Cross-framework, cross-platform, cross-service mapping component library
    • TMS Web Academy launch

    March 2021: 

    April 2021: 

    May 2021: 



    Get full access with: 

    • Easy plan: All current and new products for 1 price!
    • Easy renewal: Yearly renewal only for 495 EUR!
    • Easy install: all products accessible via TMS Subscription Manager tool
    • Easy support: Full online support center access to all areas
    • Easy up-to-date: Early access to product betas
    • Easy learning: Access to TMS WEB Academy


    Act now:

    So what are you waiting for?

    Find out more about our products included in TMS ALL-ACCESS.

    NOTE: Contact sales for special upgrading pricing for existing customers of other products.



    Another webinar with another first ...

    $
    0
    0

    TMS Software Delphi  Components

    If any of these technologies are of interest to you, we have a new webinar coming up with another first!
    Reserve your first class seat now while it can here

    We will show you for the first time in public, the latest state of project Miletus in TMS WEB Core

    To give you a hint, we only show today this screenshot:

    TMS Software Delphi  Components

    First time you hear about the word Miletus? Read up here and then reserve your seat! 

    TMS Software Delphi  Components
    With your registration, you'll see & discover it first-hand in our online webinar running in our own fully web-based TMS Web Academy platform developed here using TMS WEB Core for Visual Studio.


    We look forward to meet you in the webinar and discuss via the platform the many new capabilities that open up for Delphi developers.  See you!




    Discover our wide range of TMS FNC Components

    $
    0
    0


    TMS Software Delphi  Components

    FNC stands for Framework Neutral Components. This means that the components can be used cross-framework and cross-platform. With our TMS FNC Component studio bundle you will get access to all our current FNC components!

    That is not all! You will save over 45% now and even more with new free FNC component releases!

    All products come with perpetual licenses and 1 year of FREE updates & support. Subscriptions can be extended for another year at 70% discount!

    Products included in the bundle:


      Get full access to FNC components:

      • Components for Delphi & C++Builder that can be used in all project types.
      • Only one learning curve: VCL, FMX, LCL and WEB.
      • Target Windows, macOS, iOS, Android, Linux and WEB.
      • Growing range of components, new products on the way.
      • Yearly renewal only 145 EUR* instead of regular 495 EUR.
      • All products accessible via TMS Subscription Manager tool.
      • Full online support through TMS Support Center.

      *The price is valid for this moment & depends on the price of the pack at the moment of renewal.


      Learn More:

      Want to learn more about FNC and how you can benefit from it? Join us on our upcoming webinar: "What is FNC : Introduction to the growing family of FNC technology based components", on June 22, 2021.


      Act now:

      Find out more about our products included in TMS FNC Component Studio and get your license today!

      NOTE: Contact sales for special upgrading pricing for existing customers of other products.



      TMS WEB Core & FNC trial support

      $
      0
      0

      A small update with a big impact

      We have recently changed the way the trial version is being built and now works together with other TMS WEB Core enabled products. Evaluating a product before considering a purchase is important and therefore we have bundled all of our resources and investigated a solution for the ongoing issues trying out the combination of trial FNC & trial TMS WEB Core. Today we can announce there are no issues compiling FNC trial & TMS WEB core trial together. Enjoy and test the trial version of the following products in combination with TMS WEB Core trial today!


      In case you didn't notice

      This week is also FNC week. Please read the following blog post to know more about FNC & FNC Studio and the benefits it has to offer: https://tmssoftware.com/site/blog.asp?post=806

      TMS Software Delphi  Components




      Google Maps heat maps

      $
      0
      0

      We recently received a question whether TMS FNC Maps supports heat maps. Heat maps are supported in Google Maps and we immediately went exploring the capabilities upon this request. More info and a sample about heat maps can be found here: https://developers.google.com/maps/documentation/javascript/examples/layer-heatmapTMS FNC Maps comes with a lot of great functionality as well as a (lesser visible) customization events and methods to add your own extensions. To add support for heat maps, we had to override 2 virtuals and we can do this by inheriting from the TTMSFNCGoogleMaps class.


      Customizing the API URL

      TTMSFNCMaps (and TTMSFNCGoogleMaps), expose a lot of virtuals to extend and customize existing or new functionality. To support heat maps, we needed to add an extra parameter to the URL that is required to load the Google Maps JavaScript API. This can be done by overriding the GetHeadLinks procedure.

      procedure GetHeadLinks(AList: TTMSFNCMapsLinksList; ACheckReady: Boolean = True); override;
      Additionally, we need to change the link to include an additional parameter: "libraries=visualization" to enable the library required to load the heat map layer.

      procedure TTMSFNCGoogleMapsEx.GetHeadLinks(AList: TTMSFNCMapsLinksList;
        ACheckReady: Boolean);
      begin
        AList.Add(TTMSFNCMapsLink.CreateScript('https://maps.googleapis.com/maps/api/js?key=' + MapsProperties.GetAPIKey + '&language=' +
          MapsProperties.GetLocale + '&libraries=visualization', 'text/javascript', 'utf-8', '', True, True));
      end;


      Adding additional JavaScript functionality

      After extending the API URL to load the required libraries we need to add custom JavaScript to visualize the heat map. According to the sample (following the link at the top), we need to add code to visualize heat maps in San Francisco. To add custom JavaScript functionality, override the DoCustomizeMap virtual.

      procedure DoCustomizeMap(var ACustomizeMap: string); override;

      The code specified in the sample translated to Delphi code is shown below.

      procedure TTMSFNCGoogleMapsEx.DoCustomizeMap(var ACustomizeMap: string);
      var
        h: string;
      begin
        h := 'var heatMapData = [' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.447), weight: 0.5},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.445),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.443), weight: 2},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.441), weight: 3},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.439), weight: 2},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.437),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.435), weight: 0.5},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.447), weight: 3},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.445), weight: 2},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.443),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.441), weight: 0.5},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.439),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.437), weight: 2},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.435), weight: 3}' + LB +
        '];' + LB +
      
        'var heatmap = new ' + MAPSERVICEVAR + '.visualization.HeatmapLayer({' + LB +
        '  data: heatMapData' + LB +
        '});' + LB +
      
        'heatmap.setMap(' + MAPVAR + ');';
      
        ACustomizeMap := h;
      
        inherited;
      end;

      Bundling this all together produces the following result.

      TMS Software Delphi  Components


      The complete code snippet (written in FMX, but can be used in any framework)

      unit UGoogleMapsHeatMap;
      
      interface
      
      uses
        System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
        FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TMSFNCGoogleMaps, FMX.TMSFNCMaps;
      
      type
        TTMSFNCGoogleMapsEx = class(TTMSFNCGoogleMaps)
        protected
          procedure GetHeadLinks(AList: TTMSFNCMapsLinksList; ACheckReady: Boolean = True); override;
          procedure DoCustomizeMap(var ACustomizeMap: string); override;
        end;
      
        TFormHeatMap = class(TForm)
          procedure FormCreate(Sender: TObject);
        private
          g: TTMSFNCGoogleMapsEx;
          { Private declarations }
          procedure DoMapInitialized(Sender: TObject);
        public
          { Public declarations }
        end;
      
      var
        FormHeatMap: TFormHeatMap;
      
      implementation
      
      uses
        FMX.TMSFNCMaps.GoogleMaps, FMX.TMSFNCMapsCommonTypes;
      
      {$R *.fmx}
      
      procedure TFormHeatMap.DoMapInitialized(Sender: TObject);
      begin
        g.SetCenterCoordinate(CreateCoordinate(37.774546, -122.433523)); //San Francisco
      end;
      
      procedure TFormHeatMap.FormCreate(Sender: TObject);
      begin
        g := TTMSFNCGoogleMapsEx.Create(Self);
        g.Parent := Self;
        g.APIKey := MY API KEY;
        g.OnMapInitialized := DoMapInitialized;
      end;
      
      { TTMSFNCGoogleMapsEx }
      
      procedure TTMSFNCGoogleMapsEx.DoCustomizeMap(var ACustomizeMap: string);
      var
        h: string;
      begin
        h := 'var heatMapData = [' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.447), weight: 0.5},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.445),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.443), weight: 2},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.441), weight: 3},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.439), weight: 2},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.437),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.782, -122.435), weight: 0.5},' + LB +
      
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.447), weight: 3},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.445), weight: 2},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.443),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.441), weight: 0.5},' + LB +
        'new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.439),' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.437), weight: 2},' + LB +
        '{location: new ' + MAPSERVICEVAR + '.LatLng(37.785, -122.435), weight: 3}' + LB +
        '];' + LB +
      
        'var heatmap = new ' + MAPSERVICEVAR + '.visualization.HeatmapLayer({' + LB +
        '  data: heatMapData' + LB +
        '});' + LB +
        'heatmap.setMap(' + MAPVAR + ');';
      
        ACustomizeMap := h;
      
        inherited;
      end;
      
      procedure TTMSFNCGoogleMapsEx.GetHeadLinks(AList: TTMSFNCMapsLinksList;
        ACheckReady: Boolean);
      begin
        AList.Add(TTMSFNCMapsLink.CreateScript('https://maps.googleapis.com/maps/api/js?key=' + MapsProperties.GetAPIKey + '&language=' +
          MapsProperties.GetLocale + '&libraries=visualization', 'text/javascript', 'utf-8', '', True, True));
      end;
      
      end.

      Explore!

      Want to rebuild this sample? Download TMS FNC Maps today. Already have TMS FNC Maps installed? There are no additional requirements, the above code can be applied as-is on the TTMSFNCGoogleMaps class.





      One subscription for all current VCL components!

      $
      0
      0

      TMS Software Delphi  Components

      TMS VCL Subscription is our two year subscription to all our current & future VCL products for Windows application development.

      With this bundle you will save over 60% now and even more with new free VCL component releases! This bundle includes many unique products like TMS VCL UI Pack with over 600 VCL  components, grids, flexcel, charts, ...

      All products come with perpetual licenses and 2 years of FREE updates & support. After 2 years the subscription can be extended at 50% discount!

      Products included in the bundle:

      • TMS Logging
      • TMS VCL UI Pack
      • TMS VCL Cloud Pack
      • TMS VCL Chart
      • TMS VCL WebGMaps
      • TMS PassKit
      • TMS FlexCel Component Suite for VCL
      • TMS Scripter
      • TMS Query Studio
      • TMS Unicode Component Pack
      • TMS GUIMotions
      • TMS Async
      • TMS VCL Plugin Framework
      • TMS VCL Security System
      • TMS VCL Instrumentation Workshop
      • TMS Workflow Studio
      • TMS Diagram Studio
      • TMS MultiTouch SDK


      Get full access to VCL components:

      • Best bundle for Delphi / C++Builder VCL Win32 or Win64 application developer.
      •  2 years free updates & free support.
      •  Renewal after 2 years at 50% discount instead of regular 795 EUR*.
      • Always current with latest Delphi & C++Builder releases.
      • All products accessible via TMS Subscription Manager tool.
      • Full online support through TMS Support Center.


      *The price is valid for this moment & depends on the price of the pack at the moment of renewal.


      Act now:

      Find out more about our products included in TMS VCL Subscription and get your license today!

      NOTE: Contact sales for special upgrading pricing for existing customers of other products.



      Introducing TMS XData Query Builder and more!

      $
      0
      0

      Photo by Adyant Pankaj on Unsplash

      We're excited to announce TMS XData 5.2 version with several new great features. TMS XData is the more straightforward way to take your Delphi app to the cloud, allowing you to create secure, robust REST servers along with a huge ecosystem around it. Here are the major features in this release, which also includes several minor improvements and bug fixes.

      XData Query Builder

      XData allows you to easily query entities from automatic entity set endpoints using a full query syntax. For example, to query customers which name is "Paul" or birthday date is lower then August 1st, 1940, ordered by name in descending order, you can write a code like this:

      Customers := Client.List<TCustomer>('$filter=(Name eq ''Paul'') or (Birthday lt 1940-08-01)&$orderby=Name desc');
      

      But now you don't need to worry to write the raw query syntax anymore. The new XData Query Builder allows you to build such filter using a fluent interface which already does some validation (like checking for correct property names) before the data is sent to the server:

      Customers := Client.List<TCustomer>(
        CreateQuery
          .From(TCustomer)
          .Filter(
            (Linq['Name'] = 'Paul') 
            or (Linq['Birthday'] < EncodeDate(1940, 8, 1))
          )
          .OrderBy('Name', False)
          .QueryString
        );
      

      You can build logical expressions and projections in a similar way you would do with TMS Aurelius criteria.

      Receive DTO objects in URL query string

      Service operations can now receive DTOs (Delphi objects) from the query string of the request URL (as long the DTO only have properties of scalar types). In this case, each DTO property will be a separated query param. Suppose you have a class TCustomerDTO which has properties Id and Name, then you can declare the method like this:

         [HttpGet] function FindByIdOrName(Customer: TCustomerDTO): TList<TCustomer>;
      

      And have your Customer parameter be received from a request URL like this:

      GET /CustomerService/FindByIdOrName?Id=10&Name='Paul'.

      XData queries in service operations

      Now, in addition to send queries to automatic CRUD entity set endpoints, you can also benefit from XData query mechanism in service operations. You can now easily receive and process XData query syntax like $filter, $orderby, $top and $skip in service operations, even for DTOs, and create Aurelius criteria from it. You can declare a service like this:

      type  IMyService = interface(IInvokable)
          [HttpGet] function List(Query: TXDataQuery): TList<TCustomer>;
      

      Which means clients can now invoke the endpoint passing the same $filter, $orderby, $top and $skip parameters, like this:

      GET /MyService/List/?$filter=Name eq 'Foo'&$orderby=Name&$top=10&$skip=30

      Or, of course, passing the raw filter string with TXDataClient:

        Customer := Client.Service<IMyService>
          .List('$filter=Name eq 'Foo'&$orderby=Name&$top=10&$skip=30');
      

      Or even using the brand new XData query builder fluent interface:

        Customer := Client.Service<IMyService>.List(
          CreateQuery.From(TCustomer)
            .Filter(Linq['Name'] eq 'Foo')
            .OrderBy('Name')
            .Top(10).Skip(30)
            .QueryString
        );
      

      From server-side, you can create an Aurelius criteria automatically from the passed XData query:

      function TMyService.List(Query: TXDataQuery): TList<TCustomer>;
      begin
        Result := TXDataOperationContext.Current
          .CreateCriteria<TCustomer>(Query).List;
      end;
      

      Not only that, you can even create a criteria from a query based on a DTO object:

      function TMyService.List(Query: TXDataQuery): TList<TCustomer>;
      begin
        Result := TXDataOperationContext.Current
          .CreateCriteria<TCustomer>(Query, TCustomerDTO).List;
      end;
      

      In the example above, even though you are creating a criteria for the TCustomer class, the query will be validated based on the TCustomerDTO class. This means XData will only accept queries that use properties from TCustomerDTO. For example, if the filter string is $filter=Status eq 2, even if the Status property exists in TCustomer, if it does not exist in class TCustomerDTO the query will not be accepted and an error "property Status does not exist" will return to the client.

      And there is more!

      There are even more new features:

      • Nullable<T> types are now supported in plain Delphi objects (in addition to existing support in Aurelius entities). When using plain Delphi objects as DTOs, like when receiving or returning them in service operations, they are now properly serialized/deserialized as JSON.

      • A new Forward middleware processes x-forwarded-* headers for improved behavior of Sparkle servers behind reverse or edge proxies.

      • A new "Sparkle app" demo shows how to create a Windows XData server application working as both VCL application (for Debug) or Windows service (for Release).

      Want to know more?

      Great new features in this release! If you are already a TMS XData user, please comment below, let us know what do you think about this exciting release!

      If want to know more about XData, you can always check the documentation of course, but also don't hesitate to contact us to get help. Also check the existing content in our channels: subscribe to our e-mail newsletter, follow our Facebook page and subscribe to our YouTube channel!

      (*) Photo by Adyant Pankaj on Unsplash



      TMS WEB Core for Visual Studio Code v1.3 released

      $
      0
      0

      Today is an exciting day as we reached yet another milestone, this time the release of TMS WEB Core for Visual Studio Code v1.3.

      You are an Object Pascal expert, your customers look out to access your applications everywhere via the web and without any install hassle, you love the RAD component based development methodology, you can do this software development from the operating system you love ... then having a look at TMS WEB Core for Visual Studio Code v1.3 will open a whole new world!

      TMS Software Delphi  Components  What brings TMS WEB Core for Visual Studio Code


      • Fully Object Pascal based web development
      • RAD component methodology driven fast & productive development 
      • Hosted in a free hugely popular IDE for web developers
      • Running on Windows, macOS and Linux
      • Using a WYSIWYG form designer
      • A framework that has a similarities with the familiar VCL framework where possible

      TMS Software Delphi  Components  What is on the table in v1.3


      This major update is packed with significant new features:

      Support for packages
      Yes, from now on, it is easy to create your own custom components and compile & install these for use at design-time. The package system is similar to how you can use packages to install components in the Delphi IDE or Lazarus

      Support for workspaces
      With workspaces, now you can have multiple projects in a folder and activate and select which project to compile/run/debug.

      Support for using the entire FNC component suite
      As our entire set of FNC components is ready for use in web client applications, the new package support in TMS WEB Core for Visual Studio Code enables to install all TMS FNC components in the TMS WEB Core for Visual Studio Code IDE. The latest FNC registered version updates released last week contain all the required packages for this.

      Brings the framework up-to-date with latest version as available in TMS WEB Core for Delphi/Lazarus v1.7
      The TMS WEB Core framework that comes with TMS WEB Core for Visual Studio Code is now in sync and up-to-date with the latest release of TMS WEB Core for Delphi. This means that all new components and component improvements are now also available for Visual Studio Code users. This includes the new USB components, local file access components, new Firestore dataset capabilities, extensions to grids, web socket connection improvements and much more...  One important note though, the Miletus application type is not yet in our release for Visual Studio Code release but scheduled for the next release.


      TMS Software Delphi  Components  Get started!


      There are several ways to get started with TMS WEB Core for Visual Studio Code

      • TMS ALL-ACCESS users have immediate & free access to TMS WEB Core for Visual Studio Code
      • TMS WEB Core for Visual Studio Code registered users will find the new release now on your "My Products" page
      • A fully functional trial version for Windows, macOS or Linux is available
      • We have a webinar coming up that will guide you around the IDE, the framework, the possibilities
      • Check more details about FNC and using FNC components from Visual Studio code in this blog

      TMS Software Delphi  Components  Learn how to use packages, write your own components or use FNC components

      We have already two videos for you and more are coming.

      Chief architect José Leon Serna explains how to start using packages:


      José Leon Serna and Bruno Fierens host a webinar giving a sneak preview of v1.3


      TMS Software Delphi  Components Get in touch

      We have our Support Center to help you getting started, contact our sales team to assist in any licensing queries, our let us know what you think about TMS WEB Core for Visual Studio Code and in what direction you wish to see things being developed further! We are eager to hear your feedback and further shape the product to your needs & wishes!





      Custom control development with TMS WEB Core for Visual Studio Code, an intern reports

      $
      0
      0

      TMS Software Delphi  Components
      At tmssoftware.com we love to have interns. I believe it is a fantastic experience to learn young aspiring software developers about Delphi, its eco-system, its amazing productivity and at the same time, absorb a lot of energy, get new fresh ideas and get to know what drives and motivates the next generation of software developers.
      So, also this school year, we had 2 interns learning all about Delphi, TMS WEB Core, Object Pascal, components and much more here at tmssoftware.com.
      Today, we want to give the word to our intern, and very soon colleague, Bradley who did a lot of stunning work this school year and tells in his first blog post about building his first custom control for TMS WEB Core. Nothing but an amazing and exciting story! So, with pleasure, I give the word to Bradley:

      A first custom web control

      Having only one year experience using Delphi and TMS WEB Core, I was assigned the (at first sight) daunting task of developing my first ever custom web control, namely a chat box control. I had only been developing web applications since I joined tmssoftware.com as an intern, so clearly writing a component was something completely new. Using the available documentation and the experience of my mentor Bruno I quickly obtained the necessary skills for developing a TMS WEB Core custom control, the TWebChatBox control.
      TMS Software Delphi  Components

      First steps

      To gain some experience, I first developed another very small control, a custom toggle button control to switch a web application between full-screen and normal mode. I went for developing the custom control directly from TMS WEB Core for Visual Studio Code because I just feel more comfortable for web projects to use this IDE. I had the "privilege" to use first the alpha and then the beta version of TMS WEB Core for Visual Studio Code v1.3 (released yesterday btw) as this introduced component package support. Using the new package support for installing custom controls made it very easy to test the control, also at design-time. The development of this first small control allowed me to get used to the concept of custom controls as well as the steps involved to get it installed in the IDE.

      Starting the chatbox implementation

      After I was comfortable enough with the principle of custom controls. I’ve started building the chat box component. As there were a ton of features on the requirements list, I had actually no idea where to start. I’ve focused first of all on a working way of sending/receiving messages. When I had that going, I started focusing on adding a basic design, followed up by adding the necessary properties for manipulating said chat box. 
      I think implementing collections as a way of managing messages was the only thing I really struggled with. Having never implemented anything like this, figuring out how to implement the chat message bubbles at collection item level was a challenge. If you add a message to your collection, a chat message bubble will be generated and displayed in the chat control. The collection item will also keep a reference of the chat message bubble so that you can manipulate it later on.
      A chat bubble contains several HTML elements. We have a SPAN for the message. For the user avatar there is an IMG element. And both the sender name and time are in P elements. The other elements are mostly used for positioning all the elements. The IMG element will not be shown when you choose to disable user images.

      FChatMessage := TJSHTMLElement(document.createElement('SPAN'));
      FMessageInfo := TJSHTMLElement(document.createElement('DIV'));
      FTimeStamp := TJSHTMLElement(document.createElement('P'));
      FChatSender := TJSHTMLElement(document.createElement('P'));
      FImageArea := TJSHTMLElement(document.createElement('DIV'));
      FMessageArea := TJSHTMLElement(document.createElement('DIV'));
      FImage := TJSHTMLElement(document.createElement('IMG'));
      These chat bubbles are, after being made, displayed on the chat display which is nothing more than a flexbox DIV. 
      Underneath the chat display we have our input area. This area contains several elements like an input for the message, a send button and an emoji picker.
      FArea := TJSHTMLElement(document.createElement('DIV'));
      FInputDisplay := TJSHTMLElement(document.createElement('DIV'));
      FInputTextBox := TJSHtmlElement(document.createElement('TEXTAREA'));
      FSendButton := TJSHTMLElement(document.createElement('BUTTON'));
      FInputDisplay.appendChild(FInputTextBox);
      FArea.appendChild(FInputDisplay);
      FArea.appendChild(FSendButton);
      ElementHandle.appendChild(FArea);
      FSendButton.addEventListener('click', @HandleSendMessageButtonClick);
      FInputTextBox.addEventListener('keyup', @HandleSendMessageInputEnter);
      There are 2 event listeners. You can press the send button for sending messages. But pressing enter will also send your message. When sending/receiving messages the chat box will auto scroll with all messages except if you scroll up.
      Another feature that the chat box has, is an auto scroll. when you send a message the chat box will automatically scroll down as long as the user hasn’t scrolled up. To check if a user has scrolled up is nothing more than using this function:
      function TChatBox.isScrolledToBottom: Boolean;
      begin
        Result := (FChatDisplay.scrollHeight – 
      FChatDisplay.clientHeight <= FChatDisplay.scrollTop + 1);
      end;
      To effectively scroll down you use this procedure:
      procedure TChatBox.scrollToBottom;
      begin
        FChatDisplay.scrollTop := FChatDisplay.scrollHeight;
      end;
      The Emoji Picker is an external JavaScript library. The emoji picker is added by instantiating the EmojiPicker class and then use the generateElements function and giving the input textbox as a parameter.    
       asm 
         (()=> {
           var picker = new EmojiPicker();
           picker.generateElements(element);
         })();
       end;
      
      This library will add a dropdown button linked to your input textbox and will allow you to choose an emoji.

      TMS Software Delphi  Components

      Typing some of the mostly used emoji shortcuts in the textbox will also change to an emoji when sending the message. The same applies for hyperlinks that are converted into clickable links in the chat bubble.

      Conclusion

      While web custom controls appear like complex magic black boxes at first sight, after some effort to learn their architecture and how to build them mapping to HTML elements, it turns out to be actually relatively straight-forward and doable. The advantage of TMS WEB Core certainly is that the built-in controls all come with full source code, so this is also a great resource to learn from. Using the Visual Studio Code IDE made it easy to immediately see the result on the form designer. Overall, I’m happy with the result. The chatbox will be integrated in the TMS WEB Academy application and will also become standard part of TMS WEB Core. I look forward to develop many more exciting web custom controls for TMS WEB Core! 


      Thank you

      Thank you Bradley for a great collaboration during the internship. We look forward to go to the next level as a colleague in the tms family in July! The year went by fast and let's see who will take on the challenge to become an intern at tmssoftware.com in the next school year!  



      JSON backstage pass for TMS WebCore developers

      $
      0
      0

      TMS Software Delphi  Components

      Guest post about web wizardry with TMS WEB Core

      With pleasure, we present today a new guest post. We might go as far as saying that it might change your experience with dealing with JSON in TMS WEB Core apps as Object Pascal developer radically. This wizardry is presented today in this blog by Martin Suer. Martin was already present and enthusiast about TMS WEB Core when we first started showing the product early 2018. Since then, Martin initially started working and doing projects with TMS WEB Core for Delphi and for some time now, he and his colleagues at SymProject GmbH are doing web projects with TMS WEB Core for Visual Studio Code (btw, there is a webinar if you want to learn about it on June 3).
      I give the word to Martin. Bookmark this article, it will make your TMS WEB Core application code so much better!


      Introduction

      Every web frontend developer sooner or later has the pleasure of processing or creating JavaScript objects. In today’s world of web services and microservice architectures, results of function calls are often represented in JSON. To access this as a TMS WEB Core developer, the RTL packages JS and WEBLib.JSON contain many useful classes and functions. With the help of these packages it is possible to convert JSON into JavaScript objects and vice versa, to access single attributes of a JavaScript object or to create JavaScript objects. Dr. Holger Flick has described in his book "TMS WEB Core" in a whole chapter how these RTL packages can be used. (Teaser: The book is worth every cent) Today we will look a little behind the scene and learn about new possibilities beyond that, where we will use the advantages of TMS WEB Core to access JavaScript objects very elegantly and thus combine the advantages of both worlds. As object Pascal developers we are used to work with static type safety. Our data structures contain attributes where the compiler detects errors before runtime. If we try to assign a number to a variable of type string it will be detected by the compiler upfront. In the JavaScript world this is not possible. This gives a JavaScript developer more freedom and flexibility in using variables but also a bigger potential for errors if typos or assumptions about the types of values contained in a variable are incorrect. There errors occur only at runtime. No problem at all, you might think. We use TMS WEB Core and are type safe. But if we access JavaScript objects, we leave the nice guys and visit the dark side…


      2. First things first

      In the previous section we used the terms JSON and JavaScript object. But what is it exactly and what are the differences? In JavaScript, a variable can contain a simple value as well as an object or an array.

      // JavaScript
      let i = 5;
      let s = 'some text';
      let o = {
        a = 'text',
        b = i
      }

      In this example the variable o contains a JavaScript object with the two attributes a and b where a contains a text and b contains a number.

      In TMS WEB Core there is a type JSVALUE for the value of a variable from the JavaScript world. Whenever we want to access the value of a JavaScript variable, we can use the JSVALUE type for it.

      // WEBCore
      
      function sampleJavascriptVariable : JSVALUE; assembler;
      asm
        let i = 5;
        return i;
      end;
      
      procedure AccessJsvalue;
      var
        javascriptvalue: JSVALUE;
        i: integer;
      
      begin
        javascriptvalue := sampleJavascriptVariable;
        // value will now contain the JavaScript VALUE (hence JSVALUE) 5
        i := integer(javascriptvalue);
        // we needed to typecast javascriptvalue to integer because the compiler
        // doesn't know the type of the value actually stored in that variable
      end;

      So whenever we want to access JavaScript values directly from TMS WEB Core, we need a typecast to tell the compiler which type the value in the variable with the type JSVALUE contains. This is already cumbersome with simple types and also error-prone and it gets even more complex when we want to access JavaScript objects.

      Let’s summarize: A JavaScript object is an object in the JavaScript world that is stored in an internal representation in a JavaScript variable. To store such a JavaScript object in an Object Pascal variable in TMS WEB Core, the object pascal type JSVALUE is available.

      And what has all this to do with JSON ?

      JSON (JavaScript Object Notation) is a string that represents a JavaScript object. A JavaScript object can be converted to JSON and a JSON string can be converted to a JavaScript object. More information about the syntax definition of JSON can be found here: https://www.json.org/json-en.html.

      Whenever we talk about JSON, we mean a string from an Object Pascal point of view. Whenever we talk about a JavaScript object we mean a JSVALUE from an Object Pascal point of view.

      // WEBCore
      
      var
        json: String;
        javascriptobject: JSValue;
      
      begin
        json := '{ "x" : 5, "y" : 7 }';
        javascriptobject := TJSJSON.parse(json);
      end;

      In this example the JSON variable contains a JSON representation (data type String) of an object with the members x and y, both containing numbers. This string is parsed into a JavaScript object using the static method TJSJSON.parse() and stored in the variable javascriptobject.

      Note: All sample sources use only the units that are already automatically included in the uses list of a standard TMS WEB Core web project. So you can try and reproduce everything directly in TMS WEB Core when you include the source texts in the unit1.pas of a newly created project. To keep the example source code short, only necessary code is shown, in real world code you would need to add some exception handling here and there…

      All well and good, now we have a JavaScript object in an Object Pascal JSVALUE variable in TMS WEB Core. But how to access it and the individual elements?


      3. Practical uses

      This is where the backstage pass comes in handy: backstage we learn what the pas2js compiler actually does with the TMS WEB Core source code when our source code is compiled.

      type
        TCoordinate = record
          x: double;
          y: double;
        end;
      
      var
        javascriptobject: JSValue;
        json: string;
        coordinate: TCoordinate;
      
      begin
        json := '{ "x" : 5, "y" : 7 }';
        javascriptobject := TJSJSON.parse(json);
        coordinate := TCoordinate(javascriptobject);
        ShowMessage(Format('The coordinate is (%f,%f).',[coordinate.x, coordinate.y]));
      end;

      Backstage information: The pas2js compiler creates JavaScript code from our Object Pascal source code and finally there is (almost) no difference between a record and a JavaScript object. A record is a JavaScript object in the resulting JavaScript code.

      As in the previous example with the typecast from JSVALUE to integer, we can tell the compiler with a typecast from JSVALUE to TCoordinate that we expect an object in the javascriptobject variable that corresponds to the structure of the TCoordinate record type we defined.

      In the following ShowMessage() we can then access the values coordinate.x and coordinate.y and it even works with auto code completion.

      Let that sink in.

      If we tell the compiler what the structure of the JavaScript object looks like by typecasting JSVALUE to an arbitrary record type, we only need one typecast and can then continue programming exactly as if the variable would contain an Object Pascal record. Code completion works and error detection already at compile time. No more cumbersome single accesses with potential typos.

      What can we do with this new bag of tricks now?

      To clarify this and to understand the following sample code let’s have a quick look at the service https://jsonplaceholder.typicode.com. Here we can retrieve various JSON object representations of sample data through a simple HTTPRequest and use them for testing purposes.

      For example, the call https://jsonplaceholder.typicode.com/users/1 returns the following object as JSON:

      {
      "id": 1,
      "name": "Leanne Graham",
      "username": "Bret",
      "email": "Sincere@april.biz",
      "address": {
        "street": "Kulas Light",
        "suite": "Apt. 556",
        "city": "Gwenborough",
        "zipcode": "92998-3874",
        "geo": {
          "lat": "-37.3159",
          "lng": "81.1496"
          }
        },
      "phone": "1-770-736-8031 x56442",
      "website": "hildegard.org",
      "company": {
        "name": "Romaguera-Crona",
        "catchPhrase": "Multi-layered client-server neural-net",
        "bs": "harness real-time e-markets"
        }
      }

      Accessing this data with our new knowledge is now pretty simple:

      procedure TMainForm.BtnFetchUserClick(Sender: TObject);
      type
        TUser = record
          id: integer;
          name: string;
          email: string;
        end;
      
      var
        request: TJSXMLHttpRequest;
        json: string;
        javascriptobject: JSValue;
        user: TUser;
      
      begin
        WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/1';
        request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
        json := string(request.response);
        javascriptobject := TJSJSON.parse(json);
        user := TUser(javascriptobject);
        ShowMessage(Format('User %d''s name is %s. You can reach him/her at %s', [user.id, user.name, user.email]));
      end;

      Note: For this code example to work, we first place a non-visual component TWebHttpRequest on the main form and change its name to WebHttpRequest in the Object Inspector. Additionally, in the declaration of the form it is necessary to mark the BtnFetchUserClick method with the [async] attribute:

      type
        TMainForm = class(TWebForm)
          WebHTTPRequest: TWebHTTPRequest;
          [async] 
          procedure BtnFetchUserClick(Sender: TObject);
        end;

      In this example we have declared a record type TUser with the fields id, name and email. Using the WebHttpRequest component, we fetch the JSON for a user from the typicode.com web service and store the result in a JSON string variable. Then we convert the obtained JSON to a javascriptobject using TJSJSON.parse(json) and cast it to our TUser type and store it in the user variable.

      The variables user and javascriptobject then contain the same values. Once as type TUser and once as JSVALUE. But both reference the same object in memory.

      Using the variable user we can now access the individual members of the user and display them in the ShowMessage() method as in this example. When entering the code we are supported by the editor with Code Completion.

      The question arises what happened to the rest of the values of the JavaScript object for which we did not define any members in our TUser type declaration?

      Well, they are still there, we just can’t access them with our TUser type because the compiler doesn’t know about their existence. With our TUser type declaration we have not changed any data. We only told the compiler how we imagine the representation of the JavaScript object and how we want to access it. A typecast does not result in additional JavaScript code. It is merely information for the compiler that enables it to perform static type checks at compile time.

      If we want to access more details of the JavaScript object from the example, we can simply extend our TUser type and get full access to all details:

      procedure TMainForm.BtnFetchComplexUserClick(Sender: TObject);
      type
        TGeo = record
          lat, lng: string;
        end;
      
        TAddress = record
          street, suite, city, zipcode: string;
          geo: TGeo;
        end;
      
        TCompany = record
          name, catchPhrase, bs: string;
        end;
      
        TUser = record
          id: integer;
          name: string;
          email: string;
          address: TAddress;
          company: TCompany;
          nonExistentField: string;
        end;
      
      var
        request: TJSXMLHttpRequest;
        json: string;
        javascriptobject: JSValue;
        user: TUser;
        doesOrDoesNot, msg: string;
      
      begin
        WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users/3';
        request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
        json := string(request.response);
      
        javascriptobject := TJSJSON.parse(json);
      
        user := TUser(javascriptobject);
      
        if isDefined(user.nonExistentField) then
          doesOrDoesNot := 'does'
        else
          doesOrDoesNot := 'does not';
      
        ShowMessage( Format('%s lives in %s (Geolocation [%s,%s]) and works for %s. '
          + 'The field "nonExistentField" %s exist.',
          [ user.name, user.address.city, user.address.geo.lat, user.address.geo.lng, user.company.name, doesOrDoesNot ]) );
      
        ShowMessage('Value of user.nonExistentField is ' + user.nonExistentField);
      end;

      In the extended example we use a modified TUser type that contains all the details of the JSON supplied by typicode.com and even defines an additional field.

      The rest of the example code is the same for now and in the ShowMessage() we can access all the details of the user, including the sub objects. Like such as using user.address.geo.lat to access the coordinate of the address.

      Since we have defined a field nonExistendField in our record type, which does not occur in the JSON string at all, the typecast itself won’t be a problem. However, we would get a runtime error if we access such field in the program code assuming that the field exists and contains a valid value. The good news is: with the isDefined function, it is possible to check for the presence of a field value.

      Again, no additional code is generated with our typecast. It only tells the compiler which structure of the JavaScript object we expect and how we want to access it. A JavaScript object is a JavaScript object and it is still a JavaScript object after the typecast. BUT we can use it after the typecast as if it were an Object Pascal record.

      Of course, this can go wrong if the structure of the JavaScript object does not match the type definition. Is that a problem? It depends…

      You usually know what data you expect from a service or in an object. If the assumptions are not correct, you may get a runtime error, but it can be dealt with with exception handling.

      Is this an additional new problem I’m introducing with TypeCasts? No, the same problem exists in the pure JavaScript world. (But there it exists even without typecasts) There too, as a developer, you make assumptions about the contents of my data structures. There too, a possible runtime error has to be handled. In the JavaScript world, however, there is no compiler that helps you out with type checks.


      4. Going even further

      As we have seen, a JavaScript object is easy to use in TMS WEB Core if we have defined a suitable record type for it.

      The pas2js compiler (or better transpiler) converts Object Pascal to JavaScript and records become JavaScript objects. But not only that, Object Pascal arrays become JavaScript arrays. Better said: Records are JavaScript objects, Arrays are JavaScript arrays. And since JavaScript arrays are just a special form of JavaScript objects we can take advantage of this and access JavaScript arrays in the same way we access JavaScript objects.

      This is illustrated by the following example, where we retrieve a whole list of users via https://jsonplaceholder.typicode.com/users. The corresponding code looks like this:

      procedure TMainForm.BtnFetchMultipleUsersClick(Sender: TObject);
      type
        TUser = record
          id: integer;
          name: string;
          email: string;
        end;
      
        TUserArray = array of TUser;
      
      var
        request: TJSXMLHttpRequest;
        json: String;
        javascriptarray: JSValue;
        users: TUserArray;
        user: TUser;
      
      begin
        WebHTTPRequest.URL := 'https://jsonplaceholder.typicode.com/users';
        request := await ( TJSXMLHttpRequest, WebHttpRequest.Perform() );
        json := string(request.response);
        javascriptarray := TJSJSON.parse(json);
        users := TUserArray(javascriptarray);
      
        user := users[4];
      
        ShowMessage(Format('Received %d users. User %d''s name is %s. You can reach him/her at %s',
         [Length(users), user.id, user.name, user.email]));
      end;

      Here we convert the result of the web request using a type cast into a standard array and can thus access all contained user records of the array in a type-safe manner as shown in the ShowMessage() method.


      5. The other way round

      In the previous examples we always cast JavaScript objects into records to be able to access JavaScript objects easily and comfortably from TMS WEB Core. Often we need to go the other way. If we need to pass more complex data structures to web services, we need to serialize records or objects to pass them to JavaScript functions. Our new principle works fine in this direction as well. Wherever a JSVALUE is expected, we can easily pass a record and this time we don’t even need a typecast, because the receiving side (JavaScript) only knows universal variables, in which arbitrary values or objects can be stored.

      Here is a small example:

      function AddAToB(value : JSValue): string; assembler;
      asm
        value.result = value.a + value.b;
        value.s = 'The sum is ' + value.result;
        return JSON.stringify(value);
      end;
      
      procedure TMainForm.BtnTheOtherWayRoundClick(Sender: TObject);
      type
        TSampleRecord = record
          a, b, c, result : double;
          s : string;
        end;
      
      var
        sample: TSampleRecord;
        json: string;
      
      begin
        sample.a := 4.5;
        sample.b := 3.2;
        json := AddAToB(sample);
      
        ShowMessage (json);
      end;

      In this example, we use the assembler directive to define a JavaScript function AddAToB() that expects a JavaScript object as a parameter. This function does not know the type of the passed object, but it makes assumptions about it in its code (welcome to the dark side). And so it performs an addition of the values value.a and value.b and stores the result in value.result. Also, the result is stored as a string in value.s and the entire value object in JSON representation is returned as the result.

      To call this dark side function AddAToB(), we define a TSampleRecord with the values a and b, which we assign numbers to and then use that record as parameter for the function call.

      Because of the type declaration, we as callers are not able to preset the values a and b with strings. The type check of the compiler will prevent that.

      We can get another interesting information from the function result: The member c is contained in the result and has the value 0. Although the function AddAToB neither knows about the existence of c nor has set the value. This is because we passed our record as a parameter to the function and there the c is defined and during the initialization of the record in the Object Pascal world all members were preset with default values.

      So if we use records for values passed to JavaScript functions, we can’t forget to create necessary members in the code, because they are always pre-allocated by the record usage.

      We hope that this blog article gave you some interesting insights into the backstage of JSON and JavaScript objects in the TMS WEB Core concert.

      Author: Martin Suer

      Symproject GmbH https://www.symproject.com




      Viewing all 1006 articles
      Browse latest View live


      <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>