Skip to content

Instantly share code, notes, and snippets.

@rafaelrinaldi
Created April 28, 2014 23:36
Show Gist options
  • Save rafaelrinaldi/11387067 to your computer and use it in GitHub Desktop.
Save rafaelrinaldi/11387067 to your computer and use it in GitHub Desktop.
onboarding.js
//= require modules/onboarding/onboarding_single_choice
//= require modules/onboarding/onboarding_multiple_choice
Module('OnboardingController.Index', function(Index) {
/**
* 1. Grab survey model remotely
* 2. Register dynamic components
* 3. Bootstrap Vue app
*/
Index.fn.initialize = function() {
var self = this;
this._getSurveyModel()
.then(this._registerComponents)
.then(this._registerPartials)
.then(this._registerEffects)
.done(this._startup);
};
/**
* Register all effects used by Vue to render the survey app.
*/
Index.fn._registerEffects = function() {
Vue.effect('fade', {
enter: function(el, insert, timeout) {
insert();
$(el).css({opacity: 0}).delay(550).animate({opacity: 1}, 500);
},
leave: function(el, remove, timeout) {
$(el).css({opacity: 1}).animate({opacity: 0}, .5, function() {
remove();
});
}
});
};
/**
* Register all partials used by Vue to render the survey app.
*/
Index.fn._registerPartials = function() {
Vue.partial('onboarding-header', $('#onboarding-header-template').text());
};
/**
* Register all components used by Vue to render the survey app.
* @param {Object} model Questions model.
* @return {Object} Questions model.
*/
Index.fn._registerComponents = function(model) {
var Component,
// Questions model
questions = model.questions,
// Available components
components = {
'single-choice': Onboarding.Survey.SingleChoice,
'multiple-choice': Onboarding.Survey.MultipleChoice
},
// Simple helper to get a string template based on its type
getTemplate = function(type) {
return $('#onboarding-' + type + '-template').text();
};
// Looping throught all question models
$.each(questions, function(index, question) {
// Find the component based on the question type (single choice, multiple choice...)
Component = components[question.type];
// Create the component instance passing the model and the template string
new Component(question, getTemplate(question.type));
});
// Return the model object so we can pipe the data to the next promise task
return model;
};
/**
* @return {Object} Survey model.
*/
Index.fn._getSurveyModel = function() {
return $.getJSON('/surveys/onboarding');
};
/**
* Instantiate the Vue application.
* @param {Object} model Questions model.
*/
Index.fn._startup = function(model) {
new Vue({
// DOM element that contains the survey
el: '#onboarding-survey',
data: {
title: '',
questionType: '',
// Serialized data of all survey questions.
serialize: {
}
},
// Fired once the app is ready (all instances are ok and DOM is ready)
ready: function() {
this.titleEl = $('.js-onboarding-title');
// Listen for `currentView:submit` channel.
this.$on('currentView:submit', this.processLinks);
// Set the current view to the first question item
this.setQuestion(model.questions[0]);
// Show onboarding app container
$(this.$el).show();
},
// All methods the app have internally.
methods: {
/**
* Process links based on their answer model.
* @param {Object} answer Answer model.
*/
processLinks: function(answer, serialize) {
var self = this;
// Never override app's `serialize` data, will mix into existing
this.serialize = $.extend(this.serialize, serialize);
// Loop through links and process them sequentially
$.each(answer.links, function(index, link) {
self.processLink(link.rel, link.href);
});
},
/**
* Process a single link.
* @param {String} rel Link `rel` property.
* @param {String} href Link `href` property.
*/
processLink: function(rel, href) {
switch(rel) {
case 'analytics' :
this.onTrackAnalytics(href);
break;
case 'submit' :
this.onSubmit(href);
break;
case 'proceed' :
this.onProceed(href);
break;
}
},
/**
* Send the `serialize` data to the web service.
*/
sendData: function(url, callback) {
$.ajax({
url: url,
data: this.getSerializedData(),
dataType: 'script',
type: 'POST'
}).done(callback);
},
/**
* @param {String} id Question id.
* @return {Object} Question model.
*/
getQuestionModel: function(id) {
return $.grep(model.questions, function(question) {
return question.field === id;
}).pop();
},
/**
* Set current question.
* @param {Object} question Question model.
*/
setQuestion: function(question) {
this.currentView = question.field;
this.questionType = question.type;
this.setTitle(question.title);
},
/**
* Set header title.
* @param {String} title Header title.
*/
setTitle: function(title) {
var self = this,
delay = 350;
this.titleEl.fadeOut(delay, function() {
self.title = title;
self.titleEl.fadeIn(delay);
});
},
/**
* @return {String} Serialize data to send to the web service.
*/
getSerializedData: function() {
return {answers: this.serialize};
},
/**
* Submit an action.
* @param {String} href Action reference.
*/
onSubmit: function(href) {
this.sendData(href);
},
/**
* Proceed to the next view
* @param {String} nextView Next view id (that comes from the questions model)
*/
onProceed: function(nextView) {
var question = this.getQuestionModel(nextView);
this.setQuestion(question);
},
/**
* Skip answer
* @param {Object} answer Answer model.
*/
onSkip: function(answer) {
this.processLinks(answer);
},
/**
* Track analytics tag.
* @param {String} href Tracking tag.
*/
onTrackAnalytics: function(href) {
AppAnalytics.track({
name: href,
properties: this.getSerializedData()
});
}
}
});
return model;
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment