Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ This repo has branches that represent the code for various blog posts. Here is
* Introduction to Backbone.js with JQuery Mobile => master
* Sorting collections with Backbone.js and Jquery Mobile => sort
* From List to Details View using jQuery Mobile and Backbone.js => details-display
* Using jQuery Mobile and Backbone.js for handling forms => new-and-edit
* Using jQuery Mobile and Backbone.js for handling forms => new-and-edit
* Using jQuery Mobile 1.4.0 and Backbone 1.1.0 => update-to-jqm14-bb11
89 changes: 78 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,55 @@
<head>
<title>Exercise</title>

<link rel="stylesheet" href="style/jquery.mobile-1.0.css" />
<link rel="stylesheet" href="style/jquery.mobile-1.4.0.css" />
<link rel="stylesheet" href="style/jquery.mobile.icons-1.4.0.min.css" />
<link rel="stylesheet" href="style/exercise.css"/>

<script src="js/vendor/jquery-1.6.4.min.js"></script>
<script src="js/vendor/jquery.mobile-1.0.min.js"></script>
<script src="js/vendor/underscore.js"></script>
<script src="js/vendor/backbone.js"></script>

<!-- App specific scripts -->
<script src="js/app.js"></script>

<!-- templates -->
<script type="text/template" id="activity-list-item-template">
<li><a href="#activity-details" identifier="<%= id %>"><%= date %> - <%= type %></a></li>
<li><a href="#activity-details" data-transition="slide" identifier="<%= id %>"><%= displayDate %> - <%= type %></a></li>
</script>

<script type="text/template" id="activity-details-template">
<h3><%= type %></h3>
<ul data-role="listview" id="activitiy-fields" data-inset="true">
<li>Date: <%= displayDate %></li>
<li>Minutes: <%= minutes %></li>
<li>Distance: <%= distance %></li>
<li>Comments: <%= comments %></li>
</ul>
</script>

<script type="text/template" id="activity-form-template">
<label for="date" class="select">Date</label>
<% if (navigator.userAgent.indexOf('iPhone') >= 0 ||
navigator.userAgent.indexOf('iPad') >= 0 ||
navigator.userAgent.indexOf('Chrome') >= 0
) { %>
<input type="date" name="date" id="date" placeholder="Date" value="<%= dateInputType %>" />
<% }else{ %>
<input type="date" name="date" id="date" placeholder="Date" value="<%= displayDate %>" />
<% } %>

<label for="type" class="select">Type</label>
<select name="type" id="type">
<option>Run</option>
<option>Bike</option>
<option>Swim</option>
<option>Walk</option>
</select>

<label for="distance">Distance</label>
<input type="range" name="distance" id="distance" data-highlight="true" min="0" max="180" value="<%= minutes %>" >
<!-- <input type="text" name="distance" id="distance" placeholder="" value="<%= distance %>" /> -->

<label for="minutes">Minutes</label>
<!-- <input type="tel" name="minutes" id="minutes" placeholder="" value="<%= minutes %>" /> -->
<input type="range" name="minutes" id="minutes" data-highlight="true" min="0" max="180" value="<%= minutes %>" >

<div data-role="fieldcontain">
<textarea name="comments" id="comments" placeholder="Comments"><%= comments %></textarea>
</div>
</script>

<!-- maximum scale added to stop zoom on select controls -->
Expand All @@ -26,12 +62,43 @@
<div data-role="page" id="activities">
<div data-role="header">
<h1>Activities</h1>
<a href="#" data-role="button" data-icon="add" id="add-button">Add</a>
<a href="#activity-form" id="add-button" data-transition="slide" class="ui-btn ui-btn-right ui-corner-all ui-shadow ui-mini ui-btn-icon-left ui-icon-plus">Add</a>
</div>
<div data-role="content">
<!-- the contents of the list view will be rendered via the backbone view -->
</div>
</div>

<div data-role="page" id="activity-details">
<div data-role="header" data-add-back-btn="true">
<a href="#activity-form" id="edit-activity-button" data-transition="slide" class="ui-btn ui-btn-right ui-corner-all ui-shadow ui-mini ui-btn-icon-left ui-icon-edit">Edit</a>
<h1>Activity Details</h1>
</div>
<div data-role="content" id="activity-details-content">
<!-- the contents of the list view will be rendered via the backbone view -->
</div>
</div>

<div data-role="page" id="activity-form" >
<div data-role="header" data-add-back-btn="true">
<a href="#" data-role="button" data-icon="check" data-theme="b" data-rel="back" id="save-activity-button" class="ui-btn-right">Save</a>
<h1>New Activity</h1>
</div>
<div data-role="content" id="activity-form-content">
<!-- the contents of the form will be rendered via the backbone view -->
<form id="activity-form-form" action="#"></form>
</div>
</div>

<script src="js/vendor/jquery-1.11.0.min.js"></script>
<script src="js/vendor/jquery.mobile-1.4.0.min.js"></script>
<script src="js/vendor/underscore-min.js"></script>
<script src="js/vendor/backbone-min.js"></script>
<script src="js/vendor/formparams.js"></script>

<!-- App specific scripts -->
<script src="js/app.js"></script>


</body>
</html>
201 changes: 183 additions & 18 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,81 @@
var exercise = {};

(function($){

exercise.Activity = Backbone.Model.extend({
defaults: {
date: new Date(),
type: '',
distance: '',
comments: '',
minutes: ''
},

set: function(attributes, options) {
var aDate;
if (attributes.date){
//TODO future version - make sure date is valid format during input
console.log("attributes.date ", attributes.date);
aDate = new Date(attributes.date);
if ( Object.prototype.toString.call(aDate) === "[object Date]" && !isNaN(aDate.getTime()) ){
attributes.date = aDate;
}
}
Backbone.Model.prototype.set.call(this, attributes, options);
},

dateInputType: function(){
return exercise.formatDate(this.get('date'), "yyyy-mm-dd"); //https://github.com/jquery/jquery-mobile/issues/2755
},

displayDate: function(){
return exercise.formatDate(this.get('date'), "mm/dd/yyyy");
},

toJSON: function(){
var json = Backbone.Model.prototype.toJSON.call(this);
return _.extend(json, {dateInputType : this.dateInputType(), displayDate: this.displayDate()});
}
});

exercise.formatDate = function(date, formatString){
var yyyy, month, mm, day, dd, formatedDate;

if (date instanceof Date){
yyyy = date.getFullYear();
month = date.getMonth() + 1;
mm = month < 10 ? "0" + month : month;
day = date.getDate();
dd = day < 10 ? "0" + day : day;

formatedDate = formatString.replace(/yyyy/i, yyyy);
formatedDate = formatedDate.replace(/mm/i, mm);
formatedDate = formatedDate.replace(/dd/i, dd);
}else{
formatedDate = "";
}

return formatedDate;
};

exercise.Activities = Backbone.Collection.extend({
model: exercise.Activity,
url: "exercise.json"
url: "exercise.json",
comparator: function(activity){
var date = new Date(activity.get('date'));
return date.getTime();
}
});

exercise.ActivityListView = Backbone.View.extend({
tagName: 'ul',
id: 'activities-list',
attributes: {"data-role": 'listview'},

initialize: function() {
this.collection.bind('add', this.add, this);
initialize: function(options) {
this.options = options || {};
this.collection.bind('add', this.render, this);
this.collection.bind('change', this.changeItem, this);
this.collection.bind('reset', this.render, this);
this.template = _.template($('#activity-list-item-template').html());
},

Expand All @@ -29,21 +88,71 @@ var exercise = {};
template = this.template,
listView = $(this.el);

container.empty();
$(this.el).empty();
activities.each(function(activity){
listView.append(template(activity.toJSON()));
});
this.renderItem(activity);
}, this);
container.html($(this.el));
container.trigger('create');
return this;
},

add: function(item) {
var activitiesList = $('#activities-list'),
template = this.template;
renderItem: function(item) {
var template = this.template,
listView = $(this.el),
renderedItem = template(item.toJSON()),
$renderedItem = $(renderedItem);

$renderedItem.jqmData('activityId', item.get('id'));
$renderedItem.bind('click', function(){
//set the activity id on the page element for use in the details pagebeforeshow event
$('#activity-details').jqmData('activityId', $(this).jqmData('activityId')); //'this' represents the element being clicked
});

listView.append($renderedItem);
},

changeItem: function(item){
this.collection.sort();
this.render();
}
});

exercise.ActivityDetailsView = Backbone.View.extend({
//since this template will render inside a div, we don't need to specify a tagname
initialize: function(options) {
this.options = options || {};
this.template = _.template($('#activity-details-template').html());
},

render: function() {
var container = this.options.viewContainer,
activity = this.model,
renderedContent = this.template(this.model.toJSON());

container.html(renderedContent);
container.trigger('create');
return this;
}
});

exercise.ActivityFormView = Backbone.View.extend({
//since this template will render inside a div, we don't need to specify a tagname, but we do want the fieldcontain
attributes: {"data-role": 'fieldcontain'},

initialize: function(options) {
this.options = options || {};
this.template = _.template($('#activity-form-template').html());
},

render: function() {
var container = this.options.viewContainer,
renderedContent = this.template(this.model.toJSON());

activitiesList.append(template(item.toJSON()));
activitiesList.listview('refresh');
container.html(renderedContent);
container.trigger('create');
return this;
}
});

Expand All @@ -54,18 +163,74 @@ var exercise = {};

}(jQuery));

$('#activities').live('pageinit', function(event){
$('#activities').on('pageinit', function(event){
var activitiesListContainer = $('#activities').find(":jqmData(role='content')"),
activitiesListView;
exercise.initData();
activitiesListView = new exercise.ActivityListView({collection: exercise.activities, viewContainer: activitiesListContainer});
activitiesListView.render();
});

$('#add-button').live('click', function(){
var today = new Date(),
date;
$(document).ready(function(){

$('#add-button').on('click', function(){
var activity = new exercise.Activity(),
activityForm = $('#activity-form-form'),
activityFormView;

//clear any existing id attribute from the form page
$('#activity-details').jqmRemoveData('activityId');
activityFormView = new exercise.ActivityFormView({model: activity, viewContainer: activityForm});
activityFormView.render();
});

$('#activity-details').on('pagebeforeshow', function(){
console.log('activityId: ' + $('#activity-details').jqmData('activityId'));
var activitiesDetailsContainer = $('#activity-details').find(":jqmData(role='content')"),
activityDetailsView,
activityId = $('#activity-details').jqmData('activityId'),
activityModel = exercise.activities.get(activityId);

activityDetailsView = new exercise.ActivityDetailsView({model: activityModel, viewContainer: activitiesDetailsContainer});
activityDetailsView.render();
});

$('#edit-activity-button').on('click', function() {
var activityId = $('#activity-details').jqmData('activityId'),
activityModel = exercise.activities.get(activityId),
activityForm = $('#activity-form-form'),
activityFormView;

activityFormView = new exercise.ActivityFormView({model: activityModel, viewContainer: activityForm});
activityFormView.render();
});

date = (today.getMonth() + 1) + "/" + today.getDate() + "/" + today.getFullYear();
exercise.activities.add({id: 6, date: date, type: 'Walk', distance: '2 miles', comments: 'Wow...that was easy.'});
});
$('#save-activity-button').on('click', function(){
var activityId = $('#activity-details').jqmData('activityId'),
activity,
dateComponents,
formJSON = $('#activity-form-form').formParams();

//if we are on iOS and we have a date...convert it from yyyy-mm-dd back to mm/dd/yyyy
//TODO future version - for non-iOS, we would need to validate the date is in the expected format (mm/dd/yyyy)
if (formJSON.date && (navigator.userAgent.indexOf('iPhone') >= 0 ||
navigator.userAgent.indexOf('iPad') >= 0 ||
navigator.userAgent.indexOf('Chrome') >= 0
)
){
dateComponents = formJSON.date.split("-");
formJSON.date = dateComponents[1] + "/" + dateComponents[2] + "/" + dateComponents[0];
}

if (activityId){
//editing
activity = exercise.activities.get(activityId);
activity.set(formJSON); //not calling save since we have no REST backend...save in memory
}else{
//new (since we have no REST backend, create a new model and add to collection to prevent Backbone making REST calls)
activity = new exercise.Activity(formJSON);
activity.set({'id': new Date().getTime()}); //create some identifier
exercise.activities.add(activity);
}
});
});
2 changes: 2 additions & 0 deletions js/vendor/backbone-min.js

Large diffs are not rendered by default.

Loading