//Define Schema var user_schema = { "User": { "id": "User", "type": "object", "properties": { "name": { "type": "string", "optional": false }, "email": { "type": "string", "format": "email", }, "awesome": { "type": "boolean" } } } }; //Feed it to Builder - resolves dependencies and preps var builder = new Y.inputEx.JsonSchema.Builder({ 'schemaIdentifierMap': user_schema, 'defaultOptions': { 'showMsg': true } }); // Todo: More appropriate way to show this in an example? UserNS = Y.namespace('MySchemApp.User'); // Create inputex definition from schema UserNS.inputEx_def = builder.schemaToInputEx(user_schema.User); UserNS.schema = user_schema; Y.UserModel = Y.Base.create('userModel', Y.Model, [], { schemApp: Y.MySchemApp.User, // This tells the Model to use a localStorage sync provider (which we will // create below) to save and load information about a user item. sync: Y.LocalStorageSync('SchemApp_User') }, { ATTRS: UserNS.schema.User.properties }); // -- User view ----------------------------------------------------------- // The UserView class extends Y.View and customizes it to represent the content // of a single user entry in the list. It also handles DOM events on the entry to // allow it to be edited and removed from the list. Y.UserView = Y.Base.create('userView', Y.View, [], { container: Y.one('#user-item-template').getContent(), // Delegated DOM events to handle interactions within this view. events: { // When the text of this item is clicked or focused, switch to edit // mode to allow editing. '.user-view': { mousedown: 'edit', focus: 'edit' }, // When the remove icon is clicked, delete this user. '.user-remove': { click: 'remove' } }, initializer: function() { // Gets passed when view is instanced. var model = this.model, form_def = model.schemApp.inputEx_def, view = this; //Dirty hack to prevent saving too often view.oldVals = ""; Y.inputEx.use([form_def], function() { //Create form view.form = Y.inputEx(form_def); // TODO: Move to event handler or callback // This obviously should not be here // but right now we cannot be sure if we already rendered //Set Values view.form.setValue(model.getAttrs()); view.form.disable(); //Append to container view.container.append(view.form.divEl); // Attach form listener view.form.after('updated', view.save, view); }); // Re-render this view when the model changes, and destroy this view // when the model is destroyed. model.after('change', this.render, this); model.after('destroy', this.destroy, this); }, render: function() { var container = this.container, model = this.model, form = this.form, form_def = model.schemApp.inputEx_def; //TODO: See above re callbacks or events if (form) { //Set Values form.setValue(model.getAttrs()); //Append to container container.append(form.divEl); } this.inputNode = container.one('input[name="name"]'); return this; }, // -- Event Handlers ------------------------------------------------------- // Toggles this item into edit mode. edit: function() { this.form.enable(); }, // Removes this item from the list. remove: function(e) { e.preventDefault(); this.constructor.superclass.remove.call(this); this.model.destroy({ 'delete': true }); }, // Toggles this item out of edit mode and saves it. save: function() { var currVals = this.form.getValue(), oldVals = this.oldVals; if (this.form.validate() && oldVals != currVals) { oldVals = currVals; Y.log('saving'); this.form.disable(); this.model.setAttrs(currVals).save(); } } }); // -- ModelList ---------------------------------------------------------------- // The UserList class extends Y.ModelList and customizes it to hold a list of // UserModel instances, and could provide some convenience methods for getting // information about the items in the list. Y.UserList = Y.Base.create('userList', Y.ModelList, [], { // This tells the list that it will hold instances of the UserModel class. model: Y.UserModel, sync: Y.LocalStorageSync('SchemApp_User'), // Returns an array of all models in this list with the `awesome` attribute // set to `false`, or maybe true - you figure it out. awesome: function(match) { return Y.Array.filter(this.toArray(), function(model) { return model.get('awesome') == match; }); } }); // -- AppView ---------------------------------------------------------------- UserAppView = Y.UserAppView = Y.Base.create('userAppView', Y.View, [], { // The container node is the wrapper for this view. container: Y.one('#container1'), template: Y.one('#user-stats-template').getContent(), // This is a custom property that holds a reference to the // "Add User" input field. addUserNode: Y.one('.add-user'), // This is where we attach DOM events for the view. The `events` object is a // mapping of selectors to an object containing one or more events to attach // to the node(s) matching each selector. events: { // Handle clicks on the "Add User" element. '.add-user': { click: 'createUser' }, // Show only awesome users '.only-awesome': { click: 'onlyAwesome' } }, // The initializer runs when a UserAppView instance is created, and gives // us an opportunity to set up the view. initializer: function(config) { // Create a new UserList instance to hold the users. var list = this.userList = new Y.UserList(); // Update the display when a new item is added to the list, or when the // entire list is reset. list.after('add', this.add, this); list.after('reset', this.reset, this); // Re-render the stats in the footer whenever a user is added, removed // or changed, or when the entire list is reset. list.after(['add', 'reset', 'remove', 'userModel:doneChange'], this.render, this); // Load saved items from localStorage, if available. list.load(); }, // The render function is called whenever an user is added, removed, or // changed, thanks to the list event handler we attached in the initializer // above. render: function() { var userList = this.userList, stats = this.container.one('#user-stats'), numAwesome, numUnAwesome; // If there are no users, then clear the stats. if (userList.isEmpty()) { stats.empty(); return this; } // Figure out how many awesome users there are. numAwesome = userList.awesome(true).length; numUnAwesome = userList.awesome(false).length; // Update the statistics. stats.setContent(Y.Lang.sub(this.template, { numUnAwesome: numUnAwesome, numAwesome: numAwesome, unAwesomeLabel: numUnAwesome === 1 ? 'user' : 'users', awesomeLabel: numAwesome === 1 ? 'user' : 'users' })); // If everyone is awesome, get rid of the "Purge // Users" link. if (!numUnAwesome) { stats.one('.only-awesome').remove(); } return this; }, // -- Event Handlers ------------------------------------------------------- // Creates a new UserView instance and renders it into the list whenever a // user is added to the list. add: function(e) { var view = new Y.UserView({ model: e.model }); this.container.one('#user-list').append(view.render().container); }, // Removes all unAwesome from the list. onlyAwesome: function(e) { var notSoAwesome = this.userList.awesome(false); e.preventDefault(); // Remove all unAwesome users from the list, but do it silently so as not // to re-render the app view after each item is removed. this.userList.remove(notSoAwesome, { silent: true }); // Destroy each removed UserModel instance. Y.Array.each(notSoAwesome, function(user) { // Passing {'delete': true} to the `destroy()` method on user model // tells it to delete itself from localStorage as well. user.destroy({ 'delete': true }); }); // Finally, re-render the app view. this.render(); }, // Creates a new user item when "Add User" is clicked createUser: function(e) { // This tells the list to create a new UserModel instance with the // specified values and automatically save it to localStorage in a // single step. this.userList.create({ "name": "", "email": "", "awesome": false }); }, // Creates and renders views for every user item in the list when the entire // list is reset. reset: function(e) { var fragment = Y.one(Y.config.doc.createDocumentFragment()); Y.Array.each(e.models, function(model) { var view = new Y.UserView({ model: model }); fragment.append(view.render().container); }); this.container.one('#user-list').setContent(fragment); } }); Y.userApp = new Y.UserAppView(); var awesomeUser = { "name": "Joe Developer", "email": "joe.d.developer@gmail.com", "awesome": true }; var gotAwesome = Y.Array.find(Y.userApp.userList.toArray(), function(u) { var ua = u.getAttrs(); return (ua.name === awesomeUser.name && ua.awesome === true); }); if (!gotAwesome) { Y.userApp.userList.create(awesomeUser); }