Skip to content

Commit

Permalink
fixes, semantics, UX, docs
Browse files Browse the repository at this point in the history
fixes, semantics, UX, docs
  • Loading branch information
taoteh1221 committed Oct 14, 2023
1 parent d65ea79 commit 2796bd6
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 38 deletions.
3 changes: 3 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ MIT License

Copyright (c) 2013-2018 Jennifer Wachter

Copyright (c) 2022-2023 Michael Kilday ([email protected])


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A jQuery plugin that allows you to easily create repeatable groups of form field

#### Markup

At a bare minimum, the html form should contain a container for which the repeatable items will populate and a button that when clicked, tells jquery.repeatable to add another item. The classes you use can be different than the example below, but make sure you register any differences when you call the plugin. See [settings](#settings) below.
At a bare minimum, the html form should contain a container for which the repeatable items will populate and a button that when clicked, tells jquery.repeatable to add another item. The classes you use can be different than the example below, but make sure you register any differences when you call the plugin. See [config](#config) below.

```html
<form>
Expand All @@ -24,17 +24,18 @@ At a bare minimum, the html form should contain a container for which the repeat
The field group template contains html for the group of form fields that will be repeatable. Here are a few things to keep in mind:

* The form fields need to be within a container element.
* The form fields should contain the attribute `data-track-index` (see example below), so jquery.repeatable can run checks to avoid clashes from creating duplicate array index key names.
* Include the symbol `{?}` when you need a unique value. You must at least include this symbol in the `name` attribute of your form fields to ensure they are all unique.
* If you want users to be able to delete items, include a delete button in the template.

```html
<script type="text/template" id="people">
<div class="field-group">
<label for="firstname_{?}">First name</label>
<input type="text" name="firstname_{?}" value="" id="firstname_{?}" />
<input data-track-index="{?}" type="text" name="firstname_{?}" value="" id="firstname_{?}" />

<label for="lastname_{?}">First name</label>
<input type="text" name="lastname_{?}" value="" id="lastname_{?}" />
<input data-track-index="{?}" type="text" name="lastname_{?}" value="" id="lastname_{?}" />

<input type="button" class="delete" value="X" />
</div>
Expand All @@ -56,10 +57,11 @@ $(function() {
When a user clicks on the `.add` button, the script will render a new `.field-group` within the `.repeatable-container`. Each `{?}` will be replaced with a unique value so that each field is unique in the scope of the form. If a user clicks on a `.delete` button within a group, that group will be removed from the form.


<a name="settings"></a>
### Settings
<a name="config"></a>
### Config


* __prefix__: _Optional_. (string) The form array index key name to be prepended to the form array index number. This can be set to '', so only the form array index number is used. Default: "new"
* __addTrigger__: _Optional_. (string) The selector that when clicked, a new field group will be added to the repeatable item container. Default: ".add"
* __deleteTrigger__: _Optional_. (string) The selector that when clicked, the field group the delete selector is within will be removed from the form. Default: ".delete"
* __itemContainer__: _Optional_. (string) The selector that represents each field group container. Default: ".field-group"
Expand Down
12 changes: 12 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,16 @@

-BUG FIX: Properly track current amount of repeated elements (so any server-side-rendered elements are not overwritten next request submission)

-------------------------------------------------------------------


-------------------------------------------------------------------
0.40.3:

-Bug fixes

-User experience improvements

-Refactored semantics

-------------------------------------------------------------------
12 changes: 6 additions & 6 deletions example/Repopulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ class Repopulator
{
public static $templates = array(
"todos" => '<div class="field-group controls-row">
<input type="text" class="span6" name="todos[{?}][task]" value="{task}" placeholder="Task to do">
<input type="text" class="span2" name="todos[{?}][duedate]" value="{duedate}" placeholder="Due by">
<input data-track-index="{?}" type="text" class="span6" name="todos[{?}][task]" value="{task}" placeholder="Task to do">
<input data-track-index="{?}" type="text" class="span2" name="todos[{?}][duedate]" value="{duedate}" placeholder="Due by">
<input type="button" class="btn btn-danger span-2 delete" value="X" />
</div>',
"todos_labels" => '<div class="field-group controls-row">
<div class="span6">
<label for="task_{?}">Task to do</label>
<input type="text" class="span6" name="todos_labels[{?}][task]" value="{task}" id="task_{?}">
<input data-track-index="{?}" type="text" class="span6" name="todos_labels[{?}][task]" value="{task}" id="task_{?}">
</div>
<div class="span4">
<label for="duedate_{?}">Due date</label>
<input type="text" class="span2" name="todos_labels[{?}][duedate]" value="{duedate}" id="duedate_{?}">
<input data-track-index="{?}" type="text" class="span2" name="todos_labels[{?}][duedate]" value="{duedate}" id="duedate_{?}">
<input type="button" class="btn btn-danger span-2 delete" value="X" />
</div>
</div>',
"people" => '<div class="field-group controls-row">
<input type="text" class="span4" name="people[{?}][firstname]" value="{firstname}" placeholder="First name">
<input type="text" class="span4" name="people[{?}][lastname]" value="{lastname}" placeholder="Last name">
<input data-track-index="{?}" type="text" class="span4" name="people[{?}][firstname]" value="{firstname}" placeholder="First name">
<input data-track-index="{?}" type="text" class="span4" name="people[{?}][lastname]" value="{lastname}" placeholder="Last name">
<input type="button" class="btn btn-danger span-2 delete" value="X" />
</div>'
);
Expand Down
1 change: 1 addition & 0 deletions example/labels.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<script>
$(function() {
$(".todos_labels .repeatable").repeatable({
prefix: 'new',
addTrigger: ".todos_labels .add",
deleteTrigger: ".todos_labels .delete",
template: "#todos_labels",
Expand Down
1 change: 1 addition & 0 deletions example/one.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<script>
$(function() {
$(".todos .repeatable").repeatable({
prefix: 'new',
addTrigger: ".todos .add",
deleteTrigger: ".todos .delete",
template: "#todos",
Expand Down
2 changes: 2 additions & 0 deletions example/two.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<script>
$(function() {
$(".todos .repeatable").repeatable({
prefix: 'new',
addTrigger: ".todos .add",
deleteTrigger: ".todos .add",
template: "#todos",
Expand All @@ -77,6 +78,7 @@
});

$(".people .repeatable").repeatable({
prefix: 'new',
addTrigger: ".people .add",
deleteTrigger: ".people .add",
template: "#people",
Expand Down
104 changes: 77 additions & 27 deletions jquery.repeatable.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,51 @@

// version 0.40.2, 2022/JUNE/2ND
/*
version 0.40.3, 2023/OCTOBER/14TH
MIT License
Copyright (c) 2013-2018 Jennifer Wachter
Copyright (c) 2022-2023 Michael Kilday ([email protected])
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/


(function ($) {

$.fn.repeatable = function (userSettings) {
$.fn.repeatable = function (devConfig) {

/**
* Default settings
* Default config
* @type {Object}
*/
var defaults = {
prefix: "new",
addTrigger: ".add",
deleteTrigger: ".delete",
max: null,
min: 0,
min: 0,
template: null,
itemContainer: ".field-group",
beforeAdd: function () {},
Expand All @@ -30,10 +62,10 @@
var target = $(this);

/**
* Blend passed user settings with defauly settings
* Blend passed user config with default config
* @type {array}
*/
var settings = $.extend({}, defaults, userSettings);
var config = $.extend({}, defaults, devConfig);

/**
* Total templated items found on the page
Expand All @@ -42,8 +74,8 @@
* @return null
*/
var total = function () {
calc_total = $(target).find(settings.itemContainer).length;
console.log(calc_total);
calc_total = $(target).find(config.itemContainer).length;
//console.log(calc_total); // DEBUGGING ONLY
return calc_total;
}();

Expand All @@ -52,8 +84,6 @@
* repeatable element unique
* @type {Number}
*/
var i = total > 0 ? total : 0;


/**
* Add an element to the target
Expand All @@ -63,9 +93,9 @@
*/
var addOne = function (e) {
e.preventDefault();
settings.beforeAdd.call(this);
config.beforeAdd.call(this);
var item = createOne();
settings.afterAdd.call(this, item);
config.afterAdd.call(this, item);
};

/**
Expand All @@ -76,13 +106,13 @@
*/
var deleteOne = function (e) {
e.preventDefault();
if (total === settings.min) return;
var item = $(this).parents(settings.itemContainer).first();
settings.beforeDelete.call(this, item);
if (total === config.min) { alert('Minimum allowed entries is ' + config.min + ', please just delete the data inside the fields, and update / save the settings.'); return; }
var item = $(this).parents(config.itemContainer).first();
config.beforeDelete.call(this, item);
item.remove();
total--;
maintainAddBtn();
settings.afterDelete.call(this);
config.afterDelete.call(this);
};

/**
Expand All @@ -103,11 +133,31 @@
* @return {jQuery object}
*/
var getUniqueTemplate = function () {
var template = $(settings.template).html();
i = i + 1;
template = template.replace(/{\?}/g, "new" + i); // {?} => iterated placeholder

while ( duplicateCheck() == 'yes' ) {
total++;
}

var template = $(config.template).html();
template = template.replace(/{\?}/g, config.prefix + total); // {?} => iterated placeholder
template = template.replace(/\{[^\?\}]*\}/g, ""); // {valuePlaceholder} => ""
return $(template);

};

/**
* Checks for duplicate indexes in form arrays
*/
var duplicateCheck = function () {

if ( 1 < $(config.itemContainer + ' input[data-track-index=' + config.prefix + total + ']').length || 1 < $(config.itemContainer + ' select[data-track-index=' + config.prefix + total + ']').length ) {
console.log('POTENTIAL duplicate form array index clash, upping count...')
return 'yes';
}
else {
return 'no';
}

};

/**
Expand All @@ -116,14 +166,14 @@
* @return null
*/
var maintainAddBtn = function () {
if (!settings.max) {
if (!config.max) {
return;
}

if (total === settings.max) {
$(settings.addTrigger).attr("disabled", "disabled");
} else if (total < settings.max) {
$(settings.addTrigger).removeAttr("disabled");
if (total === config.max) {
$(config.addTrigger).attr("disabled", "disabled");
} else if (total < config.max) {
$(config.addTrigger).removeAttr("disabled");
}
};

Expand All @@ -132,11 +182,11 @@
* @return null
*/
(function () {
$(settings.addTrigger).on("click", addOne);
$("form").on("click", settings.deleteTrigger, deleteOne);
$(config.addTrigger).on("click", addOne);
$("form").on("click", config.deleteTrigger, deleteOne);

if (!total) {
var toCreate = settings.min - total;
var toCreate = config.min - total;
for (var j = 0; j < toCreate; j++) {
createOne();
}
Expand Down

0 comments on commit 2796bd6

Please sign in to comment.