{"id":25092825,"url":"https://github.com/biril/backbone-proxy","last_synced_at":"2025-09-17T15:54:07.063Z","repository":{"id":58218319,"uuid":"23027847","full_name":"biril/backbone-proxy","owner":"biril","description":"Model proxies for Backbone","archived":false,"fork":false,"pushed_at":"2015-03-01T13:09:42.000Z","size":865,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-07T15:53:03.046Z","etag":null,"topics":["backbone","backbonejs","model"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/biril.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-08-16T21:50:21.000Z","updated_at":"2017-05-25T15:56:50.000Z","dependencies_parsed_at":"2022-08-31T00:12:16.435Z","dependency_job_id":null,"html_url":"https://github.com/biril/backbone-proxy","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fbackbone-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fbackbone-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fbackbone-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fbackbone-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/biril","download_url":"https://codeload.github.com/biril/backbone-proxy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675393,"owners_count":21143768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backbone","backbonejs","model"],"created_at":"2025-02-07T14:35:02.664Z","updated_at":"2025-09-17T15:54:02.035Z","avatar_url":"https://github.com/biril.png","language":"JavaScript","readme":"Backbone Proxy\n==============\n\n[![Build Status](https://travis-ci.org/biril/backbone-proxy.png)](https://travis-ci.org/biril/backbone-proxy)\n[![NPM version](https://badge.fury.io/js/backbone-proxy_.png)](http://badge.fury.io/js/backbone-proxy_)\n[![Bower version](https://badge.fury.io/bo/backbone-proxy.png)](http://badge.fury.io/bo/backbone-proxy)\n\nModel proxies for Backbone. Notably useful in applications where sharing a single model instance\namong many components (e.g. views) with different concerns is a common pattern. In such cases, the\nneed for multiple components to reference the same model-encapsulated state prohibits the\nspecialization of model-behaviour. Additionaly it leads to a design where 'all code is created\nequal', i.e. all components have the exact same priviledge-level in respect to data-access.\nBackboneProxy faciliates the creation of model-proxies that may be specialized in terms of\nbehaviour while referencing the same, shared state.\n\nFor example, you can create:\n\n\n#### Proxies that log attribute changes\n\n```javascript\n// Create a UserProxy class. Instances will proxy the given user model\n// Note that user is a model _instance_ - not a class\nvar UserProxy = BackboneProxy.extend(user);\n\n// Instantiate a logging proxy - a proxy that logs all invocations of .set\nvar userWithLog = new UserProxy();\nuserWithLog.set = function (key, val) {\n  console.log('setting ' + key + ' to ' + val + ' on user');\n\n  // Delegate to UserProxy implementation\n  UserProxy.prototype.set.apply(this, arguments);\n};\n\n// Will not log\nuser.set('name', 'Norman');\n\n// Will log 'setting name to Mairy on user'\nuserWithLog.set('name', 'Mairy');\n\n// Will log 'user name is Mairy'\nconsole.log('user name is ' + user.get('name'));\n```\n\n\n#### Readonly proxies\n\n```javascript\n// Create a UserProxy class\nvar UserProxy = BackboneProxy.extend(user);\n\n// Instantiate a readonly proxy - a proxy that throws on any invocation of .set\nvar userReadonly = new UserProxy();\nuserReadonly.set = function () {\n  throw 'cannot set attributes on readonly user model';\n};\n\n// Attributes cannot be set on userReadonly\n// However attribute changes can be listened for\nuserReadonly.on('change:name', function () {\n  alert('user name was set to ' + userReadonly.get('name'));\n});\n\n// Will throw\nuserReadonly.set('name', 'Mairy');\n\n// Will alert 'user name was set to Mairy'\nuser.set('name', 'Mairy');\n```\n\n\n#### View-specific proxies\n\n```javascript\n// Create a UserProxy class\nvar UserProxy = BackboneProxy.extend(user);\n\n// Instantiate a proxy to pass to view1\nvar userProxy1 = new UserProxy();\nuserProxy1.set = function (key, val, opts) {\n\n  // Handle both key/value and {key: value} - style arguments\n  var attrs;\n  if (typeof key === 'object') { attrs = key; opts = val; }\n  else { (attrs = {})[key] = val; }\n\n  // Automatically update lastUpdatedBy to 'view1' on every invocation of set\n  attrs.lastUpdatedBy = 'view1';\n\n  // Delegate to UserProxy implementation\n  UserProxy.prototype.set.call(this, attrs, opts);\n};\n\n// Instantiate a proxy to pass to view2. Similar to userProxy1 -\n//  will automatically set the lastUpdatedBy attr to 'view2'\nvar userProxy2 = new UserProxy();\nuserProxy2.set = function (key, val, opts) {\n  var attrs;\n  if (typeof key === 'object') { attrs = key; opts = val; }\n  else { (attrs = {})[key] = val; }\n\n  attrs.lastUpdatedBy = 'view2';\n\n  UserProxy.prototype.set.call(this, attrs, opts);\n};\n\nvar view1 = new SomeView({ model: userProxy1 });\nvar view2 = new SomeOtherView({ model: userProxy2 });\n\n// Will log modifications of user object '..by view1' / '..by view2'\nuser.on('change', function () {\n  console.log('user modified by ' + user.get('lastUpdatedBy'));\n});\n\n```\n\n\nSet up\n------\n\n* install with bower, `bower install backbone-proxy`,\n* install with npm, `npm install backbone-proxy_` (please note the intentional underscore) or\n* just include the\n    [latest stable `backbone-proxy.js`](https://github.com/biril/backbone-proxy/releases).\n\nBackbone proxy may be used as an exported global, a CommonJS module or an AMD module depending on\nthe current environment:\n\n* In projects targetting _browsers, without an AMD module loader_, include backbone-proxy.js\n    after backbone.js:\n\n    ```html\n    ...\n    \u003cscript type=\"text/javascript\" src=\"backbone.js\"\u003e\u003c/script\u003e\n    \u003cscript type=\"text/javascript\" src=\"backbone-proxy.js\"\u003e\u003c/script\u003e\n    ...\n    ```\n\n    This will export the `BackboneProxy` global.\n\n* `require` when working _with CommonJS_ (e.g. Node). Assuming BackboneProxy is `npm install`ed:\n\n    ```javascript\n    var BackboneProxy = require('backbone-proxy');\n    ```\n\n* Or list as a dependency when working _with an AMD loader_ (e.g. RequireJS):\n\n    ```javascript\n    // Your module\n    define(['backbone-proxy'], function (BackboneProxy) {\n      // ...\n    });\n    ```\n\n    Note that the AMD definition of BackboneProxy depends on `backbone` and `underscore` so some\n    loader setup will be required. For non-AMD compliant versions of Backbone (\u003c 1.1.1) or\n    Undescore (\u003c 1.6.0), [James Burke's amdjs forks](https://github.com/amdjs) may be used instead,\n    along with the necessary paths configuration\n\n    ```javascript\n    require.config({\n      baseUrl: 'myapp/',\n      paths: {\n        'underscore': 'mylibs/underscore',\n        'backbone': 'mylibs/backbone'\n      }\n    });\n    ```\n\n    or you may prefer to just [shim them](http://requirejs.org/docs/api.html#config-shim).\n\n\nAt the time of this writing, BackboneProxy has only been tested against Backbone 1.2.1\n\n\nUsage\n-----\n\nBackboneProxy exposes a single `extend` method as the means of creating a Proxy 'class' for any\ngiven `model` (or model proxy):\n\n```javascript\n// Create Proxy class for given model\nvar Proxy = BackboneProxy.extend(model);\n\n// Instantiate any number of proxies\nvar someProxy = new Proxy();\nvar someOtherProxy = new Proxy();\n\n// Yes, you can proxy a proxy\nvar ProxyProxy = BackboneProxy.extend(someProxy);\nvar someProxyProxy = new ProxyProxy();\n```\n\nFor any given `proxied`/`proxy` models that have a proxied-to-proxy relationship, the following\napply:\n\nAny attribute `set` on `proxied` will be set on `proxy` and vice versa (the same applies for\n`unset` and `clear`):\n\n```javascript\nproxied.set({ name: 'Betty' });\nconsole.log(proxy.get('name')); // Will log 'Betty'\n\nproxy.set({ name: 'Charles' });\nconsole.log(proxied.get('name')); // Will log 'Charles'\n```\n\nBuilt-in model events (add, remove, reset, change, destroy, request, sync, error, invalid)\ntriggered in response to actions performed on `proxied` will also be triggered on `proxy`. And vice\nversa:\n\n```javascript\nproxy.on('change:name', function (model) {\n  console.log('name set to ' + model.get('name'))\n});\nproxied.set({ name: 'Betty' }); // Will log 'name set to Betty'\n\nproxied.on('sync', function () {\n  console.log('model synced');\n});\nproxy.fetch(); // Will log 'model synced'\n\n```\n\nUser-defined events triggered on `proxied` will also be triggered on `proxy`. The opposite is _not_\ntrue:\n\n```javascript\nproxied.on('boo', function () {\n  console.log('a scare on proxied');\n});\nproxy.on('boo', function () {\n  console.log('a scare on proxy');\n});\n\nproxied.trigger('boo'); // Will log 'a scare on proxied' \u0026 'a scare on proxy'\nproxy.trigger('boo'); // Will only log 'a scare on proxy'\n\n```\n\nAdditions and removals of event listeners are, generally speaking, 'scoped' to each model. That is\nto say, event listeners may be safely added on, and - primarily - removed from the proxied (/proxy)\nwithout affecting event listeners on the proxy (/proxied). This holds when removing listeners by\ncallback or context. For example, when removing listeners by callback:\n\n```javascript\nvar onModelChange = function (model) {\n  console.log('model changed');\n};\n\nproxied.on('change', onModelChange);\nproxy.on('change', onModelChange);\n\nproxied.set({ name: 'Betty' }); // Will log 'model changed' twice\n\n// Will only remove the listener previously added on proxied\nproxied.off(null, onModelChange);\n\nproxied.set({ name: 'Charles' }); // Will log 'model changed' once\n```\n\nRemoving listeners by event name works similarly:\n\n```javascript\nproxied.on('change:name', function () {\n  console.log('caught on proxied');\n});\nproxy.on('change:name', function () {\n  console.log('caught on proxy');\n});\n\nproxied.set({ name: 'Betty' }); // Will log 'caught on proxied' \u0026 'caught on proxy'\n\n// Will only remove the listener previously added on proxied\nproxied.off('change:name');\n\nproxied.set({ name: 'Charles' }); // Will only log 'caught on proxy'\n```\n\n**However**, removing listeners registered for the 'all' event presents a special case as it will\ninterfere with BackboneProxy's internal event-forwarding. Essentially, you should avoid\n`.off('all')` for models which are proxied - it will disable event notifications on the proxy:\n\n```javascript\nproxy.on('change:name', function () {\n  console.log('changed name');\n});\nproxy.on('change:age', function () {\n  console.log('changed age');\n});\n\n// Will log 'changed name' \u0026 'changed age'\nproxied.set({\n  name: 'Betty',\n  age: 29\n});\n\n// Bad move\nproxied.off('all');\n\n// Will log nothing\nproxied.set({\n  name: 'Charles',\n  age: 31\n});\n```\n\nThe `model` argument passed to a listener will always be set to the model to which the listener was\nadded. The same is true for the context (as long as it's not explicitly set):\n\n```javascript\nvar onModelChanged = function (model) {\n    model.dump(); // Or alternatively, this.dump();\n  };\n\nproxied.dump = function () {\n  console.log('proxied changed: ' + JSON.stringify(this.changedAttributes()));\n};\nproxy.dump = function () {\n  console.log('proxy changed: ' + JSON.stringify(this.changedAttributes()));\n};\n\nproxied.on('change', onModelChanged);\nproxy.on('change', onModelChanged);\n\nproxied.set({ name: 'Betty' }); // Will log 'proxied changed: ..' \u0026 'proxy changed: ..'\n\nproxy.off(null, onModelChanged);\nproxy.set({ name: 'Charles' }); // Will log 'proxied changed: ..'\n```\n\nBackbone's 'overridables', i.e. properties and methods that affect model behaviour when set\n(namely `collection`, `idAttribute`, `sync`, `parse`, `validate`, `url`, `urlRoot` and `toJSON`)\nshould be set on the proxied model. That is to say, the _root_ proxied `Backbone.Model`. Setting\nthem on a proxied model is not meant to (and will generally _not_) produce the intended result. As\nan example:\n\n```javascript\n// Setting a validate method on a proxy ..\nproxy.validate = function (attrs) {\n  if (attrs.name === 'Betty') {\n    return 'Betty is not a valid name';\n  }\n};\n\n// .. will not work: This will log 'validation error: none'\nproxy.set({ name: 'Betty' }, { validate: true });\nconsole.log('validation error: ' + (proxy.validationError || 'none'));\n\n// Setting a validate method on the proxied ..\nproxied.validate = function (attrs) {\n  if (attrs.name === 'Charles') {\n    return 'Charles is not a valid name';\n  }\n};\n\n// .. will produce the intended result:\n//  This will log 'validation error: Charles is not a valid name'\nproxy.set({ name: 'Charles' }, { validate: true });\nconsole.log('validation error: ' + (proxy.validationError || 'none'));\n\n```\n\nAside from the prior examples, the\n[annotated version of the source](http://biril.github.io/backbone-proxy/) is available as a\nreference.\n\n\nContributing ( / Testing )\n--------------------------\n\nContributions are obviously appreciated. In lieu of a formal styleguide, take care to maintain the\nexisting coding style. Please make sure your changes test out green prior to pull requests. The\nQUnit test suite may be run in a browser (test/index.html) or on the command line, by running\n`make test` or `npm test`. The command line version runs on Node and depends on\n[node-qunit](https://github.com/kof/node-qunit) (`npm install` to fetch it before testing). A\n[coverage report](http://biril.github.io/backbone-proxy/lcov-report/backbone-proxy/backbone-proxy.js.html)\nis also available.\n\n\nLicense\n-------\n\nLicensed and freely distributed under the MIT License (LICENSE.txt).\n\nCopyright (c) 2014-2015 Alex Lambiris\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiril%2Fbackbone-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbiril%2Fbackbone-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiril%2Fbackbone-proxy/lists"}