Toll Free: +1-855-656-NUVI

It's Finally Here! NativeScript Animated Side Menu

It's Finally Here! NativeScript Animated Side Menu
  • Share:

A navigation pattern for mobile apps that is still pretty hot at the time of this writing is a side drawer that playfully slides out like the one in the Telerik NEXT app. Or a navigation page that gets revealed as main content slides out of the way, like the Facebook and Spotify apps. This allows many more options to be shown than would fit on a tab bar.

Since this UI element is not available out of the box yet, I decided to take a stab at building one. This post will show you how I’ve done it.

 

NativeScript_AnimatedMenu_01_a

 

 

Note: There are some animation pieces in the module that are iOS only, but they can easily be augmented with their Android equivalents and since this project is open sourced on GitHub, I hope someone will make those Android additions.

 

The key to making this work is being able to dynamically load pages that are automatically bound to their code by NativeScript

 

I tried several configurations before deciding on a method that offers the most flexibility. There are a few things that I thought were necessary.

  • Smooth animation to reveal the menu
  • Give the developer the ability to use already proven and familiar steps to design each page.
    1. Use XML to layout the main content and navigation pages
    2. Allow separate CSS styling for the main and menu pages
    3. Allow the same MVVM pattern that we’ve seen since the NativeScript Hello World days, where a separate view model can be bound to the main and menu pages individually
  • Abstract all the dirty details into a NativeScript module and make it available as an NPM package

 

Animated Menu in Action

You can download the demo app that shows this animated menu in action. Or you can attempt to build an app yourself following the steps below.

Note: Make sure you have NativeScript installed. If you don’t, follow the steps here.

 

The Setup

Create a new NativeScript project from a directory of your choice, this will create a directory called animatedmenudemo and populate some NativeScript related base code and directory structure.

tns create animatedmenudemo

Change directory to your new project

cd animatedmenudemo

Add the iOS platform to your project

tns platform add ios

Install the nativescript-animated-menu module via NPM

npm install nativescript-animated-menu --save-dev

 

The Page Structure

The key to making this work is being able to dynamically load pages that are automatically bound to their code by NativeScript. The animated menu module will dynamically load the menu and main pages specified by its attributes.

The minimum required structure has three pages:

  1. root – this is the host page for the animated menu
  2. menu-page – this is the page that defines the menu and it’s functions
  3. main-page – this is the main content that slides out of the way to reveal the menu
Conceptual structure of the pages NativeScript_AnimatedMenu_02

 

Create the XML and corresponding JavaScript files for these three pages. You will end up with the following files:

NativeScript_AnimatedMenu_03

Note: the root.js file is optional. Use it only if you want to introduce higher-level logic. I decided to omit this file because I don’t need it for this sample.

Add any view models that you deem necessary. In this case I’ve added a view model for the main page to bind the menu trigger button text, and a view model for the menu page to handle menu button clicks.

NativeScript_AnimatedMenu_04

 

The Code

There really is only one part that is out of the ordinary here as far as your standard NativeScript development goes and that’s the way the main and menu pages are loaded with the help of the animated menu element (AniMenu) placed on the root page. But let’s start at the beginning.

 

Change the app.js file to set it’s mainModule property to the root page.

app.js
var application = require("application");
application.mainModule = "root";
application.cssFile = "./app.css";
application.start();

 

The root.xml file hosts the AniMenu element that’s included in the NPM module downloaded earlier. I cover loading custom UI elements in my post on the topic.

root.xml

    
    
  

Note: Pay close attention to the attributes on the AniMenu custom element. It has two important attributes; main and menu. The values of these elements represent the pages that should be loaded that have the main content and menu content.

 

You’re going to put a menu trigger button on the main page in this example. Add the button element to the main-page.xml file along with any other content that your beautiful main content page deserves. The important thing here is that the button indicated a loaded handler in the code.

main-page.xml

 

In the main page JavaScript code, handle the loaded event for the button. The handler attaches a tap event to the button by delegating it to the animated menu module. Whenever the menu trigger button is tapped, the animated menu will be notified and handle the tap appropriately.

main-page.js
var vmModule = require("./main-view-model");
var aniMenu = require( "./node_modules/nativescript-animated-menu/animatedmenu" );

function mainContentLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
}

function menuBtnLoaded(args) {
    var btn = args.object;
    btn.on('tap', aniMenu.menuTriggerAction);
}

exports.mainContentLoaded = mainContentLoaded;
exports.menuBtnLoaded = menuBtnLoaded;

Note: You must require the animated menu module in the main-page.js file so you can reference it’s trigger button handler.

 

Your menu page will be a simple collection of buttons that can perform different actions, based on what you need. For now, they will all just pop up an alert.

menu-page.xml

    
        
        
        
        
    

 

Menu page JavaScript will just bind our menu view model to the page.

menu-page.js
var vmModule = require("./menu-view-model");

function menuLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.menuViewModel;
}
exports.menuLoaded = menuLoaded;

 

And although pretty self explanatory, I’m including the view models here for the sake of completion.

main-view-model.js
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var observable = require("data/observable");
var MainViewModel = (function (_super) {
    __extends(MainViewModel, _super);
    function MainViewModel() {
        _super.call(this);
        this.set("menuText", "\u2630");
    }
    return MainViewModel;
})(observable.Observable);
exports.MainViewModel = MainViewModel;
exports.mainViewModel = new MainViewModel();
menu-view-model.js
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var observable = require("data/observable");
var MenuViewModel = (function (_super) {
    __extends(MenuViewModel, _super);
    function MenuViewModel() {
        _super.call(this);
    }
    MenuViewModel.prototype.tapMenuItem = function() {
        alert('menu tap');
    };
    return MenuViewModel;
})(observable.Observable);
exports.MenuViewModel = MenuViewModel;
exports.menuViewModel = new MenuViewModel();

 

I hope you enjoy!

Alexander Ziskind
Alexander Ziskind

From the latest tech in web development to the latest electronic music hardware and software, Alex loves to get his hands on new stuff and hack on it. Follow this feed on Nuvious related news; so web and cloud stuff here.

2 Comments

Sam
January 21, 2016 Sam

Will there be an update for NativeScript 1.5? I keep getting errors that I think are due to the differences in file structures

Alexander Ziskind
January 23, 2016 Alexander Ziskind

Hi Sam,

Thanks for your comment. While the folder structure of NativeScript apps has changed from 1.0.1 to 1.5.2 (current version), if you clone the demo git repository that is linked at the start of this post, and run the app, everything will run, I've done this myself as a test. Once you get that running as a separate project, you can use it as an example to bring it into your own project that conforms to the directory structure of the latest version.

Alex

Leave your comment