a Lianja app is made up of pages.
Pages are made up of sections of which their are many types, and
form sections are made up of fields and gadgets.
if you want to hand code php you create your php files and specify them as the URL in either WebView sections or WebView gadgets.
Each section or gadget can be written in a different scripting language which is specified in the attributes for the UI element.
In Lianja however when you design a page/section you can bind server side data to the UI elements contained within them.
There is no need to write any server side CRUD code at all. It is all magically handled for you between the web client and the Lianja cloud server.
This reduces a large mount of development and later on, maintenance time.
The Visual Foxpro 9 core UI classes are available in Lianja for use from any of the supported scripting languages.
You visually design pages iteratively, laying them out as you see them in your mind.
You customize their behavior using attributes and delegates.
Server side php and python will be added in a future release as stated on the roadmap.
Note that the Lianja cloud server has its own implementation of Lianja/VFP embedded in it as well as support for any third party ODBC data source using virtual tables.
These provide the same functionality as .php pages with many extensions to simplify development.
So just to summarize, you don’t need to write oodles of php code to handle database CRUD operations as this is all handled by Lianja without the need to code anything.
The whole point of Lianja is to simplify the development and deployment of business applications and this is achieved cross platform and cross device by providing a common design and development philosophy which is…
Lianja Apps are made out of pages. Pages are built out of a wide range of sections and form sections out of formitems and gadgets.
you are using the standard sections and tying to customize them to fit what you are trying to accomplish.
It’s a valid approach. No doubt about that.
However, you may be relying too much on the no code aspect of Lianja, and not utilizing the custom code that sounds more appropriate to your needs.
Look at the developers guide for creating custom sections in VFP.
When you need the level of control you are looking for, I would suggest handling the majority of what you are looking to do with your own code.
There is an appropriate place for no code (drag and drop) and appropriate place for custom code.
Meaning – you do not use the action bar at the bottom of the page, but rather create your own custom canvas section to handle navigation.
you have done this before in VFP, so you know how to do it.
You can create your own grid, append it, filter it, delete from it and the behavior will be exactly as you want it to be.
Look at the Lianja example app called “Lianja Demo” look at the menu options “Custom” and then select VFP Custom Section” and “VFP Custom Gadget”
I think you should figure out what the core functionality of your application is, get that working, then see how you can hook into the features you like from the standard sections.
I would suggest that you create your own classes and subclass them. You can 100% do that in the custom code.
I personally am combining RSP pages with standard sections, it’s what works for me.
Lianja is giving you many options, you just need to make sure you understand how each works so you mix them appropriately.
The Lianja App Center is the focal point for running apps that you have built and deployed using the Lianja App Builder.
You firstly need to use the Lianja App Builder to build your apps.
You then deploy them with their database
and run them under the Lianja App Center.
There are many example apps which you can open and run inside the App Builder as desktop, web or mobile apps.
Users authenticate in the App Center using credentials that you setup in the App Builder “Users” workspace.
The default user is admin/admin.
In the beginning there is data. Without data we have nothing to build LOB (Line Of Business) Apps against.
The primary goal of the Lianja is to provide an Enterprise RAD platform for Desktop, Web and Mobile Apps.
When building large enterprise class applications with 100’s of tables that are used in multiple Apps and their associated Lianja UI elements, it can be tedious and error prone to apply the same section, field and grid column attributes consistently throughout the UI using the attributes panel in the App Builder.
To alleviate this problem and to speed up app development we are extending the Lianja App Builder and the Lianja engine to handle metadata with a new MetaDataBuilder API. This mechanism provides a way of dynamically setting up UI element attributes when a new section is created or when an app is loaded and its pages and their corresponding sections are loaded.
Also, and probably more importantly, any changes to the metadata for the tables and/or their columns contained in the database schema that are used in many different Lianja apps and their UI elements (Sections, Fields, Grid Columns) are propagated throughout apps dynamically as they are loaded at design time.
The metadata can be created externally by a CASE/builder tool (such as XCase) and imported into the Lianja App Builder using “Import from XCase…” which will be added to the context menu in the “Data” workspace. This will populate (or update) a database with table schemas (native tables or virtual tables) and the associated metadata for the database, tables and columns.
Let’s now take a look at how this all works.
Lianja Databases contain tables and their associated indexes. You can associate metadata with a database.
create database mydb metadata <cExp>open database mydb omd = databaseMetaData()
Each table in a Lianja database can have an associated active data dictionary which contains business rules and triggers for the table itself and its columns.
You manipulate these using standard SQL DDL
alter table customers modify constraint name set default “Barry” alter table customers modify constraint name drop default alter table customers modify constraint name set check not empty(name) alter table customers modify constraint name drop check alter table customers modify constraint name set error “Name cannot be blank” alter table customers modify constraint name drop error alter table customers modify constraint name set picture “@!” alter table customers modify constraint name drop picture alter table customers modify constraint name set choices “Apples,Oranges,Bananas” alter table customers modify constraint name drop choices alter table customers modify constraint name set not null alter table customers modify constraint name set null alter table customers modify constraint pkid set autoinc alter table customers modify constraint pkid drop autoinc alter table customers modify constraint name set help “Customer name” alter table customers modify constraint name drop help
Triggers (written in Lianja/VFP):
alter table customers modify onopen “customers_onopen” alter table customers modify onclose “customers_onclose” alter table customers modify oninsert “customers_oninsert” alter table customers modify onbeforeinsert “customers_onbeforeinsert” alter table customers modify onafterinsert “customers_onafterinsert” alter table customers modify onupdate “customers_onupdate” alter table customers modify onbeforeupdate “customers_onbeforeupdate” alter table customers modify onafterupdate “customers_onafterupdate” alter table customers modify ondelete “customers_ondelete” alter table customers modify onbeforedelete “customers_onbeforedelete” alter table customers modify onafterdelete “customers_onafterdelete”
We call this an “Active Data Dictionary” as these business rules are associated with the table whenever it is used in a desktop App, Lianja SQL Server or Lianja Cloud Server. Altering any of the attributes has immediate effect on all applications that use the corresponding table without needing to make any code changes.
The Active Data Dictionary can also be associated and used with Virtual Tables (third party databases e.g MSSQL, MySQL, PostgreSQL etc) when the tables are opened.
To provide a flexible solution so that any type of user defined metadata can be associated with a table you use the METADATA clause on the CREATE/ALTER table SQL DDL commands.
The metadata you specify should be a JSON encoded character string. After a table is opened you can obtain a metadata object using the new TABLEMETADATA() function.
Use customers omd = tableMetaData()
The TABLEMETADATA() function returns an object which is created by decoding the JSON encoded METADATA string from the Active Data Dictionary associated with the table. This can be a complex object containing properties that are arrays of objects or nested objects.
You can change the elements of the metadata object then update the Active Data Dictionary with the new metadata.
use customers omd = tableMetaData() omd.searchpanelvisible = .t. use alter table customers metadata json_encode(omd)
It is important to realize that the members of the metadata object are completely up by the data architect who creates the database schema. This provides them with the ability to create a complex relationship of database tables and their associated UI attributes in a database design tool such as XCase or other similar type of ER CASE tool.
Metadata can also be associated with columns of a table. You use the columnMetaData( nExp | cExp) function to obtain a JSON decoded object containing the column metadata.
omd = object() omd.caption = “Customer Name” omd.searchfield = .t. alter table customers modify constraint name metadata json_encode(omd) use customers omd = columnMetaData(“name”) omd.caption = “Customer” omd.picture = “@!” omd.searchfield = .t. alter table customers modify constraint name metadata json_encode(omd)
How does this all work with the Lianja App Builder?
When you create a new section by dragging a table onto a page (or opening an existing App) Lianja will layout the section, then if the script setupUI.prg exists in the App or setupUI_tablename exists in the database it will be called with the section id as the first parameter and the cursor for the table bound to that section will be current.
// File: database:setupUI_tablename.prg then app:setupUI.prg parameter id local osection = Lianja.getElementByID( id ) // check to see if UI section attributes are same version as last time applied // and if so do nothing (improves performance) if osection.metaDataVersion = tableMetaDataVersion() return endif osection.metaDataVersion = tableMetaDataVersion() local omd = tableMetaData() // Interrogate the metadata and apply the attributes to the section // … // foreach formitem in the form section (can also traverse grid columns) local i local oitem local count = osection.count for i=1 to count oitem = osection.item( i ) omd = columnMetaData( oitem.controlsource ) if not isObject(omd) next endif // Interrogate the column metadata and apply the attributes to the field or grid column // ... endfor
If your App is a Web or Mobile App you will now need to “Preview” it and then “Deploy” it (or rebuild a Mobile App) to reflect the changes made in the metadata.
So I am a beginner in Lianja, after watching all the video tutorials, I understood some of the basic features of the click and drops.
Unfortunately, I still have no idea where the coding begins and how they integrate.
I have no idea where to put lianja code vs where to put the PHP code, and what are the limit of these codes.
For example, I tried putting echo ‘hello world’ (simple PHP) and see how that would affect a section / gadget, but I don’t see hello world displayed anywhere in any section / gadget
Lianja is designed to help you be as productive as possible by allowing you to create Apps with little to no code. That being said, you can use code in a couple of ways.
First, you can write code to be executed by custom delegates at the Page, Section, or Field level.
The other way to use code in Lianja is in a Custom Section where you can write anything you like.
For example, adding a combobox to a canvas section.
a) I want to create my own version of that combo so I need to know how to subclass the existing combo and modify my version’s properties and methods, preferably without having to do that entirely in code
b) once I’ve got the class the way I want it, I want to be able to place it on the page, then tweak it’s properties through the UI
You cannot subclass components like that in Lianja.
you have a rich attribute set , which already includes some of the “new” (non VFP default) properties I use. (eg Mandatory input)
But I still need to add some of my own
No these attributes are fixed and are not user extensible. Delegates are scripting language independent.
there are a fixed set of UI controls which are customized by attributes and delegates.
You asked if you could call the same code from different delegates was my understanding and the answer is yes.
Open up your library in the init load or ready delegates and call the procs from your delegates. IOW use them as a wrapper.
When v2 development is underway we will be looking at the ability to subclass components from a component library
you can do whatever you want in custom sections but canvas sections do not support Subclassing of custom components.
You have to bear in mind that Lianja is not just for building desktop apps but for web and mobile also so we always look at the best way to implement functionality that makes sense across all clients.
we can’t “clone” pages and apps? That EVERY app has to be created from scratch?
we can go in and modify example apps, save them as our own and continue to modify…
If we can do that I cannot understand what would stop me copying a control from one app to another
Yes you can copy apps and save pages and sections as templates.
The Lianja Web Framework which is used in the Lianja Web Client provides a much higher level of abstraction.
You bind data to the sections and their UI elements. The heavy lifting is done for you.
Just design your Apps visually and use the delegates properly.
When data is added or updated (any CRUD operation in fact) the data is fetched back from the server automatically which will result in cascading section relationships and other related content such as sidebar content.
Delegates can remotely call server side procedures/functions. There is no need for any complex MVC style of page requests. Just develop as you would for a client server desktop App.
Validation can be client side or server side also.
Also bear in mind the anatomy of a section and how it is self contained and provides a great deal of built-in functionality.
– Section header
– Section menu
– Search panel
– Section content
– Section footer
Then further consider that all Lianja Apps are built out of pages containing a lot of built-in functionality.
– Page header
– Instant search
– Left collapsible sidebar
– Instant selections
– Recently viewed records
– Recently modified records
– Dynamic collapsible right sidebar
– Sections that can be related together (collapsible or accordion style)
– Page footer
– Data navigation
Page navigation is all handled for you and all the pages of a Lianja App are preloaded so that page navigation is instantaneous.
This level of abstraction is the same in desktop and web/mobile apps.
It’s a different way of thinking I do realize that but Lianja was specifically designed this way to provide a high level of abstraction closer to the way you think about how you want your Apps to look and how you want to be able to navigate between the pages that the App consists of.
The sample apps that I’ve reviewed all seem to bind the UI elements directly binding to tables/datasets?
Where and how does one implement a business logic layer?
Much of what other technologies force you into writing backend server business logic for is simpler in Lianja due to its data centric design paradigm.
I’m just trying to understand how to
1. Separate the UI from business logic and determine which files represent each.
2. Perform business logic before updating the database. i.e. The UI is interacting with objects rather than directly with the DB.
Simple Example – How do I implement the following …
When an Order is saved before completing the Order …
the UnitsInStock needs to be checked for each Product included in the Order to make sure there are Products available.
If there are UnitsInStock, update the UnitInStock for the Product and complete the Order.
If not, do not complete the Order and notify the user.
example_webapp iuses NoCode you are correct.
You can use validation, delegates and database triggers to do that.
In your validation you can call any server-side business logic (call a function) passing arguments from the active records that are represented by local data cursors in the client. (See the description of macro substitution in the Lianja HTML5 Client API).
When does the data actually get changed?
I have added code in the section beforeupdate() and afterupdate() events using curval() and oldval().
I have also accessed the field itself and tried the same code for the changed() and datachanged() events. Nothing seems to display the new value.
If I were to use SCATTER and SCATTER OLDVALUES, when would I access this?
Lianja uses row buffering. A record is updated when the buffer is flushed or when you commit or navigate to another record.
Data from the UI does not magically transfer into the buffers of the embedded database until the field loses focus at which point it will be pushed into the current buffer.
When a record is read, it becomes the “Active record”.
There are two buffers. The “old data” and the “current data“.
Changes in the UI update the “current data”.
When a check for updating a record is made there is an internal flag to denote “dirty” and the contents of the “old” and “new” values are compared.
Thats what oldval() and curval() give you.
The “Changed” delegate is called whenever data changes in the UI. There is a delegate for “Formitem”, “section” and “page”. Lianja looks up the UI hierarchy to determine which one to call. This means you can have a one for a section and not have to duplicate it for each formitem in the section. Thats where you would use SCATTER etc.
You should consider each section to be a “Visual representation” of a SQL table.
So, you would put a “form” section as the first section on a page e.g. “products”.
Then underneath that you create a “Grid” section for “Suppliers”.
You “relate” the “products” section to the “Suppliers” using the relationship builder. Lianja will make a “best guess” of the primary key in products and the foreign key in suppliers. You can change this by double clicking on the section header (or click the “cog” icon) and change the foreign key expression.
Now when you navigate data using the “navigation bar” the “products” will change and this will cascade through all related sections; in this case all “Suppliers” that can supply that product will be displayed in the “suppliers” grid.
You can then release another table from “suppliers” so that when you click on a row in the “suppliers” grid it will automatically relate the sections that have the “suppliers” section as their parent.
So, in summary, think of sections as visual representations of SQL tables and relate then accordingly.
Lianja is an Apps platform. An application is built out of smaller Apps that are grouped together by functional category. These appear in the Lianja App Center.
Apps are built out of pages which in turn are built out of sections. Standard built-in sections are those that are in the “Sections” menu of the “Form Tools Bar”. I would have thought that obvious. This is how Lianja achieves device independence. You build Apps using a high level of abstraction which is mentioned all over the website and forums… Lianja Apps are built out of pages, pages are built out of sections. There are a wide range of built-in sections including form, grid, calendar, report, canvas, tabview, imagestrip etc….. It’s all about a “high level of abstraction” not wasting time with pixel positioning of each UI control.
To build an Application, break it down into smaller discrete Apps and group them together into categories.
Think about how you want the UI to look like, create a page, add sections and relate them together. In other words prototype the whole of your App visually then go back and add specific business logic such as validation, input masks etc.
You use delegates to respond to various actions that occur (Read the blog entry about ART). Delegates can be coded as functions or be inline if they perform simple LOM manipulation (Read the article about the Lianja Object Model). Delegates are a scripting language independent methodology.
Desktop, Web, Tablet and phone Apps are laid out differently based on their UI personality. In other words certain sections can be excluded based on the device. This is all explained in the “resources” menu on the main website.
Tablet and Phone Apps will be able to be built as native apps in v3 as detailed in the roadmap.
if you want to build an an App that will run in desktop, web and mobile “Best practices” dictate you don’t use desktop bound logic nor would you write your delegates in VFP with fat client data manipulation commands; USE, APPEND, REPLACE, SCAN, DELETE etc. Web Apps have a client server architecture. You have to design for it. That’s what the Lianja architecture provides you with; A high level of abstraction with pages and built-in sections with various means of navigating between pages of a SPA (Single Page Application).
The whole concept of sections is that they can be looked upon as being a table in relational database terminology. You then relate the sections together just as you would join tables in SQL.
if your existing VFP application has been written with a clear separation between the UI and the business logic then you can call your business procedures from the client.
If I am developing on my windows box and I run ‘web’, I assume this is also using the cloud-server as you mentioned (just a local version)?
Do you mean “Web App View”?
That is running against your development environment.
Clicking “Preview” deploys that app and fires up your default browser running against the cloud server which is installed when you install APaaS Developer.
The cloud server running on your development machine can be used to run web apps from other machines on your LAN or external if you have a fixed IP address or use something like dyndns.org
underlying UI control contained within the formitem has it’s properties exposed. The textbox is only one of them, there is also checkbox etc.
a formitem is what you reference from the LOM.
a formitem is a field or gadget.
it is a container that has a caption and a data element.
Fields are “formitem” which consist of fields and gadgets.