Ready to get started?

Join Adocasts Plus for $8.00/mo, or sign into your account to get access to all of our lessons.

robot mascot smiling

A Deep Dive Into Mixins & Compose

In this lesson, we'll walk through what mixins are and how they work by first inspecting them as plain objects then working our way into classes then finally TypeScript. We'll then implement mixins via AdonisJS' compose utility.

Published
Dec 17, 23
Duration
11m 21s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

Ready to get started?

Join Adocasts Plus for $8.00/mo, or sign into your account to get access to all of our lessons.

Join The Discussion! (6 Comments)

Please sign in or sign up for free to join in on the dicussion.

  1. Commented 5 months ago

    Thank you for the video. I have few questions : Can we use decorators inside the mixin ? For exemple if we want to define a relationship between the model and itself (a recursive relationship)

    1

    Please sign in or sign up for free to reply

    1. Commented 5 months ago

      Hey Arthur! I just gave it a go and everything seems to be working a-okay with using decorators in a mixin! Only caveat is, it looks like you have to give the class a name and return it via that name on a separate line for TypeScript to be happy. Below is what I tried, note the imports are for AdonisJS 6, but the functionality would be the same between 5 & 6.

      
      
      import { BaseModel, belongsTo, column } from "@adonisjs/lucid/orm"
      import { NormalizeConstructor } from '@adonisjs/core/types/helpers'
      import Organization from "#models/organization"
      import type { BelongsTo } from "@adonisjs/lucid/types/relations"
      
      export const WithOrganization = <T extends NormalizeConstructor<typeof BaseModel>>(superclass: T) => {
        class parentclass extends superclass {
          @column()
          declare organizationId: number
      
          @belongsTo(() => Organization)
          declare organization: BelongsTo<typeof Organization>
        }
      
        return parentclass
      }
      Copied!
      • app
      • models
      • mixins
      • organization.ts
      
      
      import { DateTime } from 'luxon'
      import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
      import type { HasMany } from '@adonisjs/lucid/types/relations'
      import Course from '#models/course'
      import { compose } from "@adonisjs/core/helpers"
      import { WithOrganization } from "#models/mixins/organization"
      
      export default class Difficulty extends compose(BaseModel, WithOrganization) {
        @column({ isPrimary: true })
        declare id: number
      
        @column()
        declare name: string
      }
      
      Copied!
      • app
      • models
      • difficulty.ts

      As a heads up, you can define a parent/child relationship directly on the model without using compose. It'll look like the below.

      export default class Taxonomy extends BaseModel {
        // other columns
      
        @column()
        declare parentId: number | null // parent taxonomy's id (null for top-level)
      
        @belongsTo(() => Taxonomy, {
          foreignKey: 'parentId', // use parentId to define parent relationship
        })
        declare parent: BelongsTo<typeof Taxonomy>
      }
      Copied!

      Hope this helps!
      —-
      Edit: Updated to account for the TypeScript requirement of the mixin class needing a name and for the return to be on a separate line. I missed the small red squiggly on the decorator when I first attempted this.

      0

      Please sign in or sign up for free to reply

  2. Commented 5 months ago

    Hello, what about this in adonisJS v6 , there is no such a Normalizing class or maybe i cannot find it in documentation ? Thanks for answear :)

    1

    Please sign in or sign up for free to reply

    1. Commented 5 months ago

      Hi Danilesky17! Hmm, yeah it seems to have slipped from the documentation. Luckily though, it is still within AdonisJS, it's just now under the core helpers. Below is the example from this lesson updated for AdonisJS 6!

      import { BaseModel } from '@adonisjs/lucid/orm'
      import { NormalizeConstructor } from '@adonisjs/core/types/helpers'
      
      export const WithExtras = <T extends NormalizeConstructor<typeof BaseModel>>(superclass: T) => {
        return class extends superclass {
          serializeExtras: boolean = true
      
          get meta() {
            return this.$extras
          }
        }
      }
      Copied!
      import { compose } from '@adonisjs/core/helpers'
      import { WithExtras } from './mixins/serialize.js'
      import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
      
      export default class Topic extends compose(BaseModel, WithExtras) {
        @column({ isPrimary: true })
        declare id: number
      
        @column()
        declare name: string
      }
      Copied!

      Hope this helps!

      1

      Please sign in or sign up for free to reply

      1. Commented 5 months ago

        Tom, thank you for quick response! It helped me a lot. I couldn't find this constructor before and VS code didn't offer me when i was trying to find it. Now it seems to be working correctly. Thanks.

        1

        Please sign in or sign up for free to reply

        1. Commented 5 months ago

          Anytime!! Yeah, I tried the same thing and had the same result. It's a hidden little bugger lol 😊

          0

          Please sign in or sign up for free to reply