Sometimes you can’t generate Breeze metadata on the server or don’t want to do so. This topic explains how to write the metadata yourself on the client in JavaScript.
We know it seems daunting at first, especially if you’ve seen the server response from a metadata request. Perhaps you’ve examined the output from entityManager.metadataStore.exportMetadata
or rooted around a MetadataStore
in the debugger.
What you’re seeing is a representation of metadata meant for a machine … not for a human. While it’s sort of readable in JSON, no one would want to write that … and we don’t expect you to write that.
We’ll show you how authoring metadata with the Breeze Labs Metadata-Helper can be easy and intuitive.
Breeze has a native API for defining an EntityType
and adding it to a MetadataStore
. This API affords great control over every detail of metadata definition. You should learn it.
We recognize that it can seem a tad verbose and fussy. Sometimes you wish for something that was a bit friendlier and forgiving. That’s why we offer the Breeze Labs Metadata-Helper.
The Metadata-Helper is a domain-specific language (DSL) that strives to minimize the information you have to provide when writing Breeze metadata. It relies on common conventions to make good guesses about the details of your model’s types, properties and associations. If it guesses incorrectly about a particular point (or can’t guess at all), you can configure that specific point explicitly using the core metadata API.
The Breeze Labs
MetadataHelper
extension is defined in breeze.metadata-helper.js. Download it from GitHub and install it on your page after loading breeze.
It is also available as a NuGet package:
Install-Package Breeze.Metadata.Helper
In this topic, we’ll concentrate on using the Metadata-Helper when it gets it right … which is most of the time.
The related topic, “Metadata by hand (in depth)” delves into the details of how the helper works, how you can intervene, and how it all relates to the underlying metadata schema that Breeze uses at runtime. Visit that page when you’re ready … if you have to.
The following simple example queries the github API for the members of the AngularJS team. The API developers didn’t know about breeze and the API doesn’t conform to OData. The response from a request for github Members is an array of simple JSON objects with no type information.
We can’t change the github server to suit Breeze. If we want Breeze to treat these objects as cached entities, we’ll have to create their metadata on the client … which we do in the dataservice
service defined in the script.js file. Check out the README.md file for more details.
This sample only works with modern browsers (IE10+, FF, Chrome).
Let’s look at the JavaScript metadata for the “Code Camper” model from John Papa’s Plural Sight course, “Building Data-Centric Single Page Applications with Breeze”.
The model has five entity types:
These entities are related. A Session has a Room, TimeSlot, Track, and a Speaker (a Person who delivers the talk). You should be able to write session.room
, session.track
, session.speaker
, etc.
A Speaker may give more than one talk so the Person type has a collection of one or more Sessions which you access by writing person.speakerSessions
.
Here is the metadata you might write to describe that model.
This code extract comes from a test helper file in the DocCode sample. See related tests in metadataOnClientTests.js.
You see these tests running live in this plunker. Feel free to explore these metadata further by adding your own tests right there in plunker.
Remember to install and enable the Breeze Labs
MetadataHelper
.</p>
function fillMetadataStore(store) {
// Metadata-Helper instance configured with default namespace and key generator for this model
var helper = new breeze.config.MetadataHelper( 'CC.Model', breeze.AutoGeneratedKeyType.Identity);
// DataTypes
var DT = breeze.DataType;
var BOOL = DT.Boolean;
var DATE = DT.DateTime;
var ID = DT.Int32;
// type order is irrelevant
addPerson();
addSession();
addRoom();
addTimeSlot();
addTrack();
// addType - make it easy to add the type to the store using the helper
function addType(type) {
helper.addTypeToStore(store, type);
};
function addPerson() {
addType({
name: 'Person',
dataProperties: {
id: { type: ID },
firstName: { max: 50, required: true },
lastName: { max: 50, required: true },
// could add validators here; let model.validation add them
email: { max: 400 },
blog: { max: 400 },
twitter: { max: 400 },
gender: { max: 1 },
imageSource: { max: 400 },
// could let Breeze add unmapped but we do so to lock in the Boolean data type
isPartial: { type: BOOL, required: true, isUnmapped: true },
isSpeaker: { type: BOOL, required: true, isUnmapped: true }
},
navigationProperties: {
speakerSessions: { type: 'Session', hasMany: true }
}
});
}
function addSession() {
addType({
name: 'Session',
dataProperties: {
id: { type: ID },
title: { max: 50, required: true },
code: { max: 10 },
description: { max: 4000 },
level: { max: 30 },
tags: { max: 4000 },
roomId: { type: ID, required: true },
speakerId: { type: ID, required: true },
timeSlotId: { type: ID, required: true },
trackId: { type: ID, required: true },
isPartial: { type: BOOL, required: true, isUnmapped: true }
},
// Let model.validation add the requireReferenceEntity validators
navigationProperties: {
room: 'Room',
speaker: 'Person',
timeSlot: 'TimeSlot',
track: 'Track'
}
});
}
function addRoom() {
addType({
name: 'Room',
dataProperties: {
id: { type: ID },
name: { max: 50, required: true }
}
});
}
function addTimeSlot() {
addType({
name: 'TimeSlot',
dataProperties: {
id: { type: ID },
start: { type: DATE, required: true },
isSessionSlot: { type: BOOL, required: true },
duration: { type: ID, required: true }
}
});
}
function addTrack() {
addType({
name: 'Track',
dataProperties: {
id: { type: ID },
name: { max: 50, required: true }
}
});
}
}
Now you’re ready for a more complex, multiple entity, live code example in this plunker. Look in particular at the metadataFactory.js script.