bhylu.com

Advertisements

AngularJS Services and Factories

A service or factory provides shared behavior through an injector. A factory returns its value; a service registration constructs an instance. Both need a deliberate lifetime and public contract.

How it works

The desk counter is registered as a factory with one private count and two operations. The controller reads and changes it through those methods instead of exposing a mutable field to every consumer. The instance belongs to its injector, not to an individual template row. That makes it useful for sharing, but also means an overly broad service can become accidental global state.

Run the code

AngularJS 1.8.3 is a legacy framework whose official support ended in January 2022. These examples teach maintenance, not a recommendation for a new application. They need a separate local browser page; Bhylu has no AngularJS playground.

In an empty folder save the HTML below as services.html and the server as lab-server.mjs. Make a vendor folder and save the four pinned libraries there, retaining their MIT notices. With Node 22 or later run node lab-server.mjs services.html, then open http://127.0.0.1:8136/. Stop that lab before running a different lesson. The server supplies fixed read-only HTTP fixtures; no data is saved and it does not connect to SQL. No PHP, Apache or MySQL configuration changes are needed.

Example

services.html

<!doctype html>
<html lang="en" ng-app="repairLab" ng-strict-di>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>AngularJS Services and Factories</title>
    <style>
      :root {
        font:
          18px/1.55 system-ui,
          sans-serif;
        color: #0f3464;
        background: #fff;
        color-scheme: light;
      }
      * {
        box-sizing: border-box;
      }
      body {
        margin: 0;
      }
      main {
        max-width: 50rem;
        margin: auto;
        padding: 1.2rem;
      }
      h1 {
        font-size: 1.65rem;
      }
      label {
        display: block;
        margin: 0.6rem 0;
      }
      input,
      select,
      textarea,
      button {
        font: inherit;
        max-width: 100%;
        padding: 0.35rem;
        margin: 0.2rem;
      }
      button {
        border: 1px solid #034fa0;
        border-radius: 0.25rem;
        background: #034fa0;
        color: white;
      }
      button:disabled {
        opacity: 0.55;
      }
      output,
      [role="status"] {
        display: block;
        min-height: 1.5rem;
      }
      table {
        border-collapse: collapse;
        width: 100%;
      }
      td,
      th {
        padding: 0.4rem;
        border: 1px solid #a5bbd4;
        text-align: left;
      }
      pre {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
      }
      :focus-visible {
        outline: 3px solid #0078d4;
        outline-offset: 3px;
      }
      [ng-cloak] {
        display: none !important;
      }
      .marked {
        font-weight: 700;
      }
      .error {
        color: #a02424;
      }
      :root[data-theme="dark"] {
        color: #d6e7fb;
        background: #061d30;
        color-scheme: dark;
      }
      :root[data-theme="dark"] .error {
        color: #ffb4b4;
      }
    </style>
  </head>
  <body>
    <main ng-controller="DeskController as vm" ng-cloak>
      <h1>Community repair desk</h1>
      <p>AngularJS 1.8.3 · Legacy maintenance lab · Local demonstration</p>
      <button type="button" ng-click="vm.record()">Record inspection</button>
      <p data-result>{{ vm.count() }} inspections</p>
    </main>
    <script src="/vendor/angular.min.js"></script>
    <script>
      (function () {
        "use strict";
        var app = angular.module("repairLab", []);
        app.factory("inspectionCount", [
          function () {
            var count = 0;
            return {
              read: function () {
                return count;
              },
              record: function () {
                count += 1;
              },
            };
          },
        ]);
        app.controller("DeskController", [
          "inspectionCount",
          function (counter) {
            this.count = counter.read;
            this.record = counter.record;
          },
        ]);
      })();
    </script>
  </body>
</html>

Focus on the bindings and controller

This excerpt leaves out the document shell, styles and library files. Use the complete download to run it.


<button type="button" ng-click="vm.record()">Record inspection</button>
<p data-result>{{ vm.count() }} inspections</p>
<script>
  (function () {
    "use strict";
    var app = angular.module("repairLab", []);
    app.factory("inspectionCount", [
      function () {
        var count = 0;
        return {
          read: function () {
            return count;
          },
          record: function () {
            count += 1;
          },
        };
      },
    ]);
    app.controller("DeskController", [
      "inspectionCount",
      function (counter) {
        this.count = counter.read;
        this.record = counter.record;
      },
    ]);
  })();
</script>

Expected result

0 inspections

The click changes the count through the shared service operation. A reload starts a fresh local application and loses the count. This is not persistence, and a browser singleton cannot coordinate several users editing real stock or repair capacity.

Advertisements