CanJS - Place My Order

canjs

https://github.com/canjs/canjs/tree/master/guides/examples/PlaceMyOrder/chapter_9
http://canjs.com/guides/Tutorial.html (CanJS v2.3)

What is the recommended folder structure?

└── /
    ├── app.js
    ├── components
    ├── css
    ├── images
    ├── index.html
    ├── libs
    └── models
  1. app.js:
  2. components: The components folder is where we’ll put all of the parts that make up our application. A component-based structure makes it easier to both manage and port our application's components (should you, for example, want to use a component in another application).
  3. css: The css folder contains a style.css file that is used for the entire app. We would typically recommend putting any CSS files in their respective component folders
  4. images: The images folder contains images that are used throughout the app. Again, if you had images that were specific to a component, we would recommend putting them in their respective component folder.
  5. index.html: The index.html file is the HTML file that loads all of the app’s scripts and styles
  6. libs: The libs folder contains all of the app’s JavaScript dependencies.
  7. models: The models folder contains some JSON files that we will use as fake server responses and some JavaScript files that we’ll fill out later to mock an HTTP server.

Boilerplate code:

<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8">
    <title>Place My Order</title>
    <link rel="stylesheet" type="text/css" href="css/style.css"/>
  </head>
  <body>
    <!-- CanJS application root -->
    <div id="can-main"></div>
    <script src="libs/jquery.js"></script>
    <script src="libs/can.custom.js"></script>
    <script src="libs/can.fixture.js"></script>
    <!-- Replace with order component script -->
    <!-- Replace with order details component script -->
    <!-- Replace with order history component script -->
    <!-- Replace with order list component script -->
    <!-- Replace with order phone component script -->
    <!-- Replace with restaurant details component script -->
    <!-- Replace with restaurant list component script -->
    <script src="app.js"></script>
  </body>
</html>

What is the purpose of app.js?

The app.js file is the script that will bootstrap our application. Basically, the code that should be run when the DOM is ready should be inside this file:

$(function () {
    $('#can-main').html('The Requisite “Hello World” Message');
});
$(function () {
    var AppState = can.Map.extend({});
    var appState = new AppState();

    // Bind the application state to the root of the application
    $('#can-main').html(can.view('main.stache', appState));

    // Set up the routes
    can.route(':page', { page: 'home' });
    can.route(':page/:slug', { slug: null });
    can.route(':page/:slug/:action', { slug: null, action: null });

    // Prevent default action on some links
    $('body').on('click', 'a[href="javascript://"]', function(ev) {
        ev.preventDefault();
    });

    // Bind the application state to the can.route
    can.route.map(appState);
    can.route.ready();

    //appState.attr('page', 'restaurants');

    appState.bind('change', function(ev, prop, change, newVal, oldVal) {
        alert('Changed the “' + prop + '” property from “' + oldVal + '” to “' + newVal + '”.');
    });
});

Code for this examples:

index.html:

<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8">
    <title>Place My Order</title>
    <link rel="stylesheet" type="text/css" href="css/style.css"/>
  </head>
  <body>
    <!-- CanJS application root -->
    <div id="can-main"></div>
    <script src="libs/jquery.js"></script>
    <script src="libs/can.custom.js"></script>
    <script src="libs/can.fixture.js"></script>
    <!-- Replace with order component script -->
    <!-- Replace with order details component script -->
    <!-- Replace with order history component script -->
    <!-- Replace with order list component script -->
    <!-- Replace with order phone component script -->
    <!-- Replace with restaurant details component script -->
    <script src="models/city.js"></script>
    <script src="models/state.js"></script>
    <script src="models/fixtures.js"></script>
    <script src="components/restaurant_list/restaurant_list.js"></script>
    <script src="app.js"></script>
  </body>
</html>

components/restaurant_list/restaurant_list.js:

var RestaurantListViewModel = can.Map.extend({
  define: {
    state: {
      value: null,
      set: function() {
        // Remove the city when the state changes
        this.attr('city', null);
      }
    },
    states: {
        get: function() {
            return State.findAll({});
        }
    },
    cities: {
        get: function() {
            var state = this.attr('state');
            return state ? City.findAll({ state: state }) : null;
       }
    },
    citiesByState: {
      get: function() {
        var citiesByState = {};
        this.attr('states').forEach(function(state) {
          citiesByState[state.name] = state.cities;
        });
        return citiesByState;
      }
    },
    city: {
      value: null
    }
  }
});
can.Component.extend({
  tag: 'pmo-restaurant-list',
  template: can.view('components/restaurant_list/restaurant_list.stache'),
  viewModel: RestaurantListViewModel
});

components/restaurant_list/restaurant_list.stache:

<div class="restaurants">
  <h2 class="page-header">Restaurants</h2>
      <form class="form">
        <div class="form-group">
          <label>State</label>
          <select can-value="{state}" {{#if states.isPending}}disabled{{/if}}>
            {{#if states.isPending}}
              <option value="">Loading...</option>
            {{else}}
              {{^if state}}
              <option value="">Choose a state</option>
              {{/if}}
              {{#each states.value}}
              <option value="{{short}}">{{name}}</option>
              {{/each}}
            {{/if}}
          </select>
        </div>
        <div class="form-group">
          <label>City</label>
          <select can-value="city" {{^if state}}disabled{{/if}}>
            {{#if cities.isPending}}
              <option value="">Loading...</option>
            {{else}}
              {{^if city}}
              <option value="">Choose a city</option>
              {{/if}}
              {{#each cities.value}}
              <option>{{name}}</option>
              {{/each}}
            {{/if}}
          </select>
        </div>
      </form>
  <!-- Restaurants code will go here -->
</div>

models/state.js:

var State = can.Model.extend({
  findAll: 'GET /api/states'
}, {
  // Include second, empty parameter object to set instanceProperties
});

models/city.js:

var City = can.Model.extend({
  findAll: 'GET /api/cities'
}, {
  // Include second, empty parameter object to set instanceProperties
});

models/fixtures.js:

    can.fixture('GET /api/states', 'models/states.json');
    can.fixture('GET /api/cities', function(request, response) {
        can.ajax({
            url: 'models/' + request.data.state + '.json',
            success: function(data) {
                response(data);
            }
        });
    });
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License