forms-angular 0.12.0-beta.3 → 0.12.0-beta.300

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,91 +1,440 @@
1
1
  declare module fng {
2
- var formsAngular: angular.IModule;
2
+ export interface IFng extends angular.IModule {
3
+ beforeProcess?: (scope: IFormScope, cb: (err?: Error) => void) => void;
4
+ beforeHandleIncomingDataPromises?: () => angular.IPromise<any>[];
5
+ pseudo?: (token: string, upperFirst: boolean) => string;
6
+ title?: { prefix?: string; suffix?: string };
7
+ // when provided, the named function (assumed to be present on $rootscope) will be used to determine the visibility
8
+ // of menu items and control groups
9
+ hiddenSecurityFuncName?: string;
10
+ // when provided, the named function (assumed to be present on $rootscope) will be used to determine the disabled
11
+ // state of menu items and individual form input controls
12
+ disabledSecurityFuncName?: string;
13
+ // when provided, the named function (assumed to be present on $rootscope) will be called each time a new page
14
+ // or popup is accessed, providing the host app with the opportunity to confirm whether there are ANY hidden elements
15
+ // at all on that page. where there are not, we can optimise by skipping logic relating to DOM element visibility.
16
+ skipHiddenSecurityFuncName?: string;
17
+ // when provided, the named function (assumed to be present on $rootscope) will be called each time a new page
18
+ // or popup is accessed, providing the host app with the opportunity to confirm whether there are ANY disabled elements
19
+ // at all on that page. where there are not, we can optimise by skipping logic relating to disabling DOM elements.
20
+ skipDisabledSecurityFuncName?: string;
21
+ // when provided, the named function (assumed to be present on $rootscope) will be called each time a new page
22
+ // or popup is accessed, providing the host app with the opportunity to confirm whether there are ANY elements on that
23
+ // page that require their child elements to be disabled. where there are not, we can optimise by skipping
24
+ // disabled ancestor checks.
25
+ skipDisabledAncestorSecurityFuncName?: string;
26
+ // how the function identified by elemSecurityFuncName should be bound. "instant" means that it will be called
27
+ // as the markup is being constructed, with 'hidden' elements not included in the markup at all, and disabled elements
28
+ // given a simple DISABLED attribute. this is the most efficient approach. "one-time" will add ng-hide and
29
+ // ng-disabled directives to the relevant elements, with one-time binding to the security function. this is
30
+ // also reasonably efficient (but not as efficient as "instant", due to the need for watches). "normal" will not use
31
+ // one-time binding, which has the potential to be highly resource-intensive on large forms. which
32
+ // option is chosen will depend upon when the function identified by elemSecurityFuncName will be ready to
33
+ // make the necessary determination.
34
+ elemSecurityFuncBinding?: "instant" | "one-time" | "normal";
35
+ hideableAttr?: string; // an attribute to mark all elements that can be hidden using security
36
+ disableableAttr?: string; // an attribute to mark all elements that can be disabled using security
37
+ disableableAncestorAttr?: string; // an attribute to mark all elements whose children can all be disabled using "disabled + children" security
38
+ // if an element's id is a partial match on any of this array's contents, it will never be marked with hideableAttr/disableableAttr
39
+ ignoreIdsForHideableOrDisableableAttrs?: string[];
40
+ keyboardShortCuts? : {
41
+ letter: string;
42
+ keycode: number;
43
+ id: string;
44
+ desc: string;
45
+ } [];
46
+ }
47
+ var formsAngular: IFng;
48
+
49
+ /*
50
+ Type definitions for types that are used on both the client and the server
51
+ */
52
+ type formStyle = "inline" | "vertical" | "horizontal" | "horizontalCompact" | "stacked";
53
+
54
+ export interface IBaseArrayLookupReference {
55
+ property: string;
56
+ value: string;
57
+ }
58
+
59
+ interface ILookupItem {
60
+ id: string;
61
+ text: string;
62
+ }
63
+ /*
64
+ IInternalLookupreference makes it possible to look up from a list (of key / value pairs) in the current record. For example
65
+
66
+ var ShelfSchema = new Schema({
67
+ location: {type: String, required: true}
68
+ }); // Note that this schema needs an _id as it is an internal lookup
69
+
70
+ var ESchema = new Schema({
71
+ warehouse_name: {type: String, list: {}},
72
+ shelves: {type: [ShelfSchema]},
73
+ favouriteShelf: {type: Schema.Types.ObjectId, internalRef: {property: 'shelves', value:'location'};
74
+ });
75
+ */
76
+ export interface IFngInternalLookupReference extends IBaseArrayLookupReference {
77
+ noConvert?: boolean; // can be used by a tricksy hack to get around nesting limitations
78
+ }
79
+
80
+ /*
81
+ ILookupListReference makes it possible to look up from a list (of key / value pairs)
82
+ in a document in another collection for example:
83
+
84
+ const LSchemaDef : IFngSchemaDefinition = {
85
+ descriptin: {type: String, required: true, list: {}},
86
+ warehouse: {type: Schema.Types.ObjectId, ref:'k_referencing_self_collection', form: {directive: 'fng-ui-select', fngUiSelect: {fngAjax: true}}},
87
+ shelf: {type: Schema.Types.ObjectId, lookupListRef: {collection:'k_referencing_self_collection', id:'$warehouse', property: 'shelves', value:'location'}},
88
+ };
89
+ */
90
+ export interface IFngLookupListReference extends IBaseArrayLookupReference {
91
+ collection: string; // collection that contains the list
92
+ /*
93
+ Some means of calculating _id in collection. If it starts with $ then it is property in record
94
+ */
95
+ id: string;
96
+ }
97
+
98
+ /*
99
+ showWhen allows conditional display of fields based on values elsewhere.
100
+
101
+ For example having prompted whether someone is a smoker you may want a field asking how many they smoke a day:
102
+ smoker: {type: Boolean},
103
+ howManyPerDay: {type: Number, form:{showWhen:{lhs:"$smoker", comp:"eq", rhs:true}}}
104
+
105
+ As you can see from the example there are three parts to the showIf object:
106
+
107
+ lhs (left hand side) a value to be compared. To use the current value of another field in the document preceed it with $.
108
+ comp supported comparators are 'eq' for equality, 'ne' for not equals, 'gt' (greater than), 'gte' (greater than or equal to),
109
+ 'lt' (less than) and 'lte' (less than or equal to)
110
+ rhs (right hand side) the other value to be compared. Details as for lhs.
111
+ */
112
+ export interface IFngShowWhen {
113
+ lhs: any;
114
+ comp: "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
115
+ rhs: any;
116
+ }
117
+
118
+ /*
119
+ link allows the setting up of hyperlinks for lookup reference fields
120
+ */
121
+ export interface IFngLinkSetup {
122
+ linkOnly?: boolean; // if true then the input element is not generated (this overrides label)
123
+ label?: boolean; // Make a link out of the label (causes text to be overridden) (this overrides text)
124
+ form?: string; // can be used to generate a link to a custom schema
125
+ linktab?: string; // can be used to generate a link to a tab on a form
126
+ text?: string; // the literal value used for the link. If this property is omitted then text is generated from the field values of the document referred to by the link.
127
+ }
128
+
129
+ export type FieldSizeString = "mini" | "small" | "medium" | "large" | "xlarge" | "xxlarge" | "block-level"; // sets control width. Default is 'medium''
130
+
131
+ export interface IFngSchemaTypeFormOpts {
132
+ /*
133
+ The input type to be generated - which must be compatible with the Mongoose type.
134
+ Common examples are email, url.
135
+
136
+ In addition to the standard HTML5 types there are some 'special' types:
137
+ textarea: a textarea control
138
+ radio: a radio button control
139
+ select: a select control
140
+
141
+ Note that if the field type is String and the name (or label) contains the string
142
+ 'password' then type="password" will be used unless type="text".
143
+
144
+ If the Mongoose schema has an enum array you can specify a radio button group
145
+ (instead of a select) by using a type of radio
146
+ */
147
+ type?: string;
148
+
149
+ hidden?: boolean; // inhibits this schema key from appearing on the generated form.
150
+ label?: string | null; // overrides the default input label. label:null suppresses the label altogether.
151
+ ref?: string; // reference to another collection
152
+ internalRef?: IFngInternalLookupReference;
153
+ lookupListRef?: IFngLookupListReference;
154
+ id?: string; // specifies the id of the input field (which defaults to f_name)
155
+
156
+ placeHolder?: string; // adds placeholder text to the input (depending on data type).
157
+ help?: string; // adds help text under the input.
158
+ helpInline?: string; // adds help to the right of the input.
159
+ popup?: string; // adds title (popup help) as specified.
160
+ ariaLabel?: string; // adds aria-label as specified.
161
+ order?: number; // allows user to specify the order / tab order of this field in the form. This overrides the position in the Mongoose schema.
162
+ size?: FieldSizeString;
163
+ readonly?: boolean | string; // adds the readonly or ng-readonly attribute to the generated input (currently doesn't work with date - and perhaps other types).
164
+ rows?: number | "auto"; // sets the number of rows in inputs (such as textarea) that support this. Setting rows to "auto" makes the textarea expand to fit the content, rather than create a scrollbar.
165
+ tab?: string; // Used to divide a large form up into a tabset with multiple tabs
166
+ showWhen?: IFngShowWhen | string; // allows conditional display of fields based on values elsewhere. string must be an abular expression.
167
+
168
+ /*
169
+ add: 'class="myClass"' allows custom styling of a specific input
170
+ Angular model options can be used - for example add: 'ng-model-options="{updateOn: \'default blur\', debounce: { \'default\': 500, \'blur\': 0 }}" '
171
+ custom validation directives, such as the timezone validation in this schema
172
+ */
173
+ add?: string; // allows arbitrary attributes to be added to the input tag.
174
+
175
+ class?: string; // allows arbitrary classes to be added to the input tag.
176
+ inlineRadio?: boolean; // (only valid when type is radio) should be set to true to present all radio button options in a single line
177
+ link?: IFngLinkSetup; // handles displaying links for ref lookups
178
+ asText?: boolean; // (only valid when type is ObjectId) should be set to true to force a simple text input rather than a select. presumed for advanced cases where the objectid is going to be pasted in.
179
+
180
+ /*
181
+ With a select / radio type you can specify the options.
182
+ You can either do this by putting the option values in an array and passing it directly, or by putting them in an
183
+ array on the scope and passing the name of the array (which allows run-time modification
184
+ */
185
+ options?: Array<string> | string;
186
+
187
+ /* Directive allows you to specify custom behaviour.
188
+
189
+ Gets passed attributes from form-input (with schema replaced with the current element - so add can be used to pass data into directives).
190
+ */
191
+ directive?: string;
192
+ /* Inhibits the forms-angular client from looking up the possible values for a
193
+ IFngLookupReference or IFngInternalLookupReference field
194
+ (when a directive has a an alternative way of handling things)
195
+ */
196
+ noLookup?: boolean;
197
+
198
+ /*
199
+ The next few options relate to the handling and display of arrays (including arrays of subdocuments)
200
+ */
201
+ noAdd?: boolean | string; // inhibits an Add button being generated for arrays.
202
+ noneIndicator?: boolean; // show "None" where there's no add button and no array items
203
+ unshift?: boolean; // (for arrays of sub documents) puts an add button in the sub schema header which allows insertion of new sub documents at the beginning of the array.
204
+ noRemove?: boolean | string; // inhibits a Remove button being generated for array elements.
205
+ formstyle?: formStyle; // (only valid on a sub schema) sets style of sub form.
206
+ sortable?: boolean | string; // Allows drag and drop sorting of arrays - requires angular-ui-sortable
207
+ ngClass?: string; // Allows for conditional per-item styling through the addition of an ng-class expression to the class list of li elements created for each item in the array
208
+ filterable?: boolean; // Add a data-ng-hide to all array elements, referring to subDoc._hidden. Does not actually (yet) provide a UI for managing this property, however (which needs to be done via an external directive)
209
+ subDocContainerType?:
210
+ | "fieldset"
211
+ | "well"
212
+ | "well-large"
213
+ | "well-small"
214
+ | string
215
+ | ((info) => { before: ""; after: "" }); // allows each element in the array to be nested in a container
216
+ subDocContainerProps?: any; // the parameters that will be passed if subDocContainerType is a function
217
+
218
+ /*
219
+ The next section relates to the display of sub documents
220
+ */
221
+ customSubDoc?: string; // Allows you to specify custom HTML (which may include directives) for the sub doc
222
+ customHeader?: string; // Allows you to specify custom HTML (which may include directives) for the header of a group of sub docs
223
+ customFooter?: string; // Allows you to specify custom HTML (which may include directives) for the footer of a group of sub docs
224
+
225
+ /*
226
+ Suppresses warnings about attenpting deep nesting which would be logged to console in some circumstances when a
227
+ directive fakes deep nesting
228
+ */
229
+ suppressNestingWarning?: boolean;
230
+ }
3
231
 
4
232
  // Schema passed from server - derived from Mongoose schema
5
- export interface IFieldViewInfo {
233
+ export interface IFieldViewInfo extends IFngSchemaTypeFormOpts {
6
234
  name: string;
7
235
  schema?: Array<IFieldViewInfo>;
8
236
  array?: boolean;
9
- showIf? : any;
10
- showWhen? : string;
11
- directive?: string;
237
+ showIf?: any;
12
238
  required?: boolean;
13
- step? : number;
14
- noLookup? : boolean;
15
- readonly? : boolean;
16
- help? : string;
17
- size? : string;
239
+ step?: number;
18
240
  }
19
241
 
20
- // Schema used internally on client - often derived from IFieldViewInfo pased from server
242
+ export type fieldType =
243
+ | "string"
244
+ | "text"
245
+ | "textarea"
246
+ | "number"
247
+ | "select"
248
+ | "link"
249
+ | "date"
250
+ | "checkbox"
251
+ | "password"
252
+ | "radio";
253
+
254
+ // Schema used internally on client - often derived from IFieldViewInfo passed from server
21
255
  export interface IFormInstruction extends IFieldViewInfo {
22
- id? : string; // id of generated DOM element
23
- type?: 'string' | 'text' | 'textarea' | 'number' | 'select' | 'link' | 'date' | 'checkbox' | 'password';
24
- rows? : number
25
- label: string;
256
+ id?: string; // id of generated DOM element
257
+ nonUniqueId?: string; // where this field is part of a sub-sub-schema, id is likely to include $index from the sub-schema, to ensure uniqueness. provide it here without reference to $parent.$index for use in security evaluations.
258
+ type?: fieldType;
259
+ defaultValue?: any;
260
+ rows?: number;
261
+ label?: string;
26
262
  options?: any;
27
263
  ids?: any;
28
264
  hidden?: boolean;
29
265
  tab?: string;
30
- add? : string;
31
- ref? : string;
32
- link? : any;
33
- linkText?: string;
34
- form?: string; // the form that is linked to
35
- select2? : any; // deprecated
266
+ add?: string;
267
+ ref?: any;
268
+ link?: any;
269
+ linktext?: string;
270
+ linklabel?: boolean;
271
+ form?: string; // the form that is linked to
272
+ select2?: any; // deprecated
273
+ schema?: IFormInstruction[]; // If the field is an array of fields
274
+ intType?: "date";
275
+ coloffset?: number;
276
+ [directiveOptions: string]: any;
277
+ }
278
+
279
+ interface IContainerInstructions {
280
+ before?: string;
281
+ after?: string;
282
+ omit?: boolean;
283
+ }
284
+
285
+ export type HiddenTabReintroductionMethod = "none" | "tab";
286
+
287
+ export interface IContainer {
288
+ /*
289
+ Type of container, which determines markup. This is currently only available when the schema is generated by
290
+ the client for use independent of the BaseController
291
+ In the case of a string which does not match one of the predefined options
292
+ the generated container div is given the class of the name
293
+ */
294
+ containerType: "fieldset" | "well" | "tabset" | "tab" | "+tab" | "well-large" | "well-small" | string | ((info: IContainer) => IContainerInstructions);
295
+ title?: string;
296
+ /*
297
+ Applies only to tabs - causing them to be rendered with a x for closing ('hiding') the tab in question.
298
+ Where hideable is true, hiddenTabArrayProp can be used to specify a property name (which can include ".", and which
299
+ will be assumed to identiy an array) that should be used to store the ids of closed ('hidden') tabs.
300
+ Where this is not specified, record.hiddenTabs will be used.
301
+ If hiddenTabReintroductionMethod is set to "tab", an additional tab will be added to the end of the tabset
302
+ with a + heading, and clicking on this will provide a UI for re-introducing hidden tabs.
303
+ */
304
+ hideable?: boolean;
305
+ hiddenTabArrayProp?: string;
306
+ hiddenTabReintroductionMethod?: HiddenTabReintroductionMethod;
307
+ /*
308
+ h1...h6 will use a header style
309
+ anything else will be used as a paragraph stype
310
+ */
311
+ titleTagOrClass?: string;
312
+ content: IFormSchemaElement[];
36
313
  }
37
314
 
315
+ export type IFormSchemaElement = IFormInstruction | IContainer;
316
+
317
+ export type IFormSchema = IFormSchemaElement[];
318
+ export type IControlledFormSchema = IFormInstruction[];
319
+
38
320
  export interface IEnumInstruction {
39
321
  repeat: string;
40
322
  value: string;
41
- label? : string;
323
+ label?: string;
42
324
  }
43
325
 
44
- export interface IRecordHandler {
45
- convertToMongoModel(schema: Array<IFieldViewInfo>, anObject: any, prefixLength: number, scope: fng.IFormScope): any;
46
- createNew(dataToSave: any, options: any, scope: fng.IFormScope): void;
47
- deleteRecord(model: any, id: any, scope: fng.IFormScope, ctrlState: any): void;
48
- updateDocument(dataToSave : any, options: any, scope: fng.IFormScope, ctrlState: any) : void;
49
- readRecord($scope: fng.IFormScope, ctrlState);
50
- scrollTheList($scope: fng.IFormScope);
51
- getListData($scope: fng.IFormScope, record, fieldName, listSchema);
326
+ export interface IFngCtrlState {
327
+ master: any;
328
+ allowLocationChange: boolean; // Do we allow location change or prompt for permission
329
+ }
330
+ export interface IRecordHandlerService {
331
+ convertToMongoModel(schema: IControlledFormSchema, anObject: any, prefixLength: number, scope: IFormScope): any;
332
+ createNew(dataToSave: any, options: any, scope: IFormScope, ctrlState: IFngCtrlState): void;
333
+ deleteRecord(id: string, scope: IFormScope, ctrlState: IFngCtrlState): void;
334
+ updateDocument(dataToSave: any, options: any, scope: IFormScope, ctrlState: IFngCtrlState): void;
335
+ beginReadingRecord($scope: IFormScope): void;
336
+ finishReadingThenProcessRecord($scope: IFormScope, ctrlState): void;
337
+ scrollTheList($scope: IFormScope);
338
+ getListData(record, fieldName, listSchema?, $scope?: IFormScope);
52
339
  suffixCleanId(inst, suffix);
53
- setData(object, fieldname, element, value);
54
- setUpSelectOptions(lookupCollection, schemaElement, $scope: fng.IFormScope, ctrlState, handleSchema);
340
+ setData(object, fieldname: string, element, value);
341
+ getData(object, fieldname: string, element?: any);
342
+ setUpLookupOptions(lookupCollection, schemaElement, $scope: IFormScope, ctrlState, handleSchema);
343
+ setUpLookupListOptions: (
344
+ ref: IFngLookupListReference,
345
+ formInstructions: IFormInstruction,
346
+ $scope: IFormScope,
347
+ ctrlState: IFngCtrlState
348
+ ) => void;
349
+ handleInternalLookup($scope: IFormScope, formInstructions, ref): void;
55
350
  preservePristine(element, fn): void;
56
351
  convertIdToListValue(id, idsArray, valuesArray, fname);
57
- decorateScope($scope:fng.IFormScope, $uibModal, recordHandlerInstance : fng.IRecordHandler, ctrlState);
58
- fillFormFromBackendCustomSchema(schema, $scope:fng.IFormScope, formGeneratorInstance, recordHandlerInstance, ctrlState);
59
- fillFormWithBackendSchema($scope: fng.IFormScope, formGeneratorInstance, recordHandlerInstance, ctrlState);
60
- handleError($scope: fng.IFormScope);
352
+ decorateScope($scope: IFormScope, $uibModal, recordHandlerInstance: IRecordHandlerService, ctrlState);
353
+ fillFormFromBackendCustomSchema(
354
+ schema,
355
+ $scope: IFormScope,
356
+ formGeneratorInstance,
357
+ recordHandlerInstance,
358
+ ctrlState
359
+ );
360
+ fillFormWithBackendSchema($scope: IFormScope, formGeneratorInstance, recordHandlerInstance, ctrlState);
361
+ handleError($scope: IFormScope);
362
+ convertToAngularModel($scope: IFormScope);
363
+ convertToAngularModelWithSchema(schema: IControlledFormSchema, data, $scope: IFormScope)
364
+ }
365
+
366
+ export interface IFormGeneratorService {
367
+ generateEditUrl(obj, $scope: IFormScope): string;
368
+ generateViewUrl(obj, $scope: IFormScope): string;
369
+ generateNewUrl($scope: IFormScope): string;
370
+ handleFieldType(formInstructions, mongooseType, mongooseOptions, $scope: IFormScope, ctrlState);
371
+ handleSchema(
372
+ description: string,
373
+ source,
374
+ destForm,
375
+ destList,
376
+ prefix,
377
+ doRecursion: boolean,
378
+ $scope: IFormScope,
379
+ ctrlState
380
+ );
381
+ updateDataDependentDisplay(curValue, oldValue, force, $scope: IFormScope);
382
+ add(fieldName: string, $event, $scope: IFormScope, modelOverride?: any);
383
+ unshift(fieldName: string, $event, $scope: IFormScope, modelOverride?: any);
384
+ remove(fieldName: string, value, $event, $scope: IFormScope, modelOverride?: any);
385
+ hasError(formName, name, index, $scope: IFormScope);
386
+ decorateScope($scope: IFormScope, formGeneratorInstance: IFormGeneratorService, recordHandlerInstance: IRecordHandlerService, sharedStuff, pseudoUrl?: string);
387
+ }
388
+
389
+ export interface IFngSingleLookupHandler {
390
+ formInstructions: IFormInstruction;
391
+ lastPart: string;
392
+ possibleArray: string;
393
+ // If the looked-up record changes, we use these fields to see if the old lookup value also exists in the new lookup record
394
+ oldValue?: string | string[];
395
+ oldId?: string | string[];
396
+ }
397
+
398
+ export interface IFngLookupHandler {
399
+ lookupOptions: string[];
400
+ lookupIds: string[];
401
+ handlers: IFngSingleLookupHandler[];
402
+ }
403
+
404
+ export interface IFngInternalLookupHandlerInfo extends IFngLookupHandler {
405
+ ref: IFngInternalLookupReference;
406
+ }
407
+
408
+ export interface IFngLookupListHandlerInfo extends IFngLookupHandler {
409
+ ref: IFngLookupListReference;
61
410
  }
62
411
 
63
- export interface IFormGenerator {
64
- generateEditUrl(obj, $scope:fng.IFormScope): string;
65
- generateNewUrl($scope: fng.IFormScope): string;
66
- handleFieldType(formInstructions, mongooseType, mongooseOptions, $scope: fng.IFormScope, ctrlState);
67
- handleSchema(description: string, source, destForm, destList, prefix, doRecursion: boolean, $scope: fng.IFormScope, ctrlState);
68
- updateDataDependentDisplay(curValue, oldValue, force, $scope: fng.IFormScope);
69
- add(fieldName, $event, $scope: fng.IFormScope);
70
- unshift(fieldName, $event, $scope: fng.IFormScope);
71
- remove(fieldName, value, $event, $scope: fng.IFormScope);
72
- hasError(formName, name, index, $scope: fng.IFormScope);
73
- decorateScope($scope: fng.IFormScope, formGeneratorInstance, recordHandlerInstance: fng.IRecordHandler, sharedStuff);
412
+ // we cannot use an enum here, so this will have to do. these are the values expected to be returned by the
413
+ // function on $rootScope with the name formsAngular.disabledSecurityFuncName.
414
+ // false = not disabled,
415
+ // true = disabled,
416
+ // "+" = this and all child elements disabled
417
+ export type DisabledOutcome = boolean | "+";
418
+
419
+ export interface ISecurableScope extends angular.IScope {
420
+ // added by ISecurityService
421
+ isSecurelyHidden: (elemId: string) => boolean;
422
+ isSecurelyDisabled: (elemId: string) => boolean;
423
+ requiresDisabledChildren: (elemId: string) => boolean;
74
424
  }
75
425
 
76
426
  /*
77
427
  The scope which contains form data
78
428
  */
79
- export interface IFormScope extends angular.IScope {
429
+ export interface IFormScope extends ISecurableScope {
80
430
  sharedData: any;
81
- modelNameDisplay : string;
431
+ modelNameDisplay: string;
82
432
  modelName: string;
83
433
  formName: string;
84
- cancel(): any;
85
- showError: (error: any, alertTitle? : string) => void;
86
434
  alertTitle: any;
435
+ errorVisible: boolean;
87
436
  errorMessage: any;
88
- prepareForSave: (cb: (error: string, dataToSave?: any) => void) => void;
437
+ errorHideTimer: number;
89
438
  save: any;
90
439
  newRecord: boolean;
91
440
  initialiseNewRecord?: any;
@@ -96,32 +445,38 @@ declare module fng {
96
445
  isCancelDisabled: any;
97
446
  isNewDisabled: any;
98
447
  isSaveDisabled: any;
99
- disabledText: any;
448
+ whyDisabled: string;
100
449
  unconfirmedDelete: boolean;
101
450
  getVal: any;
102
451
  sortableOptions: any;
103
- tabDeselect: any;
104
- tabs?: Array<any>; // In the case of forms that contain a tab set
105
- tab?: string; // title of the active tab - from the route
452
+ tabs?: Array<any>; // In the case of forms that contain a tab set
453
+ tab?: string; // title of the active tab - from the route
106
454
  activeTabNo?: number;
107
- topLevelFormName: string; // The name of the form
455
+ topLevelFormName: string; // The name of the form
108
456
  record: any;
109
- originalData: any; // the unconverted data read from the server
457
+ originalData: any; // the unconverted data read from the server
110
458
  phase: any;
111
459
  disableFunctions: any;
112
460
  dataEventFunctions: any;
113
461
  listSchema: any;
114
462
  recordList: any;
115
463
  dataDependencies: any;
464
+ internalLookups: IFngInternalLookupHandlerInfo[];
465
+ listLookups: IFngLookupListHandlerInfo[];
116
466
  conversions: any;
117
467
  pageSize: any;
118
468
  pagesLoaded: any;
119
- formSchema: IFormInstruction[];
120
-
121
- //functions
122
- baseSchema() : Array<any>;
469
+ redirectOptions?: { redirect?: string; allowChange?: boolean };
470
+ cancel: () => any;
471
+ showError: (error: any, alertTitle?: string) => void;
472
+ prepareForSave: (cb: (error: string, dataToSave?: any) => void) => void;
473
+ setDefaults: (formSchema: IFormSchema, base?: string) => any;
474
+ formSchema: IControlledFormSchema;
475
+ baseSchema: () => Array<any>;
123
476
  setFormDirty: any;
124
477
  add: any;
478
+ hideTab: (event, tabTitle: string, hiddenTabArrayProp: string) => void;
479
+ addTab: (event, tabTitle: string, hiddenTabArrayProp: string) => void;
125
480
  hasError: any;
126
481
  unshift: any;
127
482
  remove: any;
@@ -130,17 +485,70 @@ declare module fng {
130
485
  skipCols: any;
131
486
  setPristine: any;
132
487
  generateEditUrl: any;
488
+ generateViewUrl: any;
133
489
  generateNewUrl: any;
134
490
  scrollTheList: any;
135
491
  getListData: any;
136
- dismissError: any;
137
- handleHttpError(response: any): void;
492
+ phaseWatcher: any;
493
+ dismissError: () => void;
494
+ stickError: () => void;
495
+ clearTimeout: () => void;
496
+ handleHttpError: (response: any) => void;
138
497
  dropConversionWatcher: () => void;
498
+ readingRecord?: angular.IPromise<any>;
499
+ onSchemaFetch?: (description: string, source: IFieldViewInfo[]) => void;
500
+ onSchemaProcessed?: (description: string, formSchema: IFormInstruction[]) => void;
501
+ updateQueryForTab?: (tab: string) => void;
502
+ showLoading? : boolean; // a spinner that fades in
503
+ showSpinner? : boolean; // an immediate spinner
504
+ tabDeselect?: ($event: any, $selectedIndex: number) => void;
505
+ setUpCustomLookupOptions?: (
506
+ schemaElement: IFormInstruction,
507
+ ids: string[],
508
+ options: string[],
509
+ baseScope: any
510
+ ) => void;
511
+ }
512
+
513
+ export interface IContextMenuDivider {
514
+ divider: boolean;
515
+ }
516
+ export interface IContextMenuBaseOption {
517
+ // provided to the security hook (see elemSecurityFuncName) - optional where that is not being used
518
+ id?: string;
519
+
520
+ text?: string;
521
+ textFunc?: () => string;
522
+ isDisabled?: () => boolean;
523
+ isHidden?: () => boolean;
524
+
525
+ // Does the option appear in the following contexts?
526
+ listing: boolean;
527
+ creating: boolean;
528
+ editing: boolean;
529
+ }
530
+ export interface IContextSubMenuOption extends IContextMenuBaseOption {
531
+ items: ContextMenuItem[];
532
+ }
533
+ export interface IContextMenuOption extends IContextMenuBaseOption{
534
+ // For it to make any sense, a menu option needs one of the next three properties
535
+ url?: string;
536
+ fn?: (...args: any) => void;
537
+ urlFunc?: () => string;
538
+ broadcast?: string;
539
+ args?: any[];
540
+ }
541
+ export type ContextMenuItem = IContextMenuOption | IContextSubMenuOption | IContextMenuDivider;
542
+
543
+ export interface IModelCtrlService {
544
+ loadControllerAndMenu: (sharedData: any, titleCaseModelName: string, level: number, needDivider: boolean, scope: angular.IScope) => void;
139
545
  }
140
546
 
141
547
  export interface IModelController extends IFormScope {
142
- onBaseCtrlReady? : (baseScope: IFormScope) => void; // Optional callback after form is instantiated
143
- onAllReady? : (baseScope: IFormScope) => void; // Optional callback after form is instantiated and populated
548
+ onBaseCtrlReady?: (baseScope: IFormScope) => void; // Optional callback after form is instantiated
549
+ onAllReady?: (baseScope: IFormScope) => void; // Optional callback after form is instantiated and populated
550
+ contextMenu?: ContextMenuItem[];
551
+ contextMenuPromise?: Promise<ContextMenuItem[]>;
144
552
  }
145
553
 
146
554
  export interface IBaseFormOptions {
@@ -148,13 +556,13 @@ declare module fng {
148
556
  * The style of the form layout. Supported values are horizontalcompact, horizontal, vertical, inline
149
557
  */
150
558
  //TODO supported values should be in an enum
151
- formstyle?: string;
559
+ formstyle?: formStyle;
152
560
  /**
153
561
  * Model on form scope (defaults to record).
154
562
  * <li><strong>model</strong> the object in the scope to be bound to the model controller. Specifying
155
563
  * the model inhibits the generation of the <strong>form</strong> tag unless the <strong>forceform</strong> attribute is set to true</li>
156
564
  */
157
- model? : string;
565
+ model?: string;
158
566
  /**
159
567
  * The name to be given to the form - defaults to myForm
160
568
  */
@@ -163,60 +571,272 @@ declare module fng {
163
571
  * Normally first field in a form gets autofocus set. Use this to prevent this
164
572
  */
165
573
  noautofocus?: string;
574
+ /*
575
+ Suppress the generation of element ids
576
+ (sometimes required when using nested form-inputs in a directive)
577
+ */
578
+ noid?: boolean;
166
579
  }
167
580
 
168
581
  export interface IFormAttrs extends IFormOptions, angular.IAttributes {
169
582
  /**
170
583
  * Schema used by the form
171
584
  */
172
- schema : string;
173
- forceform?: string; // Must be true or omitted. Forces generation of the <strong>form</strong> tag when model is specified
585
+ schema: string;
586
+ forceform?: string; // Must be true or omitted. Forces generation of the <strong>form</strong> tag when model is specified
587
+ noid?: boolean;
174
588
  }
175
589
 
176
590
  export interface IFormOptions extends IBaseFormOptions {
177
- schema? : string;
591
+ schema?: string;
178
592
  subkey?: string;
179
593
  subkeyno?: number;
180
- subschema? : string;
181
- subschemaroot? : string;
594
+ subschema?: string;
595
+ subschemaroot?: string;
596
+ viewform?: boolean;
597
+ suppressNestingWarning?: boolean;
182
598
  }
183
599
 
184
600
  export interface IBuiltInRoute {
185
601
  route: string;
186
- state: string;
187
- templateUrl: string;
188
- options? : any;
602
+ state?: string;
603
+ templateUrl?: string;
604
+ options?: {
605
+ authenticate?: boolean;
606
+ templateUrl?: string | (() => void);
607
+ template?: string;
608
+ controller?: string;
609
+ }
189
610
  }
190
611
 
191
612
  export interface IRoutingConfig {
192
- hashPrefix: string;
613
+ hashPrefix?: string;
193
614
  html5Mode: boolean;
194
- routing: string; // What sort of routing do we want? ngroute or uirouter.
195
- // TODO Should be enum
196
- prefix: string; // How do we want to prefix out routes? If not empty string then first character must be slash (which is added if not)
197
- // for example '/db' that gets prepended to all the generated routes. This can be used to
198
- // prevent generated routes (which have a lot of parameters) from clashing with other routes in
199
- // the web app that have nothing to do with CRUD forms
615
+ routing: string; // What sort of routing do we want? ngroute or uirouter.
616
+ // TODO Should be enum
617
+ prefix: string; // How do we want to prefix out routes? If not empty string then first character must be slash (which is added if not)
618
+ // for example '/db' that gets prepended to all the generated routes. This can be used to
619
+ // prevent generated routes (which have a lot of parameters) from clashing with other routes in
620
+ // the web app that have nothing to do with CRUD forms
200
621
  fixedRoutes?: Array<IBuiltInRoute>;
201
- templateFolder?: string; // The folder where the templates for base-list, base-edit and base-analysis live. Internal templates used by default. For pre 0.7.0 behaviour use 'partials/'
202
- add2fngRoutes?: any; // An object to add to the generated routes. One use case would be to add {authenticate: true}
203
- // so that the client authenticates for certain routes
622
+ templateFolder?: string; // The folder where the templates for base-list, base-edit and base-analysis live. Internal templates used by default. For pre 0.7.0 behaviour use 'partials/'
623
+ add2fngRoutes?: any; // An object to add to the generated routes. One use case would be to add {authenticate: true}
624
+ // so that the client authenticates for certain routes
204
625
 
205
- variantsForDemoWebsite? : any; // Just for demo website
206
- variants?: any; // Just for demo website
626
+ variantsForDemoWebsite?: any; // Just for demo website
627
+ variants?: any; // Just for demo website
628
+ onDelete?: string; // Supports literal (such as '/') or 'new' (which will go to a /new of the model) default is to go to the list view
207
629
  }
208
630
 
209
631
  export interface IFngRoute {
210
- newRecord?: boolean;
211
- analyse?: boolean;
212
- modelName?: string;
213
- reportSchemaName? : string;
214
- id? : string;
215
- formName? : string;
216
- tab? : string;
217
- variant? : string; // TODO should be enum of supported frameworks
632
+ newRecord?: boolean;
633
+ analyse?: boolean;
634
+ modelName?: string;
635
+ reportSchemaName?: string;
636
+ id?: string;
637
+ formName?: string;
638
+ tab?: string;
639
+ variant?: string; // TODO should be enum of supported frameworks
218
640
  }
219
641
 
642
+ interface IBuildingBlocks {
643
+ common: string;
644
+ sizeClassBS3: string;
645
+ sizeClassBS2: string;
646
+ compactClass: string;
647
+ formControl: string;
648
+ modelString: string;
649
+ disableableAncestorStr: string;
650
+ }
651
+
652
+ interface IProcessedAttrs {
653
+ info: IFormInstruction;
654
+ options: IFormOptions;
655
+ directiveOptions: any;
656
+ }
657
+
658
+ interface IGenDisableStrParams {
659
+ forceNg?: boolean;
660
+ nonUniqueIdSuffix?: string;
661
+ }
662
+
663
+
664
+ interface IPluginHelperService {
665
+ extractFromAttr: (
666
+ attr: any,
667
+ directiveName: string,
668
+ scope: fng.IFormScope
669
+ ) => { info: IFormInstruction; options: IFormOptions; directiveOptions: any };
670
+ buildInputMarkup: (
671
+ scope: angular.IScope,
672
+ attrs: any,
673
+ params: {
674
+ processedAttrs?: IProcessedAttrs;
675
+ fieldInfoOverrides?: Partial<IFormInstruction>;
676
+ optionOverrides?: Partial<IFormOptions>;
677
+ addButtons?: boolean;
678
+ needsX?: boolean;
679
+ },
680
+ generateInputControl: (buildingBlocks: IBuildingBlocks) => string
681
+ ) => string;
682
+ genIdString: (scope: angular.IScope, processedAttrs: IProcessedAttrs, idSuffix: string) => string;
683
+ genDisabledStr: (
684
+ scope: angular.IScope,
685
+ processedAttrs: IProcessedAttrs,
686
+ idSuffix: string,
687
+ params?: fng.IGenDisableStrParams
688
+ ) => string;
689
+ genIdAndDisabledStr: (
690
+ scope: angular.IScope,
691
+ processedAttrs: IProcessedAttrs,
692
+ idSuffix: string,
693
+ params?: fng.IGenDisableStrParams
694
+ ) => string;
695
+ genDateTimePickerDisabledStr: (scope: angular.IScope, processedAttrs: IProcessedAttrs, idSuffix: string) => string;
696
+ genDateTimePickerIdAndDisabledStr: (
697
+ scope: angular.IScope,
698
+ processedAttrs: IProcessedAttrs,
699
+ idSuffix: string
700
+ ) => string;
701
+ genUiSelectIdAndDisabledStr: (
702
+ scope: angular.IScope,
703
+ processedAttrs: IProcessedAttrs,
704
+ idSuffix: string
705
+ ) => string;
706
+ handlePseudos: (scope: fng.IFormScope, str: string) => string;
707
+ genDisableableAncestorStr: (processedAttrs: IProcessedAttrs) => string;
708
+ }
709
+
710
+ interface ISecurityVisibility {
711
+ omit?: boolean;
712
+ visibilityAttr?: string;
713
+ }
714
+
715
+ interface IGenerateDisableAttrParams {
716
+ attr?: string;
717
+ attrRequiresValue?: boolean;
718
+ forceNg?: boolean;
719
+ }
720
+
721
+ type SecurityType = "hidden" | "disabled";
722
+
723
+ interface ISecurityService {
724
+ canDoSecurity: (type: SecurityType) => boolean;
725
+ canDoSecurityNow: (scope: fng.ISecurableScope, type: SecurityType) => boolean;
726
+ isSecurelyHidden: (elemId: string, pseudoUrl?: string) => boolean;
727
+ isSecurelyDisabled: (elemId: string, pseudoUrl?: string) => boolean;
728
+ decorateSecurableScope: (securableScope: ISecurableScope, params?: { pseudoUrl?: string, overrideSkipping?: boolean }) => void;
729
+ doSecurityWhenReady: (cb: () => void) => void;
730
+ considerVisibility: (id: string, scope: fng.ISecurableScope) => ISecurityVisibility;
731
+ considerContainerVisibility: (contentIds: string[], scope: fng.ISecurableScope) => fng.ISecurityVisibility;
732
+ getDisableableAttrs: (id: string) => string;
733
+ getHideableAttrs: (id: string) => string;
734
+ getDisableableAncestorAttrs: (id: string) => string;
735
+ generateDisabledAttr: (id: string, scope: fng.ISecurableScope, params?: IGenerateDisableAttrParams) => string;
736
+ }
737
+
738
+ interface IListQueryOptions {
739
+ limit?: number;
740
+ find?: any; // e.g., { "careWorker.isCareWorker": true }
741
+ aggregate?: any;
742
+ projection?: any;
743
+ order?: any; // e.g., { familyName: -1, givenName: -1 }
744
+ skip?: number;
745
+ concatenate?: boolean; // whether the list fields should be concatenated into a single .text property
746
+ }
747
+
748
+ interface ISubmissionsService {
749
+ // return all of the list attributes of the record from db.<modelName>.<id>
750
+ // where returnRaw is true, the document (albeit with only its list attributes present) will be returned without transformation
751
+ // otherwise, the list fields will be concatenated (space-seperated) and returned as the list property of a record { list: string }
752
+ // e.g., "John Doe", in the case of a person
753
+ getListAttributes: (
754
+ modelName: string,
755
+ id: string,
756
+ returnRaw?: boolean
757
+ ) => angular.IHttpPromise<{ list: string } | any>;
758
+ readRecord: (modelName: string, id: string, formName?: string) => angular.IHttpPromise<any>;
759
+ getAll: (modelName: string, _options: any) => angular.IHttpPromise<any[]>;
760
+ getAllListAttributes: (ref: string) => angular.IHttpPromise<ILookupItem[]>;
761
+ getPagedAndFilteredList: (
762
+ modelName: string,
763
+ options: IListQueryOptions
764
+ ) => angular.IHttpPromise<any[]>;
765
+ getPagedAndFilteredListFull: (
766
+ modelName: string,
767
+ options: IListQueryOptions
768
+ ) => angular.IHttpPromise<any[]>;
769
+ deleteRecord: (model: string, id: string, formName: string) => angular.IHttpPromise<void>;
770
+ updateRecord: (modelName: string, id: string, dataToSave: any, formName?: string) => angular.IHttpPromise<any>;
771
+ createRecord: (modelName: string, dataToSave: any, formName?: string) => angular.IHttpPromise<any>;
772
+ useCache: (val: boolean) => void;
773
+ clearCache: () => void;
774
+ getCache: () => boolean;
775
+ }
776
+
777
+ interface IRoutingServiceProvider {
778
+ start: (options: IRoutingConfig) => void;
779
+ addRoutes: (fixedRoutes: Array<IBuiltInRoute>, fngRoutes:Array<IBuiltInRoute>) => void;
780
+ registerAction: (action: string) => void;
781
+ $get: () => IRoutingService;
782
+ }
783
+
784
+ interface IRoutingService {
785
+ router: () => string;
786
+ prefix: () => string;
787
+ parsePathFunc: () => (location: string) => void;
788
+ html5hash: () => string;
789
+ buildUrl: (path: string) => string;
790
+ buildOperationUrl: (
791
+ prefix: string,
792
+ operation: string,
793
+ modelName: string,
794
+ formName: string,
795
+ id: string,
796
+ tabName?: string
797
+ ) => string;
798
+ redirectTo: () => (operation: string, scope: IFormScope, LocationService: angular.ILocationService, id?: string, tab?: string) => void;
799
+ }
800
+
801
+ interface ICssFrameworkServiceProvider {
802
+ setOptions: (options: { framework: string }) => void;
803
+ $get: () => ICssFrameworkService;
804
+ }
805
+
806
+ interface ICssFrameworkService {
807
+ framework: () => string;
808
+ span: (cols: number) => string;
809
+ offset: (cols: number) => string;
810
+ rowFluid: () => string;
811
+ }
812
+
813
+ interface IFngUiSelectHelperService {
814
+ windowChanged: (width: number, height: number) => boolean;
815
+ addClientLookup: (lkpName: string, lkpData: any) => void;
816
+ clearCache: () => void;
817
+ lookupFunc: (value: string, formSchema: IFormInstruction, cb: (formSchema: IFormInstruction, value: ILookupItem ) => void) => void;
818
+ doOwnConversion: (scope: IFormScope, processedAttrs: any, ref: string) => void;
819
+ }
820
+
821
+ interface IFormMarkupHelperService {
822
+ isHorizontalStyle: (formStyle: string, includeStacked: boolean) => boolean;
823
+ isArrayElement: (scope: angular.IScope, info: fng.IFormInstruction, options: fng.IFormOptions) => boolean;
824
+ fieldChrome: (scope: fng.IFormScope, info: fng.IFormInstruction, options: fng.IFormOptions) => { omit?: boolean, template?: string, closeTag?: string };
825
+ label: (scope: fng.IFormScope, fieldInfo: fng.IFormInstruction, addButtonMarkup: boolean, options: fng.IFormOptions) => string;
826
+ glyphClass: () => string;
827
+ allInputsVars: (scope: angular.IScope, fieldInfo: fng.IFormInstruction, options: fng.IFormOptions, modelString: string, idString: string, nameString: string) => Partial<fng.IBuildingBlocks>;
828
+ inputChrome: (value: string, fieldInfo: fng.IFormInstruction, options: fng.IFormOptions, markupVars) => string;
829
+ generateSimpleInput: (common: string, fieldInfo: fng.IFormInstruction, options: fng.IFormOptions) => string;
830
+ controlDivClasses: (options: fng.IFormOptions, fieldInfo: fng.IFormInstruction) => string[];
831
+ handleInputAndControlDiv: (inputMarkup: string, controlDivClasses: string[]) => string;
832
+ handleArrayInputAndControlDiv: (inputMarkup: string, controlDivClasses: string[], scope: fng.IFormScope, info: fng.IFormInstruction, options: fng.IFormOptions) => string;
833
+ addTextInputMarkup: (allInputsVars: Partial<fng.IBuildingBlocks>, fieldInfo: fng.IFormInstruction, requiredStr: string) => string;
834
+ handleReadOnlyDisabled: (partialFieldInfo: { name: string, id?: string, nonUniqueId?: string, readonly?: boolean | string }, scope: fng.IFormScope) => string[];
835
+ generateArrayElementIdString: (idString: string, info: fng.IFormInstruction, options: fng.IFormOptions) => string;
836
+ genDisableableAncestorStr: (id: string) => string;
837
+ generateNgShow: (showWhen: IFngShowWhen, model: string) => string;
838
+ handlePseudos: (scope: fng.IFormScope, str: string, dynamicFuncName?: string) => string;
839
+ }
220
840
  }
221
841
 
222
- declare var formsAngular: angular.IModule;
842
+ declare var formsAngular: fng.IFng;