Saturday, May 31, 2014

Sencha Touch Create Navigation Drawer (Slide Navigation Like Gmail)

Recently in one of our sencha touch project we have created Navigation Drawer for sencha touch. In this blog I will explain how to create it.

Let's first understand what is navigation drawer. We are all familiar with Facebook slide navigation. It has button on top left corner of toolbar, when you tap on it, the content in center slide to right and menu opens with left to right animation. When you again tap on that menu is closed with right to left navigation and center content slides to left. Navigation drawer is introduced in Android 4.0. It's  slight variation of slide menu. Here the center content does not slide left or right but the menu comes on top of slide content. However the top toolbar is still accessible so user can still close the menu. Now lets see how to create this in sencha touch. Please note that this is the one way we used to create navigation drawer, there could be other options as well.

First lets create a list which will act as navigation menu.

Ext.define('SlideNav.view.Navigation', {
    extend: 'Ext.List',
    xtype: 'navigation',
    modal: true,
    hideOnMaskTap: false,
    requires : ['Ext.data.Store'],
    config: {
        width: 250,
        itemTpl : '{title}',
        data : [
            {
                title : 'Item 1'
            },
            {
                title : 'Item 2'
            },
            {
                title : 'Item 3'
            }
        ]
    }
});

As you can see in above code we created a list and set it as modal element with modal config. List extends Ext.container so we can open it as modal element. This is our navigation menu. Now lets add a button on top left corner of our toolbar and create the main view.

Ext.define('SlideNav.view.Main', {
    extend: 'Ext.Container',
    xtype: 'main',
    config: {
        style: {
            zIndex: -1,
            position: 'absolute'
        },
        layout:{
            type: 'card',
            align: 'stretch'
        },
        items: [
            {
                xtype: 'toolbar',
                docked: 'top',
                title: 'Slide Navigation',
                items: [
                    {
                        xtype: 'button',
                        iconCls: 'list',
                        ui: 'plain',
                        itemId: 'slideNavBtn',
                        id: 'slideNavBtn'
                    }
                ]
            },
            {
                xtype: 'panel',
                itemId: 'slideContainer',
                layout: 'card',
                id: 'slideContainer',
                items: [
                    {
                        xtype: 'panel',
                        html: 'Hello Welcome to The Design Shop.Sencha Touch is very good framework.'
                    }
                ]
            }
        ]
    }
});

So this is our main view and it has toolbar with top navigation button. Now lets add tap event for it in controller and add logic to open and close the menu.

Ext.define('SlideNav.controller.App',{
    extend: 'Ext.app.Controller',
    config:{
        refs:{
            main : 'main',
            navigation : 'navigation',
            navBtn : '#slideNavBtn'
        },
        control : {
            navBtn : {
                tap : 'toggleNav'
            }
        }
    },
    init: function() {
        this.toggle = 0;
    },
    toggleNav : function(){
        var me = this;
        if(!me.getNavigation()) {
            Ext.create('SlideNav.view.Navigation');
            Ext.Viewport.add(me.getNavigation());
            me.getNavigation().show();
        }
        if(this.toggle == 0) {
            Ext.Animator.run({
                        element: me.getNavigation().element,
                        duration: 500,
                        easing: 'ease-in',
                        preserveEndState: true,
                        from: {
                            left: -250
                        },
                        to: {
                           left: 0
                        }
        });
        this.toggle = 1;
        }
        else {
        Ext.Animator.run({
                            element: me.getNavigation().element,
duration: 500,
easing: 'ease-in',
preserveEndState: true,
from: {
left: 0
},
to: {
left: -250
}
});
            this.toggle = 0;
        }
    }
});

As you can see in above code we are using one controller variable toggle to know the state of menu and and on tap of it first we create the navigation and menu and add it to viewport and then we are using Ext.Animator to open it with animation. So basically we are setting left property from -250 to 0 as 250 is the width of the menu. You can change it according to your requirement or set dynamic width here.

Wednesday, May 28, 2014

AngularJs and $scope.$apply - When and How to use $apply for AngularJs scope

In this blog I will explain when to use $apply for AngularJs $scope and how to use it. You may have encountered the situation where you update the variables bind to AngularJs view but it does not update views. So here I will explain why it happens and how to resolve it. Also you need to know how to use $apply carefully else it may give you error that $apply is already in progress.

First lets understand how the AngularJs data binding works.AngularJs framework monitors data changes with the digest cycles. That means it checks it frequently in digest cycle then framework will be notified if you have made any changes in managed code. But there are chances that you have some external events like Ajax callbacks or some native click events. These event runs outside the scope of AngularJs digest cycles. So the framework does not know about any changes made during this events. You have to tell the framework that there are changes. Here $apply comes to the picture. It lets you start digest cycle explicitly. So that your frameworks knows that there are changes. Lets assume that you have variable $scope.myVar and you changed its value in native click event. So you need to tell Framework that it is changed. So if you simply use,

$scope.myVar = 'New Value';

It will not work. Instead of this you have to use

$scope.$apply(function(){
      $scope.myVar = 'New Value';
});

or you can use this syntax.

$scope.myVar = 'New Value';
$scope.$apply();


This shall notify the framework that the value is changed. It's not always safe to use $apply as I mentioned earlier. If the digest cycle is already in progress and you try to set it and with $apply it will give you an error. So if you have condition like your variable is updated at common function which you calls from AngularJs managed code and outside events. you have to first check it digest cycle is already in progress. If not then only use $apply. You can check it with following code.

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
       $scope.myVar = 'New Value';
       $scope.$apply();
}

This will be the safe apply and it will not give you any error .


Friday, May 23, 2014

Make Sencha Touch Site With Routes SEO Friendly

Recently in one of our project which was a sencha touch app we faced an issue with SEO. As far as I know a site should have all the unique URLs for better SEO. With unique url Google crawler can crawl your URLs more efficiently. Now this could be the issue with Sencha Touch app as we know. We open sencha touch app with our domain URL and then we don't have any URL changes. All our views are loaded locally. So our url will stay like http://mydomain.com/app/index.html.

Now this is not good for SEO as your URL is not changing so google can not index your site. Now this is not good if you are selling your products on your site as normally people search with product name and they can not find your site urls in Google. So what to do? To resolve this issue sencha has introduced routes and history support. So what it does is it changes your URL as and when you navigate through sencha app. Something like

http://mydomain.com/app/index.html#productlist/cat1name
http://mydomain.com/app/index.html#productdetail/product-name

So now you have all the unique urls on your site so those urls can be indexed by Google and can be displayed in search result. But wait we have another problem here. Google ignores all the content of the url after hash tag. So after using routes we are back to the same problem again.  Still our sencha touch app is not SEO friendly. So what to do now. Google suggest to use Hash Bangs(#!) instead of Hash tag (#) So if use this our routes will not work as it works only on has tag so what to do? We have to modify our sencha touch routes logic to work with Hash Bangs (#!) For that if you are manually adding history as follow, you have to override add history function.

MyAppName.app.getHistory().add(new Ext.app.Action({
            'key': 'value'
}), true);

It will convert your url as follow.

http://mydomain.com/app/index.html#key/value

You have manually insert exclamation mark (!) here.

MyAppName.app.getHistory().add(new Ext.app.Action({
            '!key': 'value'
}), true);

Now your url looks like below.

http://mydomain.com/app/index.html#!key/value

So it's a Hash Bangs and now this URL can be indexed by Google. But that will break your routes logic as now your key would be !key. To make it working you have to change your root definition and add you have to prefix it with . See the example below.

routes: {
            '! productlist/: text': 'goToProductList',
            '! productdetail/:text' : 'goToProductDetail'
}

That's it and your routes will work as usual. So with this trick a developer is happy, a customer is happy and a SEO guy is happy.

Passing Variables to Laravel View Blades from Controllers

Hello,

This is my first blog on Laravel. I have been working with Laravel since last few weeks. It's a nice and flexible framework. In this blog I am going to explain how you can pass variables to Laravel view blade from controllers.

This will help you when you want to pass some variables that may have boolean values using which you want to show hide some part of UI or you want to pass arrays to view and iterate through it and create dynamic html. So first we are going to create views inside controller as follow.

$this->layout->content = View::make('Views.View1');

This will call Views.View1.blade.php file and render it's layout.

Now lets assume we want to pass variables to it as we have following code in view.

 @if ($showDiv == true)
<div> This div will only be visible if $showDiv variable is set to true </div&gt
@endif

Also lets assume we want to iterate through and array and create dynamic list.

<ul class="dropdown-menu">
      @foreach ($names as $name)
               <li>{{$name}}</li>
      @endforeach
</ul>

Now lets see how to pass this.

In your controller create variables as folllow

$showDiv = true;
$names = array("John", "Jack", "Smith", "James");

Now we will share this variable with view using share function.

View::share('showDiv', $showDiv);
View::share('names', $names);

That's it, now these variables are available in view and views will be rendered according to logic. So key thing is to use share function of view to share variables with view. If you don't share like this you may get an error that variable in blade undefined.



Thursday, May 22, 2014

Add Buttons on Headings of AngularJs BootstrapAccordion

Recently in one of my projects we were using AngularJs and Bootstrap UI. We have a layouts where we have buttons on heading of accordion and we have to add click events for it.



So we know the behavior of accordion. When we click on headings of accordion it actually expands it. In My case I have to stop and that and invoke button click function. In this blog I will explain this.

Basic idea is to stop event propagation. So when we click on button it will invoke handler for it and and if we stop propagation of event in that function it will not expand the accordion view. Lets assume we have following delete button on accordion heading.

So when I click on delete it should actually remove that record instead of opening the accordion. For that add click handler for the buttons as follow.

<accordion-heading>
     <div>
          Options
     </div>
     <div style="float:right">
          <button ng-click="deleteItem(item, $event)" type="button" class="btn" style="color: #000"               >Delete</button>
     </div>
</accordion-heading>

As you can see in above code we have added click event handle with ng-click and we are passing item and event as parameter. Now in controller add the event handler.

$scope.deleteItem = function(item, $event){
      $event.stopPropoagation();
}

As you can see in above code we are using stopPropagation method of event object to stop event propagation. Hence the event does not propagated and accordion will not expand. If you click anywhere else it will open it but if you click on delete button it will not invoke it. Hope this helps you.

Friday, May 16, 2014

Ad hoc App Installation Failed in iOS Devices

This blog post is about the recent problem I faced in installing Ad Hoc application in iOS devices. Recently I was working with a app where I generated add hoc iPA file for the distribution on registered devices. But some how it was not installed on registered devices. When you try installing application with iTunes, it starts installations and after couple of minutes it stuck and never finish installation. It took some time to figure out the issue so here in this blog I will explain this.

So when you face this situation and if you see the device log you will find following error in it.

install_application: Could not preflight application install

That means something is wrong with installation and most probably it's the issue of the provisioning profile you are using. For that first clean the build from Xcode. Check the device id in list of the registered devices. If it's not there add it and regenerate your distribution profile. Now go to Xcode and select the project and go to general tab. Make sure you have added the apple developer account and selected the correct team.
 Now go to Build Settings tab and go to Code Signing section. Make sure you have selected correct distribution certificate for release and selected correct distribution profile. See the screenshot attached below.


Here if you have selected development provisioning profile and then it will not work so right selection for code signing identity and provisioning profile is must. Now you can generate archive and export the iPA file when you sync it with iTunes it will get installed properly.  Hope this will help you.

Monday, May 12, 2014

iOS 7 Phonegap Change Background Color of Status Bar

Hello,

Recently I was working on Sencha Touch, Phonegap application where we have a requirement to change background color of top status bar of iPhone where we have carrier, wifi and battery symbols. There are two ways to do it. In this blog I will explain both the steps.

First Step

In iOS 7 if you have status bar visible your UI will overlap the status bar and we will take advantage of it. First select your project and go to Deployment info. Make sure you have Hide during application launch checkbox and set status bar style as default.

Now your webview will overlap the UI and the status bar will be transparent. So we can add a component on top of our page with height 20 pixel and preferred background color so your status bar will have same background color. For example I added docked panel in Sencha Touch with fixed height and background color in my main container. 

Ext.define('MyApp.view.LaunchView', {
    extend : 'Ext.Panel',
    xtype : 'launchmain',

    config : {
        layout : 'card',
        
           items : [{
                    xtype: 'panel',
                    docked: 'top',
                    style: {
                        'background-color': '#34495E',
                        'color':'#ffffff'
                    },
                    height: 20
                    
           }]
    }
});

Since this is may main container all the views added in this container will have this panel and top. Now issue could be if you have some external pages loaded in app on which you don't have control then this panel will not be there you can not have top component with background color. For that follow step 2

Second Step

Here in this step we will add component on top of our webview in our main iOS 7 view. Hence it will be available throughout the app. For that add following code in didFinishLaunchingWithOptions method of AppDelegate.m

 if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        UIView *topView = [[UIView alloc] init];
CGRect screenRect = [[UIScreen mainScreen] bounds];
        topView.frame = CGRectMake(0, 0, screenRect.size.width, 20);
        topView.backgroundColor = [UIColor colorWithRed:52/255. green:73/255. blue:94/255. alpha:1];
        [self.window.rootViewController.view addSubview: topView];
    }

Here we are added another view with required background color on top of webview. Now we have to shift down webview for 20 pixel so that this view would be visible. For that add following code to viewDidLoad function of your MainViewController.m file. 

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        CGRect viewBounds = [self.webView bounds];
        viewBounds.origin.y = 20;
        viewBounds.size.height = viewBounds.size.height - 20;
        self.webView.frame = viewBounds;
    }

As you can see in above code we are changing the origin of webview and shift it 20 pixel down. You can simply add above code to avoid overlapping of UI.