Skip to content

Commit

Permalink
Merge commit '44769ca5eafc26ac45799fa4cdaf50956b8f00fd' into develop
Browse files Browse the repository at this point in the history
* commit '44769ca5eafc26ac45799fa4cdaf50956b8f00fd': (21 commits)
  bump version
  clarify security (closes Meteor-Community-Packages#520)
  autosave contenteditable inputs (closes Meteor-Community-Packages#494)
  Bugfix: if type of keys is Number
  fixes to selectOptions merge
  Feature suggest: autoform.options from hashtable
  bump version
  fix Meteor-Community-Packages#474 bootstrap-radios values
  array field style fix
  fix Meteor-Community-Packages#504
  suggest values for afFieldValueContains
  Fix multi-select issue where we were determining selection status based on 'opt' value (which is at the group level) instead of the actual option ('subOpt'). Multi-select 'selected' now works.
  fixes issue Meteor-Community-Packages#497
  allow providing an onSuccess result from onSubmit (fixes Meteor-Community-Packages#479)
  set this.docId for insert form onSuccess, too
  workaround for resetting update forms; fixes to hot code push migration
  clarify before hooks a bit (Meteor-Community-Packages#477)
  list add on packages available from others
  If value invalid return unmodified value, not null
  Check if view isn't destroyed
  ...
  • Loading branch information
aramk committed Dec 13, 2014
2 parents 015bcf4 + 44769ca commit 4c0bfa6
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 56 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,25 @@ forms with automatic insert and update events, and automatic reactive validation

## Change Log

### 4.0.2 - 4.0.6
### 4.2.0

* As an alternative to the current method of specifying `options` as an array of objects, you can now specify options as an object with {value: label} format. Values are coerced into the expected type. (Thanks @comerc)
* When you have an update form with `autosave=true`, fields with `type="contenteditable"` now properly autosave. (Thanks @MichalW)


### 4.1.0

* The "boolean-radios" input type now outputs the correct field value (`true`, `false`, or `undefined`) in all cases.
* The "boolean-checkbox" input type now always returns either `true` or `false` as its value. Previously, it could return `undefined` instead of `false` sometimes.
* You can now pass a comma-separated list of values to `afFieldValueContains` and it will return `true` if any of those values are in the array. (thanks @comerc)
* The "select-multiple" input type now has initial values set properly. (thanks @BigDSK)
* The "select-checkbox-inline" input type now has initial values set properly. (thanks @AlainPaumen)
* When handling submission with `onSubmit`, you can now call `this.done(null, result)` when done, and `result` will be passed to `onSuccess` as the second argument. If there are multiple `onSubmit` hooks, only the first provided result will be passed to `onSuccess`.
* When `onSuccess` is called after an `insert`, `this.docId` is now set to the new document `_id`.
* Update forms should now reset properly (to values from database).
* Other fixes (thanks @zimme and @mjgallag)

### 4.0.2 - 4.0.7

Several fixes

Expand Down
111 changes: 107 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ AutoForm 4.0 is out with support for custom input types, but also lots of compat
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*

- [Installation](#installation)
- [Community Add-On Packages](#community-add-on-packages)
- [Demo](#demo)
- [Example](#example)
- [A Basic Insert Form](#a-basic-insert-form)
Expand Down Expand Up @@ -91,6 +92,27 @@ In a Meteor app directory, enter:
$ meteor add aldeed:autoform
```

### Community Add-On Packages

*Submit a pull request to add your package to this list!*

The following community packages provide additional custom input types that you can use in your autoforms:

* [aldeed:autoform-select2](https://atmospherejs.com/aldeed/autoform-select2)
* [aldeed:autoform-bs-datepicker](https://atmospherejs.com/aldeed/autoform-bs-datepicker)
* [cfs:autoform](https://atmospherejs.com/cfs/autoform)
* [yogiben:autoform-file](https://atmospherejs.com/yogiben/autoform-file)
* [naxio:autoform-file](https://atmospherejs.com/naxio/autoform-file)
* [mpowaga:autoform-summernote](https://atmospherejs.com/mpowaga/autoform-summernote)

[yogiben:admin](https://github.com/yogiben/meteor-admin) provides CRUD admin pages using autoforms.

[forwarder:autoform-wizard](https://atmospherejs.com/forwarder/autoform-wizard) and [planifica:wizard](https://atmospherejs.com/planifica/wizard) provide wizard components for autoform.

[yogiben:autoform-modals](https://atmospherejs.com/yogiben/autoform-modals) provides autoform bootstrap modals to insert, update, remove docs from collections.

[jameslefrere:autoform-semantic-ui](https://atmospherejs.com/jameslefrere/autoform-semantic-ui) provides a Semantic UI theme for autoform.

## Demo

[Live](http://autoform.meteor.com)
Expand Down Expand Up @@ -135,6 +157,8 @@ Books.attachSchema(new SimpleSchema({
}));
```

*Be sure to define proper insert security for untrusted code if you've removed the `insecure` package. Call allow/deny or use [ongoworks:security](https://atmospherejs.com/ongoworks/security).*

### A Basic Insert Form

```html
Expand Down Expand Up @@ -168,6 +192,8 @@ document with the original values to be updated:

This example uses `doc=this`, assuming that you use something like iron:router's `data` function to set the template's data context to the book document. This is a common way to do it, but you could also use a helper function that returns the document.

*Be sure to define proper update security for untrusted code if you've removed the `insecure` package. Call allow/deny or use [ongoworks:security](https://atmospherejs.com/ongoworks/security).*

### A Custom Insert Form

If you want to customize autogenerated forms for *all* forms, you can easily
Expand Down Expand Up @@ -369,6 +395,18 @@ Template.registerHelper("yearOptions", function() {
});
```

Alternatively, you can specify options as an object with {value: label} format. Values are coerced into the expected type.

```js
Template.registerHelper("yearOptions", function() {
return {
2013: "2013",
2014: "2014",
2015: "2015"
};
});
```

You can also mix in optgroups. See [the demo](http://autoform.meteor.com/select).

### afFieldMessage
Expand Down Expand Up @@ -491,6 +529,20 @@ An `afArrayField` (or an `afQuickField` for an array) supports the additional at

An `afArrayField` (or an `afQuickField` for an array) also supports the `initialCount` attribute. Use it to override the default initial count to be something other than 1, including 0. Note that `minCount` will still always take precedence. That is, if the `minCount` is 1 and you specify `initialCount=0`, the initial count will be 1.

To specify options for each item in the array you can set
```
'arrayFieldName.$': {
...
autoform: {
afFieldInput: {
options: function () {
//return options object
}
}
}
}
```

At the moment, the add and remove buttons disappear when you can't use them. This could be changed to make them disabled. You can do this yourself with a custom template, but if you have thoughts about how it should work out of the box, submit an issue to discuss.

### afEachArrayItem
Expand Down Expand Up @@ -700,17 +752,41 @@ all the supported hooks:
AutoForm.hooks({
myFormId: {
before: {
insert: function(doc, template) {},
update: function(docId, modifier, template) {},
"methodName": function(doc, template) {}
insert: function(doc, template) {
//return doc; (synchronous)
//return false; (synchronous, cancel)
//this.result(doc); (asynchronous)
//this.result(false); (asynchronous, cancel)
},
update: function(docId, modifier, template) {
//return modifier; (synchronous)
//return false; (synchronous, cancel)
//this.result(modifier); (asynchronous)
//this.result(false); (asynchronous, cancel)
},
"methodName": function(doc, template) {
//return doc; (synchronous)
//return false; (synchronous, cancel)
//this.result(doc); (asynchronous)
//this.result(false); (asynchronous, cancel)
}
},

// The same as the callbacks you would normally provide when calling
// collection.insert, collection.update, or Meteor.call
after: {
insert: function(error, result, template) {},
update: function(error, result, template) {},
"methodName": function(error, result, template) {}
},

// Called when form does not have a `type` attribute
onSubmit: function(insertDoc, updateDoc, currentDoc) {},
onSubmit: function(insertDoc, updateDoc, currentDoc) {
// You must call this.done()!
//this.done(); // submitted successfully, call onSuccess
//this.done(new Error('foo')); // failed to submit, call onError with the provided error
//this.done(null, "foo"); // submitted successfully, call onSuccess with `result` arg set to "foo"
},

// Called when any operation succeeds, where operation will be
// "insert", "update", "submit", or the method name.
Expand All @@ -719,7 +795,13 @@ AutoForm.hooks({
// Called when any operation fails, where operation will be
// "validation", "insert", "update", "submit", or the method name.
onError: function(operation, error, template) {},

// Called every time the form is revalidated, which can be often if keyup
// validation is used.
formToDoc: function(doc, ss, formId) {},

// Called whenever `doc` attribute reactively changes, before values
// are set in the form fields.
docToForm: function(doc, ss, formId) {},

// Called at the beginning and end of submission, respectively.
Expand Down Expand Up @@ -1225,6 +1307,8 @@ To specify options for any field, use the `options` attribute and provide an arr
objects, where each object has a `label` property and a `value` property. There are several
different ways you can do this.

Alternatively, you can specify options as an object with {value: label} format. Values are coerced into the expected type.

#### Use allowed values array from the schema as both the label and the value

```js
Expand Down Expand Up @@ -1260,6 +1344,24 @@ Fields generated by `quickForm` or `afObjectField` or `afArrayField` use `allowe
}
```

*Alternative syntax:*

```js
{
favoriteColor: {
type: String,
allowedValues: ['red', 'green', 'blue'],
autoform: {
options: {
red: "Red",
green: "Green",
blue: "Blue
}
}
}
}
```
```html
{{> afQuickField name="favoriteColor"}}
```
Expand Down Expand Up @@ -1334,6 +1436,7 @@ on the site, too. If the code is publicly available, link to that, too.
* While developing, be sure to call `AutoForm.debug()` in your client code to enable extra logging.
* If nothing happens when you click the submit button for your form and there are
no errors, make sure the button's type is `submit`.
* If your `before` hook is called but the form is never submitted, make sure you are returning the `doc` or `modifier` from the hook or eventually calling `this.result(doc)` if you're doing something asynchronous.
## Contributing
Expand Down
36 changes: 20 additions & 16 deletions autoform-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ Template.autoForm.events({
if (data.resetOnSuccess !== false) {
AutoForm.resetForm(formId, template);
}
// Set docId in the context for insert forms, too
if (name === "insert") {
cbCtx.docId = result;
}
_.each(onSuccess, function onSuccessEach(hook) {
hook.call(cbCtx, name, result, template);
});
Expand Down Expand Up @@ -224,7 +228,7 @@ Template.autoForm.events({
// result of all of them immediately because they can return
// false to stop normal form submission.

var hookCount = hooks.length, doneCount = 0, submitError;
var hookCount = hooks.length, doneCount = 0, submitError, submitResult;

if (hookCount === 0) {
// Run endSubmit hooks (re-enabled submit button or form, etc.)
Expand All @@ -242,15 +246,18 @@ Template.autoForm.events({
resetForm: function () {
AutoForm.resetForm(formId, template);
},
done: function (error) {
done: function (error, result) {
doneCount++;
if (!submitError && error) {
submitError = error;
}
if (!submitResult && result) {
submitResult = result;
}
if (doneCount === hookCount) {
var submitCallback = makeCallback('submit');
// run onError, onSuccess, endSubmit
submitCallback(submitError);
submitCallback(submitError, submitResult);
}
}
};
Expand Down Expand Up @@ -416,13 +423,6 @@ Template.autoForm.events({
if (!data)
return;

// Update cached form values for hot code reload persistence
if (self.preserveForm !== false) {
formPreserve.registerForm(formId, function autoFormRegFormCallback() {
return getFormValues(template, formId, data.ss).insertDoc;
});
}

// Mark field value as changed for reactive updates
updateTrackedFieldValue(formId, key);

Expand All @@ -443,7 +443,7 @@ Template.autoForm.events({
'reset form': function autoFormResetHandler(event, template) {
var formId = this.id || defaultFormId;

formPreserve.unregisterForm(formId);
formPreserve.clearDocument(formId);

// Reset array counts
arrayTracker.resetForm(formId);
Expand All @@ -459,12 +459,16 @@ Template.autoForm.events({
// there is no need to reset validation for it. No error need be thrown.
}

//XXX We should ideally be able to call invalidateFormContext
// in all cases and that's it, but we need to figure out how
// to make Blaze forget about any changes the user made to the form
if (this.doc) {
event.preventDefault();
AutoForm.invalidateFormContext(formId);

// Use destroy form hack since Meteor doesn't give us an easy way to
// invalidate changed form attributes yet.
afDestroyUpdateForm.set(true);
Tracker.flush();
afDestroyUpdateForm.set(false);
Tracker.flush();

template.$("[autofocus]").focus();
} else {
// This must be done after we allow this event handler to return
Expand All @@ -475,7 +479,7 @@ Template.autoForm.events({
updateAllTrackedFieldValues(formId);

// Focus the autofocus element
if (template && template.view._domrange) {
if (template && template.view._domrange && !template.view.isDestroyed) {
template.$("[autofocus]").focus();
}
});
Expand Down
2 changes: 1 addition & 1 deletion autoform-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ regHelper('afFieldValueContains', function autoFormFieldValueContains(options) {
options = parseOptions(options, 'afFieldValueContains');

var currentValue = AutoForm.getFieldValue(options.formId, options.name);
return _.isArray(currentValue) && _.contains(currentValue, options.value);
return _.isArray(currentValue) && (_.contains(currentValue, options.value) || options.values && _.intersection(currentValue, options.values.split(",")));
});

/*
Expand Down
6 changes: 6 additions & 0 deletions autoform-inputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ getInputData = function getInputData(defs, hash, value, label, submitType) {
else if (typeof selectOptions === "function") {
selectOptions = selectOptions();
}
// Hashtable
if (_.isObject(selectOptions) && !_.isArray(selectOptions)) {
selectOptions = _.map(selectOptions, function(v, k) {
return {label: v, value: schemaType(k)};
});
}

/*
* Return the context. This is the object that becomes `this` in the
Expand Down
2 changes: 2 additions & 0 deletions autoform.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ if (typeof Tracker === "undefined" && typeof Deps !== "undefined") {
Tracker = Deps;
}

afDestroyUpdateForm = new ReactiveVar(false);

// reactive templates
globalDefaultTemplate = "bootstrap3"
defaultTypeTemplates = {};
Expand Down
3 changes: 3 additions & 0 deletions components/afArrayField/afArrayField.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.autoform-remove-item {
margin-right: 15px;
}
6 changes: 5 additions & 1 deletion components/autoForm/autoForm.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<template name="autoForm">
{{#unless afDestroyUpdateForm}}
{{! afDestroyUpdateForm is a workaround for sticky input attributes}}
{{! See https://github.com/meteor/meteor/issues/2431 }}
<form {{atts}}>
{{#with innerContext ..}}
{{> UI.contentBlock this}}
{{/with}}
</form>
</template>
{{/unless}}
</template>
12 changes: 12 additions & 0 deletions components/autoForm/autoForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ Template.autoForm.helpers({
// Preserve outer context, allowing access within autoForm block without needing ..
_.extend(innerContext, outerContext);
return innerContext;
},
afDestroyUpdateForm: function () {
return afDestroyUpdateForm.get();
}
});

Expand Down Expand Up @@ -66,6 +69,15 @@ Template.autoForm.created = function autoFormCreated() {
// the original referenced object.
var doc = data.doc ? EJSON.clone(data.doc) : null;

// Update cached form values for hot code reload persistence
if (data.preserveForm === false) {
formPreserve.unregisterForm(formId);
} else if (!formPreserve.formIsRegistered(formId)) {
formPreserve.registerForm(formId, function autoFormRegFormCallback() {
return getFormValues(template, formId, ss).insertDoc;
});
}

// Retain doc values after a "hot code push", if possible
var retrievedDoc = formPreserve.getDocument(formId);
if (retrievedDoc !== false) {
Expand Down
Loading

0 comments on commit 4c0bfa6

Please sign in to comment.