diff --git a/README.md b/README.md index e8cb8de..38ffb3a 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ or add into the `composer.json` file under `require` section - `wizardContainerId (string)`: Id of the main container for the wizard. - `forceBsVersion (int)`: Force use of the bootstrap version in case you have some extension having dependencies on `yiisoft\yii2-bootstrap4` even though you are using `yiisoft\yii2-bootstrap` on the site overall, since the extension checks for yii2-bootstrap4 first and if it finds it will load the yii2-bootstrap4 assets and having both of the extensions installed the widget will always go for the bootstrap4. although we dont recommend doing that but still there are scenarios wher people are using in this manner and fce layout problems. default value for this option is `false` which means widget will detect automatically, you can use the provided constants `FormWizard::BS_3` or `FormWizard::BS_4`. +- `editMode (boolean)` : if form wizard should be loaded in edit mode. Default value is `false`. see [wiki](https://github.com/buttflattery/yii2-formwizard/wiki/Edit-Mode) for details. - `formOptions (array)`: Specify the [ActiveForm](https://www.yiiframework.com/doc/api/2.0/yii-widgets-activeform) properties. - `labelNext (string)` : Next button label, default value `Next`. - `labelPrev (string)` : Previous button label, default value `Previous`. diff --git a/src/FormWizard.php b/src/FormWizard.php index db35104..4018a97 100644 --- a/src/FormWizard.php +++ b/src/FormWizard.php @@ -125,6 +125,23 @@ class FormWizard extends Widget */ public $steps = []; + /** + * Enable Edit mode for a saved record, it will enable all the steps + * and user can jump to any steps by just clicking on the step anchor + * so that if any single information needs to be changed in any step + * he/she wont have to go through every step serially. + * + * @var mixed + */ + public $editMode = false; + + /** + * The array of steps that have errors + * + * @var array + */ + public $errorSteps = []; + /** * The Options for the ActiveForm see the * https://www.yiiframework.com/doc/api/2.0/yii-widgets-activeform @@ -504,6 +521,7 @@ public function getPluginOptions() 'keyNavigation' => false, 'autoAdjustHeight' => $this->autoAdjustHeight, 'disabledSteps' => $this->disabledSteps, + 'errorSteps' => $this->errorSteps, 'backButtonSupport' => false, 'theme' => $this->theme, 'transitionEffect' => $this->transitionEffect, @@ -515,8 +533,8 @@ public function getPluginOptions() 'toolbarExtraButtons' => $this->toolbarExtraButtons, ], 'anchorSettings' => [ - 'anchorClickable' => false, - 'enableAllAnchors' => false, + 'anchorClickable' => $this->editMode, + 'enableAllAnchors' => $this->editMode, 'markDoneStep' => $this->markDoneStep, 'markAllPreviousStepsAsDone' => $this->markAllPreviousStepsAsDone, 'removeDoneStepOnNavigateBack' => $this->removeDoneStepOnNavigateBack, @@ -1002,7 +1020,7 @@ public function registerScripts($pluginOptions, $isBs3, $jsOptionsPersistence) //bind Yii ActiveForm event afterValidate to check //only current steps fields for validation and allow to next step if($('#{$this->formOptions["id"]}').yiiActiveForm('data').attributes.length){ - $.formwizard.validation.bindAfterValidate('#{$this->formOptions["id"]}'); + $.formwizard.formValidation.bindAfterValidate('#{$this->formOptions["id"]}'); } //fields list @@ -1022,6 +1040,7 @@ classNext:'{$this->classNext}', classPrev:'{$this->classPrev}', classFinish:'{$this->classFinish}', enablePreview:'{$this->enablePreview}', + editMode:'{$this->editMode}', bsVersion:'{$this->_bsVersion}', classListGroup:'{$this->classListGroup}', classListGroupHeading:'{$this->classListGroupHeading}', diff --git a/src/assets/js/formwizard.js b/src/assets/js/formwizard.js index c40342c..370b469 100644 --- a/src/assets/js/formwizard.js +++ b/src/assets/js/formwizard.js @@ -111,7 +111,7 @@ $.formwizard = { setTimeout( function () { if ($(form).yiiActiveForm("data").attributes.length) { - return $.formwizard.validation.run(form, e); + return $.formwizard.formValidation.run(form, e); } if ($(e.target).hasClass("formwizard_finish")) { $(form).yiiActiveForm("submitForm"); @@ -238,7 +238,7 @@ $.formwizard = { `; } }, - validation: { + formValidation: { run: function (form, event) { $.formwizard.resetCurrentTarget = false; @@ -295,14 +295,14 @@ $.formwizard = { } }, }; - - if(inputTypes.hasOwnProperty(input.type)){ - if(inputTypes[input.type].call(this,input)===false){ - allEmpty=false; + + if (inputTypes.hasOwnProperty(input.type)) { + if (inputTypes[input.type].call(this, input) === false) { + allEmpty = false; return false; } } - + }); //if skippable step and all the inputs are empty then @@ -322,21 +322,54 @@ $.formwizard = { event.preventDefault(); + //form name let formName = $(this).attr("id"); + //current step index let currentIndex = $.formwizard.helper.currentIndex(form); + //is last step const isLastStep = currentIndex == $(form + " .step-anchor").find("li").length - 1; - const isPreviewEnabled = $.formwizard.options[formName].enablePreview && isLastStep; + //is preview step + const isPreviewStep = $.formwizard.options[formName].enablePreview && isLastStep; + //is skipable step const isSkippableStep = $("#step-" + currentIndex).data('step').skipable; - + let res; //check if the preview step OR skippable step then skip validation messages check - if (isPreviewEnabled || isSkippableStep) { + if (isPreviewStep || isSkippableStep) { res = 0; } else { res = $.formwizard.fields[formName][currentIndex].diff(messages); } + //if edit mode then highlight error steps + if ($.formwizard.options[formName].editMode) { + //get all fields + let allFields = $.formwizard.fields[formName]; + let errorSteps = []; + + //iterate all the fields + $.each(allFields, function (index, stepFields) { + //if the step is skipable + let isSkippable = $("#step-" + index).data('step').skipable; + + //if not skipable and has errors + if (!isSkippable && stepFields.diff(messages).length) { + //push the step index to the errorsteps array + errorSteps.push(index); + } + }); + + //update error step higlightening + let wizardContainerId = $.formwizard.options[formName].wizardContainerId; + $("#" + wizardContainerId).smartWizard('updateErrorStep', errorSteps); + + //if no error steps then slear errors + if (errorSteps.length) { + $.formwizard.formNavigation.goToStep("#"+wizardContainerId, errorSteps[0]); + } + } + if (!res.length) { //check if last step then submit form @@ -533,7 +566,7 @@ $.formwizard = { newFields.push(element.id); if (typeof fieldOptions !== 'undefined') { //add field to the activeform validation - $.formwizard.validation.addField(formId, fieldOptions); + $.formwizard.formValidation.addField(formId, fieldOptions); } } }); @@ -559,7 +592,7 @@ $.formwizard = { $.formwizard.helper.removeField(element); //remove from the ActiveForm validation list - $.formwizard.validation.removeField(element); + $.formwizard.formValidation.removeField(element); }); rowContainer.remove(); diff --git a/src/assets/js/jquery.smartWizard.js b/src/assets/js/jquery.smartWizard.js index 0000490..4b958b7 100644 --- a/src/assets/js/jquery.smartWizard.js +++ b/src/assets/js/jquery.smartWizard.js @@ -140,9 +140,7 @@ } // Error steps if (this.options.errorSteps && this.options.errorSteps.length > 0) { - $.each(this.options.errorSteps, function (i, n) { - mi.steps.eq(n).parent('li').addClass('danger'); - }); + mi.updateErrorStep(this.options.errorSteps); } // Hidden steps if (this.options.hiddenSteps && this.options.hiddenSteps.length > 0) { @@ -625,7 +623,25 @@ break; } } - } + }, + updateErrorStep: function (errorSteps = []) { //update error step highlightening + var mi = this; + //if errorSteps array not empty + if (errorSteps.length) { + + //add error classes to steps + $.each(errorSteps, function (i, n) { + mi.steps.eq(n).parent('li').addClass('danger'); + }); + return; + } + + //remove the error class from the steps if empty array + $.each(mi.steps, function (i, n) { + $(n).parent('li').removeClass('danger'); + }); + }, + }); // Wrapper for the plugin