Image

dynamically add/remove siblings

1. App.js

App = Ember.Application.create();

App.IndexRoute = Ember.Route.extend({
});

App.IndexController = Ember.Controller.extend({
  items: [{
    id: 1,
    name: 'Pizza'
  },
  {
    id: 2,
    name: 'Nachos'
  },{
    id: 3,
    name: 'Spaghetti'
  }],
  actions: {
    remove: function (item) {
      this.items.removeObject(item);
      for(var i = 0; i < this.items.length; i++) {
         var temp = this.items.objectAt(i);
         Ember.set(temp, "id", (i+1));
      }
    },
    add: function (index) {
      this.items.insertAt(this.items.length, {
        name: 'Hot Dogs',
        id: this.items.length+1,
      });
    },
    summary: function() {
      Ember.$('#sum').text(JSON.stringify(this.items));
    }
  }
});

App.ListContainerComponent = Ember.Component.extend({
  tagName: 'ul',
  items: [],
  actions: {
    onRemove: function (item) {
      this.sendAction('onRemove', item);
    },
    onAdd: function (item) {
      var insertAt = this.items.indexOf(item) + 1;
      this.sendAction('onAdd', insertAt);
    }
  }
});

App.ListItemComponent = Ember.Component.extend({
  tagName: 'li'
});

2. The Markup

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
    <script src="//builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
  </head>
<body>
  <script type="text/x-handlebars" data-template-name="application">
  <h1>Ember Food</h1>
  {{outlet}}
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    {{list-container items=items onRemove='remove' onAdd='add'}}
    <div id="sum" {{action 'summary'}}>Summary</div>
  </script>
  
  <script type="text/x-handlebars" data-template-name="components/list-container">
    {{#each items as |item|}}
      {{#list-item content=item label=item.id }}
        {{item.name}}
        <button {{action 'onAdd' item}}>+</button>
        <button {{action 'onRemove' item}}>-</button>
      {{/list-item}}
    {{/each}}
  </script>
  
  <script type="text/x-handlebars" data-template-name="components/list-item">
    {{label}} 
    {{yield}}
  </script>
</body>
</html>

filter component

1. All urls to 1 route

Router.map(function() {
  this.route('about', {path: '/'});
  this.route('about', { path: '/*wildcard' });
});

2. Dom Manipulation

// in any controller
Ember.$('#component-x').val('My Value');

3. Adapter: rental.js

import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  'namespace': 'api'
});

4. Template: index.hbs

<div class="jumbo">
  <div class="right tomster"></div>
  <h2>Welcome!</h2>
  <p>
    We hope you find exactly what you're looking for in a place to stay.
    <br>Browse our listings, or use the search box above to narrow your search.
  </p>
</div>

{{input value=filterText type='text' placeholder='Search City'}}
  {{#unless filteredResults.length}}
    <h4>no record matched your criteria</h4>
  {{/unless}}

  <ul>
    {{#each filteredResults as |item|}}
      <li>{{item.id}} {{item.attributes.city}}</li>
    {{/each}}
  </ul>

5. Route: index.js

import Ember from 'ember';

export default Ember.Route.extend({
  filterText: '',

  setupController(controller, model) {
    this._super(controller, model);
    controller.set('filterText', this.get('filterText'));
  },

  queryParams: {
    search: {
      refreshModel: true
    }
  },

  beforeModel: function(transition) {
    // console.log('before model: '+transition.queryParams.search);
    this.set('filterText', transition.queryParams.search);
  },

  model: function(params) {
    var endPoint = 'http://localhost:4200/api/deals';
    var results = [];

    endPoint = (params.search)? endPoint+'/city='+params.search : endPoint;

    $.ajax({
      url: endPoint,
      type: 'GET',
      accepts: 'application/json',
      success: function(data) {
        if(data.rentals) {
          data.rentals.forEach(function(deal) {
            results.addObject(deal);
          });
        } else if(data.data === 'no records found'){
          console.log('No results found');
        } else {
          data.data.forEach(function(deal) {
            results.addObject(deal);
          });
        }
      },
      error: function() {
          console.log('DEBUG: GET Deals Failed');
      },async:false
    });
    return results;
  }
});

6. Controller: index.js

import Ember from 'ember';

export default Ember.Controller.extend({
  queryParams: ['search'],

  onFilterTextChange: function() {
    Ember.run.debounce(this, this.applyFilter, 0);
  }.observes('filterText'),

  applyFilter: function() {
    this.set('search', this.get('filterText'));
    console.log('search set: ' + this.get('search'));
  },
  
  filteredResults: function() {
    return this.get('model');
  }.property('search'),
});

7. Mock Response(http-mock): rentals.js

/*jshint node:true*/
module.exports = function(app) {
  var express = require('express');
  var rentalsRouter = express.Router();
  var rentals = [
      {
        type: 'rentals',
        id: 1,
        attributes: {
          title: 'Grand Old Mansion',
          owner: 'Veruca Salt',
          city: 'San Francisco',
          type: 'Estate',
          bedrooms: 15,
          image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
        }
      }, {
        type: 'rentals',
        id: 2,
        attributes: {
          title: 'Urban Living',
          owner: 'Mike Teavee',
          city: 'Seattle',
          type: 'Condo',
          bedrooms: 1,
          image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
        }
      }, {
        type: 'rentals',
        id: 3,
        attributes: {
          title: 'Downtown Charm',
          owner: 'Violet Beauregarde',
          city: 'Portland',
          type: 'Apartment',
          bedrooms: 3,
          image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
        }
      }
    ];

  rentalsRouter.get('/', function(req, res) {
    res.send({
      rentals
    });
  });


  rentalsRouter.post('/', function(req, res) {
    res.status(201).end();
  });

  /*rentalsRouter.get('/:id', function(req, res) {
    res.send({
      '':req.params
    });
  });*/

  rentalsRouter.get('/:city', function(req, res) {
    let val = req.params.city.split("=");
    let filteredRentals = rentals.filter(function(i) {
      return i.attributes.city.toLowerCase().indexOf(val[1].toLowerCase()) !== -1;
    });

    if(filteredRentals.length>=1) {
      res.send({
        'data': filteredRentals
      });
    } else {
      res.send({
        'data': 'no records found'
      });
    }

    
  });

  rentalsRouter.put('/:id', function(req, res) {
    res.send({
      'rentals': {
        id: req.params.id
      }
    });
  });

  rentalsRouter.delete('/:id', function(req, res) {
    res.status(204).end();
  });

  // The POST and PUT call will not contain a request body
  // because the body-parser is not included by default.
  // To use req.body, run:

  //    npm install --save-dev body-parser

  // After installing, you need to `use` the body-parser for
  // this mock uncommenting the following line:
  //
  //app.use('/api/rentals', require('body-parser').json());
  app.use('/api/rentals', rentalsRouter);
};

Journal – publish bower package; setup ember mock

1. Publish Bower Package

Bower was started by twitter.
https://bower.io/docs/creating-packages/

2. Simple Ember App

Super Rentals

3. Mocks with EmberJS

EmberJS comes with ExpressJS (internally, http-mock) here are steps to setup a mock. Once setup it works like a real service & it actually be used to build REST API for your backend if it is fueled with real data.

  • generate ember project with ember-cli
  • generate an express route
    ember g http-mock destinations
  • to view: http://localhost:4200/api/destinations
  • Unlike fixtures, this helps you test your Adapter as well
  • setting up a mock response
// server/mocks/destinations.js
/*jshint node:true*/
module.exports = function(app) {
  var express = require('express');
  var destinationsRouter = express.Router();

  destinationsRouter.get('/', function(req, res) {
    res.send({
      'destinations': [
		{
			id:'001',
			loc: 'london'
		}, {
			id:'002', 
			loc: 'rotterdam'
		}, {
			id:'003', 
			loc: 'amsterdam'
		}
	  ]
    });
  });

  destinationsRouter.post('/', function(req, res) {
    res.status(201).end();
  });

  destinationsRouter.get('/:id', function(req, res) {
    res.send({
      'destinations': {
        id: req.params.id
      }
    });
  });

  destinationsRouter.put('/:id', function(req, res) {
    res.send({
      'destinations': {
        id: req.params.id
      }
    });
  });

  destinationsRouter.delete('/:id', function(req, res) {
    res.status(204).end();
  });

  // The POST and PUT call will not contain a request body
  // because the body-parser is not included by default.
  // To use req.body, run:
  //    npm install --save-dev body-parser

  // After installing, you need to `use` the body-parser for
  // this mock uncommenting the following line:
  //app.use('/api/destinations', require('body-parser').json());
  app.use('/api/destinations', destinationsRouter);
};

Ember Cheatsheet

01. REST Adapter for Cross Domain Access

02. Hello World
https://www.ludu.co/course/ember

03. Redirect Ember 2.0
// app/routes/index.js
import Ember from ’ember’;

export default Ember.Route.extend({
beforeModel() {
this.transitionTo(‘products’);
}
});

02. Run .ru file for https://www.toptal.com/javascript/a-step-by-step-guide-to-building-your-first-ember-js-app
./server.ru;

03. Multiple models for a Controller

model : function() {
  return Ember.RSVP.hash({
    users: this.store.find('User'),
    customers: this.store.find('Customer')
  });
},

setupController: function(controller, model) {
  controller.set('model', model.users);
  controller.set('customers', model.customers);
}

04. Creating and Handling Controller Vars

In the Controller

App.ArtistsController = Ember.ArrayController.extend({
  newName: '',
  disabled: function() {
    return Ember.isEmpty(this.get('newName'));
  }.property('newName')
});

In the Route

App.ArtistsRoute = Ember.Route.extend({
  ...
  actions: {
    createArtist: function() {
      var name = this.get('controller').get('newName');

      Ember.$.ajax('http://localhost:9393/artists', {
        type: 'POST',
        dataType: 'json',
        data: { name: name },
        context: this,
        success: function(data) {
          var artist = App.Artist.createRecord(data);
          this.modelFor('artists').pushObject(artist);
          this.get('controller').set('newName', '');
          this.transitionTo('artists.songs', artist);
        },
        error: function() {
          alert('Failed to save artist');
        }
      });
    }
  }
});

05. An adapter (RESTful) for each model

App.PostAdapter = DS.RESTAdapter.extend({
  namespace: 'api/v2',
  host: 'https://api.example2.com'
});

App.PhotoAdapter = DS.RESTAdapter.extend({
  namespace: 'api/v1',
  host: 'https://api.example.com'
});

06. Artists via Rest Call

App.ArtistsRoute = Ember.Route.extend({
  model: function() {
    var artistObjects = [];
    Ember.$.getJSON('http://localhost:9393/artists', function(artists) {
      artists.forEach(function(data) {
        artistObjects.pushObject(App.Artist.createRecord(data));
      });
    });
    return artistObjects;
  }
});

07. Get the model from a controller

var model = this.store.modelFor('form','new-latte-form');

08. Calling transitionTo from a route or transitionToRoute from a controller will stop any transition currently in progress and start a new one, functioning as a redirect. transitionTo takes parameters and behaves exactly like the link-to helper:

{{#link-to "index"}}Home{{/link-to}}

After Model is loaded

App.Router.map(function() {
  this.resource('posts');
  this.resource('post', { path: '/post/:post_id' });
});

App.PostsRoute = Ember.Route.extend({
  afterModel: function(posts, transition) {
    if (posts.get('length') === 1) {
      this.transitionTo('post', posts.get('firstObject'));
    }
  }
});

Before Model the default method

App.Router.map(function() {
  this.resource('posts');
});

App.IndexRoute = Ember.Route.extend({
  beforeModel: function() {
    this.transitionTo('posts');
  }
});

09. RESTAdapter

# a model definition
App.Person = DS.Model.extend({
  firstName: DS.attr('string'),
  lastName:  DS.attr('string'),
  isPersonOfTheYear: DS.attr('boolean')
});

# a json returned (REST) from server should like
{
  "person": {
    "firstName": "Barack",
    "lastName": "Obama",
    "isPersonOfTheYear": true
  }
}

10. Singleton Service: Audio Player
https://guides.emberjs.com/v1.10.0/understanding-ember/dependency-injection-and-service-lookup/

11. Ember Data Model maker
http://andycrum.github.io/ember-data-model-maker/

12. Obtain the jQuery object from an ember component
http://emberjs.com/api/classes/Ember.Component.html#method__
Ember.inspect(this.$(‘input’)[0]);

13. Rest Client for Chrome
https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo

Built to last; Build with Ember

#01. Wildcard Route for 404

// app/router.js
Router.map(function() {
  // other routes
  this.route('page-not-found', { path: '/*wildcard' });
});
// app/templates/page-not-found.hbs
Page Not Found
{{outlet}}

#02. Helper Method

// app/helpers/increment.js
import Ember from "ember";

export function increment(integer){
  return ++integer;
}

export default Ember.Helper.helper(increment);
// app/templates/index.hbs
{{#each model as |contact index|}}
    #{{increment index}}. {{contact.title}}
{{/each}}

#03. Load a model within a Route

// app/routes/post.js
import Ember from 'ember';

var articles = [{
  id: 1,
  title: 'FirstPost',
  content: 'First Post'
}, {
  id: 2,
  title: 'About',
  content: 'About Splash'
}, {
  id: 5,
  title: 'Contact',
  content: 'You can reach us too!'
}];

export default Ember.Route.extend({
  model: function(params) {
    var index = params.post_id -1;
    return articles[index];
  }
});
// app/templates/post.hbs
Id:: {{model.id}}
title: {{model.title}}
body: {{model.content}}

Port an Ember App to Cordova

1. Pre-Requisites

1.1. Ember Cli
$sudo npm install -g ember-cli

1.2. Cordova
$sudo npm install -g cordova

1.3. Also consider
Git, Node.js (with NPM), Bower, Ember CLI, PhantomJS

2. create ember project

$ember new labs

3. include ember-cordova-cli in package.json

DevDependencies
“ember-cli-cordova”: “0.0.15”,

4. alter config/environment.js

modulePrefix: ‘hello’,
environment: environment,
baseURL: ‘/’,
locationType: ‘hash’,
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. ‘with-controller’: true
}
},

APP: {
// Here you can pass flags/options to your application instance
// when it is created
},

cordova: {
rebuildOnChange: false,
emulate: false
}

5. Generate Cordova Proj (reverse domain name)

sudo ember generate cordova-init com.hamzeen.hello –platform=android

6. Open Existing Proj

ember cordova:prepare

7. Open iOS Porject in Xcode

ember cordova:open

8. Build for target platform

$sudo ember cordova:build –environment=production –platform=android

9. Additional

9.1. full access
$chmod 777 -R /dir

9.2. To modify npmrc for proxies and npm registry
$vi ~/.npmrc

10. Live debug

visit this url on chrome
chrome://inspect/#devices

Reference:

introduction:

ember-cli-cordova
https://github.com/poetic/ember-cli-cordova

for iOS
http://dreamingin.codes/article/merging-ember-cli-cordova-and-make-it-emulate-ios-live/

http://lelandbatey.com/posts/2015/02/ember-and-cordova

Deploying an ember app made with Firebase Adapter

I got an Ember.js project named “hello-hamzeen” which I did to explore “Ember.js” where I chose to use Firebase as the Adapter. Here is the world’s most succinct deployment instructions ever! 🙂

 

1. If you don’t have firebase tools

$ sudo npm install -g firebase-tools

2. Otherwise, if you want to get it updated

$ sudo npm update -g firebase-tools

3. Login to your firebase account from terminal

$ sudo firebase login

Note: Upon successful login you should see something similar to the following url open up in your browser,
https://www.firebase.com/login/confirm.html?ticket=1cf04b3f-383a-4ba6-9124-71ff6f7bc9ae

 


Now to the meat of it, initialize and deploy to firebaseapp.com. Run the following from your project directory,

4. Initialize

When prompted select our project and type in the public folder you want to use. Typically this is ‘dist’ for Ember apps,
$ sudo firebase init

5. Deploy your project to firebaseapp.com

$ sudo firebase deploy

Mine can be found at, https://hello-hamzeen.firebaseapp.com
Cheers!