Breeze uses the ViewModel of the MVVM hosting infrastructure. This is generally a good solution. In addition, change tracking for entities is a fundamental concept of breeze.js (the same for Entity Framework). Its simple task is to track changes if the MVVM environment uses Observables with real getters and seters (e.g. Knockout). AngularJS, on the other hand, works with regular JavaScript objects. This makes tracking changes difficult. The only two reliable ways are ES5 properties (simple but not supported by IE8) or very deep integration in the $ digest cycle. The breeze team took the first choice - what a pity the projects that should support IE8!
Ok, let's analyze the root cause of the problem: change tracking
Do you really need this feature? At least in our project, we decided to use breeze.js / OData for reading and for a more “calm” approach when it comes to writing. If you do not need these advanced functions, the following script should solve the problem:
/******************************************************** * A replacement for the "backingStore" modelLibrary * * This is a bare version of the original backingStore, * without ANY change tracking - that why it will work in IE8! * (Object.defineProperty not required any more) * * This adapter is a "drop in" replacement for the "backingStore" adapter in Breeze core. * It has the same adapter name so it will silently replace the original "backingStore" adapter * when you load this script AFTER the breeze library. * WARNING: For obvious reasons a lot of breeze magic will be lost! * * Author: Johannes Hoppe / haushoppe-its.de * * Copyright 2014 IdeaBlade, Inc. All Rights Reserved. * Use, reproduction, distribution, and modification of this code is subject to the terms and * conditions of the IdeaBlade Breeze license, available at http://www.breezejs.com/license ******************************************************/ (function (definition, window) { if (window.breeze) { definition(window.breeze); } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { // CommonJS or Node var b = require('breeze'); definition(b); } else if (typeof define === "function" && define["amd"] && !window.breeze) { // Requirejs / AMD define(['breeze'], definition); } else { throw new Error("Can't find breeze"); } }(function (breeze) { "use strict"; var core = breeze.core; var ctor = function () { this.name = "backingStore"; this.A_BIG_FAT_WARNING = "This is a bare version of the backingStore! Change tracking won't work!"; }; var protoFn = ctor.prototype; protoFn.initialize = function() { }; protoFn.getTrackablePropertyNames = function (entity) { var names = []; for (var p in entity) { if (p === "entityType") continue; if (p === "_$typeName") continue; var val = entity[p]; if (!core.isFunction(val)) { names.push(p); } } return names; }; protoFn.initializeEntityPrototype = function (proto) { proto.getProperty = function (propertyName) { return this[propertyName]; }; proto.setProperty = function (propertyName, value) { this[propertyName] = value; return this; }; }; // This method is called when an EntityAspect is first created - this will occur as part of the entityType.createEntity call. // which can be called either directly or via standard query materialization // entity is either an entity or a complexObject protoFn.startTracking = function (entity, proto) { // assign default values to the entity var stype = entity.entityType || entity.complexType; stype.getProperties().forEach(function (prop) { var propName = prop.name; var val = entity[propName]; if (prop.isDataProperty) { if (prop.isComplexProperty) { if (prop.isScalar) { val = prop.dataType._createInstanceCore(entity, prop); } else { val = breeze.makeComplexArray([], entity, prop); } } else if (!prop.isScalar) { val = breeze.makePrimitiveArray([], entity, prop); } else if (val === undefined) { val = prop.defaultValue; } } else if (prop.isNavigationProperty) { if (val !== undefined) { throw new Error("Cannot assign a navigation property in an entity ctor.: " + prop.Name); } if (prop.isScalar) { // TODO: change this to nullstob later. val = null; } else { val = breeze.makeRelationArray([], entity, prop); } } else { throw new Error("unknown property: " + propName); } entity[propName] = val; }); }; breeze.config.registerAdapter("modelLibrary", ctor); }, this));
Download at: https://gist.github.com/JohannesHoppe/72d7916aeb08897bd256
This is a bare version of the source repository, without any tracking changes - that’s why it will work in IE8! (Object.defineProperty is no longer required) This adapter is a "replacement" for replacing the "backingStore" adapter in the Breeze core. It has the same adapter name, so when loading the script AFTER the BREEZE library. It will replace the original backingStore adapter.
Here is a demo to prove functionality:
http://jsfiddle.net/Johannes_Hoppe/bcav9hzL/5/
JsFiddle does not support IE8, use this direct link:
http://jsfiddle.net/Johannes_Hoppe/bcav9hzL/5/embedded/result/
Hooray!