My weekend in frameworks

By Andrea Stagi, deveLover @ Nephila

NATIVE VS HTML

The Giant and verbose SDK

 public class MyActivity extends Activity {
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);

         setContentView(R.layout.content_layout_id);

         final Button button = (Button) findViewById(R.id.button_id);
         button.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 // Perform action on click
             }
         });
     }
 }
 <Button
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
     android:text="@string/self_destruct"
     android:onClick="selfDestruct" />

Wanna Talk about listviews?

OH, it Looks so simple to implement...

NO!
You need an adapter, a ListView instance and a template written in an alien language!

A lot of code

External Libraries MAY HELP

(e.g. Android ROBOGUICE)

@ContentView(R.layout.content_layout_id)
class MyActivity extends RoboActivity { 
    @InjectView(R.id.button_id) Button button; 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Perform action on click
            }
        });
    } 
} 

...Now repeat this for iOS...

WEB TECHNOLOGIES FTW

.controller('NewVotesCtrl', function($scope) {
  $scope.nextPage = function() {
    // Do things
  };
})
<a class="button" ng-click="nextPage()">
    Next
</a>

The bad part

No 1-1 Native pre compilatioN

public class MainActivity extends CordovaActivity {
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        loadUrl(launchUrl);
    }
}

(Just this)

Ionic Framework

You can find all the code on Github 

https://github.com/DjangoBeer/ionic-we

Navigation and routing

<!DOCTYPE html>
<html>
  <head>

  </head>

  <body ng-app="starter">
    <ion-nav-view></ion-nav-view>
  </body>
</html>
.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider

  .state('app', {
    url: "/app",
    templateUrl: "templates/menu.html",
    controller: 'MenuCtrl'
  })

  .state('login', {
    url: "/login",
    templateUrl: "templates/login.html",
    controller: 'LoginCtrl'
  })

  $urlRouterProvider.otherwise('/app');
});
.controller('MenuCtrl', function($scope, $state) {
  $scope.goLogin = function() {
    $state.go('login');
  };
  $scope.goVotes = function() {
    $state.go('vote');
  };
  $scope.goWall = function() {
    $state.go('wall');
  };
  $scope.goMap = function() {
    $state.go('map');
  };
});

Cordova PLUGINS

JS SIDE

cordova.exec(
    function(winParam) {},
    function(error) {},
    "service",
    "action",
    ["hello", 42, false]
);
var fn_buy = function (success, fail, productId) {
    if (this.options.showLog) {
        log('buy called!');
    }
    return cordova.exec(
        success, 
        fail, 
        "InAppBillingPlugin", 
        "buy", 
        [productId]
    );
};

InAppBilling.prototype.buy = fn_buy;

NATIVE SIDE

 public class Echo extends CordovaPlugin {

    @Override
    public boolean execute(String action, JSONArray args, 
                           CallbackContext callbackContext) 
                           throws JSONException {
        if (action.equals("echo")) {
            String message = args.getString(0);
            this.echo(message, callbackContext);
            return true;
        }
        return false;
    }

    private void echo(String message, CallbackContext 
                      callbackContext) {
        if (message != null && message.length() > 0) {
            callbackContext.success(message);
        } else {
            callbackContext.error(
                "Expected one non-empty string argument."
            );
        }
    }
}
public class InAppBillingPlugin extends CordovaPlugin {
    //...
    public boolean execute(String action, JSONArray data, 
                           final CallbackContext callbackContext) {
        this.callbackContext = callbackContext;
        Boolean isValidAction = true;
        
        try {
            // Action selector
            if ("init".equals(action)) { 
            // ...
            } else if ("buy".equals(action)) {
                final String sku = data.getString(0);
                buy(sku);          
            } else {
                // No handler for the action
                isValidAction = false;
            }
        } catch (IllegalStateException e){
            // ....
        }
        return isValidAction;
    }

To be continued...

    private void buy(final String sku){
        final String payload = "";
        if (mHelper == null){
            callbackContext.error(
                IabHelper.ERR_PURCHASE + "|Not initialized"
            );
            return;
        }  
        this.cordova.setActivityResultCallback(this);
        mHelper.launchPurchaseFlow(
            cordova.getActivity(), 
            sku, RC_REQUEST, 
            mPurchaseFinishedListener, payload
        );
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        // Shutdown stuff cause you're a clean coder
    }
    
}

( ... )

Camera Live DEMO

Install Camera Plugin

$ cd myAppFolder
$ cordova plugin add org.apache.cordova.camera

Create a Camera Service

angular.module('myservices', [])
.factory('Camera', ['$q', function($q) {
  return {
    getPicture: function(options) {
      var q = $q.defer();
      navigator.camera.getPicture(function(result) {
        q.resolve(result);
      }, function(err) {
        q.reject(err);
      }, options);

      return q.promise;
    }
  }
}]);

Pick a photo

(native intent)

Custom Components

angular.module('mydirectives', [])
.directive('tilewall', function () {
  return {
    scope: {
      size: '@',
      ngModel: '=',
    },
    controller: function ($scope) {
      $scope.tiles = Array();
      for (var i = 0 ; i < parseInt($scope.size) ; i++) {
        $scope.tiles.push(i);
      }
      $scope.setTileValue = function(value) {
        $scope.ngModel = value;
      };
    },
    template: 'tile.html'
  };
})
<button ng-click="setTileValue(tile+1)" 
        ng-repeat="tile in tiles">
    {{ tile + 1 }}
</button>

LET's USE it!

<ion-view view-title="TileWall">
  <ion-content>
    <div>{{tileval}}</div>
    <tilewall size=100 ng-model='tileval'>
    </tilewall>
  </ion-content>
</ion-view>

LAB TIME! Try with size=5000
(do not try this on your phone)

Beyond Angular.js

Create A Tile list with REACT

var TilesList = React.createClass({
  render: function() {
      var that = this;
      return <tbody>{
        this.props.tiles.map(function(tile, i) {
            return <button className="styles" 
            onClick={that.props.clickHandler.bind(that, tile + 1)}>
                {tile + 1}
            </button>
        })
      }</tbody>;
  }
});

You can render it inside your Angular Directive

// Inside your directive
controller: function ($scope) {
  $scope.tiles = Array();
  for (var i = 0 ; i < parseInt($scope.size) ; i++) {
    $scope.tiles.push(i);
  }
  $scope.setTileValue = function(value) {
    $scope.ngModel = value;
    $scope.$apply();
  };
},
link:function(scope, el, attrs){
  scope.$watch('tiles', function(newValue, oldValue){
    React.render(
      <TilesList tiles={newValue} 
        clickHandler={scope.setTileValue} 
      />, el[0]
    );
  })
}
//...

...Beyond Ionic Framework...
APPGYVER
REAPP
touchstonejs

Thank you!

@4STAGI
github.com/astagi

a.stagi@nephila.it

My weekend in frameworks

By Andrea Stagi

My weekend in frameworks

  • 2,367