Created
August 11, 2012 03:29
-
-
Save pphetra/3320393 to your computer and use it in GitHub Desktop.
product catalog + form
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html> | |
| <head> | |
| <link rel="stylesheet" href="www/extjs/resources/css/ext-all.css"> | |
| <script src="www/extjs/ext-all-debug.js"></script> | |
| <style> | |
| div.product { | |
| float: left; | |
| padding: 8px 17px; | |
| margin: 5px; | |
| /* margin: 10px 0 0 25px;*/ | |
| text-align: center; | |
| line-height: 14px; | |
| color: #333; | |
| font-size: 14px; | |
| font-family: "Helvetica Neue",sans-serif; | |
| height: 130px; | |
| width: 140px; | |
| overflow: hidden; | |
| border-top: 1px solid transparent; | |
| cursor: pointer; | |
| } | |
| </style> | |
| <script> | |
| /** | |
| * @author Ed Spencer (http://sencha.com) | |
| * Transition plugin for DataViews | |
| */ | |
| Ext.define('Ext.ux.DataView.Animated', { | |
| /** | |
| * @property defaults | |
| * @type Object | |
| * Default configuration options for all DataViewTransition instances | |
| */ | |
| defaults: { | |
| duration : 750, | |
| idProperty: 'id' | |
| }, | |
| /** | |
| * Creates the plugin instance, applies defaults | |
| * @constructor | |
| * @param {Object} config Optional config object | |
| */ | |
| constructor: function(config) { | |
| Ext.apply(this, config || {}, this.defaults); | |
| }, | |
| /** | |
| * Initializes the transition plugin. Overrides the dataview's default refresh function | |
| * @param {Ext.view.View} dataview The dataview | |
| */ | |
| init: function(dataview) { | |
| /** | |
| * @property dataview | |
| * @type Ext.view.View | |
| * Reference to the DataView this instance is bound to | |
| */ | |
| this.dataview = dataview; | |
| var idProperty = this.idProperty, | |
| store = dataview.store; | |
| dataview.blockRefresh = true; | |
| dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() { | |
| this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) { | |
| element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId); | |
| }, this); | |
| }, dataview); | |
| /** | |
| * @property dataviewID | |
| * @type String | |
| * The string ID of the DataView component. This is used internally when animating child objects | |
| */ | |
| this.dataviewID = dataview.id; | |
| /** | |
| * @property cachedStoreData | |
| * @type Object | |
| * A cache of existing store data, keyed by id. This is used to determine | |
| * whether any items were added or removed from the store on data change | |
| */ | |
| this.cachedStoreData = {}; | |
| //catch the store data with the snapshot immediately | |
| this.cacheStoreData(store.data || store.snapshot); | |
| dataview.on('resize', function() { | |
| var store = dataview.store; | |
| if (store.getCount() > 0) { | |
| // reDraw.call(this, store); | |
| } | |
| }, this); | |
| dataview.store.on('datachanged', reDraw, this); | |
| function reDraw(store) { | |
| var parentEl = dataview.getTargetEl(), | |
| calcItem = store.getAt(0), | |
| added = this.getAdded(store), | |
| removed = this.getRemoved(store), | |
| previous = this.getRemaining(store), | |
| existing = Ext.apply({}, previous, added); | |
| //hide old items | |
| Ext.each(removed, function(item) { | |
| var id = this.dataviewID + '-' + item.internalId; | |
| Ext.fly(id).animate({ | |
| remove : false, | |
| duration: duration, | |
| opacity : 0, | |
| useDisplay: true, | |
| callback: function() { | |
| Ext.fly(id).setDisplayed(false); | |
| } | |
| }); | |
| }, this); | |
| //store is empty | |
| if (calcItem == undefined) { | |
| this.cacheStoreData(store); | |
| return; | |
| } | |
| this.cacheStoreData(store); | |
| var el = Ext.get(this.dataviewID + "-" + calcItem.internalId); | |
| //if there is nothing rendered, force a refresh and return. This happens when loading asynchronously (was not | |
| //covered correctly in previous versions, which only accepted local data) | |
| if (!el) { | |
| dataview.refresh(); | |
| return true; | |
| } | |
| //calculate the number of rows and columns we have | |
| var itemCount = store.getCount(), | |
| itemWidth = el.getMargin('lr') + el.getWidth(), | |
| itemHeight = el.getMargin('bt') + el.getHeight(), | |
| dvWidth = parentEl.getWidth(), | |
| columns = Math.floor(dvWidth / itemWidth), | |
| rows = Math.ceil(itemCount / columns), | |
| currentRows = Math.ceil(this.getExistingCount() / columns); | |
| //stores the current top and left values for each element (discovered below) | |
| var oldPositions = {}, | |
| newPositions = {}, | |
| elCache = {}; | |
| //find current positions of each element and save a reference in the elCache | |
| Ext.iterate(previous, function(id, item) { | |
| var id = item.internalId, | |
| el = elCache[id] = Ext.get(this.dataviewID + '-' + id); | |
| oldPositions[id] = { | |
| top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'), | |
| left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l') | |
| }; | |
| }, this); | |
| //make sure the correct styles are applied to the parent element | |
| parentEl.applyStyles({ | |
| display : 'block', | |
| position: 'relative' | |
| }); | |
| //set absolute positioning on all DataView items. We need to set position, left and | |
| //top at the same time to avoid any flickering | |
| Ext.iterate(previous, function(id, item) { | |
| var oldPos = oldPositions[id], | |
| el = elCache[id]; | |
| if (el.getStyle('position') != 'absolute') { | |
| elCache[id].applyStyles({ | |
| position: 'absolute', | |
| left : oldPos.left + "px", | |
| top : oldPos.top + "px" | |
| }); | |
| } | |
| }); | |
| //get new positions | |
| var index = 0; | |
| Ext.iterate(store.data.items, function(item) { | |
| var id = item.internalId, | |
| el = elCache[id]; | |
| var column = index % columns, | |
| row = Math.floor(index / columns), | |
| top = row * itemHeight, | |
| left = column * itemWidth; | |
| newPositions[id] = { | |
| top : top, | |
| left: left | |
| }; | |
| index ++; | |
| }, this); | |
| //do the movements | |
| var startTime = new Date(), | |
| duration = this.duration, | |
| dataviewID = this.dataviewID; | |
| var doAnimate = function() { | |
| var elapsed = new Date() - startTime, | |
| fraction = elapsed / duration, | |
| id; | |
| if (fraction >= 1) { | |
| for (id in newPositions) { | |
| Ext.fly(dataviewID + '-' + id).applyStyles({ | |
| top : newPositions[id].top + "px", | |
| left: newPositions[id].left + "px" | |
| }); | |
| } | |
| Ext.TaskManager.stop(task); | |
| } else { | |
| //move each item | |
| for (id in newPositions) { | |
| if (!previous[id]) { | |
| continue; | |
| } | |
| var oldPos = oldPositions[id], | |
| newPos = newPositions[id], | |
| oldTop = oldPos.top, | |
| newTop = newPos.top, | |
| oldLeft = oldPos.left, | |
| newLeft = newPos.left, | |
| diffTop = fraction * Math.abs(oldTop - newTop), | |
| diffLeft= fraction * Math.abs(oldLeft - newLeft), | |
| midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop, | |
| midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft; | |
| Ext.fly(dataviewID + '-' + id).applyStyles({ | |
| top : midTop + "px", | |
| left: midLeft + "px" | |
| }).setDisplayed(true); | |
| } | |
| } | |
| }; | |
| var task = { | |
| run : doAnimate, | |
| interval: 20, | |
| scope : this | |
| }; | |
| Ext.TaskManager.start(task); | |
| //show new items | |
| Ext.iterate(added, function(id, item) { | |
| Ext.fly(this.dataviewID + '-' + item.internalId).applyStyles({ | |
| top : newPositions[item.internalId].top + "px", | |
| left : newPositions[item.internalId].left + "px" | |
| }).setDisplayed(true); | |
| Ext.fly(this.dataviewID + '-' + item.internalId).animate({ | |
| remove : false, | |
| duration: duration, | |
| opacity : 1 | |
| }); | |
| }, this); | |
| this.cacheStoreData(store); | |
| } | |
| }, | |
| /** | |
| * Caches the records from a store locally for comparison later | |
| * @param {Ext.data.Store} store The store to cache data from | |
| */ | |
| cacheStoreData: function(store) { | |
| this.cachedStoreData = {}; | |
| store.each(function(record) { | |
| this.cachedStoreData[record.internalId] = record; | |
| }, this); | |
| }, | |
| /** | |
| * Returns all records that were already in the DataView | |
| * @return {Object} All existing records | |
| */ | |
| getExisting: function() { | |
| return this.cachedStoreData; | |
| }, | |
| /** | |
| * Returns the total number of items that are currently visible in the DataView | |
| * @return {Number} The number of existing items | |
| */ | |
| getExistingCount: function() { | |
| var count = 0, | |
| items = this.getExisting(); | |
| for (var k in items) { | |
| count++; | |
| } | |
| return count; | |
| }, | |
| /** | |
| * Returns all records in the given store that were not already present | |
| * @param {Ext.data.Store} store The updated store instance | |
| * @return {Object} Object of records not already present in the dataview in format {id: record} | |
| */ | |
| getAdded: function(store) { | |
| var added = {}; | |
| store.each(function(record) { | |
| if (this.cachedStoreData[record.internalId] == undefined) { | |
| added[record.internalId] = record; | |
| } | |
| }, this); | |
| return added; | |
| }, | |
| /** | |
| * Returns all records that are present in the DataView but not the new store | |
| * @param {Ext.data.Store} store The updated store instance | |
| * @return {Array} Array of records that used to be present | |
| */ | |
| getRemoved: function(store) { | |
| var removed = [], | |
| id; | |
| for (id in this.cachedStoreData) { | |
| if (store.findBy(function(record) {return record.internalId == id;}) == -1) { | |
| removed.push(this.cachedStoreData[id]); | |
| } | |
| } | |
| return removed; | |
| }, | |
| /** | |
| * Returns all records that are already present and are still present in the new store | |
| * @param {Ext.data.Store} store The updated store instance | |
| * @return {Object} Object of records that are still present from last time in format {id: record} | |
| */ | |
| getRemaining: function(store) { | |
| var remaining = {}; | |
| store.each(function(record) { | |
| if (this.cachedStoreData[record.internalId] != undefined) { | |
| remaining[record.internalId] = record; | |
| } | |
| }, this); | |
| return remaining; | |
| } | |
| }); | |
| Ext.onReady(function() { | |
| Ext.define('Brand', { | |
| extend: 'Ext.data.Model', | |
| fields: [ | |
| 'name' | |
| ] | |
| }); | |
| // สร้าง model Product | |
| Ext.define('Product', { | |
| extend: 'Ext.data.Model', | |
| fields: [ | |
| {name: 'name', type: 'string'}, | |
| {name: 'price', type: 'double'}, | |
| {name: 'brand', type: 'string'} | |
| ] | |
| }); | |
| // กำหนด store ของ Product | |
| var productStore = Ext.create('Ext.data.Store', { | |
| model: 'Product', | |
| proxy: { | |
| type: 'rest', | |
| url: 'products', | |
| reader: { | |
| type: 'json', | |
| root: 'products' | |
| } | |
| }, | |
| autoLoad: true | |
| }); | |
| cartStore = Ext.create('Ext.data.Store', { | |
| model: 'CartItem', | |
| proxy: { | |
| type: 'rest', | |
| url: 'carts' | |
| } | |
| }); | |
| var brandStore = Ext.create('Ext.data.Store', { | |
| model: 'Brand', | |
| proxy: { | |
| type: 'rest', | |
| url: 'brands', | |
| reader: { | |
| type: 'json', | |
| root: 'brands' | |
| } | |
| }, | |
| autoLoad: true | |
| }); | |
| // กำหนด template การ render Product | |
| var catalogTpl = new Ext.XTemplate( | |
| '<tpl for=".">', | |
| '<div class="product">', | |
| '<div><img width="64" height="64" src="{name}.png"/></div>', | |
| '{name}', | |
| '<div>{price}</div>', | |
| '<button>Buy</button>', | |
| '</div>', | |
| '</tpl>' | |
| ); | |
| // ร้อย template และ store เข้ากับ DataView | |
| var catalogView = Ext.create('Ext.view.View', { | |
| // กำหนด region ที่จะแสดงใน border ด้วย | |
| region: 'center', | |
| store: productStore, | |
| tpl: catalogTpl, | |
| emptyText: 'No Product Found', | |
| itemSelector: 'div.product', | |
| plugins : [ | |
| Ext.create('Ext.ux.DataView.Animated', { | |
| duration : 550, | |
| idProperty: 'name' | |
| }) | |
| ], | |
| listeners: { | |
| // ดัก event click ที่เกิดบนปุ่ม Buy | |
| // ถ้าพบให้ console.log(model.get('name')); | |
| itemclick: function(comp, model, el, index, evt) { | |
| console.log(arguments); | |
| if (evt.target.tagName === 'BUTTON') { //กรองเฉพาะ click ที่เกิดจาก <button> | |
| console.log(model.get('name')); | |
| f1.loadRecord(model); | |
| } | |
| } | |
| } | |
| }); | |
| f1 = Ext.create('Ext.form.Panel', { | |
| title: 'Product Form', | |
| region: 'east', | |
| width: 400, | |
| bodyPadding: 10, | |
| defaultType: 'textfield', | |
| items: [ | |
| { | |
| fieldLabel: 'Name', | |
| name: 'name', | |
| allowBlank: false | |
| }, | |
| { | |
| fieldLabel: 'ราคา', | |
| name: 'price', | |
| allowBlank: false | |
| }, | |
| { | |
| fieldLabel: 'brand่', | |
| name: 'brand', | |
| xtype: 'combo', // ระบุ xtype | |
| store: brandStore, | |
| displayField: 'name', | |
| queryMode: 'local' | |
| } | |
| ], | |
| buttons: [ | |
| { | |
| text: 'Save', | |
| formBind: true, | |
| handler: function() { | |
| var formPanel = this.up('form'); | |
| var record = formPanel.getRecord(); | |
| formPanel.getForm().updateRecord(record); | |
| } | |
| } | |
| ] | |
| }) | |
| var mainPanel = Ext.create('Ext.Panel', { | |
| layout: 'border', | |
| width: 700, | |
| height: 600, | |
| items: [ | |
| catalogView // dataview | |
| ,f1 // formPanel | |
| ,{ // ไม่ระบุ xtype, default เป็น Ext.Panel | |
| region: 'north', | |
| height: 50, | |
| items: [ | |
| { | |
| xtype: 'combo', // ระบุ xtype | |
| store: brandStore, | |
| displayField: 'name', | |
| queryMode: 'local', // ถ้าเป็น remote มันจะใช้ proxy ไปดึงค่ามา | |
| listeners: { | |
| // change: function(combo, newValue, oldValue) { | |
| // // set filter ให้กับ productStore | |
| // productStore.filter('brand', newValue); | |
| // }, | |
| select: function(combo, records) { | |
| productStore.clearFilter(); | |
| var r = records[0]; | |
| var brand = r.get('name'); | |
| productStore.filter('brand', brand); | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| ], | |
| renderTo: document.body | |
| }); | |
| }); | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment