{"id":19504831,"url":"https://github.com/nice-digital/hyperbone-view","last_synced_at":"2025-02-25T22:12:59.848Z","repository":{"id":74446231,"uuid":"109390161","full_name":"nice-digital/hyperbone-view","owner":"nice-digital","description":"Bind Hyperbone models to the dom automatically","archived":false,"fork":false,"pushed_at":"2014-09-25T15:15:44.000Z","size":624,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-08T10:45:31.987Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"green-mesa/hyperbone-view","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nice-digital.png","metadata":{"files":{"readme":"Readme.md","changelog":"History.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-03T11:55:28.000Z","updated_at":"2023-12-22T12:41:28.000Z","dependencies_parsed_at":"2023-02-22T13:45:54.954Z","dependency_job_id":null,"html_url":"https://github.com/nice-digital/hyperbone-view","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nice-digital%2Fhyperbone-view","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nice-digital%2Fhyperbone-view/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nice-digital%2Fhyperbone-view/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nice-digital%2Fhyperbone-view/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nice-digital","download_url":"https://codeload.github.com/nice-digital/hyperbone-view/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240754366,"owners_count":19852189,"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":[],"created_at":"2024-11-10T22:27:22.523Z","updated_at":"2025-02-25T22:12:59.656Z","avatar_url":"https://github.com/nice-digital.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hyperbone View\r\n\r\n[![Build Status](https://travis-ci.org/green-mesa/hyperbone-view.png?branch=master)](https://travis-ci.org/green-mesa/hyperbone-view)\r\n\r\n## Please note that this module is no longer being maintained. \r\n\r\n## (Having said that I've just updated it to stop using the deprecated replaceWholeText() functionality deprecated in Firefox and soon everythign else.. )\r\n\r\nIt has been replaced with [HalogenJS View](https://github.com/halogenjs/view).\r\n\r\n## Installing\r\n\r\n```sh\r\n$ npm install --save hyperbone-view\r\n```\r\n\r\n## Running tests\r\n\r\nOnce:\r\n```sh\r\n$ npm install -g grunt-cli browserify\r\n```\r\nOnce after cloning repo\r\n```sh\r\n$ npm install\r\n```\r\nRunning tests\r\n```sh\r\n$ npm test\r\n```\r\n\r\n## tl;dr \r\n\r\nPush style template system for Hyperbone (and probably Backbone) models, allowing strict model/view separation.\r\n\r\nYou get 'if', 'if-not', 'hb-trigger', 'hb-click-toggle', 'hb-with' and 'hb-bind' as the only custom attributes you need to learn [See paper on this subject](http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf).\r\n\r\n## Features\r\n\r\n- Logicless moustache-eseque templates for attributes and innertext. \r\n- Define your own custom helpers to do advanced string processing\r\n- Hypermedia extensions: Automatically insert href attributes for recognised rels.\r\n- 'hb-trigger' custom attribute to trigger Hyperbone events on a model\r\n- 'if' custom attribute to conditionally display elements.\r\n- 'hb-bind' custom attribute to link an input to a model attribute\r\n- 'hb-with' to change scope of a template and render out collections (partials, in effect)\r\n- API for adding additional attributes for when you HAVE to touch the DOM.\r\n\r\n## Example\r\n\r\nHTML in your page:\r\n```html\r\n\u003cdiv if=\"getting-name\" id=\"some-view\" class=\"{{type}}\"\u003e\r\n  \u003cp\u003eHello, {{name}}\u003c/p\u003e\r\n  \u003clabel\u003eEnter your name: \u003cinput hb-bind=\"name\"\u003e\u003c/label\u003e\r\n  \u003cdiv class=\"description\"\u003e{{strip(description)}}\u003c/div\u003e\r\n  \u003ca rel=\"some-rel\"\u003e Some link \u003c/a\u003e\r\n  \u003ca rel=\"self\" class=\"{{clicked}}\" hb-trigger=\"special-link-clicked\"\u003eA link to myself\u003c/a\u003e\r\n  \u003cul hb-with=\"noodle-flavours\"\u003e\r\n    \u003cli class=\"flavour {{className}}\"\u003e\u003ca rel=\"self\"\u003e{{flavour}}\u003c/a\u003e\u003c/li\u003e\r\n  \u003c/ul\u003e\r\n\u003c/div\u003e\r\n```\r\nJSON HAL document on the server\r\n```json\r\n{\r\n  \"_links\" : {\r\n    \"self\" : {\r\n      \"href\" : \"/a-link-to-me\"\r\n    },\r\n    \"some-rel\" : {\r\n      \"href\" : \"/some-link\"\r\n    }\r\n  },\r\n  \"description\" : \"This is __very__ exciting\",\r\n  \"type\" : \"testing-thing\",\r\n  \"_embedded\" : {\r\n    \"noodle-flavours\" : [\r\n      {\r\n        \"_links\" : {\r\n          \"self\" : {\r\n            \"href\" : \"/flavours/chicken\"\r\n          }\r\n        },\r\n        \"flavour\" : \"Chickenesque\",\r\n        \"classification\" : \"edible\"\r\n      },\r\n      {\r\n        \"_links\" : {\r\n          \"self\" : {\r\n            \"href\" : \"/flavours/beef\"\r\n          }\r\n        },\r\n        \"flavour\" : \"Spicy Beef substitute\",\r\n        \"classification\" : \"toxic\"\r\n      },\r\n      {\r\n        \"_links\" : {\r\n          \"self\" : {\r\n            \"href\" : \"/flavours/curry\"\r\n          }\r\n        },\r\n        \"flavour\" : \"Curry. Just Curry.\",\r\n        \"classification\" : \"edible\"\r\n      }\r\n    ]\r\n  }\r\n  \"name\" : \"\"\r\n}\r\n```\r\nPresume that we've loaded this JSON into a HyperboneModel instance..\r\n\r\n```js\r\n\r\nvar HyperboneView = require('hyperbone-view').HyperboneView;\r\n\r\n// we want to register a helper called 'strip'. This will be available to all Views in the system.\r\n\r\nrequire('hyperbone-view').registerHelper('strip', function( str ){\r\n  return markdownStripper( str )\r\n})\r\n\r\n// now we can create our view instance.\r\nnew HyperboneView({\r\n\r\n  // the model...\r\n  model : myHypermediaDocument,\r\n\r\n  // our view root\r\n  el : '#some-view'\r\n});\r\n\r\n// set our editing flag to true so that we can see our html\r\nmyHypermediaDocument.set('editing', true);\r\n\r\n// bind to a hyperbone event that'll trigger when the user\r\n// clicks on the particular link.\r\nmyHypermediaDocument.on('special-link-clicked', function( model ){\r\n  model.set('clicked', 'clicked');\r\n})\r\n\r\n```\r\nAs soon as the initial processing is done, our DOM has been transformed.\r\n\r\nSome things to note:\r\n\r\n- The collection of flavours has been expanded\r\n- Each flavour has automatically had its own href added to the link because of the rel='self'\r\n- Our link to rel='some-rel' has had its href added as well.\r\n- Our 'strip' helper has removed the markdown from 'description'.\r\n\r\n```html\r\n\u003cdiv id=\"some-view\" class=\"testing-thing\"\u003e\r\n  \u003cp\u003eHello, \u003c/p\u003e\r\n  \u003clabel\u003eEnter your name: \u003cinput hb-bind=\"name\"\u003e\u003c/label\u003e\r\n  \u003cdiv class=\"description\"\u003eThis is very exciting\u003c/div\u003e\r\n  \u003ca href=\"/some-link\" rel=\"some-rel\"\u003e Some link \u003c/a\u003e\r\n  \u003ca href=\"/a-link-to-me\" rel=\"self\" class=\"\"\u003eA link to myself\u003c/a\u003e\r\n  \u003cul\u003e\r\n    \u003cli class=\"flavour edible\"\u003e\u003ca href=\"/flavour/chicken\" rel=\"self\"\u003eChickenesque\u003c/a\u003e\u003c/li\u003e\r\n    \u003cli class=\"flavour toxic\"\u003e\u003ca href=\"/flavour/beef\" rel=\"self\"\u003eSpicy Beef substitute\u003c/a\u003e\u003c/li\u003e\r\n    \u003cli class=\"flavour edible\"\u003e\u003ca href=\"/flavour/curry\" rel=\"self\"\u003eCurry. Just Curry.\u003c/a\u003e\u003c/li\u003e\r\n    \u003cli\u003e\r\n  \u003c/ul\u003e\r\n\u003c/div\u003e\r\n```\r\nIf you happen to do this in your code....\r\n```js\r\nmyHypermediaDocument.set('type', 'sure-hope-this-works')\r\n```\r\nThen the page automatically updates to...\r\n```html\r\n\u003cdiv id=\"some-view\" class=\"sure-hope-this-works\"\u003e\r\n```\r\nAnd if you happen to click on `A link to myself`, the Hyperbone event fires, updates the model and that results in..\r\n```html\r\n  \u003ca href=\"/a-link-to-me\" rel=\"self\" class=\"clicked\"\u003eA link to myself\u003c/a\u003e\r\n\u003c/div\u003e\r\n```\r\nAnd if you type something into the the 'Enter your name box'\r\n```html\r\n\u003cp\u003eHello, something\u003c/p\u003e\r\n```\r\nAnd if you do\r\n```js\r\nmyHypermediaDocument.set('editing', false);\r\n```\r\nThen the element gets hidden. \r\n\r\n## Installation\r\n\r\nInstall with [component(1)](http://component.io):\r\n\r\n```sh\r\n    $ component install green-mesa/hyperbone-view\r\n```\r\n\r\nHyperbone View has a number of dependencies which are installed at the same time. These are:\r\n\r\n- Underscore\r\n- component/dom\r\n- Parts of Backbone\r\n\r\nNote that unlike Backbone View this does not have a dependency on jQuery. It does use a tiny standalone dom manipulation component called Dom instead.\r\n\r\n## Module API\r\n\r\n### require('hyperbone-view').registerHelper(name, fn)\r\n\r\nRegister a helper function for use inside templates. It becomes globally available to all views.\r\n\r\nExample:\r\n```js\r\n  require('hyperbone-view').registerHelper('shout', function( str ){\r\n\r\n  \treturn str.toUpperCase();\r\n\r\n  });\r\n  new HyperboneView({ model: new HyperboneModel({ name : \"squirrel\"}), el : dom('#namebox')});\r\n```\r\nThe template calls the helper...\r\n```html\r\n\u003cp id=\"namebox\"\u003eHello {{shout(name)}}\u003c/p\u003e\r\n```\r\nWhich produces\r\n```html\r\n\u003cp\u003eHello SQUIRREL\u003c/p\u003e\r\n```\r\n\r\n### require('hyperbone-view').registerAttributeHandler(name, fn)\r\n\r\nRegister a custom attribute handler for extending the capabilities of View. More on this below.\r\n\r\n\r\n### require('hyperbone-view').HyperboneView\r\n\r\nYour reference to the HyperboneView prototype.\r\n\r\n```js\r\nvar HyperboneView = require('hyperbone-view').HyperboneView;\r\n\r\nnew HyperboneView({\r\n    model : model,\r\n    el : el,\r\n    initialised : function(){\r\n\r\n      // i get called after it's initialised for the first time.\r\n\r\n    }\r\n});\r\n```\r\nor\r\n```js\r\nnew HyperboneView().create(el, model);\r\n```\r\n\r\n## HyperboneView Instance API\r\n\r\n### .on( event, callback )\r\n\r\nHyperboneView instances are Backbone event emitters. There are three events emitted currently: `initialised`, `updated` and `delegate-fired`.\r\n\r\nThe callbacks are passed a [dom](http://github.com/component/dom) object, which is the view HTML and the model. For updated and delegate fired, information about what has changed is also added.\r\n\r\nThe philosphy behind these events is that they're useful for running integration tests, keeping a track on your application's state directly.\r\n\r\n```js\r\nview.on('initialised', function(el, model){\r\n  // I want to set some stuff in the model that's specific to the view but isn't in the Hypermedia\r\n  // that came from the server.\r\n  model.set('status', 'active');\r\n\r\n})\r\n```\r\n\r\n```js\r\nview.on('updated', function(el, model, event){\r\n  // event is 'change:someproperty' or something like that\r\n\r\n  if (event===\"change:status\"){\r\n    // do some horrible philosphy breaking stuff here\r\n  }\r\n\r\n})\r\n```\r\n\r\n```js\r\nview.on('delegate-fired', function(el, model, selector){\r\n\r\n  if (selector===\"click a.status\"){\r\n    logger('a.status clicked');\r\n  }\r\n\r\n})\r\n```\r\n\r\n### .create( dom, hyperboneModel )\r\n\r\nIf you want to postpone the view initialising, you can manually triggered this by invoking HyperboneView without a model and el and then calling .create(). Pass it either a CSS selector or a `dom` List object along with the model and this then binds the model to the view.\r\n\r\n### .addDelegate(obj | name, fn)\r\n\r\nIf you're using the .create() method, you can manually set up actual DOM event delegates, although this... probably isn't wise.\r\n\r\n```js\r\nnew HyperboneView({\r\n  delegates : {\r\n    'click .icon' : function( event ){\r\n      // do something here. Scope is the model.\r\n    }\r\n  },\r\n  model : model,\r\n  el: el\r\n})\r\n```\r\nis equivilant to \r\n```js\r\nnew HyperboneView()\r\n  .addDelegate('click .icon', function(event){\r\n    // do something here\r\n  })\r\n  .create(el, model)\r\n```\r\n\r\n\r\n## Hyperbone HTML Attributes\r\n\r\nHyperbone attributes can be added to the HTML, and allow for additional functionality not provided in the logicless attribute/innerText templates.\r\n\r\n### if=\"attribute\"\r\n\r\nGiven the truthiness of the model attribute, it will conditionally display the element.\r\n\r\n```html\r\n\u003cp if=\"organisation\"\u003e{{organisation}}\u003c/p\u003e\r\n```\r\nThis is as complex as the logic gets. How do I do an 'else' or an 'or' or an 'and' I hear you cry. Anything more complex than this is a job for code. It's what code is good at. The philosophy is that you do your difficult logic stuff in your code.\r\n\r\n\r\n### hb-with=\"attribute\"\r\n\r\nChanges the scope for the innerHTML to the selected model or collection. In effect the nested elements become a partial.\r\n\r\nThis HTML...\r\n```html\r\n\u003cdiv hb-with=\"nested-model\"\u003e\r\n  \u003cp\u003e{{greeting}}\u003c/p\u003e\r\n\u003c/div\u003e\r\n```\r\n... is equivilant to\r\n```html\r\n\u003cdiv\u003e\u003cp\u003e{{nested-model.greeting}}\u003c/p\u003e\u003c/div\u003e\r\n```\r\n... except when you use `hb-with` for a model you create a subview and any change events that fire show only the sub-view and the sub-model.\r\n\r\nSlightly more useful than this is the ability to iterate through collections with `hb-with`\r\n```html\r\n\u003cul hb-with=\"nested-collection\"\u003e\r\n  \u003cli\u003e{{name}}\u003c/li\u003e\r\n\u003c/ul\u003e\r\n```\r\n... this then automatically clones the li tag for every model inside the collection.\r\n\r\n### hb-trigger=\"hyperbone-event\"\r\n\r\nOn clicking an element with the hb-trigger attribute, a subscribeable hyperbone event is fired. The handler is passed three parameters - the originating model, the name of the signal and a function to cancel any default DOM events.\r\n\r\nThis solves a particular problem of being able to access individual models within collections without doing horrible things to the DOM.\r\n\r\nA futher example:\r\n\r\nOur model contains...\r\n```js\r\n{\r\n  filters : [\r\n    {\r\n      name : \"Filter one\",\r\n      active : true\r\n    },\r\n    {\r\n      name : \"Filter two\",\r\n      active : false\r\n    }\r\n  ]\r\n}\r\n```\r\nAnd our view makes a new li for each filter. The scope of each li is the individual model in the collection.\r\n```html\r\n\u003cul hb-with=\"filters\"\u003e\r\n  \u003cli class=\"if(model.get('active'), 'active')\" hb-trigger=\"filter-changed\"\u003e{{name}}\u003c/li\u003e\r\n\u003c/ul\u003e\r\n```\r\nWhich means when that li is clicked, the 'filters-changed' event fires on the 'filters' object (in backbone style that's `filters-changed:filters`), and the first parameter is the individual filter.\r\n```js\r\nmodel.on('filters-changed:filters', function( filter, signal, cancelDefault ){\r\n\r\n  // call cancelDefault() to prevent the default DOM event from firing.\r\n\r\n  filter.set('active', true);\r\n})\r\n```\r\n\r\n### hb-click-toggle=\"model-attribute\"\r\n\r\nThe most common use case for `hb-trigger` is actually just toggling a flag on or off, so this custom attribute automates this for you.\r\n\r\n```html\r\n\u003csection\u003e\r\n  \u003csection if-not=\"editing\"\u003e\r\n    \u003cbutton hb-click-toggle=\"editing\"\u003eEdit\u003c/button\u003e\r\n    \u003cp\u003eHello {{Name}}\u003c/p\u003e\r\n  \u003c/section\u003e\r\n  \u003csection if=\"editing\"\u003e\r\n    \u003cbutton hb-click-toggle=\"editing\"\u003eView\u003c/button\u003e\r\n    \u003cp\u003eEnter your name:\u003cinput hb-bind=\"Name\"\u003e\u003c/p\u003e\r\n  \u003c/section\u003e\r\n\u003c/section\u003e\r\n```\r\n\r\nThat's really all there is to it. You can, of course, bind to the change event and do somethign else... \r\n\r\n```js\r\napp.on('change:editing', function(){\r\n  // editing has changed!\r\n})\r\n```\r\n\r\n### hb-bind\r\n\r\nThis attribute allows two-way binding to form inputs to allow an easy way to let your users interact with your model. \r\n\r\n```html\r\n\u003cbody class=\"{{theme}}\"\u003e\r\n  \u003cselect hb-bind=\"theme\"\u003e\r\n    \u003coption value=\"default\"\u003eDefault\u003c/option\u003e\r\n    \u003coption value=\"dark\"\u003eDark\u003c/option\u003e\r\n    \u003coption value=\"light\"\u003eLight\u003c/option\u003e\r\n  \u003c/select\u003e\r\n\u003c/body\u003e\r\n```\r\nWhen used with a model..\r\n```js\r\n{\r\n  theme : \"default\"\r\n}\r\n```\r\n...results in the class on the body tag being automatically updated when the user changes the select. Etc.\r\n\r\n### Adding your own custom attributes\r\n\r\nBecause Hyperbone View enforces a strict separation of model and view, your applications shouldn't be touching the DOM at all. However, sometimes, you do in fact need to touch the DOM. When you do, the idea is that you use your own custom attributes. Luckily Hyperbone View exposes an API for this.\r\n\r\n### require('hyperbone-view').registerAttributeHandler( attributeName, fn )\r\n### require('hyperbone-view').use( attributeHandlers : { attributeName : fn })\r\n\r\n`fn` is called when HyperboneView finds an element with your attribute. When called, it is passed the element, the value of the attribute as arguments and a 'cancel' function. The scope is the instance of HyperboneView itself, meaning you can use this.model and this.el (this may not be true forever)\r\n\r\nThe cancel function should be called if you do not wish the View to continue processing the node (i.e, recurse into the childNodes etc).\r\n\r\nHere's a non-disruptive non-cancelled example. We want a link to switch between `.on` and `.off` whenever it's clicked..\r\n```html\r\n\u003ca x-switch=\"status:off|on\" class=\"{{status}}\" href=\"#\"\u003e\u003c/a\u003e\r\n```\r\n```js\r\n// create a model\r\nvar model = new HyperboneModel({\r\n  status : \"\"\r\n});\r\n// register an attribute handler\r\nrequire('hyperbone-view').registerAttributeHandler('x-switch', function(node, propertyValue, cancel){\r\n\r\n    var self = this; // hey, 'this' is the HyperboneView.\r\n\r\n    // it's a custom attribute so you need to do your own \r\n    // parsing. You get 'status:on|off' passed to you.\r\n    var parts = propertyValue.split(\":\");\r\n    var prop = parts[0];\r\n    var options = parts[1].split(\"|\");\r\n\r\n    // we're in the HyperboneView scope so this works... \r\n    this.model.set(prop, options[1]);\r\n\r\n    // Create a click handler for this element..\r\n    dom(node).on('click', function(e){\r\n\r\n      e.preventDefault();\r\n\r\n      // we tweak the model here.. \r\n      if (self.model.get(prop) === options[0]){\r\n        self.model.set(prop, options[1])\r\n      } else {\r\n        self.model.set(prop, options[0])\r\n      }\r\n\r\n    })\r\n\r\n    // we don't call cancel here, so the childNodes will be processed as normal\r\n\r\n  });\r\n// create a view\r\nnew HyperboneView({ model: model, el : html});\r\n```\r\n\r\nA disruptive 'cancelling' example: Creating a new instance of HyperboneView with a different model to process the element and all its children.\r\n\r\nThis is the parent Hypermedia document. Note that it contains a rel `some-rel` which points to `/some-other-document`.\r\n```json\r\n{\r\n  \"_links\" : {\r\n    \"self\" : {\r\n      \"href\" : \"/some-document\"\r\n    },\r\n    \"some-rel\" : {\r\n      \"href\" : \"/some-other-document\"\r\n    }\r\n  },\r\n  \"greeting\" : \"Welcome to the magic world of Hypermedia\"\r\n}\r\n```\r\nAnd this is the JSON for `/some-other-document`\r\n```json\r\n{\r\n  \"_links\" : {\r\n    \"self\" : {\r\n      \"href\" : \"/some-other-document\"\r\n    },\r\n    \"other-thing\" : {\r\n      \"href\" : \"/some-document\"\r\n    }\r\n  },\r\n  \"greeting\" : \"Woooo!\"\r\n}\r\n```\r\nOur HTML. We want to manually embed `/some-other-document` into our page. We don't use the href, only the rel.\r\n```html\r\n\u003cdiv\u003e\r\n\u003cp\u003e{{greeting}}\u003c/p\u003e\r\n\u003cdiv x-embed=\"some-rel\"\u003e\u003cp\u003e{{greeting}}\u003c/p\u003e\u003c/div\u003e\r\n\u003c/div\u003e\r\n```\r\nNow we add our custom attribute handler...\r\n```js\r\n// add attribute handler\r\nrequire('hyperbone-view').registerAttributeHandler('x-embed', function(node, propertyValue, cancel){\r\n\r\n    // remove the attribute so that when we create a subview\r\n    // we don't end up back inside this handler.\r\n    node.removeAttribute('x-embed');\r\n\r\n    // Hyperbone Models have a special helper method for looking\r\n    // up the hrefs of rels.\r\n    var uri = this.model.rel(propertyValue);\r\n\r\n    // wrap our naked element in a dom object.\r\n    var root = dom(node);\r\n\r\n    // load the model...\r\n    request.get(uri).set('Accept', 'application/json+hal').end( function(err, doc){\r\n\r\n      if(!err){\r\n\r\n        // create a new view, passing it our wrapped element and a new Hyperbone Model.\r\n        new HyperboneView()\r\n          .create( root, new HyperboneModel( doc ) );\r\n\r\n      }\r\n\r\n    });\r\n\r\n    // and we don't want the original View to continue processing this node\r\n    // and the node's children, so we...\r\n    cancel();\r\n\r\n  });\r\n\r\n// create a view\r\nnew HyperboneView({model : someModel, el : myElement });\r\n\r\n```\r\nWHich should, after everything's loaded, result in..\r\n```html\r\n\u003cdiv\u003e\r\n\u003cp\u003eWelcome to the magic world of Hypermedia\u003c/p\u003e\r\n\u003cdiv x-embed=\"some-rel\"\u003e\u003cp\u003eWoooo!\u003c/p\u003e\u003c/div\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\nAs these two examples should demonstrate, using the custom attribute handler API is fairly powerful, largely unopinionated... and very very easy to abuse.\r\n\r\n## Logicless Template rules\r\n\r\nIt looks like moustache templating but it's not. It supports referencing model attributes, calling custom helpers (which are passed the referenced model attribute) and... if you really really must... you can just send in arbitrary javascript so long as it's inside a call to a custom helper.\r\n\r\nBuilt ins:\r\n\r\n- `{{property}}` automatically becomes model.get(\"property\")\r\n- `{{get(property)}}` for when you absolutely want everyone to know there's some backbone happening\r\n- `{{url()}}` gets the _links.self.href\r\n- `{{rel('some-rel')}}` gets a specific rel\r\n- `{{expresion(1 + 2 + model.get('current-value'))}}` - expression helper lets you add arbitrary javascript. Note the use of model.get to access data in the model is required in this situation.\r\n\r\nCustom helpers:\r\n\r\n- `{{myHelper(property)}}` passes model.get('property') to your custom handler\r\n\r\nWon't work:\r\n\r\n- `{{1 + 2}}` \r\n\r\n\r\n## License\r\n\r\n  MIT\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnice-digital%2Fhyperbone-view","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnice-digital%2Fhyperbone-view","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnice-digital%2Fhyperbone-view/lists"}