<html>
<head>
<meta name="description" content="Blog/Comment nested model example" />
<meta charset="utf-8">
<title>Post/Comment nested model example</title>
<link rel="stylesheet" href="http://www.mattburns.co.uk/temp/ember/css/normalize.css">
<link rel="stylesheet" href="http://www.mattburns.co.uk/temp/ember/css/style.css">
</head>
<body>
<script type="text/x-handlebars">
<h1>A blog full of {{#link-to 'posts'}}posts{{/link-to}}</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="posts">
<h2>Latest Posts</h2>
{{#each model}}
<li>
{{#link-to 'post' this}}
{{title}}
{{/link-to}}
</li>
{{/each}}
Add a post: {{view Ember.TextField id="new-post" placeholder="enter title"
valueBinding="newTitle" action="create"}}
</script>
<script type="text/x-handlebars" data-template-name="post">
<h2>{{title}}</h2>
Read the {{#link-to 'comments' this}}comments{{/link-to}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="comments">
<h3>Comments for {{controllers.post.title}}</h3>
{{#each controllers.post.comments}}
<li>
{{message}}
</li>
{{/each}}
Add a comment: {{view Ember.TextField id="new-comment" placeholder="enter message"
valueBinding="newMessage" action="create"}}
</script>
<script src="http://www.mattburns.co.uk/temp/ember/js/libs/jquery-1.9.1.js"></script>
<script src="http://www.mattburns.co.uk/temp/ember/js/libs/handlebars-1.0.0.js"></script>
<script src="http://www.mattburns.co.uk/temp/ember/js/libs/ember-1.0.0.js"></script>
<script src="http://builds.emberjs.com/ember-data-latest.js"></script>
<script type="text/javascript">
/*global Ember*/
/*global DS*/
'use strict';
DS.LSAdapter = DS.Adapter.extend(Ember.Evented, {
init: function () {
this._loadData();
},
generateIdForRecord: function () {
return Math.random().toString(32).slice(2).substr(0, 5);
},
find: function (store, type, id) {
var namespace = this._namespaceForType(type);
return Ember.RSVP.resolve(Ember.copy(namespace.records[id]));
},
findMany: function (store, type, ids) {
var namespace = this._namespaceForType(type);
var results = [];
for (var i = 0; i < ids.length; i++) {
results.push(Ember.copy(namespace.records[ids[i]]));
}
return Ember.RSVP.resolve(results);
},
// Supports queries that look like this:
//
// {
// <property to query>: <value or regex (for strings) to match>,
// ...
// }
//
// Every property added to the query is an "AND" query, not "OR"
//
// Example:
//
// match records with "complete: true" and the name "foo" or "bar"
//
// { complete: true, name: /foo|bar/ }
findQuery: function (store, type, query, recordArray) {
var namespace = this._namespaceForType(type);
var results = this.query(namespace.records, query);
return Ember.RSVP.resolve(results);
},
query: function (records, query) {
var results = [];
var id, record, property, test, push;
for (id in records) {
record = records[id];
for (property in query) {
test = query[property];
push = false;
if (Object.prototype.toString.call(test) === '[object RegExp]') {
push = test.test(record[property]);
} else {
push = record[property] === test;
}
}
if (push) {
results.push(record);
}
}
return results;
},
findAll: function (store, type) {
var namespace = this._namespaceForType(type);
var results = [];
for (var id in namespace.records) {
results.push(Ember.copy(namespace.records[id]));
}
return Ember.RSVP.resolve(results);
},
createRecord: function (store, type, record) {
var namespace = this._namespaceForType(type);
this._addRecordToNamespace(namespace, record);
this._saveData();
return Ember.RSVP.resolve();
},
updateRecord: function (store, type, record) {
var namespace = this._namespaceForType(type);
var id = record.get('id');
namespace.records[id] = record.toJSON({ includeId: true });
this._saveData();
return Ember.RSVP.resolve();
},
deleteRecord: function (store, type, record) {
var namespace = this._namespaceForType(type);
var id = record.get('id');
delete namespace.records[id];
this._saveData();
return Ember.RSVP.resolve();
},
// private
_getNamespace: function () {
return this.namespace || 'DS.LSAdapter';
},
_loadData: function () {
var storage = localStorage.getItem(this._getNamespace());
this._data = storage ? JSON.parse(storage) : {};
},
_saveData: function () {
localStorage.setItem(this._getNamespace(), JSON.stringify(this._data));
},
_namespaceForType: function (type) {
var namespace = type.url || type.toString();
return this._data[namespace] || (
this._data[namespace] = {records: {}}
);
},
_addRecordToNamespace: function (namespace, record) {
var data = record.serialize({includeId: true});
namespace.records[data.id] = data;
}
});
</script>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |