Skip to content

Instantly share code, notes, and snippets.

@nikhgupta
Last active January 4, 2016 03:19
Show Gist options
  • Save nikhgupta/8560998 to your computer and use it in GitHub Desktop.
Save nikhgupta/8560998 to your computer and use it in GitHub Desktop.
FuelUX Form Wizard in AngularJS
<wizard class="form-horizontal" id="timeline">
<step title="Welcome">
<label for="title">Title (required):</label>
<input type="text" name="title" id="title" class="form-control"
required placeholder="Your Timeline Title" ng-model="currEL.title" />
<ul ng-show="$$prevSibling.step.title.$dirty && $$prevSibling.step.title.$invalid" class="ng-error-list">
<li ng-show="$$prevSibling.step.title.$error.required">An eventline, definitely, needs a title. Don't you think?</li>
</ul>
<!-- TODO: maybe use errors like this for every step -->
<!-- <errors> -->
<!-- <error for="title" on="required">An eventline, definitely, needs a title. Don't you think?</error> -->
<!-- </errors> -->
</step>
<step title="Add Events">
<!-- .. -->
</step>
<step title="Finish">
<!-- .. -->
</step>
</wizard>
<div class="step-pane" ng-class="{active: active}" ng-transclude id='{{title | lowercase}}' ng-form="step">
<!-- TODO: filter should convert title to snake case -->
</div>
<form novalidate class='{{class}}' id='{{id}}' name='{{id}}'>
<div class="wizard clearfix">
<ul class="steps">
<li ng-repeat='step in steps()' ng-class='{active: step.active}' data-target='#{{step.title | lowercase}}'><span class="badge" ng-class="{'badge-info': step.active}">{{step.id}}</span>{{step.title}}</li>
</ul>
</div>
<div class="step-content">
<div ng-transclude></div>
<div class="actions m-t">
<button type="button" ng-disabled="current().id < 2" class="btn btn-default btn-sm btn-prev" ng-click="prev()">Prev</button>
<button type="button" ng-disabled="stepIsInvalid()" class="btn btn-default btn-sm btn-next" ng-click="next()" data-next="Next" data-last="Finish">Next</button>
</div>
</div>
</form>
@app.directive "wizard", ->
restrict: "EA"
replace : true,
require: "wizard"
transclude: true
controller: "WizardCtrl"
templateUrl: "wizard.html"
scope: {
id: "@"
class: "@"
}
link: (scope, element, attrs, wizardCtrl) ->
wizardCtrl.addWizard scope, element
@app.directive "step", ->
require: "^wizard"
restrict: "EA"
transclude: true
replace: true
templateUrl: "step.html"
scope: {
active: "=?"
title: "@"
}
link: (scope, element, attrs, wizardCtrl) ->
wizardCtrl.addStep scope, element
# when the scope is destroyed then remove the step from current steps array
scope.$on '$destroy', ->
wizardCtrl.removeStep scope
# keep a watch on when this step becomes active
# scope.$watch 'active', (active) ->
# wizardCtrl.select(scope) if active
@app.controller 'WizardCtrl', ($scope) ->
# some needed variables
steps = @steps = []
@currentStep = null
destroyed = false
@element = null
# some utility functions
@stepId = (step) -> steps.indexOf(step)
@totalSteps = -> steps.length
@hasSteps = -> @totalSteps > 0
@lastStep = -> steps[@totalSteps - 1]
# add the wizard a.k.a. store somethings..
@addWizard = (wizard, element) ->
$scope.$element = element
# find out the next and previous buttons
@nextBtn = element.find(".actions .btn-next")
@prevBtn = element.find(".actions .btn-prev")
@nextBtn.on 'click', @moveForward
@prevBtn.on 'click', @moveBackward
# add a step to the wizard
@addStep = (step, element) ->
step.id = steps.length + 1
step.$element = element
steps.push step
stepContent = element.closest(".step-content")
@activateStep step if steps.length is 1 or step.active
# remove a step from the wizard
@removeStep = (step) ->
index = @stepId(step)
steps.splice index, 1
# activate the previous step when this step was removed
previousIndex = if index >= @totalSteps then index - 1 else index
@activateStep steps[previousIndex]
# select a step and make it active
@activateStep = (step) ->
step.active = true
@currentStep = step
@stepIsValid = ->
@currentStep.step.$dirty && @currentStep.step.$valid
@moveForward = ->
console.log 'moving forward'
@moveBackward = ->
console.log 'moving backward'
@moveTo = (step) ->
console.log 'moving to given step'
# set a flag to know when this directive is being destroyed..
$scope.$on '$destroy', -> destroyed = true
# expose certain methods..
$scope.steps = -> steps
$scope.current = => @currentStep
$scope.isActive = (step) => @currentStep == step
$scope.select = (step) => @activateStep step
$scope.stepIsInvalid = => not @stepIsValid()
return
@xescuder
Copy link

Have you a full example working? I see that the directive is in CoffeeScript format, and after I've compiled to Javascript, I'm not been able to make working in angular way with my application module.

Thanks!

@Misiu
Copy link

Misiu commented Jan 14, 2015

Could You please post working example link with JS version?

Copy link

ghost commented Jan 22, 2015

Thanks for the example, but agreed with the previous - you should post Javascript instead of CoffeeScript in the future. Bit unnecessary to post a precompiler language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment