DEV: Add a widget API for injecting services

DEV: Add a widget API for injecting services

When declaring your widget you can now add an option like: services: ['cool']

And your widget instances will automatically get a this.cool property which will resolve to the service. This saves having to look it up yourself.

diff --git a/app/assets/javascripts/discourse/app/widgets/header.js b/app/assets/javascripts/discourse/app/widgets/header.js
index 0c292de..bca43e1 100644
--- a/app/assets/javascripts/discourse/app/widgets/header.js
+++ b/app/assets/javascripts/discourse/app/widgets/header.js
@@ -335,6 +335,7 @@ export function attachAdditionalPanel(name, toggle, transformAttrs) {
 export default createWidget("header", {
   tagName: "header.d-header.clearfix",
   buildKey: () => `header`,
+  services: ["router"],
 
   defaultState() {
     let states = {
@@ -465,9 +466,7 @@ export default createWidget("header", {
         params = `?context=${context.type}&context_id=${context.id}&skip_context=${this.state.skipSearchContext}`;
       }
 
-      const currentPath = this.register
-        .lookup("service:router")
-        .get("_router.currentPath");
+      const currentPath = this.router.get("_router.currentPath");
 
       if (currentPath === "full-page-search") {
         scrollTop();
@@ -545,9 +544,7 @@ export default createWidget("header", {
 
     state.contextEnabled = false;
 
-    const currentPath = this.register
-      .lookup("service:router")
-      .get("_router.currentPath");
+    const currentPath = this.router.get("_router.currentPath");
     const blocklist = [/^discovery\.categories/];
     const allowlist = [/^topic\./];
     const check = function (regex) {
diff --git a/app/assets/javascripts/discourse/app/widgets/widget.js b/app/assets/javascripts/discourse/app/widgets/widget.js
index 27faa31..158f213 100644
--- a/app/assets/javascripts/discourse/app/widgets/widget.js
+++ b/app/assets/javascripts/discourse/app/widgets/widget.js
@@ -148,6 +148,11 @@ export default class Widget {
     this.appEvents = register.lookup("service:app-events");
     this.keyValueStore = register.lookup("key-value-store:main");
 
+    // We can inject services into widgets by passing a `services` parameter on creation
+    (this.services || []).forEach((s) => {
+      this[s] = register.lookup(`service:${s}`);
+    });
+
     this.init(this.attrs);
 
     // Helps debug widgets
diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js
index 6d18467..d8f7198 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js
@@ -37,6 +37,24 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
   });
 
+  componentTest("widget services", {
+    template: hbs`{{mount-widget widget="service-test"}}`,
+
+    beforeEach() {
+      createWidget("service-test", {
+        tagName: "div.base-url-test",
+        services: ["router"],
+        html() {
+          return this.router.rootURL;
+        },
+      });
+    },
+
+    test(assert) {
+      assert.equal(queryAll(".base-url-test").text(), "/");
+    },
+  });
+
   componentTest("hbs template - no tagName", {
     template: hbs`{{mount-widget widget="hbs-test" args=args}}`,
 

GitHub sha: 17f28d4018f8534c5fdcc994566d697c50ac701f

This commit appears in #13946 which was approved by markvanlan. It was merged by eviltrout.