Skip to content
Newer
Older
100644 225 lines (141 sloc) 8.52 KB
fccc685 Initial open-source release
MLstate authored
1 Guidelines for writing new Components
2 =====================================
3 Mathieu Baudet <mathieu.baudet@mlstate.com>
4 v1.1
5 (C) MLstate 2010
6
7 ////////////////////
8 asciidoc format: use 'asciidoc GUIDELINES' to compile into HTML (or not)
9 ////////////////////
10
11
12 == Inherited guidelines ==
13
14 The following sections of the guidelines of widgets (see +../widgets/GUIDELINES+) apply as well to components:
15
16 - Definitions
17
18 - Package and naming conventions
19 (for modules, replace +W+ by +C+, as in +CDataSheet+)
20
21 - Configuration of widgets
22
23 - Management of DOM identifiers
24
25 - HTML styles and CSS classes
26
27 - Tests and examples
28
29
30 == General design of components ==
31
32 Components are active, in the sense that they use one or several OPA sessions to operate an internal state.
33
34 Components should never be connected directly to the database. They must rely on an API provided by the developer to do so.
35
36 .Graphical components (client-side only)
37 Purely graphical components are created and installed in one step.
38
39 More precisely, a client-side call to creation function will:
40
41 - initialize the internal session(s) using the given configuration and parameters
42
43 - compute and then install the HTML of the components on the given id
44
45 - (optionnaly) process the already given data to fill the view in a synchroneous way
46
47 - return an abstract value containing the channel(s) of the created session(s) as well as the constant parameters (e.g. the id).
48
49 .General components (client-side and server-side)
50 More complex components (e.g. Chat, ApplicationFrame) that are not purely graphical may require a prior initialization on the server to create a server-side object (generally a session containing a network of clients).
51
52 The graphical part of such a general component is designed exactly as a purely graphical component.
53
54 [NOTE]
55 Components that are often used to render the content of the initial home page of a web site may include a 'bootstrapping widget' (see section below) to make content indexation possible.
56
57
58 == Client/server slicing ==
59
60 Graphical components should be entirely run on the client-side, unless they are explicitly provided with server functions, or connected to server sessions. (This applies in particular to the graphical part of a general component).
61
62 Default configurations must be available on both sides and the functions that they contain must be serializable.
fe16af9 @OpaOnWindowsNow [feature] stdlib,opabsl: removing all S3 occurences
OpaOnWindowsNow authored
63 => In current version, this means that the functions must be defined at top-level and themselves available on both sides or that they should be obtained with @public_env.
fccc685 Initial open-source release
MLstate authored
64
65 CAUTION: Please ensure these properties by testing your components, e.g. by deconnecting the server after the page is loaded. OPA experts may also use the compiler option +--dump-slicer+.
66
67
68 == Public interface of graphical components ==
69
70 .Functions and types
71 The minimal API of a component includes
72
73 - one creation function
74
75 - a function to send messages to the component's session
76
77 The main expected functions are
78
79 ---------------------------------
80 create(config : CXxx.config, id : string, initial_display : CXxx.display, initial_content : CXxx.content, data_writer : CXxx.data_writer, data_requester : CXxx.data_requester) : CXxx.instance
81
82 send(obj : CXxx.instance, message : CXxx.message) : void
83 ---------------------------------
84
85 where
86
87 - +CXxx.config+ is the type of configurations,
88
89 - +CXxx.content+ is the type for the content of the component
90
91 - +CXxx.instance+ is the type for an instance of the component itself,
92
93 - +CXxx.display+ is the type of the display parameters (display parameters may evolve during time as opposed to a configuration),
94
95 - +CXxx.data_writer+ is an automaton that will process the updates coming from the user interface and forward them (by side-effect) to the ``outside world".
96
97 - +CXxx.data_requester+ is a set of callbacks to request some content for the component.
98
99 A typical definition for the data_writer is:
100 ------------------------------
101 type CXxx.data_writer = CXxx.value -> void
102 ------------------------------
103
104 but more complex definitions may assume that a state is passed along as in:
105 ------------------------------
106 type CXxx.data_writer('state) = {
107 initial_state : 'state
108 on_change : 'state, CXxx.content -> CXxx.instruction('state)
109 }
110
111 type CXxx.instruction('a) = Session.instruction('a)
112 ------------------------------
113
114 NOTE: The implementation of +CXxx.instance+ should be private to the package.
115
116
117 A typical definition for the data_requester is:
118 ------------------------------
119 type CXxx.data_requester = () -> option(CXxx.content)
120 ------------------------------
121 A +data_requester+ is expected to return +{ none }+ if the data is not immediately available. Otherwise, the data may be sent later to the component using a message (see below).
122
123 IMPORTANT: By default, data should be requested whenever the display is being updated and a value to be displayed is missing.
124
125 NOTE: For bigger components with multiple contents, +CXxx.content+ may be replaced by a map from +CXxx.address+ to +CXxx.value+s.
126 -----
127 create(...initial_content : list(CXxx.address, CXxx.value) ...)
128
129 type CXxx.data_requester = CXxx.address -> option(CXxx.value)
130
131 type CXxx.data_writer('state) = {
132 initial_state : 'state
133 on_change : 'state, CXxx.address, Cxxx.value -> CXxx.instruction('state)
134 }
135 -----
136
137
138 .Typical messages handled by the components session
139
140 ------------------
141 type CXxx.message = // public interface of the session
142 / { setDisplay : Cxxx.display }
143 / { setContent : CXxx.content)) }
144 / { clearContent : void } // mark the content as undefined (i.e. missing)
145 / { getContent : option(CXxx.content) -> void) } // iterate on the content
146 / { stop } // Terminate the session of the component
147
148 type CXxx.internal_message = // internal messages (if needed)
149 CXxx.message
150 / { notifyContent : option(CXxx.content))) } // may be needed internally when the value has just changed, and the data_writer needs to be called
151 ------------------
152
153 Compared to the +data_writer+ and +data_requester+, the messages +setContent+, +getContent+, +clearContent+ above provide an alternative asynchroneous interface to execute read/write operations on the data.
154
155 == Interface of general components ==
156
157 In the case of general components, a +server+ must be initiated first:
158
159 ---------------------------------
160 init(config : CXxx.config, ...) : CXxx.server
161
162 create(config : CXxx.config, server : CXxx.server, id : string, initial_display : CXxx.display, initial_content : CXxx.content, data_writer : CXxx.data_writer, data_requester : CXxx.data_requester) : CXxx.instance
163
164 send(obj : CXxx.instance, message : CXxx.message) : void
165 ---------------------------------
166
167 Server-side operations acting on the objects +CXxx.server+ may be defined as well.
168
169 == Typical usage of the interface ==
170
171 The typical way to install a new component somewhere in the DOM is
172
173 - select an +id+ to install the component and that can be used as a prefix
174
175 - create a config :
176
177 ---------------------------------
178 config = { default_config with foo = myvalue }
179 ---------------------------------
180
181 - (for general component only) initiate a server-side object:
182 ---------------------------------
183 server = init(config, ...)
184 ---------------------------------
185
186 - create and install the component,
187
188 for graphical components:
189 ---------------------------------
190 obj = create(config, id, ...)
191 ---------------------------------
192
193 for general components:
194 ---------------------------------
195 obj = create(config, server, id, ...)
196 ---------------------------------
197
198
199 == Bootstrapping widget (optional) ==
200
201 The 'bootstrapping widget' of a component is a pure function
202
203 ---------------------------------
204 html(config : CXxx.config, id : string, initial_display : CXxx.display, initial_value : CXxx.content, connect_me : CXxx.type -> void) : xhtml
205 ---------------------------------
206
207 or for general components
208 ---------------------------------
209 html(config : CXxx.config, server : CXxx.server, id : string, initial_display : CXxx.display, initial_value : CXxx.content, connect_me : CXxx.type -> void) : xhtml
210 ---------------------------------
211
212 that is used by the server to create a temporary, robot-friendly version of the page (typically for indexation).
213
214 It is expected that the server uses this function like this
215
216 ---------------------------------
217 <div id=#{id}>
218 { html(config, id, ...) }
219 </div>
220 ---------------------------------
221
222 The computed HTML will include a handler +onready+ that will call the function +create+ of the component so that the boostrapping widget disappears as soon as the JS runtime is operational on the client side.
223
224 This handler will also install the object returned by +create+ by calling the appropriate callback (named +connect_me+ in the example above).
Something went wrong with that request. Please try again.