├── .gitignore ├── logo.png ├── counter.gif ├── logo.afdesign ├── package.json ├── CONTRIBUTING.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianmcli/omg-counters/HEAD/logo.png -------------------------------------------------------------------------------- /counter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianmcli/omg-counters/HEAD/counter.gif -------------------------------------------------------------------------------- /logo.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianmcli/omg-counters/HEAD/logo.afdesign -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "doctoc": "doctoc README.md" 4 | }, 5 | "devDependencies": { 6 | "doctoc": "^1.2.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We are always looking to add more frameworks to our list. The proper way to do this is to fork the repo, make your changes, and then submit a pull request. In your submission, make sure to run [doctoc](https://github.com/thlorenz/doctoc) on the README.md file in order to generate an up-to-date table of contents. 4 | 5 | You can do this by running: 6 | 7 | ``` 8 | npm install 9 | npm run doctoc 10 | ``` 11 | 12 | Each example should also have a live example. It's recommended to make this live example on [WebpackBin](http://www.webpackbin.com/), but if your language/framework requires another sandbox, that should be viable as well. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | OMG Counters 3 |

4 |

5 | ↕️️ Increment/decrement counters using various frontend frameworks. 6 |

7 | 8 |

9 | cool badge 10 | increment decrement 11 | so cool 12 |

13 | 14 | --- 15 | 16 |

17 | 18 |

19 | 20 | ### _When a hello world is too simple, but a todo app is too complex._ 21 | 22 | _No counters were harmed in the making of these examples._ 23 | 24 | ### Attribution 25 | 26 | * AngularJS ([@housseindjirdeh](https://github.com/housseindjirdeh)) 27 | * Angular 2+ ([@ashwin-sureshkumar](https://github.com/ashwin-sureshkumar)) 28 | * MobX ([@teesloane](https://github.com/teesloane)) 29 | * Choo ([@jelanithompson](https://github.com/JelaniThompson)) 30 | * Marko ([@patrick-steele-idem](https://github.com/patrick-steele-idem)) 31 | 32 | ### Table of Contents 33 | 34 | 35 | 36 | 37 | 38 | - [Vanilla JS](#vanilla-js) 39 | - [jQuery](#jquery) 40 | - [RxJS](#rxjs) 41 | - [React](#react) 42 | - [React + Redux](#react--redux) 43 | - [AngularJS](#angularjs) 44 | - [Angular 2+](#angular-2) 45 | - [Hyperapp](#hyperapp) 46 | - [Vue.js](#vuejs) 47 | - [Elm](#elm) 48 | - [Cycle.js](#cyclejs) 49 | - [Jumpsuit](#jumpsuit) 50 | - [Mobx](#mobx) 51 | - [Choo](#choo) 52 | - [Marko](#marko) 53 | 54 | 55 | 56 | _Don't see your favorite framework? Make a PR! (see [contributing guidelines](CONTRIBUTING.md))_ 57 | 58 | # Vanilla JS 59 | 60 | ```html 61 | 62 |

63 | 64 | 65 | ``` 66 | 67 | ```js 68 | // javascript 69 | let count = 0 70 | const $count = document.getElementsByClassName('count')[0] 71 | $count.textContent = count 72 | function increment() { $count.textContent = ++count } 73 | function decrement() { $count.textContent = --count } 74 | ``` 75 | 76 | [Live Example on WebpackBin](http://www.webpackbin.com/NyXDu7ytz) 77 | 78 | # jQuery 79 | 80 | ```html 81 | 82 |

83 | 84 | 85 | ``` 86 | 87 | ```js 88 | // javascript 89 | let count = 0 90 | $('.count').text(count) 91 | $('.increment').on('click', () => $('.count').text(++count)) 92 | $('.decrement').on('click', () => $('.count').text(--count)) 93 | ``` 94 | 95 | [Live Example on WebpackBin](http://www.webpackbin.com/VyZb9Z1KG) 96 | 97 | # RxJS 98 | 99 | ```html 100 | 101 | //

102 | // 103 | // 104 | ``` 105 | 106 | ```js 107 | // javascript 108 | const $factory = id => Rx.Observable.fromEvent(document.getElementById(id), 'click') 109 | const setCount = count => document.getElementById('count').textContent = count 110 | 111 | const inc$ = $factory('increment').mapTo(1) 112 | const dec$ = $factory('decrement').mapTo(-1) 113 | 114 | Rx.Observable.merge(inc$, dec$) 115 | .startWith(0) 116 | .scan((a, b) => a + b) 117 | .subscribe(setCount) 118 | ``` 119 | 120 | [Live Example on WebpackBin](http://www.webpackbin.com/NkLKZwkFM) 121 | 122 | # React 123 | 124 | ```jsx 125 | class Counter extends React.Component { 126 | state = {count: 0} 127 | 128 | increment = e => 129 | this.setState({ count: this.state.count + 1 }) 130 | 131 | decrement = e => 132 | this.setState({ count: this.state.count - 1 }) 133 | 134 | render = () => 135 |
136 |

{this.state.count}

137 | 138 | 139 |
140 | } 141 | ``` 142 | 143 | [Live Example on WebpackBin](http://www.webpackbin.com/4kvMWMkKG) 144 | 145 | # React + Redux 146 | 147 | ```jsx 148 | import { createStore } from 'redux' 149 | 150 | const reducer = (state = 0, action) => { 151 | switch (action.type) { 152 | case 'INCREMENT': return state + 1 153 | case 'DECREMENT': return state - 1 154 | default: return state 155 | } 156 | } 157 | 158 | const store = createStore(reducer) 159 | 160 | var Counter = ({ count, onIncrement, onDecrement }) => ( 161 |
162 |

{count}

163 | 164 | 165 |
166 | ) 167 | 168 | const render = () => { 169 | ReactDOM.render( 170 | store.dispatch({type: 'INCREMENT'})} 173 | onDecrement={()=> store.dispatch({type: 'DECREMENT'})} 174 | />, 175 | document.querySelector('body') 176 | ) 177 | } 178 | 179 | store.subscribe(render) 180 | render() 181 | ``` 182 | 183 | [Live Example on WebpackBin](http://www.webpackbin.com/N175AO1tz) 184 | 185 | # AngularJS 186 | 187 | ```js 188 | const CounterComponent = { 189 | template: ` 190 |
191 |

{{ $ctrl.counter }}

192 | 193 | 194 |
195 | `, 196 | controller: class CounterController { 197 | constructor() { 198 | this.counter = 0 199 | } 200 | 201 | increaseCounter() { 202 | this.counter++ 203 | } 204 | 205 | decreaseCounter() { 206 | this.counter-- 207 | } 208 | } 209 | } 210 | 211 | export default CounterComponent 212 | ``` 213 | 214 | [Live Example on WebpackBin](http://www.webpackbin.com/4JwFUVetz) 215 | 216 | # Angular 2+ 217 | 218 | ```js 219 | import { Component } from '@angular/core' 220 | 221 | @Component({ 222 | selector: 'counter', 223 | template : ` 224 |
225 |

{{counter}}

226 | 227 | 228 |
229 | ` 230 | }) 231 | export class CounterComponent { 232 | counter: number = 0 233 | 234 | onIncrement() { 235 | this.counter++ 236 | } 237 | 238 | onDecrement() { 239 | this.counter-- 240 | } 241 | } 242 | ``` 243 | 244 | [Live Example on WebpackBin](http://www.webpackbin.com/4kFun-lFM) 245 | 246 | # Hyperapp 247 | 248 | ```jsx 249 | app({ 250 | model: 0, 251 | update: { 252 | add: model => model + 1, 253 | sub: model => model - 1 254 | }, 255 | view: (model, actions) => 256 |
257 |

{model}

258 | 259 | 260 |
261 | }) 262 | ``` 263 | 264 | [Live Example on WebpackBin](http://www.webpackbin.com/VJ-dzMJYz) 265 | 266 | # Vue.js 267 | 268 | ```html 269 | 270 |
271 |

{{ count }}

272 | 273 | 274 |
275 | ``` 276 | 277 | ```js 278 | // javascript 279 | new Vue({ 280 | el: '#app', 281 | data: { count: 0 }, 282 | methods: { 283 | increment: function() { this.count++ }, 284 | decrement: function() { this.count-- } 285 | } 286 | }) 287 | ``` 288 | 289 | [Live Example on WebpackBin](http://www.webpackbin.com/VyxxXfJYM) 290 | 291 | # Elm 292 | 293 | ```elm 294 | import Html exposing (beginnerProgram, div, h1, button, text) 295 | import Html.Events exposing (onClick) 296 | 297 | 298 | main = 299 | beginnerProgram { model = 0, view = view, update = update } 300 | 301 | 302 | view model = 303 | div [] 304 | [ h1 [] [ text (toString model) ] 305 | , button [ onClick Increment ] [ text "increment" ] 306 | , button [ onClick Decrement ] [ text "decrement" ] 307 | ] 308 | 309 | 310 | type Msg = Increment | Decrement 311 | 312 | 313 | update msg model = 314 | case msg of 315 | Increment -> 316 | model + 1 317 | 318 | Decrement -> 319 | model - 1 320 | ``` 321 | 322 | [Live Example](http://elm-lang.org/examples/buttons) 323 | 324 | # Cycle.js 325 | 326 | ```js 327 | const xs = xstream.default 328 | const {div, button, h1, makeDOMDriver} = CycleDOM 329 | 330 | function main(sources) { 331 | const action$ = xs.merge( 332 | sources.DOM.select('.dec').events('click').mapTo(-1), 333 | sources.DOM.select('.inc').events('click').mapTo(+1) 334 | ) 335 | const count$ = action$.fold((x, y) => x + y, 0) 336 | const vdom$ = count$.map(count => 337 | div([ 338 | h1(count.toString()), 339 | button('.dec', 'Decrement'), 340 | button('.inc', 'Increment') 341 | ]) 342 | ) 343 | return { DOM: vdom$ } 344 | } 345 | ``` 346 | 347 | [Live Example on JSBin](https://jsbin.com/huxoduh/1/edit?html,js,output) 348 | 349 | # Jumpsuit 350 | 351 | ```js 352 | const CounterState = State({ 353 | initial: { count: 0 }, 354 | increment: ({ count }) => ({ count: count + 1 }), 355 | decrement: ({ count }) => ({ count: count - 1 }) 356 | }) 357 | 358 | const Counter = Component({ 359 | render() { 360 | return ( 361 |
362 |

{ this.props.count }

363 | 364 | 365 |
366 | ) 367 | } 368 | }, (state) => ({ count: state.counter.count })) 369 | 370 | const globalState = { counter: CounterState } 371 | Render(globalState, ) 372 | ``` 373 | 374 | [Live Example on WebpackBin](http://www.webpackbin.com/4JkiMmkKM) 375 | 376 | # Mobx 377 | 378 | ```jsx 379 | const store = new class CounterStore { 380 | @observable count = 0 381 | @action increment = () => this.count++ 382 | @action decrement = () => this.count-- 383 | } 384 | 385 | const Counter = observer(() => { 386 | return ( 387 |
388 |

{store.count}

389 | 390 | 391 |
392 | ) 393 | }) 394 | ``` 395 | 396 | [Live Example on JSBin](http://jsbin.com/zedoco/2/edit?js,output) 397 | 398 | # Choo 399 | ```js 400 | app.model({ 401 | state: { count: 0 }, 402 | reducers: { 403 | increment: (state) => ({ count: state.count + 1 }), 404 | decrement: (state) => ({ count: state.count - 1 }) 405 | } 406 | }) 407 | 408 | const view = (state, previousState, send) => { 409 | return html`
410 |

${state.count}

411 | 412 |
` 413 | 414 | function increment() { send('increment') } 415 | function decrement() { send('decrement') } 416 | } 417 | 418 | app.router([['/', view]]) 419 | document.body.appendChild(app.start()) 420 | ``` 421 | [View on WebpackBin](http://www.webpackbin.com/Nk8CLwg5M) 422 | 423 | # Marko 424 | 425 | ```marko 426 | class { 427 | onCreate() { 428 | this.state = {count: 0}; 429 | } 430 | 431 | increment(delta) { 432 | this.state.count += delta; 433 | } 434 | } 435 | 436 |
437 |

${state.count}

438 | 439 | 440 |
441 | ``` 442 | [Try Online at markojs.com](http://markojs.com/try-online/?gist=8aa2a490d9426648d9fee81b7964606f) --------------------------------------------------------------------------------