]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/data/DiffStore.js
cleanly separate sources from package build, move to own folder
[proxmox-widget-toolkit.git] / src / data / DiffStore.js
1 /*
2 * The DiffStore is a in-memory store acting as proxy between a real store
3 * instance and a component.
4 * Its purpose is to redisplay the component *only* if the data has been changed
5 * inside the real store, to avoid the annoying visual flickering of using
6 * the real store directly.
7 *
8 * Implementation:
9 * The DiffStore monitors via mon() the 'load' events sent by the real store.
10 * On each 'load' event, the DiffStore compares its own content with the target
11 * store (call to cond_add_item()) and then fires a 'refresh' event.
12 * The 'refresh' event will automatically trigger a view refresh on the component
13 * who binds to this store.
14 */
15
16 /* Config properties:
17 * rstore: the realstore which will autorefresh its content from the API
18 * Only works if rstore has a model and use 'idProperty'
19 * sortAfterUpdate: sort the diffstore before rendering the view
20 */
21 Ext.define('Proxmox.data.DiffStore', {
22 extend: 'Ext.data.Store',
23 alias: 'store.diff',
24
25 sortAfterUpdate: false,
26
27 // if true, destroy rstore on destruction. Defaults to true if a rstore
28 // config is passed instead of an existing rstore instance
29 autoDestroyRstore: false,
30
31 onDestroy: function() {
32 let me = this;
33 if (me.autoDestroyRstore) {
34 if (Ext.isFunction(me.rstore.destroy)) {
35 me.rstore.destroy();
36 }
37 delete me.rstore;
38 }
39 me.callParent();
40 },
41
42 constructor: function(config) {
43 let me = this;
44
45 config = config || {};
46
47 if (!config.rstore) {
48 throw "no rstore specified";
49 }
50
51 if (!config.rstore.model) {
52 throw "no rstore model specified";
53 }
54
55 let rstore;
56 if (config.rstore.isInstance) {
57 rstore = config.rstore;
58 } else if (config.rstore.type) {
59 Ext.applyIf(config.rstore, {
60 autoDestroyRstore: true,
61 });
62 rstore = Ext.create(`store.${config.rstore.type}`, config.rstore);
63 } else {
64 throw 'rstore is not an instance, and cannot autocreate without "type"';
65 }
66
67 Ext.apply(config, {
68 model: rstore.model,
69 proxy: { type: 'memory' },
70 });
71
72 me.callParent([config]);
73
74 me.rstore = rstore;
75
76 let first_load = true;
77
78 let cond_add_item = function(data, id) {
79 let olditem = me.getById(id);
80 if (olditem) {
81 olditem.beginEdit();
82 Ext.Array.each(me.model.prototype.fields, function(field) {
83 if (olditem.data[field.name] !== data[field.name]) {
84 olditem.set(field.name, data[field.name]);
85 }
86 });
87 olditem.endEdit(true);
88 olditem.commit();
89 } else {
90 let newrec = Ext.create(me.model, data);
91 let pos = me.appendAtStart && !first_load ? 0 : me.data.length;
92 me.insert(pos, newrec);
93 }
94 };
95
96 let loadFn = function(s, records, success) {
97 if (!success) {
98 return;
99 }
100
101 me.suspendEvents();
102
103 // getSource returns null if data is not filtered
104 // if it is filtered it returns all records
105 let allItems = me.getData().getSource() || me.getData();
106
107 // remove vanished items
108 allItems.each(function(olditem) {
109 let item = me.rstore.getById(olditem.getId());
110 if (!item) {
111 me.remove(olditem);
112 }
113 });
114
115 me.rstore.each(function(item) {
116 cond_add_item(item.data, item.getId());
117 });
118
119 me.filter();
120
121 if (me.sortAfterUpdate) {
122 me.sort();
123 }
124
125 first_load = false;
126
127 me.resumeEvents();
128 me.fireEvent('refresh', me);
129 me.fireEvent('datachanged', me);
130 };
131
132 if (me.rstore.isLoaded()) {
133 // if store is already loaded,
134 // insert items instantly
135 loadFn(me.rstore, [], true);
136 }
137
138 me.mon(me.rstore, 'load', loadFn);
139 },
140 });