Mediafly Interactives lets our customers create rich and engaging custom apps using Mediafly SalesKit and ProReview. Interactives are zip files consisting of HTML, CSS, JavaScript, and resources that can work entirely offline. You can learn more about Interactives in the APIÂ documentation.
Angular.JSÂ (or, Angular for short) is an open-source JavaScript framework that makes building MVC-like web applications simple, testable, and fun. Many Angular apps run as single-page JavaScript applications, and the framework is ideal for offline web apps.
When you combine the two, very interesting things can happen. In this post, we will explore how AngularJS and Interactives create awesome workouts. Â Specifically, Mediafly Workouts, which is a cross-device, fully responsive Interactive meant to help you improve your core through a structured workout. You can find the source code on BitBucket.
This post assumes you have a working knowledge of Angular.
How It Works
In the Mediafly Airship content management system, create a folder with all of your workouts as separate images, named with each exercise. I suggest building your workout using one of the many core workouts you can find online. We didn’t include any images in the source, so as to avoid any copyright issues that might exist.
Package and drop the Interactive as the first item in the folder*. Open and go!
On launch, the Interactive will
- Parse through the hierarchy of items and aggregate the workouts you have listed for this folder
- Show you a dialog with the intro description and ask for your rest and workout times
- When you hit ‘done’, launch you into the workouts and cycle between rest and workout for each of the exercises you have added
Interesting Areas
mflyDataInit, mflyInit and mflyResume
Mediafly apps tell Interactives information by calling JavaScript functions in those Interactives, as described in the documentation. For example, mflyDataInit is called by the app to tell the Interactive of the various features and capabilities of the device, OS, app, and Interactive itself. Interactives simply have to implement the function to catch and make use of this information.
However, implementing functions in JavaScript is not the AngularJS way of doing things. Handling Angular events is. To ease the transition, we built a simple file called mflyAngular.js that implements each relevant function and then broadcasts the event into the Angular root scope. Interactives developers simply need to include the following file (after including angular.js):
[code language=”html”]<script type=”text/javascript” src=”js/mflyAngular.js”></script>[/code]
Then, catch the events in their controllers:
[code language=”javascript”]
$scope.$on(‘Mediafly.mflyDataInit’, function(event, obj) {
// Do stuff
});
[/code]
Using Timer
Workouts make use of Angular Timer, a simple timer directive. While basic operation of timer was very smooth, there were some unclean areas of integration. Notably, the events it generates operate outside of Angular for some reason. As a result, we had to wrap the body within a $scope.$apply. For example:
[code language=”javascript”]
/**
* When the timer stops, change state and restart timer
* Note: operating from outside AngularJS
**/
$scope.$on(‘timer-stopped’, function (event, data) {
$scope.$apply(function () {
$scope.moveToNextState();
});
$scope.startTimer();
});
[/code]
Models and Views
We maintain the two states for exercises with a JSON object:
[code language=”javascript”]
$scope.STATE = {
REST: { display: ‘rest and prepare’, color: ‘#A00000’, timer: 10 },
DO: { display: ‘workout’, color: ‘#00AA00’, timer: 60 }
};
[/code]
With Angular, we can let the user change what value they want for each timer:
[code language=”html”]
<label for=”restTime”>Rest time (seconds):</label>
<input class=”form-control” id=”restTime” type=”text” />
…
<label for=”workoutTime”>Workout time (seconds):</label>
<input class=”form-control” id=”workoutTime” type=”text” />
[/code]
And then, the app can save the state into a key it defines.
Saving and Getting Key/Value Data
The latest version of Mediafly’s iOS apps (and soon, Android and Windows 8) allows Interactives to store generalized key/value data. Interactives simply need to:
1. Include mflyAngular.js in index.html, as was done earlier:
[code language=”html”]<script type=”text/javascript” src=”js/mflyAngular.js”></script>[/code]
2. Load the Mediafly.services module:
[code language=”javascript”]
angular.module(‘myApp’, [‘myApp.controllers’, ‘Mediafly.services’, ‘timer’, ‘ngSanitize’]);
[/code]
3. Pass the MflyDataService factory into your controller:
[code language=”javascript”]
angular.module(‘myApp.controllers’, []).
controller(‘WorkoutCtrl’, [‘$scope’, ‘MflyDataService’, function ($scope, MflyDataService) {
…
}]);
[/code]
4a. Save values to keys with MflyDataService.saveState. This function utilizes the Promises pattern to handle callbacks; simply implement one of the common functions (e.g. success, error, then) to take action on save completion:
[code language=”javascript”]
MflyDataService.saveState(‘state’, JSON.stringify($scope.STATE))
.success(function (result, status, xhr) {
// Save successful
}).error(function (result, status, xhr) {
// Error while saving
});
[/code]
4b. Retrieve values from keys with MflyDataService.getState, the corollary to saveState:
[code language=”javascript”]
$scope.$on(‘Mediafly.mflyResume’, function(event) {
MflyDataService.getState(‘state’)
.success(function (result, status, xhr) {
$scope.STATE = result;
})
.error(function (result, status, xhr) {
// Error while retrieving
});
});
[/code]
Next Steps
This was a great start. However, there is a lot of work do.
- Build tests. AngularJS is very testable. This sample (currently) implements none of it.
- Simplify mflyInit and migrate over to mfly://data/folder/. This will dramatically simplify the code required to build up the list of exercises.
- Clean up mflyDataInit, which currently requires hard coding of this Interactive’s item ID.
- Improve handle when the user closes then reopens the Interactive, using mflyPause and mflyResume.
In the meantime, happy workout!
* There are some configuration steps that need to take place. Take a look at README.md within the source.
Comments are closed.