Managed forms for a long time and firmly entered in the life of 1С developer. However, many people still are not familiar with them. I propose to examine what is it.
This article is aimed at the developers who had already time as it chanced to work with the managed forms. If you already know well how the MF and generally the application managed interface work, you can not read the article, since hardly learn a lot.
A little history
Historically, the client 1C application was always implemented as a «thick» client – the program that does everything in terms of data processing. This created a significant load on the network and also required the presence of powerful client machines.
Thick client was its own advantages, because it is simple, understandable and often does not require the additional architectural units as an application server.
Release 8.0 brought us a three-level architecture in which «1С:Enterprise server» appeared – a standalone software component which could help to execute a business logic code on a dedicated powerful machine (server), thereby, reducing the volume of data transferred over the network and the requirements to the hardware of client machines.
Everything would be good, but before release 8.2 and availability of managed application, implementation of server code was not necessarily part. There were a number of problems, objective and subjective, that made it difficult to use server for business logic processing. The server-side code was executed, but in very rare cases, when it was forced to executed there, with the help of crutches.
Basically, everything remained as before and the client application remained a «thick» client that transfers the hundreds of megabytes over the network.
Release 8.2 cardinally broke an established approach to the application operation and forced to look in a new way at the application architecture. This approach will be discussed in this article.
Magical beast Client-server
With release 8.0, all the training courses, books, articles on the ITS, etc., in one voice repeated to us – «data processing must be performed on the server», «everything must be executed on the server». The magic spell «on server» tightly filled the brain, so much so that no one paid attention for it, like for information noise.
The experience shows that the average programmer almost never knew hot to port the code on the server and what does it mean at all – «on server». Surprisingly often the code was met which was transferred «on server», but was written with an obvious lack of understanding of what this will bring all in all.
In fact, everything is simple. The phrase «execute on server» means that your code will be executed on the physically another machine. Entirely on the other computer (which can be located on another continent), with all the ensuing effects. There will be another structure of directories, other access rights and even another operating system.
However, many experienced developers easily wrote such code:
- Local file is selected
- Its path is transmitted to the server
- There the file tries to be opened in this path and falls with error «File not found» (of course, it remained on the client).
Transition to the server
Before release 8.2, it was possible to transfer the code execution on the server only with one way – namely, by calling the general module with flag «Server». And only with this flag. If in addition to the flag «server» to set «client», the transition will be not performed. On this occasion, a good article was published on the Infostart not so long ago about the flags in the properties of general modules (unfortunately, I cannot find a link, tell me?).
The essence of all the module flags comes to the simple rules:
- The flag show where the module code will be compiled (on client, on server, in external connection)
- If the module is compiled in several places, it will be available only in accordance with the flags. Client – on client, server – on server. The execution transition from the machine to the machine will be not performed.
- The transition of code execution is possible only if in the current execution context there is not the called module, but it is in another place (if the module is only on the server and it is not on the client, then the server call will be performed)
Flag «Server call»
In release 8.2, the flag «server call» was added which helps to settle the conditions of transition to another machine. If to set this flag for the module, the module will be available from the client, if not – an attempt to call from the client will fail. The module code will be not visible, as if it is not really present.
Thus, in the general thick client it is possible to port the code on the server only if to call the general module from the client for which:
- The flag «Server» is set
- The flag «Server call» is set
- The «client» flags are unchecked
Features of transition to the server
The platform takes care of the fact that the transition of code execution to another machine was as unnoticeable as possible. We call the functions of server code, pass the parameters there, receive the returned values, obtain the changes of parameters passed by the reference. Everything looks like there is no server at all. However, it is not the case.
Any call of server code always performs the serialization of transmitted data. All parameters are packed in the string type and passed over the network. The results of work are also transmitted back in the serialized form, where they are restored later in the usual objects.
This explains why not all the objects can be transmitted on the server. This is because not all of them support the serialization (conversion to string and back). It is not allowed to transmit on the server, for example, «DocumentObject», because the global variables can be in its object module and they can store some complex non-serialized values, like COM-objects… In short, the restrictions of exchange with server are clear and reasonable. It is necessary to ensure what we transmit on the server and return back.
And, of course, some interface things are not available on the server, like form opening. Nobody will see the forms opened on the server, nobody can click the mouse on them, because the user stayed sitting at another computer.
I think that the simplicity of server call implementation played a nasty trick on the 1С platform. The server call is so transparent that it is hard to see it, to realize that here, from this moment, we are on a completely different computer. As a result, we have such code which I wrote above.
Absence of context (state) on the server
There is such a model of server applications, where the serve only responds to the requests, but does not save anything between two requests. This model is called the «state-less» model.
All HTTP server of the modern internet operate that way. The clients send the requests, the server responds with answer and forgets everything it received from the client. Of course, there are variations, but the common model is exactly like this: the server processes the request and cleans all data.
1С server operates on the same principle. In general, it is not allowed to store in the server memory the intermediate data between the calls. It is impossible to define a variable to store data between two calls.
There are the special objects in which it is possible to store a very small part of data – so-called «session parameters», but this is a specifically allocated space to store the state. Each server call performs some job, returns a result and forgets all its data. It is required to always remember about this in the managed mode.
With the release of managed application, «1С» company dealt with client-server interaction, in my opinion, absolutely correctly. It did not start to hide it, quite the opposite, it forced everyone to see it, rubbed everyone’s nose in it: «look, here you write the code for physically different machines, think about what are you doing ».
A new approach for client and server interaction allowed creating a new model of client interface. Now, the interface is declared, the interface design starts with data, attributes and table parts. Creating an attribute, you have to think how it will look in the interface, whether it will be the required one, how it is related to the other attributes, etc.
The managed forms are a great step forward compared to the old approach. Everyone who worked with MF more or less closely does not want to return back, like in the nightmare. The managed forms allowed implementing the interface as a Web-page, and the developer does not have to think practically about what the site writes. The same code works both in the browser and native client. As for me, this is very cool.
The client application was maximally simplified, it ca no longer perform the queries to the database and hence does not required huge network resources. It can be just a browser at the last. The client window simply displays for us an interface picture, all the complex processing must be performed now on the server.
Such simplification was not given free. A lot of familiar operations started to be executed more complicated. The above-mentioned serialization did not go away, of course, and the data that could be passed to the server still cannot be passed there.
All this has led to the features that we face when developing the managed forms. The key feature, in general, is only one – the form is completely separated from the object data.
Reading data from IB in the form and writing from the form to the IB is performed almost explicitly, this moment can be captured in the form events.
Thin client has become a true «client». It does no longer process the data, it displays them and allows you to edit. The whole processing now we transfer on the server.
Structure of managed form
Let’s look at what parts the managed form consists of.
The most important part in the managed form is its attributes. Those little blue that are located in the upper right panel. In fact, these are the form data, its basis. Only what is shown in the attributes can be placed on the form форме.
In the left panel the form items are located. These are the buttons, input boxes, tables, etc. Their main difference from the general forms is that they are necessarily associated with some form attribute. A separately hovering item cannot be placed on the form, it must be associated with the data.
There is already an old joke about DataCompositionResultOutputProcessorInSpreadsheetDocumentNamedAfterNuralievBorisGeorgievich.
The managed form adds the another pretender for the title of a long and incomprehensible identifier: FormDataStructureWithCollection and also a number of new classes with the names like «FormData….bla-bla-bla». Let’s try to figure out what are the new objects.
As it was mentioned above, the enterprise server does not save its state between two calls. When we will call the server, it will execute something for us and will clean all data at once created during the call. The objects with the names «FormData…» are the place where we can save our data between two server calls. It is impossible to create on the server the global form-wide variable. This global variable should be the attribute of form data.
Please note that the classes of form data are called «FormDataStructure» or «FormDataCollection». It generally seems to me not an accident. These objects are just the modified standard Structure (or ValueTable, respectively).
Here the general forms should be recalled a little. After all, as we got used to – in the catalog form it is enough to assign the variable «Description» some text and this text will be included in the CatalogObject, and from there – in the information base.
Everything as almost the same in the managed form, except the fact that it is not so:). The platform takes a lot of efforts that in the daily programmer work he did not have to bother with it. Usually, we, as before, assign the properties to the form attributes and they get in the CatalogObject, and from there – in the IB.
The difference is that now there is no «CatalogObject» on the client. As I said earlier, this is a complex object, it is not serialized, so, it cannot be transmitted from the client to the server. Instead of «Object», there is its simplified equivalent on the client – data repository – an object FormData.
In fact, this is just a structure in which the properties coincide with the object property. With al the «similarity», these are quite different objects. And not only conceptually, this difference is clear, it can be «touched» from the code and you have to work with this difference in an explicit form.
Dorm data addressing
Form data are available directly in the code by those names as they appear in the tree of form attributes (in the designer window)
The top-level attributes are available at once. The nested attributes are available through the point. Earlier, in the general form we access to the object data directly. We wrote «Name = «АААААА» and data appeared in the object. Now, it is required to write «Object.Name = «ААААА», because an attribute «Name» is nested inside an attribute «Object».
Exchange of form with server
The main «engine» of all managed form aspects is the fact that we have a state-less server. It does not store anything, no intermediate data. The entire form state (text in the input boxes, rows of table parts, etc.) is stored on the client. All the form data is called a form context.
The entire client-server exchange is performed using the data serialization, and this, in turn, imposes the restrictions for those data types that can be transmitted to the server and back. Object FormDataStructure and its «alliance» is a sort of «repository» which walks from the client to the server and back. Only what is stored in the form data can be saved in the gaps between the server callls.
Life cycle of form object
Let’s look at what happens when we close the form of existing catalog item.
- On the client the method «OpenForm» is called or we simply open the form from any catalog list. Server call is started
- In the serve memory a new CatalogObject is created, the object module code is executed (which is written at the bottom of the module)
- The object data are read from the database, the values of attributes are assigned, the table parts of object CatalogObject are filled.
- In the server memory a new managed item form is created
- A form event OnReadAtServer is called where e new created CatalogObject is passed in the parameter CurrentObject
- Main form attribute «Object» which is highlighted in the list of attributes with bold font is filled by the data based on the data of CatalogObject. There is a usual element-wise assignment of the properties of object FormData with values written in the similar properties of object CatalogObject. In fact, «FillPropertiesValues (FormData, CatalogObject) is performed
- CatalogObject is destroyed. All its data are erased from the server memory.
- A form event «OnCreateAtServer» is called in which we receive already filled FormData
- Form data are serialized and sent on the client where the form is displayed for the user
Please note that CatalogObject to which we get used so in the general application has been destroyed. If any global variables, export and non-export, were in the object module, all of them were lost.
It is impossible to save anything in the object data, the entire state saving must be executed in the FormData.
Now, let’s consider the process of catalog data writing:
- User clicks the button «Write»
- The form processor «BeforeWrite» is executed (on client)
- Server call is performed
- In the server memory a new CatalogObject is created, it is filled with data based on the data of current form (something like FillPropertiesValues, but in the opposite direction – it is written from the form to the object)
- The form event handler «BeforeWriteAtServer» is called where a new created CatalogObject is passed. From this moment, if we want to write something into the database, it is necessary to change CatalogObject. The changes of form data will not be displayed in the written object.
- The handler «BeforeWrite» of CatalogObject is called (in the object module)
- The handler «OnWrite» of CatalogObject is called (in the object module)
- The form event handler «OnWriteAtServer» is called, the write transaction is not yet completed and it is possible to cancel writing
- The form event handler «AfterWriteAtServer» is called, the write transaction is already completed
- CatalogObject is destroyed
- Return to the client is performed where the form event handler «AfterWrite» is called
Please note that the server state is restored from scratch, each time a new CatalogObject is created. In the gaps between the server calls it is not saved.
Form data conversion
To simplify life, there are the simple ways to convert the universal objects «FormData….» in the usual applied objects and vice versa.
For example, it is possible to receive the filled with data CatalogObject base on the form data if to call method «FormAttributeInValue(«Object»). This will cause what is described above – a new CatalogObject will be created and filled with data from the specified attribute of form data.
The inverse conversion is performed using the method «ValueInFormAttrtibute». If we made some actions with data of CatalogObject, for example, cleaned the table part, we must to place our changes back in the form, otherwise, what will be?
That’s right, the changed CatalogObject will be destroyed on return to the client and the form data will remain unchanged. The object write cycle will be constructed on the old form data, and therefore, the table part modification will just disappear.
Things can’t be all that bad 😉
All of these complexities with data conversion are useful to know, however, they are not obligatory. The platform manages with them by itself perfectly. In most cases, it is enough to never think of CatalogObject and work directly with form data. The platform will do dirty work itself.
I cannot talk about the form commands a lot, this is a pretty simple thing. The command represents some actions that can be launched by the user. Just as the input boxes and flags are associated with the form attributes, the buttons and hyperlinks are associated with the commands. Each button necessarily refers to some command.
The command has the handler, this is the main difference from the buttons in the general forms. In the managed form the button has no event handlers. The buttons only launch the command execution.
Each command can be placed on the command panel, submenu or in the form itself. The commands are created in the tab «Commands» in the form designer.
The parameters are the special variables that can be passed in the form during its opening. The parameters are specified in the tab «Parameters» of form designer and we will talk about them later.
Form opening control
Form display in the managed mode also underwent certain modifications. First of all, the forms received the names by which it is possible to access to them. For example, the list form of catalog «Contractors» will have a name «Catalog.Contractors1.ListForm».
The technique of form opening is now as follows: the form must be opened with one code line and with one server call. This task is taken by the global method «OpenForm». In this method we must pass the form name (see above), form parameters, owner and uniqueness key. All parameters, except the name, are optional.
Since the form opening is an interactive operation, it can be performed only on the client. However, the form creation, as we saw from its life cycle, is performed on the server. A reasonable question appears, how to open the form with line code line, if it is required to parameterize somehow its creation, to pass it something that she knew how exactly it should be created?
The Form parameters are required exactly for this. This is a structure that can be passed in the method «OpenForm». The passed parameters can be analyzed in the server form code and do some actions based on the passed parameters.
Simple example, opening the list form with selection. To open the catalog list with selection by a certain condition, it is required to open a list form and pass it parameter with name «Select» whose value will be a structure with selection fields and selection values.
ConditionsSelection = New Structure; ConditionsSelection.Insert("ContractorType", "Supplier"); ConditionsSelection.Insert("Loyalty", "Reliable"); OpenForm("Catalog.Contractors.ListForm", "SelectionConditions");
There are the system parameters that the platform reacts itself. The mentioned parameter «Select» is a system, if it is passed to the list form, then the platform will set itself the selection form the list.
In addition, it is possible to create the arbitrary parameters in the form designer. Moreover, it is possible to pass the parameters even with those names that are note declared in the form, they will be visible in the server form code all the same.
Life cycle of parameters
All parameters passed in the form at the moment of its opening are visible in the procedure «OnCreateAtServer». After creation, all parameters are destroyed and are no longer available in the form.
The exceptions are the parameters that are declared in the form designer with the sign «Key parameter». This parameter will exist as long as the form exists itself.
Procedure OnCreateAtServer(Cancel) If Parameters.RunNuclearReactorAtOpening Then ControlReactor.Start(); EndIf; EndProcedure
The key parameters determine the window uniqueness. The general forms had such attribute «Uniqueness key». If the form was requested, firstly, it was looked for in already opened ones. The uniqueness key allowed varying the conditions under which a new form was created or the existing one was returned. The key parameters play the same role. If method «OpenForm» is called two times with the same value of key parameter, the second call will not open new window but will activate the existing one.
The managed interface added several interesting features in the form module. All these features provide the programming of client-server interaction in an explicit form. I mean the compilation directives.
For justice’ sake, it should be noted that they are available not only in the form modules, but are usually used only there. The compilation directive is a string like &AtClient before procedure declaration.
For the form module the following directives are available:
The directive tells the compiler where it is necessary to compile the specified procedure. All is clear with directives «AtClient» and «AtServer», but the addition «WithoutContext» we need to examine more in detail.
Let us return one more time to the model of client and server interaction. Imagine: the user edits a big form, it contains several table parts and a lot of attributes. All these data (unless you consider different, independent from the developer optimizations) are stored in the form context, that is, in its attributes. No data are stored on the server, it does not know about what letters are written on the fields of our form.
Now, the user clocks button that calls the procedure with directive «AtServer». All form data must be transmitted to the server so it is possible to work with them there. The procedures «with context» (AtClient and AtServer) see the form context, i.e. its data. When we call the server «with context», the context mast be transmitted on the server, because it does not exist at all there at the moment (state-less server).
All attributes and table parts are serialized, json-document is generated with form data and this document is sent to the server. On the server json is read and the server form context is raised. Next, our server code is launched which sees this miracle: on a completely different machine, even on another continent, in t he server form part all data are alive and we can work with them.
Since everything is so transparent for us, sometimes it seems: «what a beauty, do not bother, we can always write «AtServer» and everything will be available on the server». However, as it is evident from the previous paragraph, such transparency is not free. All form data must be packed, sent to another continent, unpacked there after that the server code must be executed and then the inverse pack and return of the changes on the client. It is clear that the overhead costs for context transmission are very high.
The platform performs a number of optimizations for context transmission. For example, between the client and server only the context changes are transmitted, rather than the context as a whole. However, this is not always and the form often is sent almost completely. In addition, the programmer cannot explicitly control the process and it is possible to think that the call of server method «with context» transmit all form data and then also receives them back with those changes that are made in the data on the server.
To simplify the client-server exchange, the concept of server call «without context» is entered. It differs in the fact that when calling «without context», the form context is not sent and is not available in the server code. The procedure «without context» does not see the form data. In fact, this is not a form module at all, but some kind of module which does not know anything about the form.
It is possible to pass the parameters in the method without context and to receive the returned values, however, it is impossible to access to the form data, since the form does not exist itself.
The advantage of the call without context is that it is executed quickly enough. There are no extra data, there are no extra operations, the server call is simply performed with passing several method parameters.
The calls without context allow you to communicate with server and avoid unnecessary costs to transmit the form context. Of course, it is possible to make very slow even the call without context, if to pass as a method parameter some huge text on many megabytes.
Form module-level variables
The global variables can be declared in the form, and their accessibility is regulated by the same compilation directives.
Moreover, the variable declared AtClient will be available during the entire form life cycle, and the variable AtServer will be cleaned every time on return to the server (on destruction of server call data).
Temporary repository – secret weapon
When working with managed forms, it quickly becomes clear that the restrictions of client-server interaction significantly narrow the developer’s possibilities. Therefore, 1С platform provides a cunning secret passage named «TemporaryRepository». This entity allows storing on the server certain state between two server calls. I hear the cries: «Who said that the state cannot be stored!» Reasonable remark, the state can be stored, but not for long.
The temporary repository allows leaving on the server some data and receive instead of them a short string – address of temporary repository. This address can be sent on the client, it will be stored there quietly, and when the data will be needed from the repository, we can get them at this address.
To work with temporary repository, the methods PlaceInTemproraryRepository and GetFromTemporaryRepository are used.
Everything is simple with method «Get», it returns value at the specified address which is located in the repository.
It is somewhat more complicated with method «Place». The problem is that it is possible to place in 2 different repositories – simple one and form repository. Form repository is alive on the server while the form itself is alive. Simple repository lives exactly 2 server calls. In the syntax assistant it is written as much as a few clever lines. I never understood what they meant. It was found experimentally that the life period is 2 server calls.
- The first – we place something in the repository and return the address on the client.
- The client performed the second call and can receive data at this address.
- In the third call nothing will be available at this address. The platform will clean the repository
The situation is more interesting with the form repository. If to pass as the second parameter in the method «PlaceInTemporaryRepository» the form identifier (ThisForm.ID), then the data in the repository will be associated with the form life cycle and will be not deleted neither in the first nor in the second and all other server calls.
Thus, the temporary repository is another place in which (along with the form attributes) it is possible to save the state on the server. Moreover, it is possible to store in the repository even data that are unavailable on the client and, therefore, can not be placed in the form attributes.
Temporary repository features
It is the best to imagine the temporary repository as a file on the server. Technically it is not the case, but the optimization features can drink blood. It is easier to think of it as a file.
The data that we place in the repository must be serialized. Otherwise, they can suddenly disappear from the temporary repository.
How roughly operates the temporary repository:
- Data are place in the TR
- Control is returned on the client
- Client makes the second server call
- Cluster manager switches the call to another machine from the cluster (not the one that put data in the repository)
- And here, if data in the repository were serialized, then everything is fine, we will receive them even on another cluster machine.
- If data were not serialized (CatalogObject, COM-object, something else), they will simply disappear.
If not to go into the documentation jungle, we can say something like this: the data in the temporary repository exist within two server calls or, if they are placed in the form repository, during the form life.
The repository data can be serialized at any point of time at the server option. If the data had to be serialized, they can be received back only if the type of these data supported the serialization (most platform types support).
If type of data placed in the repository did not support the serialization, the data are simply destroyed.
The data are placed in the repository by reference. This means that placing the data in the repository and changing them outside the repository, we, in fact, change the repository itself, because it contains the reference to these data. Example:
Array = New Array; Array.Add("Hello"); Address = PutToTempStorage(Array); Array.Add("Goodbye"); ArrayStorage = GetFromTempStorage(Address); Message(Array = ArrayStorage); // True Message(ArrayStorage.Count()); // 2 items
In the above code, array is placed in the repository after which one more item is added to it. We see that the repository contains the same object and the object received from the repository is changed.
However, if data had to be serialized (for example, he switch was performed to another cluster server), then the references will be already different, for obvious reasons.
On the one hand, the logic is fairly inconsistent, on the other hand, do not worry. The main thins is to remember about the pitfalls and do not apply logic for some accidental repository behavior.
- Interaction with server is not hidden now from the developer. Rather, it reminds about itself every minute. And this is good.
- 1С server does not save the data between the client accesses to the server. It is necessary to design the interaction subject to this feature
- Temporary repository is a powerful tool that eliminates the complexities of state-less interaction and allows constructing the complex schemes of server calls.
- The form has context which is transmitted from the client to the server and back in each server call. For simple calls, where the entire form is not required as a whole, it is necessary to use the calls without context.
Managed interface is a radically new approach to construct the applications on the 1С platform. It brings its complexities, but introduces a large amount of advantages. Definitely, managed interface is a future, and I did not see a person who wanted to return to the general forms after working with managed ones.
It is clear that a single article cannot describe all the aspects of managed form. For example, I did not touch at all the principles of placing the items on the forms, grouping of fields, commands, etc. I think that in this respect there is enough literature and it is not effective to retell the books.
I tried to consider the most non-obvious moments of the structure of managed forms, those that are in the documentation and observed experimentally, but are not mentioned in the textbooks. It is possible to lose a lot of time for these features, if you do not know the architectural principles and features of 1С client and server interaction. If you keep in mind how it works, then it becomes clearer why some mechanism is implemented in such a way and not otherwise.
Good luck with the development of managed forms. If you read to the end, then you have something to learn.