1 | MacGyver
|
2 | ========
|
3 | Dynamic form widgets for Angular.
|
4 |
|
5 | MacGyver is a form designer for Angular that works by being given a form definition and a data set.
|
6 |
|
7 | [LIVE DEMO](https://momsfriendlydevco.github.io/macgyver)
|
8 |
|
9 |
|
10 | Example
|
11 | -------
|
12 | ```javascript
|
13 | angular
|
14 | .module('macgyver')
|
15 | .component('myComponent', {
|
16 | controller: function() {
|
17 | var $ctrl = this;
|
18 |
|
19 | $ctrl.data = {}; // This will be populated by MacGyver
|
20 |
|
21 | $ctrl.form = { // Our form specification
|
22 | type: "mgContainer",
|
23 | items: [
|
24 | {
|
25 | id: "textInput",
|
26 | type: "mgText",
|
27 | title: "Example Text",
|
28 | },
|
29 | {
|
30 | id: "toggleControl",
|
31 | type: "mgToggle",
|
32 | default: true,
|
33 | },
|
34 | ],
|
35 | };
|
36 | },
|
37 | template: `
|
38 | <mg-form config="$ctrl.form" data="$ctrl.data"></mg-form>
|
39 | `,
|
40 | });
|
41 | ```
|
42 |
|
43 | Editing a form
|
44 | --------------
|
45 | MacGyver also ships with a form editor. To use simply make a HTML page with the `mgFormEditor` component for an interactive UI.
|
46 |
|
47 | ```html
|
48 | <mg-form-editor config="$ctrl.form" data="$ctrl.data"></mg-form-editor>
|
49 | ```
|
50 |
|
51 |
|
52 |
|
53 | API Methods
|
54 | ===========
|
55 | MagGyver works by using Angular's template system to nest widgets with two-way data binding.
|
56 |
|
57 |
|
58 | Creating MacGyver widgets
|
59 | -------------------------
|
60 | Each MagGyver widget begins with `mg` and should be registered via `$macgyver.register()`.
|
61 |
|
62 | ```javascript
|
63 | angular
|
64 | .module('macgyver')
|
65 | .config($macgyverProvider => $macgyverProvider.register('mgText', {
|
66 | title: 'Textbox',
|
67 | icon: 'fa fa-pencil-square-o',
|
68 | config: {
|
69 | placeholder: {type: 'mgText', help: 'Ghost text to display when the textbox has no value'},
|
70 | },
|
71 | }))
|
72 | .component('mgText', {
|
73 | bindings: {
|
74 | config: '<', // Config for this widget
|
75 | data: '=', // Data state for this widget
|
76 | },
|
77 | controller: function($macgyver, $scope) {
|
78 | var $ctrl = this;
|
79 | $macgyver.inject($scope, $ctrl);
|
80 |
|
81 | // Adopt default if no data value is given
|
82 | $scope.$watch('$ctrl.data', ()=> { if (_.isUndefined($ctrl.data) && _.has($ctrl, 'config.default')) $ctrl.data = $ctrl.config.default });
|
83 |
|
84 | // Optionally respond to validation requests
|
85 | $ctrl.validate = ()=> {
|
86 | if ($ctrl.config.required && !$ctrl.data) return `${$ctrl.config.title} is required`;
|
87 | };
|
88 | },
|
89 | template: `
|
90 | <input ng-model="$ctrl.data" type="text" class="form-control" placeholder="{{$ctrl.config.placeholder}}"/>
|
91 | `,
|
92 | })
|
93 | ```
|
94 |
|
95 |
|
96 | $macgyver
|
97 | ---------
|
98 | The main service / provider within Angular.
|
99 |
|
100 | **NOTE:** `$macgyverProvider` and `$macgyver` are the same. The provider is available as an alias to allow registration of components during the Angular Config phase.
|
101 |
|
102 |
|
103 | $macgyver.register(id, [properties])
|
104 | ------------------------------------
|
105 | Register a widget for use by name. Each id should begin with `id` and be in camelCase form.
|
106 |
|
107 | Widgets can contain the following meta properties:
|
108 |
|
109 | | Property | Type | Default | Description |
|
110 | |--------------------|-----------|----------------------------|--------------------------------------------------------------------------------------------------|
|
111 | | `config` | `Object` | *none* | Object detailing each optional property the widget can take as a `mgContainer` specification |
|
112 | | `icon` | `string` | *none* | The icon CSS class to use in the `mgFormEditor` UI |
|
113 | | `isContainer` | `boolean` | `false` | Indicates that the widget can contain other widgets (under the `items` array) |
|
114 | | `isContainerArray` | `boolean` | `false` | Addition to `isContainer` that indicates the widget will contain an array of rows (like a table) |
|
115 | | `template` | `string` | `<COMPONENT_NAME/>` | Rendering template to be used to draw the element |
|
116 | | `title` | `string` | The ID via `_.startCase()` | The human friendly title of the widget |
|
117 | | `userPlaceable` | `boolean` | `false` | Whether to hide the object from the user in the `mgFormEditor` UI |
|
118 |
|
119 |
|
120 | $macgyver.getForm($scope)
|
121 | -------------------------
|
122 | Finds and returns the component scope of the first available form under the given scope.
|
123 |
|
124 |
|
125 | $macgyver.inject($scope, $ctrl)
|
126 | -------------------------------
|
127 | Function used by MacGyver components to register themselves against the standard event hooks during initialization.
|
128 |
|
129 |
|
130 | $macgyver.getDataTree(root, [useDefaults=false])
|
131 | ------------------------------------------------
|
132 | Generate an empty data entity from a given widget container. This function can be used to return the 'blank' form contents. If `useDefaults == true` the defaults for each widget will be set as the value.
|
133 |
|
134 |
|
135 | $macgyver.neatenSpec(spec)
|
136 | --------------------------
|
137 | Attempt to neaten up a 'rough' MacGyver spec into a pristine one.
|
138 | This function performs various sanity checks on nested elements e.g. checking each item has a valid ID and if not adding one.
|
139 |
|
140 |
|
141 | $macgyver.specDataPrototype(spec)
|
142 | ---------------------------------
|
143 | Create an empty data structure based on the specification. This is really just used to make sure that the deeply nested objects-within-objects (or arrays) are present when Angular tries to bind to them.
|
144 |
|
145 |
|
146 | $macgyver.widgets
|
147 | -----------------
|
148 | An object containing data on each valid MacGyver widget registered. If running on the front-end this is updated as new widgets register themselves. On the backend this uses the computed version located in `./dist/widgets.json`.
|
149 |
|
150 |
|
151 | API Events
|
152 | ==========
|
153 | MacGyver components are also expected to optionally respond to the following events:
|
154 |
|
155 | mg.get(event, register)
|
156 | -----------------------
|
157 | Used by MacGyver to 'ping' controls. Each control is expected to populate the register object with its ID and `$ctrl` instance.
|
158 | This event is automatically responded to if the component calls `$macgyver.inject()` during its init cycle. If the component does not respond higher-level events such as validation will not be able to reach the component.
|
159 |
|
160 |
|
161 | mg.getForm(event, form)
|
162 | -----------------------
|
163 | Used by MacGyver during `$macgyver.getForm()` calls to retrieve the forms under the scope.
|
164 | The responding form is expected to populate the `form.form` object with its controller instance.
|