{"id":13856530,"url":"https://github.com/adrianmcli/omg-counters","last_synced_at":"2025-09-15T04:31:05.186Z","repository":{"id":73736830,"uuid":"82223243","full_name":"adrianmcli/omg-counters","owner":"adrianmcli","description":"😍 Increment decrement counters using various frontend frameworks.","archived":false,"fork":false,"pushed_at":"2017-03-28T22:39:23.000Z","size":279,"stargazers_count":48,"open_issues_count":1,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-30T00:44:08.997Z","etag":null,"topics":["counter","examples","frameworks","frontend","hello-world","javascript","reference"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adrianmcli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-02-16T20:30:21.000Z","updated_at":"2024-02-22T11:31:55.000Z","dependencies_parsed_at":"2023-04-01T10:33:17.568Z","dependency_job_id":null,"html_url":"https://github.com/adrianmcli/omg-counters","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fomg-counters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fomg-counters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fomg-counters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fomg-counters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrianmcli","download_url":"https://codeload.github.com/adrianmcli/omg-counters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233077318,"owners_count":18621511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["counter","examples","frameworks","frontend","hello-world","javascript","reference"],"created_at":"2024-08-05T03:01:03.616Z","updated_at":"2025-01-08T18:21:43.414Z","avatar_url":"https://github.com/adrianmcli.png","language":null,"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"OMG Counters\" src=\"logo.png\" width=\"440\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  ↕️️ Increment/decrement counters using various frontend frameworks.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"cool badge\" src=\"https://img.shields.io/badge/omg-counters-ff69b4.svg\"\u003e\n  \u003cimg alt=\"increment decrement\" src=\"https://img.shields.io/badge/%E2%98%9D%EF%B8%8F%EF%B8%8F%20increment-%F0%9F%91%87%20decrement-blue.svg\"\u003e\n  \u003cimg alt=\"so cool\" src=\"https://img.shields.io/badge/so-cool-brightgreen.svg\"\u003e\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src = \"counter.gif\"\u003e\n\u003c/p\u003e\n\n### _When a hello world is too simple, but a todo app is too complex._\n\n_No counters were harmed in the making of these examples._\n\n### Attribution\n\n* AngularJS ([@housseindjirdeh](https://github.com/housseindjirdeh))\n* Angular 2+ ([@ashwin-sureshkumar](https://github.com/ashwin-sureshkumar))\n* MobX ([@teesloane](https://github.com/teesloane))\n* Choo ([@jelanithompson](https://github.com/JelaniThompson))\n* Marko ([@patrick-steele-idem](https://github.com/patrick-steele-idem))\n\n### Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Vanilla JS](#vanilla-js)\n- [jQuery](#jquery)\n- [RxJS](#rxjs)\n- [React](#react)\n- [React + Redux](#react--redux)\n- [AngularJS](#angularjs)\n- [Angular 2+](#angular-2)\n- [Hyperapp](#hyperapp)\n- [Vue.js](#vuejs)\n- [Elm](#elm)\n- [Cycle.js](#cyclejs)\n- [Jumpsuit](#jumpsuit)\n- [Mobx](#mobx)\n- [Choo](#choo)\n- [Marko](#marko)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n_Don't see your favorite framework? Make a PR! (see [contributing guidelines](CONTRIBUTING.md))_\n\n# Vanilla JS\n\n```html\n\u003c!-- html --\u003e\n\u003ch1 class=\"count\"\u003e\u003c/h1\u003e\n\u003cbutton class=\"increment\" onclick=\"increment()\"\u003eIncrement\u003c/button\u003e\n\u003cbutton class=\"decrement\" onclick=\"decrement()\"\u003eDecrement\u003c/button\u003e\n```\n\n```js\n// javascript\nlet count = 0\nconst $count = document.getElementsByClassName('count')[0]\n$count.textContent = count\nfunction increment() { $count.textContent = ++count }\nfunction decrement() { $count.textContent = --count }\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/NyXDu7ytz)\n\n# jQuery\n\n```html\n\u003c!-- html --\u003e\n\u003ch1 class=\"count\"\u003e\u003c/h1\u003e\n\u003cbutton class=\"increment\"\u003eIncrement\u003c/button\u003e\n\u003cbutton class=\"decrement\"\u003eDecrement\u003c/button\u003e\n```\n\n```js\n// javascript\nlet count = 0\n$('.count').text(count)\n$('.increment').on('click', () =\u003e $('.count').text(++count))\n$('.decrement').on('click', () =\u003e $('.count').text(--count))\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/VyZb9Z1KG)\n\n# RxJS\n\n```html\n\u003c!-- html --\u003e\n// \u003ch1 id=\"count\"\u003e\u003c/h1\u003e\n// \u003cbutton id=\"increment\"\u003eIncrement\u003c/button\u003e\n// \u003cbutton id=\"decrement\"\u003eDecrement\u003c/button\u003e\n```\n\n```js\n// javascript\nconst $factory = id =\u003e Rx.Observable.fromEvent(document.getElementById(id), 'click')\nconst setCount = count =\u003e document.getElementById('count').textContent = count\n\nconst inc$ = $factory('increment').mapTo(1)\nconst dec$ = $factory('decrement').mapTo(-1)\n\nRx.Observable.merge(inc$, dec$)\n  .startWith(0)\n  .scan((a, b) =\u003e a + b)\n  .subscribe(setCount)\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/NkLKZwkFM)\n\n# React\n\n```jsx\nclass Counter extends React.Component {\n  state = {count: 0}\n\n  increment = e =\u003e\n    this.setState({ count: this.state.count + 1 })\n\n  decrement = e =\u003e\n    this.setState({ count: this.state.count - 1 })\n\n  render = () =\u003e\n    \u003cdiv\u003e\n      \u003ch1\u003e{this.state.count}\u003c/h1\u003e\n      \u003cbutton onClick={this.increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={this.decrement}\u003eDecrement\u003c/button\u003e\n    \u003c/div\u003e\n}\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/4kvMWMkKG)\n\n# React + Redux\n\n```jsx\nimport { createStore } from 'redux'\n\nconst reducer = (state = 0, action) =\u003e {\n  switch (action.type) {\n    case 'INCREMENT': return state + 1\n    case 'DECREMENT': return state - 1\n    default: return state\n  }\n}\n\nconst store = createStore(reducer)\n\nvar Counter = ({ count, onIncrement, onDecrement }) =\u003e (\n  \u003cdiv\u003e\n    \u003ch1\u003e{count}\u003c/h1\u003e\n    \u003cbutton onClick={onIncrement}\u003eIncrement\u003c/button\u003e\n    \u003cbutton onClick={onDecrement}\u003eDecrement\u003c/button\u003e\n  \u003c/div\u003e\n)\n\nconst render = () =\u003e {\n  ReactDOM.render(\n    \u003cCounter\n      count={store.getState()}\n      onIncrement={()=\u003e store.dispatch({type: 'INCREMENT'})}\n      onDecrement={()=\u003e store.dispatch({type: 'DECREMENT'})}\n    /\u003e,\n    document.querySelector('body')\n  )\n}\n\nstore.subscribe(render)\nrender()\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/N175AO1tz)\n\n# AngularJS\n\n```js\nconst CounterComponent = {\n  template: `\n   \u003cdiv\u003e\n     \u003ch1\u003e{{ $ctrl.counter }}\u003c/h1\u003e\n     \u003cbutton ng-click=\"$ctrl.increaseCounter()\"\u003eIncrement\u003c/button\u003e\n     \u003cbutton ng-click=\"$ctrl.decreaseCounter()\"\u003eDecrement\u003c/button\u003e\n   \u003c/div\u003e\n  `,\n  controller: class CounterController {\n    constructor() {\n      this.counter = 0\n    }\n\n    increaseCounter() {\n      this.counter++\n    }\n\n    decreaseCounter() {\n      this.counter--\n    }\n  }\n}\n\nexport default CounterComponent\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/4JwFUVetz)\n\n# Angular 2+\n\n```js\nimport { Component } from '@angular/core'\n\n@Component({\n selector: 'counter',\n template : `\n   \u003cdiv\u003e\n     \u003ch1\u003e{{counter}}\u003c/h1\u003e\n     \u003cbutton (click)=\"onIncrement()\"\u003eIncrement\u003c/button\u003e\n     \u003cbutton (click)=\"onDecrement()\"\u003eDecrement\u003c/button\u003e\n   \u003c/div\u003e\n `\n})\nexport class CounterComponent {\n counter: number = 0\n\n onIncrement() {\n   this.counter++\n }\n\n onDecrement() {\n   this.counter--\n }\n}\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/4kFun-lFM)\n\n# Hyperapp\n\n```jsx\napp({\n  model: 0,\n  update: {\n    add: model =\u003e model + 1,\n    sub: model =\u003e model - 1\n  },\n  view: (model, actions) =\u003e\n    \u003cdiv\u003e\n      \u003ch1\u003e{model}\u003c/h1\u003e\n      \u003cbutton onclick={actions.add}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onclick={actions.sub}\u003eDecrement\u003c/button\u003e\n    \u003c/div\u003e\n})\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/VJ-dzMJYz)\n\n# Vue.js\n\n```html\n\u003c!-- html --\u003e\n\u003cdiv id=\"app\"\u003e\n  \u003ch1\u003e{{ count }}\u003c/h1\u003e\n  \u003cbutton v-on:click=\"increment\"\u003eIncrement\u003c/button\u003e\n  \u003cbutton v-on:click=\"decrement\"\u003eDecrement\u003c/button\u003e\n\u003c/div\u003e\n```\n\n```js\n// javascript\nnew Vue({\n  el: '#app',\n  data: { count: 0 },\n  methods: {\n    increment: function() { this.count++ },\n    decrement: function() { this.count-- }\n  }\n})\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/VyxxXfJYM)\n\n# Elm\n\n```elm\nimport Html exposing (beginnerProgram, div, h1, button, text)\nimport Html.Events exposing (onClick)\n\n\nmain =\n  beginnerProgram { model = 0, view = view, update = update }\n\n\nview model =\n  div []\n    [ h1 [] [ text (toString model) ]\n    , button [ onClick Increment ] [ text \"increment\" ]\n    , button [ onClick Decrement ] [ text \"decrement\" ]\n    ]\n\n\ntype Msg = Increment | Decrement\n\n\nupdate msg model =\n  case msg of\n    Increment -\u003e\n      model + 1\n\n    Decrement -\u003e\n      model - 1\n```\n\n[Live Example](http://elm-lang.org/examples/buttons)\n\n# Cycle.js\n\n```js\nconst xs = xstream.default\nconst {div, button, h1, makeDOMDriver} = CycleDOM\n\nfunction main(sources) {\n  const action$ = xs.merge(\n    sources.DOM.select('.dec').events('click').mapTo(-1),\n    sources.DOM.select('.inc').events('click').mapTo(+1)\n  )\n  const count$ = action$.fold((x, y) =\u003e x + y, 0)\n  const vdom$ = count$.map(count =\u003e\n    div([\n      h1(count.toString()),\n      button('.dec', 'Decrement'),\n      button('.inc', 'Increment')\n    ])\n  )\n  return { DOM: vdom$ }\n}\n```\n\n[Live Example on JSBin](https://jsbin.com/huxoduh/1/edit?html,js,output)\n\n# Jumpsuit\n\n```js\nconst CounterState = State({\n  initial: { count: 0 },\n  increment: ({ count }) =\u003e ({ count: count + 1 }),\n  decrement: ({ count }) =\u003e ({ count: count - 1 })\n})\n\nconst Counter = Component({\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003e{ this.props.count }\u003c/h1\u003e\n        \u003cbutton onClick={ Actions.increment }\u003eIncrement\u003c/button\u003e\n        \u003cbutton onClick={ Actions.decrement }\u003eDecrement\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n}, (state) =\u003e ({ count: state.counter.count }))\n\nconst globalState = { counter: CounterState }\nRender(globalState, \u003cCounter/\u003e)\n```\n\n[Live Example on WebpackBin](http://www.webpackbin.com/4JkiMmkKM)\n\n# Mobx\n\n```jsx\nconst store = new class CounterStore {\n  @observable count = 0\n  @action increment = () =\u003e this.count++\n  @action decrement = () =\u003e this.count--\n}\n\nconst Counter = observer(() =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003e{store.count}\u003c/h1\u003e\n      \u003cbutton onClick={store.increment}\u003eIncrement\u003c/button\u003e\n      \u003cbutton onClick={store.decrement}\u003eDecrement\u003c/button\u003e\n    \u003c/div\u003e\n  )\n})\n```\n\n[Live Example on JSBin](http://jsbin.com/zedoco/2/edit?js,output)\n\n# Choo\n```js\napp.model({\n    state: { count: 0 },\n    reducers: {\n      increment: (state) =\u003e ({ count: state.count + 1 }),\n      decrement: (state) =\u003e ({ count: state.count - 1 })\n    }\n})\n\nconst view = (state, previousState, send) =\u003e {\n  return html`\u003cdiv\u003e\n         \u003ch1\u003e${state.count}\u003c/h1\u003e\n         \u003cbutton onclick=${increment}\u003eIncrement\u003c/button\u003e\n         \u003cbutton onclick=${decrement}\u003eDecrement\u003c/button\u003e\u003c/div\u003e`\n\n  function increment() { send('increment') }\n  function decrement() { send('decrement') }\n}\n\napp.router([['/', view]])\ndocument.body.appendChild(app.start())\n```\n[View on WebpackBin](http://www.webpackbin.com/Nk8CLwg5M)\n\n# Marko\n\n```marko\nclass {\n  onCreate() {\n    this.state = {count: 0};\n  }\n\n  increment(delta) {\n    this.state.count += delta;\n  }\n}\n\n\u003cdiv\u003e\n  \u003ch1\u003e${state.count}\u003c/h1\u003e\n  \u003cbutton on-click('increment', 1)\u003eIncrement\u003c/button\u003e\n  \u003cbutton on-click('increment', -1)\u003eDecrement\u003c/button\u003e\n\u003c/div\u003e\n```\n[Try Online at markojs.com](http://markojs.com/try-online/?gist=8aa2a490d9426648d9fee81b7964606f)","funding_links":[],"categories":["Others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrianmcli%2Fomg-counters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrianmcli%2Fomg-counters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrianmcli%2Fomg-counters/lists"}