user1429980
user1429980

Reputation: 7148

EmberJS: Refactoring Model Observers

I have a model with several observers, and after watching Stefan Penner's talk on removing observers, I'd like to do the same with mine:

import DS from "ember-data";
import Ember from "ember";
import TimeableMixin from "../mixins/timeable";

export default DS.Model.extend(TimeableMixin, {
    employer: DS.belongsTo("company", {async: true}),
    updateDescription: function() {
        let description = "";
        const hasOccupation = !Ember.isBlank(this.get("occupation")),
            hasEmployer = !Ember.isBlank(this.get("employer.name"));
        if (hasOccupation) {
            description += this.get("occupation");
        }
        else {
            if (this.get("type")) {
                description += `${Ember.String.capitalize(this.get("type"))} Job`;
            }
            else {
                description = "Full-time Job"; // we have to hard-code the default, because of async behaviour
            }
        }
        if (hasEmployer) {
            description += ` at ${this.get("employer.name")}`;
        }
        this.get("income").then((resolvedIncome) => {
            resolvedIncome.set("description", description);
        });
    }.observes("employer.name", "occupation", "type"),
    occupation: DS.attr("string"),
    income: DS.belongsTo("income", {async: true}), /* Annual income ($) */
    incomeChanged: function() {
        if (this.get("isHourly")) {
            let hourlyRate = parseInt(this.get("hourlyRate")), weeklyHours = parseInt(this.get("weeklyHours"));
            if (hourlyRate && weeklyHours) {
                let yearlySalary = (hourlyRate * weeklyHours) * 52.1775; // there are 52.1775 weeks in a year
                this.get("income").then((resolvedIncome) => {
                    resolvedIncome.set("value", yearlySalary);
                });
            }
        }
    }.observes("hourlyRate", "weeklyHours"),
    // ...
});

I have considered something akin to "actions down, data up", where the controller or component might handle such a computed property:

export default Ember.Component.extend({
    updatedDescription: Ember.computed("employment.employer.name", "employment.occupation", "employment.type", function() {
        let description = "";
        const hasOccupation = !Ember.isBlank(this.get("occupation")),
            hasEmployer = !Ember.isBlank(this.get("employer.name"));
        if (hasOccupation) {
            description += this.get("occupation");
        }
        else {
            if (this.get("type")) {
                description += `${Ember.String.capitalize(this.get("type"))} Job`;
            }
            else {
                description = "Full-time Job"; // we have to hard-code the default, because of async behaviour
            }
        }
        if (hasEmployer) {
            description += ` at ${this.get("employer.name")}`;
        }
        return description;
    }),
    employment: null,
    // ...
});

But this would prevent setting the description property on my model, which is initially what I set out to do.

How can I refactor my model observers to use computed properties, and remove them?

Upvotes: 0

Views: 134

Answers (1)

NicholasJohn16
NicholasJohn16

Reputation: 2409

Instead of setting the description property, set the description property as computed and update it when necessary:

description: Ember.computed("employer.name", "occupation", "type", function() {
    let description = "";
    const hasOccupation = !Ember.isBlank(this.get("occupation")),
        hasEmployer = !Ember.isBlank(this.get("employer.name"));

    if (hasOccupation) {
        description += this.get("occupation");
    } 
    else {
        if (this.get("type")) {
            description += `${Ember.String.capitalize(this.get("type"))} Job`;
        }    
        else {
            description = "Full-time Job";
        }
    }
    if (hasEmployer) {
        description += ` at ${this.get("employer.name")}`;
    }

    return description;
}

I generally try to avoid async operations within models because it can get pretty messy when you're waiting on a lot of stuff to load.

Upvotes: 0

Related Questions