Skip to content

Instantly share code, notes, and snippets.

@leoj3n
Last active June 2, 2016 05:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leoj3n/029cb44caeca6cdc669bb951cd6d00b8 to your computer and use it in GitHub Desktop.
Save leoj3n/029cb44caeca6cdc669bb951cd6d00b8 to your computer and use it in GitHub Desktop.
How To Start A DoneJS Project

This is a short tutorial on how to develop a new project locally based off the DoneJS place-my-order example. The main difference being this guide is more synchronous (goes more step-by-step to build the app up) and most importantly is entirely localized, so you can start editing and building your own API and assets module, in addition to the main app.

Workspace

Create a directory to hold the project, api, and any local modules:

mkdir -p ~/workspace/my-app && cd ~/workspace/my-app

Project

Generate the project:

npm install -g donejs 
donejs add app my-app
# answer dialogs

API

Clone the example api:

git clone https://github.com/donejs/place-my-order-api.git my-app-api
cd my-app-api
rm -rf .git

Edit the package.json in my-app-api:

{
  "name": "my-app-api",
  "version": "0.0.0",
  "description": "",
  "main": "lib/index.js",
  "bin": {
    "place-my-order-api": "./bin/my-app-api"
  },
  "scripts": {
    "prepublish": "npm run compile",
    "compile": "rm -rf lib/ && node_modules/.bin/babel -d lib/ src/",
    "start": "PORT=3030 bin/place-my-order-api",
    "publish": "git push origin --tags",
    "release:patch": "npm version patch && npm publish",
    "release:minor": "npm version minor && npm publish",
    "release:major": "npm version major && npm publish",
    "jshint": "jshint src/. test/. --config",
    "mocha": "mocha test/ --recursive --compilers js:babel/register",
    "test": "npm run jshint && npm run mocha"
  },
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "jshint": "^2.7.0",
    "mocha": "^2.2.5"
  },
  "dependencies": {
    "babel": "^5.5.3",
    "body-parser": "^1.12.4",
    "commander": "^2.8.1",
    "feathers": "^1.3.0",
    "feathers-hooks": "^0.5.0",
    "feathers-nedb": "^0.1.0",
    "madison": "0.0.7"
  },
  "system": {
    "npmDependencies": []
  }
}

Assets Module

Clone the example assets module:

cd ~/workspace/my-app
git clone https://github.com/donejs/place-my-order-assets.git my-app-assets
cd my-app-assets
rm -rf .git

Edit the package.json in my-app-assets:

{
  "name": "my-app-assets",
  "version": "0.0.0",
  "description": "",
  "main": "less/styles.less!",
  "scripts": {
    "release:patch": "npm version patch && npm publish",
    "release:minor": "npm version minor && npm publish",
    "release:major": "npm version major && npm publish"
  },
  "author": "",
  "license": "MIT",
  "system": {
    "map": {
      "bit-tabs/tabs.less!$less": "@empty"
    }
  }
}

Install local assets module into the project:

cd ~/workspace/my-app/my-app
npm install -g linklocal # more info: https://github.com/timoxley/linklocal#linking
npm install --save ../my-app-assets
linklocal # important command!

Run

Run the api server:

cd ~/workspace/my-app/my-app-api
npm install
mv bin/place-my-order-api bin/my-app-api
./bin/my-app-api --port 7070

Edit the scripts section of the project's package.json to use the api server:

  "scripts": {
    "test": "testee src/test.html --browsers firefox --reporter Spec",
    "start": "done-serve --proxy http://localhost:7070 --port 8080",
    "develop": "done-serve --develop --proxy http://localhost:7070 --port 8080",
    "document": "documentjs",
    "build": "node build"
  },

Edit the project's src/index.stache to can-import the assets module:

  <body>
    <can-import from="my-app-assets" />

Run the project's develop server in a new shell:

cd ~/workspace/my-app/my-app
donejs develop

Visit http://localhost:8080/ to see "Hello World!" with custom styling.

Troubleshooting

At the end, your directory structure should look like:

workspace
└── my-app
    ├── my-app
    │   ├── build.js
    │   ├── development.html
    │   ├── documentjs.json
    │   ├── node_modules
    │   ├── package.json
    │   ├── production.html
    │   ├── readme.md
    │   └── src
    ├── my-app-api
    │   ├── LICENSE
    │   ├── README.md
    │   ├── bin
    │   ├── db-data
    │   ├── lib
    │   ├── node_modules
    │   ├── package.json
    │   ├── src
    │   └── test
    └── my-app-assets
        ├── LICENSE
        ├── README.md
        ├── images
        ├── less
        └── package.json

Now let's see the most basic example possible of adding components and routes...

Home Component

Generate a new component, whose content will be loaded when visiting the web root at http://localhost:8080/:

cd ~/workspace/my-app/my-app
donejs add component home.component app-home

Next, add a route to src/app.js to load this specific component when visiting the web root:

route(':page', { page: 'home' });

Finally, load the home component by matching the current route in src/index.stache by replacing <h1>{{message}}</h1> with:

    {{#switch page}}
      {{#case "home"}}
        <can-import from="my-app/home.component" />
        <app-home/>
      {{/case}}
    {{/switch}}

Now, when visiting the web root at http://localhost:8080/, the content of the home component will be loaded.

Foobar Component

We can build off this simple pattern to add more route-component combinations to this single-page-app.

Generate a foobar component, whose content will be loaded when visiting http://localhost:8080/foobar:

donejs add component foobar.component app-foobar

Next, add a route to src/app.js to load this specific component:

route(':page/:slug', { slug: null });

Extend the switch in src/index.stache to match this route as well:

    {{#switch page}}
      {{#case "home"}}
        <can-import from="my-app/home.component" />
        <app-home/>
      {{/case}}
      {{#case "foobar"}}
        <can-import from="my-app/foobar.component" />
        <app-foobar/>
      {{/case}}
    {{/switch}}

Now, when visiting the web root at http://localhost:8080/foobar, the content of the foobar component will be loaded.

Navigation Menu

To tie everything together, add a navigation menu that will highlight the active page.

Create yet another component:

donejs add component navigation.component app-navigation

Edit the navigation component to be:

<can-component tag="app-navigation">
  <template>
    <header>
      <nav>
       <h1>my-app</h1>
       <ul>
         <li class="{{#eq page 'home'}}active{{/eq}}">
           <a href="{{routeUrl page='home'}}">Home</a>
         </li>
         <li class="{{#eq page 'foobar'}}active{{/eq}}">
           <a href="{{routeUrl page='foobar'}}">Foo Bar</a>
         </li>
       </ul>
      </nav>
    </header>
  </template>
</can-component>

Import and use this component above the switch that was added to src/index.stache:

    <can-import from="my-app/navigation.component" />
    <app-navigation {page}="page"/>

Loading Indicators

To enable loading indicators for the home and foobar components when switching routes, create a new loading component:

donejs add component loading.component app-loading

Edit the loading component to be:

<can-component tag="app-loading">
  <template>
    {{#eq state "resolved"}}
      <content></content>
    {{else}}
      <div class="loading"></div>
    {{/eq}}
  </template>
</can-component>

Edit the project's src/index.stache to can-import the loading component after the viewModel:

    <can-import from="my-app/app" export-as="viewModel" />
    <can-import from="my-app/loading.component" />

In src/index.stache update the component imports in the switch statement to use can-tag, and to wrap the custom element declaration:

    {{#switch page}}
      {{#case "home"}}
        <can-import from="my-app/home.component" can-tag="app-loading">
          <app-home/>
        </can-import>
      {{/case}}
      {{#case "foobar"}}
        <can-import from="my-app/foobar.component" can-tag="app-loading">
          <app-foobar/>
        </can-import>
      {{/case}}
    {{/switch}}

Done! You probably won't be able to see the loading gif appear because the pages load so quickly, but it will appear as the app and it's components gain in complexity.

By default DoneJS projects utilize less via the a less compiler engine that runs on the client side. This is probably not optimal, to be compiling our CSS on the client side. Also, if you want to use the new version 4 of Bootstrap, you will need to compile scss not less files. There is a steal plugin called steal-sass that will compile the CSS on the client but for performance reasons it is probably better to use a command-line compiler like node-sass and just include the CSS file in the application like normal.

Install node-sass CLI

npm install -g node-sass

Install Bootstrap

cd ~/workspace/my-app/my-app-assets
npm install bootstrap@4.0.0-alpha.2 --save

Create styles.scss

Copy Bootstraps' _variables.scss so the variables can be modified to override the defaults non-destructively.

cp node_modules/bootstrap/scss/_variables.scss styles.scss

At the end of styles.scss, import the Bootstrap like so:

@import "node_modules/bootstrap/scss/bootstrap.scss";

Update main in package.json

"main": "styles.css!",

Start watching for changes

node-sass --output-style compressed --watch styles.scss styles.css

Now when you edit styles.scss, it will compile to styles.css, and the changes will autoload in the main app!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment