Category: Uncategorized

How to Create a Many-to-Many Relationship in Realm JS React Native

How to Create a Many-to-Many Relationship in Realm JS React Native

While building my app ThinkBack I decided to use Realm as my underlying data-store. ThinkBack allows users capture notes and categorize them using hashtags. A note can have many hashtags and a hashtag can have many notes. This is the basis of a many-to-many relationship.

Realm’s documentation on model relationships shows an example using a car and manufacturer model. In their example a car has a relationship with a manufacturer, and a manufacturer has a one-to-many relationship with a car. This example creates a “to-many” relationship from manufacturer to car simply by adding a property to the manufacturer model that references a collection of cars.

By default a relationship is one-sided, for example a manufacturer has a link to cars, but cars don’t link back to the manufacturer. In order for a car to know its manufacturer Realm leverages the concept of an inverse relationship. An inverse relationship is simply a link from one object back to another, and is defined by setting the “type” of the linking property as a linkingObjects. This relationship must be explicitly defined otherwise a car will be ignorant to the fact it belongs to a manufacturer. The downside to this is that if you want to load the manufacturer associated with a car you need to issue a separate query in Realm rather than simply dot-notationining into it (e.g. myCar.manufacturer.name)

Below is the example from the documentation. Line 11 defines the “to-many” relationship from manufacturer to car, and line 27-31 define the inverse relationship from car back to manufacturer.

class ManufacturerInverse extends Realm.Object {
  _id!: BSON.ObjectId;
  name!: string;
  cars!: Realm.List<CarInverse>;
  static schema: Realm.ObjectSchema = {
    name: 'ManufacturerInverse',
    properties: {
      _id: 'objectId',
      name: 'string',
      // A manufacturer's related CarInverse objects
      cars: 'CarInverse[]',
    },
  };
}
class CarInverse extends Realm.Object {
  _id!: BSON.ObjectId;
  model!: string;
  manufacturer!: Realm.List<ManufacturerInverse>;
  miles?: number;
  static schema: Realm.ObjectSchema = {
    name: 'CarInverse',
    properties: {
      _id: 'objectId',
      model: 'string',
      miles: 'int?',
      // A car's related ManufacturerInverse objects
      manufacturer: {
        type: 'linkingObjects',
        objectType: 'ManufacturerInverse',
        property: 'cars',
      },
    },
  };
}

Creating a many-to-many relationship between Hashtag and Note

Creating a many-to-many relationship between Hashtags and Notes is exactly the same as the relationship between Cars and Manufacturers.

Note Model

Line 30 defines a “to-many” relationship between a Note and Hashtag. This is equivalent to the manufacturer-to-car relationship in the example above. When a note is queried in Realm the hashtags object will point to N number of hashtags that reference this note. This is handy if you want to load all hashtags for a given note without issuing a separate query (e.g. myNote.hashtags.forEach(...)).

import uuid from 'react-native-uuid';
import Hashtag from './Hashtag';

export interface NoteProps {
  id?: string;
  content: string;
  hashtags: Hashtag[];
}

export default class Note {
  public id: string;
  public content: string;
  public hashtags: Hashtag[];

  constructor({
    id = uuid.v4().toString(),
    content,
    hashtags,
  }: NoteProps) {
    this.id = id;
    this.content = content;
    this.hashtags = hashtags;
  }

  static schema: Realm.ObjectSchema = {
    name: 'Note',
    properties: {
      id: 'string',
      content: 'string',
      hashtags: `Hashtag[]`,
    },
    primaryKey: 'id',
  };
}

Hashtag Model

Line 26 defines a linking object property notes which links back to the hashtags property of the Notes model. This inverse relationship effectively creates a collection of notes that have a reference to this particular hashtag. When a hashtag is queried in Realm the notes object will point to N number of notes that reference this hashtag. This is handy if you want to load all notes for a given hashtag without issuing a separate query (e.g. myHashtag.notes.forEach(...)).

import uuid from 'react-native-uuid';
import Note from './Note';

interface HashtagProps {
  id?: string;
  name: string;
}

export default class Hashtag {
  public id: string;
  public name: string;
  public notes: Note[];

  constructor({ id = uuid.v4().toString(), name }: HashtagProps) {
    this.id = id;
    this.name = name;
    this.notes = [];
  }

  static schema: Realm.ObjectSchema = {
    name: 'Hashtag',
    properties: {
      id: 'string',
      name: 'string',
      // all notes who have this hashtag added in the "hashtags" property.
      notes: { type: 'linkingObjects', objectType: 'Note', property: 'hashtags' },
    },
    primaryKey: 'id',
  };
}

With this relationship in place you can now load a note or a hashtag and have a direct reference to the other side of the relationship:

const note = realm.objectForPrimaryKey<Note>('Note', id);

// load all hashtags associated with the given note and extract their names.
const hashtagNames = note.hashtags.map(h => h.name)
jQuery Validation Rules for Dynamically Generated Content

jQuery Validation Rules for Dynamically Generated Content

jQuery Validation is a very convenient client-side validation framework that has made the implementation of client-side validation much more streamlined. For many web applications, introducing validation for a static form is as simple as defining the rules, messages and a handful of other (optional) settings. What about when we have dynamic content on our form? How can we define these rules for content we generate dynamically?

Follow along with this JSFiddle, which outlines the scenario described below.

Dynamically Set jQuery Validation Rules

We’ll start with a simple use case. Pretend for a moment you have a form with a few inputs: First Name, Last Name and Age. The jQuery Validation setup might look something like this:

var formValidationSettings = {
  ignore: '[readonly], :hidden',
  highlight: function(element) {
    $(element).addClass('has-error');
  },
  unhighlight: function(element) {
    $(element).removeClass('has-error');
  },
  errorElement: 'span',
  errorClass: 'help-block error',
  errorPlacement: function(error, element) {
    // do nothing, which suppresses the validation message
  },
  rules: {
    FirstName: {
      required: true
    },
    LastName: {
      required: true
    },
    Age: {
      required: true
    }
  }
};

// initialize the form's validator, applying the static form rules
$('form').validate(formValidationSettings);

We defined a few rules stating that each of the three inputs are required, as well as specified how and where our errors should be placed. This is fairly static content, allowing us to setup the jQuery Validation once and forget it. Not bad.

Now let’s bake in the ability for the user filling out the form to define a list of dependents. This will be done by introducing a grid where the user can hit “Add”, be presented a set of additional inputs to fill out, save the record and repeat. When the save button is clicked we want to trigger validation of this dynamic content.

Let’s say the dynamic inputs we present are Dependent Name and Relationship. We could define the rules for these inputs in the original form validation setup. However, that would trigger validation of all the other inputs on the form any time we attempt to save a dependent record. In other words, before we can successfully add a dependent record, all other inputs on the form must also be valid. That isn’t ideal. Instead, let’s define the dynamic input validation rules on the fly.

Fast forward a bit: the user clicked the “Add” button to enter a new dependent. They filled in the Dependent Name but neglected to enter the Relationship. They now attempt to save. Prior to triggering the jQuery Validation (e.g. $form.valid();), we must define our contextual dependent rules:

var dependentValidationSettings = {
  rules: {
    DependentName: {
      required: true
    },
    Relationship: {
      required: true
    }
  }
};

var saveDependent = function() {
  // create a deep copy (not reference) of the original form jQuery Validator settings
  var originalSettings = $.extend(true, {}, $form.validate().settings);

  // overwrite the original form settings with those for the dependent record
  var settings = $form.validate().settings;
  $.extend(settings, dependentValidationSettings);

  if ($form.valid()) {
    // save record
  }

  // reset the original settings after validating the dependent record
  $.extend(settings, originalSettings);
};

Let’s go over what’s happening here: when the “Save” button is clicked and our Save function triggered, we first create a deep copy of the original “form-wide” validation rules and set that aside. We’ll be using this later to revert our changes. Prior to asking jQuery to validate the form we alter the validation settings by overwriting the “rules” property with our dynamic “dependent-only” validation settings. jQuery Validation is now tuned in on the dependent inputs, leaving the rest of the form alone. At the very end of all this we unwind our rule changes and put jQuery Validation back to its original form-wide state.

There we have it, a way to focus the validation rules that jQuery Validation cares about on-the-fly.