diff --git a/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js b/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js index 382cee16de8..cae5e0dddbd 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js @@ -1,5 +1,5 @@ import Controller, { inject as controller } from "@ember/controller"; -import { observes } from "discourse-common/utils/decorators"; +import { observes } from "@ember-decorators/object"; import I18n from "I18n"; import { bufferedProperty } from "discourse/mixins/buffered-content"; import { popupAjaxError } from "discourse/lib/ajax-error"; diff --git a/app/assets/javascripts/discourse-common/addon/utils/decorators.js b/app/assets/javascripts/discourse-common/addon/utils/decorators.js index 1320285db11..e4cd53ca62e 100644 --- a/app/assets/javascripts/discourse-common/addon/utils/decorators.js +++ b/app/assets/javascripts/discourse-common/addon/utils/decorators.js @@ -1,4 +1,9 @@ import { on as emberOn } from "@ember/object/evented"; +import { + observes as emberObservesDecorator, + on as emberOnDecorator, +} from "@ember-decorators/object"; + import { observer } from "@ember/object"; import { alias as EmberAlias, @@ -37,6 +42,8 @@ import handleDescriptor from "discourse-common/utils/handle-descriptor"; import isDescriptor from "discourse-common/utils/is-descriptor"; import macroAlias from "discourse-common/utils/macro-alias"; import discourseDebounce from "discourse-common/lib/debounce"; +import CoreObject from "@ember/object/core"; +import deprecated from "discourse-common/lib/deprecated"; export default function discourseComputedDecorator(...params) { // determine if user called as @discourseComputed('blah', 'blah') or @discourseComputed @@ -112,11 +119,39 @@ export function debounce(delay, immediate = false) { }; } -export const on = decoratorAlias(emberOn, "Can not `on` without event names"); -export const observes = decoratorAlias( - observer, - "Can not `observe` without property names" -); +export function on(...onParams) { + return function (target) { + if (target instanceof CoreObject) { + deprecated( + `Using 'on' from 'discourse-common/utils/decorators' as a class property decorator is deprecated. You should import it from '@ember-decorators/object' instead.`, + { id: "discourse.utils-decorators-on", from: "3.1.0.beta2" } + ); + return emberOnDecorator(...onParams)(...arguments); + } else { + return decoratorAlias( + emberOn, + "Can not `on` without event names" + )(...onParams)(...arguments); + } + }; +} + +export function observes(...observeParams) { + return function (target) { + if (target instanceof CoreObject) { + deprecated( + `Using 'observes' from 'discourse-common/utils/decorators' as a class property decorator is deprecated. You should import it from '@ember-decorators/object' instead.`, + { id: "discourse.utils-decorators-observes", from: "3.1.0.beta2" } + ); + return emberObservesDecorator(...observeParams)(...arguments); + } else { + return decoratorAlias( + observer, + "Can not `observe` without property names" + )(...observeParams)(...arguments); + } + }; +} export const alias = macroAlias(EmberAlias); export const and = macroAlias(EmberAnd); diff --git a/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js b/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js index 1c07f7dcf52..4426bd29166 100644 --- a/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js +++ b/app/assets/javascripts/discourse/tests/unit/utils/decorators-test.js @@ -6,10 +6,12 @@ import discourseComputed, { afterRender, debounce, observes, + on, } from "discourse-common/utils/decorators"; import { exists } from "discourse/tests/helpers/qunit-helpers"; import { hbs } from "ember-cli-htmlbars"; import EmberObject from "@ember/object"; +import { withSilencedDeprecations } from "discourse-common/lib/deprecated"; const fooComponent = Component.extend({ classNames: ["foo-component"], @@ -175,4 +177,74 @@ module("Unit | Utils | decorators", function (hooks) { assert.strictEqual(stub.otherCounter, 1); }); + + test("@observes works via .extend and native class syntax", async function (assert) { + let NativeClassWithObserver; + withSilencedDeprecations("discourse.utils-decorators-observes", () => { + NativeClassWithObserver = class extends EmberObject { + counter = 0; + @observes("value") + incrementCounter() { + this.set("counter", this.counter + 1); + } + }; + }); + + const ExtendWithObserver = EmberObject.extend({ + counter: 0, + @observes("value") + incrementCounter() { + this.set("counter", this.counter + 1); + }, + }); + + const nativeClassTest = NativeClassWithObserver.create(); + nativeClassTest.set("value", "one"); + await settled(); + nativeClassTest.set("value", "two"); + await settled(); + assert.strictEqual( + nativeClassTest.counter, + 2, + "observer triggered for native class" + ); + + const extendTest = ExtendWithObserver.create(); + extendTest.set("value", "one"); + await settled(); + extendTest.set("value", "two"); + await settled(); + assert.strictEqual(extendTest.counter, 2, "observer triggered for .extend"); + }); + + test("@on works via .extend and native class syntax", async function (assert) { + let NativeClassWithOn; + withSilencedDeprecations("discourse.utils-decorators-on", () => { + NativeClassWithOn = class extends EmberObject { + counter = 0; + @on("init") + incrementCounter() { + this.set("counter", this.counter + 1); + } + }; + }); + + const ExtendWithOn = EmberObject.extend({ + counter: 0, + @on("init") + incrementCounter() { + this.set("counter", this.counter + 1); + }, + }); + + const nativeClassTest = NativeClassWithOn.create(); + assert.strictEqual( + nativeClassTest.counter, + 1, + "on triggered for native class" + ); + + const extendTest = ExtendWithOn.create(); + assert.strictEqual(extendTest.counter, 1, "on triggered for .extend"); + }); });