{"id":860,"date":"2016-03-30T16:31:50","date_gmt":"2016-03-30T16:31:50","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=860"},"modified":"2016-03-30T16:31:50","modified_gmt":"2016-03-30T16:31:50","slug":"apps-with-ionic-and-stamplay","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay","title":{"rendered":"Cross-Platform Apps with Ionic and Stamplay"},"content":{"rendered":"<p>Ionic makes it easier to build a multi-platform mobile app using modern web technologies. Stamplay allows developers to use APIs as building blocks to to rapidly construct scalable, maintainable backend applications.<\/p>\n<p>By using Stamplay and Ionic together, you can focus on building a unique app, instead of spending time scaling architecture or implementing the same feature two or three times across different platforms.<\/p>\n<p>To help you get started with Ionic and Stamplay, we\u2019ve put together a starter kit to jumpstart your mobile app development. In this article, we&#8217;ll show you how to use the code in this starter kit to build a cross-platform personal to-do app with user registration.<br \/>\n<!--more--><\/p>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\" alt=\"cover\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 1124px; --smush-placeholder-aspect-ratio: 1124\/595;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\" alt=\"cover\" \/><\/noscript><\/p>\n<h3>What is Stamplay?<\/h3>\n<p>Stamplay is a Backend as a service that allows you to integrate with third-party APIs using a web-based GUI that allows you to create server side logic and workflows without needing to code anything. Stamplay integrates with many services including Twillio, Slack, Firebase, and Stripe.<\/p>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/stamplay_integration_home.png\" alt=\"Stamplay Integrations\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 1558px; --smush-placeholder-aspect-ratio: 1558\/952;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/stamplay_integration_home.png\" alt=\"Stamplay Integrations\" \/><\/noscript><\/p>\n<h3>Getting Started with Ionic and Stamplay: Set up the Starter Project<\/h3>\n<p>First, clone Ionic&#8217;s GitHub repository or <a href=\"https:\/\/github.com\/Stamplay\/stamplay-ionic-starter\" target=\"_blank\">download from GitHub<\/a>:<\/p>\n<pre><code class=\"bas\">git clone https:\/\/github.com\/Stamplay\/stamplay-ionic-starter.git\n<\/code><\/pre>\n<p>Switch to the new directory:<\/p>\n<pre><code>cd stamplay-ionic-starter\n<\/code><\/pre>\n<p>Install Ionic SDK through NPM:<\/p>\n<pre><code>npm install -g ionic\n<\/code><\/pre>\n<p>Install Stamplay CLI through NPM<\/p>\n<pre><code>npm install -g stamplay-cli\n<\/code><\/pre>\n<p>Install Gulp through NPM<\/p>\n<pre><code>npm install -g gulp\n<\/code><\/pre>\n<p>Install dependencies through NPM:<\/p>\n<pre><code>npm install &amp;&amp; gulp install\n<\/code><\/pre>\n<p>Add\/Update Ionic library files<\/p>\n<pre><code>ionic lib update\n<\/code><\/pre>\n<h3>Setup Stamplay App<\/h3>\n<p>To continue, you will need to <a href=\"https:\/\/stamplay.com\/\">create a Stamplay account<\/a>. After you have done this, you&#8217;ll want to login to the <a href=\"https:\/\/editor.stamplay.com\/\">Stamplay Editor<\/a> and create an app.<\/p>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/create-app.png\" alt=\"Create Stamplay App\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 398px; --smush-placeholder-aspect-ratio: 398\/259;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/create-app.png\" alt=\"Create Stamplay App\" \/><\/noscript><\/p>\n<p>Back on your machine, you&#8217;ll want to initialize Stamplay and enter your Stamplay AppId and APIKey when prompted, which can be found on the Stamplay Editor:<\/p>\n<pre><code>stamplay init\n<\/code><\/pre>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/appid-and-appkey.png\" alt=\"stamplay appid and apikey\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 982px; --smush-placeholder-aspect-ratio: 982\/229;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/appid-and-appkey.png\" alt=\"stamplay appid and apikey\" \/><\/noscript><\/p>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/stamplay-init-2.png\" alt=\"stamplay init\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 567px; --smush-placeholder-aspect-ratio: 567\/46;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/stamplay-init-2.png\" alt=\"stamplay init\" \/><\/noscript><\/p>\n<p>Inside <code>www\/index.html<\/code>, we will update <code>Stamplay.init(&quot;YOUR APP ID&quot;)<\/code> to include our <code>APP ID<\/code>.<\/p>\n<pre><code>&lt;script&gt;\n  Stamplay.init(&quot;test-ionic-app&quot;);\n&lt;\/script&gt;\n<\/code><\/pre>\n<p>Inside the Stamplay Editor, add a new object schema called &#8220;task&#8221; with the following properties:<\/p>\n<ul>\n<li>title &#8211; <em>string<\/em><\/li>\n<li>body &#8211; <em>string<\/em><\/li>\n<li>complete &#8211; <em>boolean<\/em><\/li>\n<\/ul>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/new-object.png\" alt=\"New Object\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 195px; --smush-placeholder-aspect-ratio: 195\/194;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/new-object.png\" alt=\"New Object\" \/><\/noscript><\/p>\n<p><img decoding=\"async\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/task-object.png\" alt=\"Task Object\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" style=\"--smush-placeholder-width: 1001px; --smush-placeholder-aspect-ratio: 1001\/650;\" \/><noscript><img decoding=\"async\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/task-object.png\" alt=\"Task Object\" \/><\/noscript><\/p>\n<p>Finally, on your machine, run <code>ionic serve<\/code> to launch your app in the browser. An important detail here is to make sure to use port 8080, or the app will not work.<\/p>\n<pre><code>ionic serve --lab -p 8080\n<\/code><\/pre>\n<p>Now, the application is set up, and you are ready to begin development.<\/p>\n<h3>Basic Configuration<\/h3>\n<p>Since your development environment is already set up, with your dependencies installed, you can go ahead and open up your <code>www\/js\/app.js<\/code> file, where your application is configured and bootstrapped.<\/p>\n<pre><code>&lt;br \/&gt;angular.module(&#039;starter&#039;, [&#039;ionic&#039;, &#039;starter.controllers&#039;, &#039;starter.services&#039;])\n\n.run(function($ionicPlatform, $rootScope, AccountService) {\n\n  AccountService.currentUser()\n    .then(function(user) {\n      $rootScope.user = user;\n    })\n\n  $ionicPlatform.ready(function() {\n    if (window.cordova &amp;&amp; window.cordova.plugins &amp;&amp; window.cordova.plugins.Keyboard) {\n      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);\n      cordova.plugins.Keyboard.disableScroll(true);\n    }\n    if (window.StatusBar) {\n      StatusBar.styleDefault();\n    }\n  });\n\n})\n\n.constant(&#039;$ionicLoadingConfig&#039;, {\n  template: &quot;&lt;ion-spinner&gt;&lt;\/ion-spinner&gt;&quot;,\n  hideOnStateChange : false\n})\n\n.config(function($stateProvider, $urlRouterProvider) {\n\n  $stateProvider\n\n    .state(&#039;home&#039;, {\n      url: &#039;\/&#039;,\n      templateUrl: &#039;templates\/home.html&#039;\n    })\n    .state(&#039;login&#039;, {\n      url: &#039;\/login&#039;,\n      templateUrl: &#039;templates\/login.html&#039;,\n      controller: &quot;AccountController&quot;,\n      controllerAs : &quot;account&quot;\n    })\n    .state(&#039;signup&#039;, {\n      url: &#039;\/signup&#039;,\n      templateUrl: &#039;templates\/signup.html&#039;,\n      controller: &quot;AccountController&quot;,\n      controllerAs : &quot;account&quot;\n    })\n    .state(&#039;tasks&#039;, {\n      cache : false,\n      url: &#039;\/tasks&#039;,\n      templateUrl: &#039;templates\/tasks.html&#039;,\n      controller: &quot;HomeController&quot;,\n      controllerAs : &quot;task&quot;\n    })\n    .state(&#039;new&#039;, {\n      url: &#039;\/new&#039;,\n      templateUrl: &#039;templates\/new.html&#039;,\n      controller: &quot;TaskController&quot;,\n      controllerAs : &quot;new&quot;\n    })\n    .state(&#039;edit&#039;, {\n      url: &#039;\/task\/:id&#039;,\n      templateUrl: &#039;templates\/edit.html&#039;,\n      controller: &quot;TaskController&quot;,\n      controllerAs : &quot;edit&quot;\n    })\n\n  $urlRouterProvider.otherwise(&#039;\/&#039;);\n\n});\n<\/code><\/pre>\n<p>Up at the top of your <code>app.js<\/code>, inject the <code>$ionicPlatform<\/code>, <code>$rootScope<\/code>, and <code>AccountService<\/code>.<\/p>\n<p>The <code>$ionicPlatform<\/code> is used to detect which platform the application is running on and to configure accordingly.<\/p>\n<p>We brought in <code>$rootScope<\/code> to set a global value after the <code>AccountService<\/code> fetches the currently logged in user, if one exists.<\/p>\n<p>After <code>.run<\/code>, we set up a <code>constant<\/code>, to avoid repeating ourselves throughout the application, when using the <code>$ionicLoading<\/code> service. We&#8217;ve taken its configuration provider, <code>$ionicLoadingConfig<\/code> and set defaults, so now every time this service is called in our application, it will have these settings.<\/p>\n<p>Last, you have your <code>.config<\/code> block, where your application state is set up.<\/p>\n<p>Note: The state <code>tasks<\/code>, options object, has a property cache that is set to <code>false<\/code>. This is because we do not want this state to load possibly stale data when we go to and from. This will cause the <code>controller<\/code> and <code>view<\/code> to re-render each time the state is entered.<\/p>\n<h3>User Accounts<\/h3>\n<p>Integrating local user accounts with Ionic and Stamplay is simple. By default, an API for managing user accounts and roles is set up within Stamplay for your application.<\/p>\n<p>In the bootstrapping phase of your application, inside the <code>.run<\/code> block, we&#8217;ve already seen a method on the <code>AccountService<\/code>. The <code>currentUser<\/code> method fetches the user who is currently logged in.<\/p>\n<p>So in our <code>.run<\/code>, we are checking and setting a user globally if one is logged in. Here is the <code>AccountService<\/code> setup:<\/p>\n<pre><code>&lt;br \/&gt;angular.module(&#039;starter.services&#039;, [])\n.factory(&#039;AccountService&#039;, [&quot;$q&quot;, function($q) {\n  return {\n    currentUser : function() {\n      var def = $q.defer();\n      Stamplay.User.currentUser()\n        .then(function(response) {\n          if(response.user === undefined) {\n            def.resolve(false);\n          } else {\n            def.resolve(response.user);\n          }\n        }, function(error) {\n          def.reject();\n        }\n      )\n      return def.promise;\n    }\n  }\n}])\n<\/code><\/pre>\n<p>If a user is logged in, the global value of the <code>$rootScope<\/code> <code>user<\/code> is set to the user data from Stamplay. Otherwise, the value is set to false.<\/p>\n<p>It&#8217;s important that this is set before the view loads, so the correct blocks of HTML will render to the type of user using the application: either a logged in user or a guest user.<\/p>\n<p>The global <code>$rootScope.user<\/code> value will first be used to display options based on if a user is logged in or not. This can be seen on the <code>home.html<\/code> template:<\/p>\n<pre><code>&lt;ion-view&gt;\n  &lt;ion-content class=&quot;home&quot;&gt;\n    &lt;div&gt;\n      &lt;p class=&quot;home-title center-align&quot;&gt;Create better apps faster&lt;\/p&gt;\n      &lt;p class=&quot;home-subtitle center-align&quot;&gt;Build complete, secure and scalable backends in your browser piecing together APIs and features as building blocks.&lt;\/p&gt;\n    &lt;\/div&gt;\n    &lt;div class=&quot;card&quot;&gt;\n      &lt;div class=&quot;item item-body center-align&quot;&gt;\n        &lt;p class=&quot;home-desc&quot;&gt;\n          This is a starter kit for &lt;a href=&quot;https:\/\/stamplay.com&quot; target=&quot;_blank&quot;&gt;Stamplay&lt;\/a&gt; using the Ionic framework. It features simple to-do like capabilities, with user accounts.\n        &lt;\/p&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n    &lt;div ng-hide=&quot;user === undefined&quot;&gt;\n      &lt;div ng-hide=&quot;user&quot;&gt;\n        &lt;div&gt;\n          &lt;button ui-sref=&quot;login&quot; class=&quot;button button-full button-balanced bold&quot;&gt;Login&lt;\/button&gt;\n          &lt;button ui-sref=&quot;signup&quot; class=&quot;button button-full button-positive bold&quot;&gt;Signup&lt;\/button&gt;\n        &lt;\/div&gt;\n        &lt;div class=&quot;center-align&quot;&gt;\n          &lt;b class=&quot;or&quot;&gt;or&lt;\/b&gt;\n        &lt;\/div&gt;\n        &lt;div&gt;\n          &lt;button ui-sref=&quot;tasks&quot; class=&quot;button button-full button-dark bold&quot;&gt;Continue as Guest&lt;\/button&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;div ng-show=&quot;user&quot; ng-controller=&quot;AccountController as account&quot;&gt;\n        &lt;button ui-sref=&quot;tasks&quot; class=&quot;button button-full button-dark bold&quot;&gt;My Tasks&lt;\/button&gt;\n        &lt;button ng-click=&quot;account.logout()&quot; class=&quot;button button-full button-stable bold&quot;&gt;Logout&lt;\/button&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  &lt;\/ion-content&gt;\n&lt;\/ion-view&gt;\n<\/code><\/pre>\n<p>Throughout the views, you can use common Angular directives, like <code>ng-hide<\/code>, <code>ng-show<\/code>, and <code>ng-click<\/code>. The <code>ng-show<\/code> and <code>ng-hide<\/code> are quite simple. The block on which these attributes are will show or hide, depending on the truthiness of the value it is assigned.<\/p>\n<p>For example, if <code>ng-show<\/code> is set to <code>ng-show=&quot;true&quot;<\/code>, that block will render into view. However, if it is false, that block will be hidden.<\/p>\n<p>Vice-versa, <code>ng-hide<\/code> will hide a block or elements if the value set to it is <code>true<\/code>, although it doesn&#8217;t have to be <code>true<\/code>, just not a falsey value, such as null or 0.<\/p>\n<p>In the above code, you are using the <code>ng-hide<\/code> directive to hide both blocks of options with a containing element, until the <code>$rootScope.user<\/code> variable is defined, regardless of whether is truthy or falsey.<\/p>\n<p>After the <code>$rootScope.user<\/code> variable is defined, the two child blocks with these directives will either show or hide, and in this case, only one will be display at a time.<\/p>\n<p>If the <code>$rootScope.user<\/code> is not populated with user data and set to <code>false<\/code>, then the first block of options to <strong>Login<\/strong>, <strong>Signup<\/strong>, or <strong>Continue As A Guest<\/strong> will be displayed.<\/p>\n<p>Otherwise, a user is logged in, and we will display the block with the option to <strong>Logout<\/strong> or go to <strong>My Tasks<\/strong>.<\/p>\n<p>The <code>login<\/code>, <code>signup<\/code>, and <code>home<\/code> states share the <code>AccountController<\/code>. This controller has three main methods:<\/p>\n<pre><code>angular.module(&#039;starter.controllers&#039;, [])\n.controller(&#039;AccountController&#039;, [&quot;AccountService&quot;, &quot;$state&quot;, &quot;$rootScope&quot;, &quot;$ionicLoading&quot;, &quot;$ionicPopup&quot;,\n  function(AccountService, $state, $rootScope, $ionicLoading, $ionicPopup) {\n\n  var vm = this;\n\n  var errorHandler = function(options) {\n    var errorAlert = $ionicPopup.alert({\n      title: options.title,\n      okType : &#039;button-assertive&#039;,\n      okText : &quot;Try Again&quot;\n    });\n  }\n\n  vm.login = function() {\n    $ionicLoading.show();\n    Stamplay.User.login(vm.user)\n    .then(function(user) {\n      $rootScope.user = user;\n      $state.go(&quot;tasks&quot;);\n    }, function(error) {\n      $ionicLoading.hide();\n      errorHandler({\n        title : &quot;&lt;h4 class=&#039;center-align&#039;&gt;Incorrect Username or Password&lt;\/h4&gt;&quot;\n      })\n    })\n  }\n\n  vm.signup = function() {\n    $ionicLoading.show();\n    Stamplay.User.signup(vm.user)\n    .then(function(user) {\n      $rootScope.user = user;\n      $state.go(&quot;tasks&quot;);\n    }, function(error) {\n      errorHandler({\n        title : &quot;&lt;h4 class=&#039;center-align&#039;&gt;A Valid Email and Password is Required&lt;\/h4&gt;&quot;\n      })\n      $ionicLoading.hide();\n    })\n  }\n\n  vm.logout = function() {\n    $ionicLoading.show();\n    var jwt = window.location.origin + &quot;-jwt&quot;;\n    window.localStorage.removeItem(jwt);\n    AccountService.currentUser()\n    .then(function(user) {\n      $rootScope.user = user;\n      $ionicLoading.hide();\n    }, function(error) {\n      console.error(error);\n      $ionicLoading.hide();\n    })\n  }\n}])\n<\/code><\/pre>\n<h3>Login<\/h3>\n<p>The <code>login<\/code> method is used within the <code>login<\/code> state. The view for your <code>login<\/code> state includes a basic form that takes in a username and password. See <code>login.html<\/code> below:<\/p>\n<pre><code>&lt;ion-view&gt;\n  &lt;ion-content&gt;\n    &lt;h1 class=&quot;account-title center-align&quot;&gt;Login&lt;\/h1&gt;\n\n    &lt;div class=&quot;list&quot;&gt;\n      &lt;label class=&quot;item item-input&quot;&gt;\n        &lt;input type=&quot;text&quot; placeholder=&quot;Email&quot; ng-model=&quot;account.user.email&quot;&gt;\n      &lt;\/label&gt;\n      &lt;label class=&quot;item item-input&quot;&gt;\n        &lt;input type=&quot;text&quot; placeholder=&quot;Password&quot; ng-model=&quot;account.user.password&quot;&gt;\n      &lt;\/label&gt;\n    &lt;\/div&gt;\n\n    &lt;button class=&quot;button button-balanced button-full&quot; ng-click=&quot;account.login()&quot;&gt;Submit&lt;\/button&gt;\n  &lt;\/ion-content&gt;\n&lt;\/ion-view&gt;\n<\/code><\/pre>\n<p>Once submitted, the Stamplay SDK sends the <code>email<\/code> and <code>password<\/code> to Stamplay to authenticate a user and grants a <strong>JSON Web Token<\/strong> if successful.<\/p>\n<p>This is stored in <code>localStorage<\/code> under the key <code>hostname-jwt<\/code>.<\/p>\n<h3>Signup<\/h3>\n<p>The <code>signup<\/code> method is used within the <code>signup<\/code> state of our application. The view for your <code>signup<\/code> state is a basic form similar to your <code>login<\/code> state view.<\/p>\n<pre><code>&lt;ion-view&gt;\n  &lt;ion-content&gt;\n    &lt;h1 class=&quot;account-title center-align&quot;&gt;Signup&lt;\/h1&gt;\n\n    &lt;div class=&quot;list&quot;&gt;\n      &lt;label class=&quot;item item-input&quot;&gt;\n        &lt;input type=&quot;text&quot; placeholder=&quot;Email&quot; ng-model=&quot;account.user.email&quot;&gt;\n      &lt;\/label&gt;\n      &lt;label class=&quot;item item-input&quot;&gt;\n        &lt;input type=&quot;text&quot; placeholder=&quot;Password&quot; ng-model=&quot;account.user.password&quot;&gt;\n      &lt;\/label&gt;\n    &lt;\/div&gt;\n\n    &lt;button class=&quot;button button-positive button-full&quot; ng-click=&quot;account.signup()&quot;&gt;Submit&lt;\/button&gt;\n  &lt;\/ion-content&gt;\n&lt;\/ion-view&gt;\n<\/code><\/pre>\n<p>On submission, the new account credentials are sent to Stamplay, an account is created, and a <strong>JSON Web Token<\/strong> is granted.<\/p>\n<p>Again, this is stored in <code>localStorage<\/code> under the key <code>hostname-jwt<\/code>.<\/p>\n<h3>Logout<\/h3>\n<p>The <code>logout<\/code> method is used within <code>home.html<\/code>. This option is only displayed when a user is logged in. When the method is called, the <strong>JSON Web Token<\/strong> is removed from <code>localStorage<\/code>, the <code>AccountService<\/code> then fetches the current user, and the value of <code>$rootScope.user<\/code> is set again.<\/p>\n<p>You can see this at the bottom of <code>AccountController<\/code>.<\/p>\n<h3>Tasks<\/h3>\n<p>The <code>task<\/code> state is accessible through the <code>home<\/code> state. Either option, <strong>Continue As A Guest<\/strong> or <strong>My Tasks<\/strong> on the <code>home<\/code> view, will navigate to the <code>tasks<\/code> state the same way.<\/p>\n<h3>Fetching tasks<\/h3>\n<p>The <code>HomeController<\/code>, connected to the <code>tasks<\/code> state, triggers a <code>vm.fetch<\/code> method on load.<\/p>\n<p>This is done through an <code>ng-init<\/code> directive inside the <code>tasks.html<\/code> template. (See below)<\/p>\n<pre><code>&lt;ion-view&gt;\n  &lt;ion-nav-bar align-title=&quot;center&quot; class=&quot;bar-light&quot;&gt;\n    &lt;ion-nav-back-button&gt;&lt;\/ion-nav-back-button&gt;\n    &lt;ion-nav-title&gt;\n      TASKS {{ &#039;(&#039; + task.tasks.length + &#039;)&#039; }}\n    &lt;\/ion-nav-title&gt;\n    &lt;ion-nav-buttons side=&quot;right&quot;&gt;\n      &lt;a class=&quot;button button-icon icon ion-plus-circled&quot; ui-sref=&quot;new&quot;&gt;&lt;\/a&gt;\n    &lt;\/ion-nav-buttons&gt;\n  &lt;\/ion-nav-bar&gt;\n  &lt;ion-content ng-init=&quot;task.fetch()&quot;&gt;\n    &lt;h1 class=&quot;account-title center-align&quot;&gt;\n      &lt;div ng-if=&quot;user&quot;&gt;\n        Your Tasks\n      &lt;\/div&gt;\n      &lt;div ng-if=&quot;!user &amp;&amp; task.tasks !== undefined&quot;&gt;\n       Community Tasks\n      &lt;\/div&gt;\n    &lt;\/h1&gt;\n    &lt;ion-list can-swipe=&quot;true&quot;&gt;\n      &lt;ion-item ng-repeat=&quot;item in task.tasks&quot; ng-class=&quot;{ &#039;active&#039; : item.id === task.active }&quot;&gt;\n        &lt;div class=&quot;row&quot;&gt;\n          &lt;div class=&quot;col col-20&quot; style=&quot;display: flex;justify-content: flex-end;align-items: center;&quot;&gt;\n            &lt;button class=&quot;button button-clear&quot; style=&quot;font-size:2rem !important&quot; ng-click=&quot;task.setStatus(item)&quot;&gt;\n              &lt;i class=&quot;ion ion-checkmark-circled&quot; ng-class=&quot;{ &#039;balanced&#039; : item.complete }&quot;&gt;&lt;\/i&gt;\n            &lt;\/button&gt;\n          &lt;\/div&gt;\n          &lt;div class=&quot;col col-80&quot; on-tap=&quot;task.setActive(item._id)&quot;&gt;\n            &lt;h2&gt;{{ item.title }}&lt;\/h2&gt;\n            &lt;p&gt;{{ item.body }}&lt;\/p&gt;\n            &lt;label class=&quot;dark&quot;&gt;\n              {{ item.dt_create | date : &quot;short&quot;}}\n            &lt;\/label&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;ion-option-button class=&quot;button-energized ion-edit&quot;\n      ui-sref=&quot;edit({ id : item._id })&quot;&gt;&lt;\/ion-option-button&gt;\n      &lt;ion-option-button class=&quot;button-assertive ion-trash-a&quot;\n      ng-click=&quot;task.deleteTask(item._id)&quot;&gt;&lt;\/ion-option-button&gt;\n      &lt;\/ion-item&gt;\n    &lt;\/ion-list&gt;\n    &lt;div class=&quot;card&quot; ng-show=&quot;task.tasks.length === 0&quot;&gt;\n      &lt;div class=&quot;item item-body center-align&quot;&gt;\n        &lt;h2 class=&quot;dark&quot;&gt;\n          Umm..it&#039;s empty in here..\n        &lt;\/h2&gt;\n        &lt;h2 class=&quot;dark&quot;&gt;Try adding a task by clicking the &lt;i class=&quot;ion-plus-circled dark&quot;&gt;&lt;\/i&gt;\n        icon in the top right corner.&lt;\/h2&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  &lt;\/ion-content&gt;\n&lt;\/ion-view&gt;\n<\/code><\/pre>\n<p>If the value of <code>$rootscope.user<\/code> is <code>false<\/code>, the <code>TaskService<\/code> will execute the method to retrieve all the <code>tasks<\/code> that do not have an owner (see <code>getGuestTasks<\/code> method on the <code>TaskService<\/code>).<\/p>\n<p>Otherwise, the <code>TaskService<\/code> executes the method, <code>getUserTasks<\/code>, and fetches all the tasks that the currently logged in user has created. The <code>HomeController<\/code> and <code>TaskService<\/code> can be seen below.<\/p>\n<pre><code>&lt;br \/&gt;.controller(&#039;HomeController&#039;, [&quot;TaskService&quot;, &quot;$ionicLoading&quot;, &quot;$rootScope&quot;, &quot;$state&quot;, function(TaskService,  $ionicLoading, $rootScope, $state) {\n  var vm = this;\n\n  var findIndex = function(id) {\n    return vm.tasks.map(function(task) {\n      return task._id;\n    }).indexOf(id);\n  }\n\n  \/\/ Display loading indicator onload\n  $ionicLoading.show();\n\n  \/\/ Fetch Tasks\n  vm.fetch = function() {\n    if(!$rootScope.user) {\n      \/\/ Get all tasks for guests.\n      TaskService.getGuestTasks()\n      .then(\n        function(response) {\n          var tasks = response.data;\n          vm.tasks = [];\n          tasks.forEach(function(item, idx, array) {\n            item.dt_create = new Date(item.dt_create).getTime();\n            vm.tasks.push(array[idx]);\n          });\n          $ionicLoading.hide();\n        }, function(error) {\n          $ionicLoading.hide();\n        })\n      } else {\n        \/\/ Get only the user signed in tasks.\n        TaskService.getUsersTasks()\n        .then(\n          function(response) {\n            var tasks = response.data;\n            vm.tasks = [];\n            tasks.forEach(function(item, idx, array) {\n              item.dt_create = new Date(item.dt_create).getTime();\n              vm.tasks.push(array[idx]);\n            });\n            $ionicLoading.hide();\n          }, function(error) {\n            $ionicLoading.hide();\n          })\n        }\n      }\n\n      \/\/ Mark Complete a task.\n      vm.deleteTask = function(id) {\n        $ionicLoading.show();\n        vm.tasks.splice(findIndex(id), 1);\n        TaskService.deleteTask(id)\n        .then(function() {\n          $ionicLoading.hide();\n        }, function(error) {\n          $ionicLoading.hide();\n        })\n      }\n\n      vm.setStatus = function(task) {\n        task.complete = task.complete ? !task.complete : true;\n        TaskService.patchTask(task)\n        .then(function(task) {\n        }, function(error) {\n        })\n      }\n}])\n<\/code><\/pre>\n<h3>Task Service<\/h3>\n<p>The <code>TaskService<\/code> contains the logic to manipulate the application data regarding tasks. This service is shared between most states in which you are dealing with task data.<\/p>\n<pre><code>.factory(&#039;TaskService&#039;, [&quot;$rootScope&quot;, &quot;$q&quot;, function($rootScope, $q) {\n\n  return {\n    getGuestTasks : function(query) {\n      var deffered = $q.defer();\n      Stamplay.Query(&quot;object&quot;, &quot;task&quot;)\n      .notExists(&quot;owner&quot;)\n      .exec()\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(error) {\n        deffered.reject(err);\n      })\n      return deffered.promise;\n    },\n\n    getUsersTasks : function(query) {\n      var deffered = $q.defer();\n\n      Stamplay.Object(&quot;task&quot;)\n      .findByCurrentUser([&quot;owner&quot;])\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(err) {\n        deffered.reject(err);\n      })\n      return deffered.promise;\n    },\n\n    getTask : function(id) {\n        var deffered = $q.defer();\n        Stamplay.Object(&quot;task&quot;).get({ _id : id })\n        .then(function(response) {\n          deffered.resolve(response)\n        }, function(error) {\n          deffered.reject(err);\n        })\n        return deffered.promise;\n    },\n\n    addNew : function(task) {\n      var deffered = $q.defer();\n\n      Stamplay.Object(&quot;task&quot;).save(task)\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(err) {\n        deffered.reject(err);\n      })\n      return deffered.promise\n    },\n    deleteTask : function(id) {\n      var deffered = $q.defer();\n      Stamplay.Object(&quot;task&quot;).remove(id)\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(err) {\n        deffered.reject(err);\n      })\n      return deffered.promise;\n    },\n    updateTask : function(task) {\n      var deffered = $q.defer();\n      Stamplay.Object(&quot;task&quot;).update(task._id, task)\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(err) {\n        deffered.reject(err);\n      })\n      return deffered.promise;\n    },\n    patchTask : function(task) {\n      var deffered = $q.defer();\n      Stamplay.Object(&quot;task&quot;).patch(task._id, { complete: task.complete})\n      .then(function(response) {\n        deffered.resolve(response)\n      }, function(err) {\n        deffered.reject(err);\n      })\n      return deffered.promise;\n    }\n\n  }\n}]);\n<\/code><\/pre>\n<h3>Deleting Tasks<\/h3>\n<p>To delete a Task, slide the item to the left, and select the <code>delete<\/code> option from the two revealed.<\/p>\n<p>This will trigger the <code>TaskService<\/code> method to remove a record from Stamplay by its <code>_id<\/code> passed to it.<\/p>\n<p>After its success, simply <code>splice<\/code> the record from the array of Tasks that still exist in your state memory.<\/p>\n<p>The <code>TaskService<\/code> <code>deleteTask<\/code> method takes an <code>id<\/code> and uses the <code>Stamplay.Object(&quot;task&quot;).remove()<\/code> method to delete the record from Stamplay.<\/p>\n<h3>Marking Tasks Complete<\/h3>\n<p>Marking Tasks complete\/incomplete is done by simply updating the record&#8217;s <code>complete<\/code> field. You can assign it to the opposite of what the current value is every time the checkmark button is selected.<\/p>\n<p>Starting with a falsey value will allow you to mark it complete, and if you wish to mark it incomplete, you can use the same method to accomplish this. The method inside of your <code>HomeController<\/code> that you can use to do this is <code>vm.setStatus<\/code>.<\/p>\n<p>The <code>TaskService<\/code> <code>patchTask<\/code> method takes an <code>task<\/code> and uses the <code>patch()<\/code> method to partially update the record from Stamplay. Here, you only update the <code>complete<\/code> field on the record. Use the <code>Stamplay.Object(&quot;task&quot;).patch()<\/code> method to accomplish this.<\/p>\n<h3>Updating A Task<\/h3>\n<p>After the initial tasks are loaded, from here, the methods to create, update, and delete tasks are the same across guest and user accounts.<\/p>\n<p>The methods pertaining to updating the content of a task and creating a new task are, however, in a separate state, and will be attached to the <code>TaskController<\/code> (See below) between both states.<\/p>\n<pre><code>.controller(&#039;TaskController&#039;, [&quot;TaskService&quot;, &quot;$ionicLoading&quot;, &quot;$rootScope&quot;, &quot;$state&quot;, &quot;$stateParams&quot;, function(TaskService,  $ionicLoading, $rootScope, $state, $stateParams) {\n  var vm = this;\n\n  if($stateParams.id) {\n    $ionicLoading.show();\n    TaskService.getTask($stateParams.id)\n      .then(function(task) {\n        $ionicLoading.hide();\n        vm.task = task.data[0];\n      }, function(err) {\n        $ionicLoading.hide();\n        console.error(err);\n      })\n  }\n\n  \/\/ Add a task.\n  vm.add = function() {\n    $ionicLoading.show();\n    TaskService.addNew(vm.task)\n    .then(function(task) {\n      $ionicLoading.hide();\n      $state.go(&quot;tasks&quot;, {}, { reload: true });\n    }, function(error) {\n      $ionicLoading.hide();\n    })\n  }\n\n  vm.save = function() {\n    $ionicLoading.show();\n    TaskService.updateTask(vm.task)\n    .then(function(task) {\n      $ionicLoading.hide();\n      $state.go(&quot;tasks&quot;, {}, { reload: true });\n    }, function(error) {\n      $ionicLoading.hide();\n    })\n  }\n\n}])\n<\/code><\/pre>\n<p>To update a task, you must slide the task to the left, after which two actions will be revealed: <code>edit<\/code> and <code>delete<\/code>.<\/p>\n<p>Once you select <code>edit<\/code>, you will be taken to the <code>edit<\/code> state, where the fields will be populated with the existing data from the task you selected.<\/p>\n<p>This is just simple data-binding to a view-model that on the click of submit will trigger the <code>TaskService<\/code> method, <code>updateTask()<\/code>, which calls the <code>Stamplay.Object(&quot;task&quot;).update()<\/code> method on the SDK to update the whole record.<\/p>\n<h3>Creating New Tasks<\/h3>\n<p>To create a new task, select the plus icon in the top right corner, and navigate to the <code>new<\/code> state, where you may submit a new record to Stamplay.<\/p>\n<p>This is also very basic, just data-binding to a view-model and, on the click of the submit button, executing the <code>TaskService<\/code> method to create a new record in Stamplay.<\/p>\n<p>To save new data to Stamplay, we simply use the <code>Stamplay.Object(&quot;task&quot;).save()<\/code>, and pass in a valid model as the first parameter as seen in our <code>TaskService<\/code>.<\/p>\n<h2>Conclusion<\/h2>\n<p>Using Ionic, this starter kit, and the Stamplay backend, you now have a fully functioning task application, without a single line of backend code! You can use Ionic and Stamplay to quickly build great mobile apps and integrate with a variety of well known APIs.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ionic makes it easier to build a multi-platform mobile app using modern web technologies. Stamplay allows developers to use APIs as building blocks to to rapidly construct scalable, maintainable backend applications. By using Stamplay and Ionic together, you can focus on building a unique app, instead of spending time scaling architecture or implementing the same [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[3],"class_list":["post-860","post","type-post","status-publish","format-standard","hentry","category-all","tag-ionic"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Cross-Platform Apps with Ionic and Stamplay - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Cross-Platform Apps with Ionic and Stamplay\" \/>\n<meta property=\"og:description\" content=\"Ionic makes it easier to build a multi-platform mobile app using modern web technologies. Stamplay allows developers to use APIs as building blocks to to rapidly construct scalable, maintainable backend applications. By using Stamplay and Ionic together, you can focus on building a unique app, instead of spending time scaling architecture or implementing the same [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-03-30T16:31:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\" \/>\n<meta name=\"author\" content=\"Isaiah Grey\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@isaiahgrey93\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Isaiah Grey\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\"},\"author\":{\"name\":\"Isaiah Grey\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/b33eb628e8885d5e55686f31655e379f\"},\"headline\":\"Cross-Platform Apps with Ionic and Stamplay\",\"datePublished\":\"2016-03-30T16:31:50+00:00\",\"dateModified\":\"2016-03-30T16:31:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\"},\"wordCount\":1620,\"commentCount\":4,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\",\"keywords\":[\"Ionic\"],\"articleSection\":[\"All\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\",\"url\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\",\"name\":\"Cross-Platform Apps with Ionic and Stamplay - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\",\"datePublished\":\"2016-03-30T16:31:50+00:00\",\"dateModified\":\"2016-03-30T16:31:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg\",\"width\":1124,\"height\":595},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Cross-Platform Apps with Ionic and Stamplay\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/b33eb628e8885d5e55686f31655e379f\",\"name\":\"Isaiah Grey\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/66953dc0ade94a39db784f185b3bdf808c23025ce78cee46f4f252e4906bfad3?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/66953dc0ade94a39db784f185b3bdf808c23025ce78cee46f4f252e4906bfad3?s=96&d=mm&r=g\",\"caption\":\"Isaiah Grey\"},\"sameAs\":[\"https:\/\/x.com\/isaiahgrey93\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/isaiah\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Cross-Platform Apps with Ionic and Stamplay - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay","og_locale":"en_US","og_type":"article","og_title":"Cross-Platform Apps with Ionic and Stamplay","og_description":"Ionic makes it easier to build a multi-platform mobile app using modern web technologies. Stamplay allows developers to use APIs as building blocks to to rapidly construct scalable, maintainable backend applications. By using Stamplay and Ionic together, you can focus on building a unique app, instead of spending time scaling architecture or implementing the same [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay","og_site_name":"Ionic Blog","article_published_time":"2016-03-30T16:31:50+00:00","og_image":[{"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg"}],"author":"Isaiah Grey","twitter_card":"summary_large_image","twitter_creator":"@isaiahgrey93","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Isaiah Grey","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay"},"author":{"name":"Isaiah Grey","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/b33eb628e8885d5e55686f31655e379f"},"headline":"Cross-Platform Apps with Ionic and Stamplay","datePublished":"2016-03-30T16:31:50+00:00","dateModified":"2016-03-30T16:31:50+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay"},"wordCount":1620,"commentCount":4,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg","keywords":["Ionic"],"articleSection":["All"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay","url":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay","name":"Cross-Platform Apps with Ionic and Stamplay - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg","datePublished":"2016-03-30T16:31:50+00:00","dateModified":"2016-03-30T16:31:50+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2016\/03\/ionic_cover.jpg","width":1124,"height":595},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/apps-with-ionic-and-stamplay#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Cross-Platform Apps with Ionic and Stamplay"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/b33eb628e8885d5e55686f31655e379f","name":"Isaiah Grey","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/66953dc0ade94a39db784f185b3bdf808c23025ce78cee46f4f252e4906bfad3?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/66953dc0ade94a39db784f185b3bdf808c23025ce78cee46f4f252e4906bfad3?s=96&d=mm&r=g","caption":"Isaiah Grey"},"sameAs":["https:\/\/x.com\/isaiahgrey93"],"url":"https:\/\/ionic.io\/blog\/author\/isaiah"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/860","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=860"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/860\/revisions"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=860"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=860"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=860"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}