So in Worklight, using JSONStore I want to initialize a collection the first time the app is ever loaded.
I want to fill it with a 'status' field with 36 instances of it. On the very first time the app is loaded I want to make all of these set to 0.
After this first initialization the app will update the status values from time to time based on user actions...
How do I initialize all the values at zero just the first time, and not again after that.
Thanks!
(and sorry if this question makes no sense..)
There's a count API you can use to get the number of documents in the collection. If that number is 0, it means it's the first time the collection was initialized, so you can add your 36 instances with status 0 there. For example:
WL.JSONStore.init(...)
.then(function () {
return WL.JSONStore.get('collection').count();
})
.then(function (numOfDocsInCollection) {
if (numOfDocsInCollection < 1) {
//this code will be reached only the first time the collection has been initialized
} else {
//this code will be reached every other time
}
});
Related
I have quite a complicated situation and i'm not amazing at Vue so I need some help.
I have a Firebase DB that gets an array (clients) and displays it.
const clientsRef = db.ref('clients')
firebase: {
clients: {
source: clientsRef,
//data has been retrieved from firebase
readyCallback: function() {
this.getSiteCount() // Get number of sites associated with client
this.loaded = true // Hide loader bar once this becomes true
}
}
},
On load complete getSiteCount() will get the clients unique ID and compare it against the sites DB and count how many exist. Below code simply loops around each client and then checks how many sites have the client_id of aClient['.key']. Not really important this works and gets the count and adds it to the clients array.
getSiteCount: function() {
this.clients.forEach((server, clientIndex) => {
this.clients[clientIndex].siteCount= 0
serverContactsRef.orderByChild('client_id').equalTo(server['.key']).on('child_added', (clientDetails) => {
this.clients[clientIndex].siteCount= this.clients[clientIndex].siteCount+ 1
})
})
},
Now in my html I have v-for="clients in filterClients" and the computed function...
filterClients: function() {
function compare(a, b) {
if (a.siteCount < b.siteCount) {
return 1
}
if (a.siteCount > b.siteCount) {
return -1
}
return 0
}
return this.clients.sort(compare)
}
I suspect because the getSiteCount() function runs once the clients have been pulled (0.5s delay) from the DB it's initial siteCount value is 0 and filterClients runs before those values get set. I need to delay filterClients() until the getSiteCount() function runs or need it to update automatically when the getSiteCount() function runs.
Can someone help me make sure the initial load of the page displays the clients in order of how many sites it has (siteCount)
It was in fact a Caveat.
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
I changed
this.clients[clientIndex].siteCount= 0
to
Vue.set(this.clients[clientIndex], 'contractsNr', 0)
Updates when the data comes in perfectly now.
Thanks Jacob
I'm using Vuex with a getter that filters a lot of data and then some components present it to the user grouped by status. The user can increment the visible count of elements per status by 5. How many items are visible currently is on the Vuex store and a getter uses this to create a "View object".
When I update this visibility object the getter is no rerun so something in the dependency tracking went south. I'm not adding or deleteing properties, still I'm using Vue.set(...) just to be sure.
This is the mutation that increments the visible amount of items for a status:
viewMore(state, status) {
console.log('viewMore')
const current = state.visibility.statuses[status]
Vue.set(state.visibility.statuses, status, current + 5)
}
This mutation is running well and I can see in the developer tools how the visibility increments reactively with every commit. Now here is the getter that depends on this data:
visibleProspects(state, getters) {
console.log('visibleProspects')
let result = {}
for (const status in getters.sourceData) {
if (!result[status]) {
result[status] = { prospects: [] }
}
getters.sourceData[status].forEach(function(prospect) {
if (result[status].prospects.length < state.visibility.statuses[status])
result[status].prospects.push(prospect)
})
}
return result
}
What this does is traverses a complex getter named sourceData (not shown here for brevity) and then depending on how many visible items there are it returns a new structure with that maximum in an array. visibleProspects is then used by my components and everything runs fine the first time or if a update the data that sourceData computes (e.g adding / editing / deleting a prospect).. but no matter what I do modifying state.visibility.statuses is not forcing visibleProspects to recompute.
How can I debug this?
You can make deep copy to make it reactive (using JSON.parse(JSON.stringify())
viewMore(state, status) {
console.log('viewMore')
const current = state.visibility.statuses[status]
state.visibility.statuses[status] = current + 5
state.visibility = JSON.parse(JSON.stringify(state.visibility))
}
#ittus 's answer should work. But the clone operation would be heavy if your state is big.
alternatively, you may try using Vue.set on the root state state.visibility instead. This should make the reactivity works as expected.
Vue.set(state.visibility, 'statuses', {
...state.visibility.statuses,
[status]: current + 5
})
I am creating a search toolbar that allows the user to see their most recent searches, using Realm Browser as my database. I save a search whenever the user types in the TextInput component, however, I don't want to add a search term after each key stroke, but only after the user has stopped typing for certain amount of time.
handleOnChange function will update state and only call getResults after 2 seconds
handleOnChange(text) {
this.setState({
searchStr: text
}, () => setTimeout(() => {
this.getResults()
}, 2000))
}
In getResults, I call my addRecentSearch function if certain criteria is met.
getResults() {
let searchTags = []
let searchCalcs = []
let tagNames = this.state.tags.map((tag) => {
return tag.name
})
if (this.state.searchStr.length >= 2 || this.state.tags.length !== 0) {
searchCalcs = Realm.searchCalcs(this.state.searchStr, tagNames)
Realm.addRecentSearch(this.state.searchStr)
}
this.setState({
results: searchCalcs,
tagsForFiltering: searchTags
})
}
So I use setTimeout to allow enough time for my state to get updated when the user types. Then, once the states been updated, I will want to add the search query. However, I'm not getting the results I expected when grabbing the most recent searches.
For example:
Type: "h"
Result: nothing happens as str must be at least 2 characters in length
Type: "he"
Result: meets criteria, and will add "he" as a recent search term.
Arr: ["he"]
Type: "heart" (Note: adding 3 characters in succession)
Result: It seems that even with the timeout function, my getResults function is being called (thus adding the search query for each character I added)
Arr: ["he", "heart", "heart", "heart"]
I want my arr to look like:
arr: ["he", "heart"]
You aren't fully debouncing in your example. You are only delaying everything by 2000ms. You need to create a timer and then reset it every time a change happens (by clearing and starting timer again). In this way, only the final 'delay' takes effect. Make sense?
You are very close to have written your own debounce function, so you can use clearTimeout, or there are some libraries that do it. See Lodash https://lodash.com/docs/#debounce
I have a few properties that have change handlers that request a repaint of a canvas. This works fairly well, but I would like to debounce the signal because a user can only see a limited number of refreshes and some of the data items can change quite frequently. This causes far more refreshes than the user can see and reduces how responsive the UI is.
I have looked at debouncing the signal in javascript (there are examples on the web for how to do that) and tie it in to QML, but I haven't figured out how to get QML and javascript to work together in that way yet.
I would love to see something like the following:
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
canvas.requestPaint()
}
onValueChanged: {
rateLimitedRefresh();
}
With the requestPaint method only being called on the canvas a maximum of 30 times per second.
I used a modification of Mertanian's answer. This modification enforces the frame rate limit at a per frame level as opposed to a per second level.
property var limitStartTime: new Date()
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
var now = new Date();
if (now - limitStartTime >= 32) {
limitStartTime = now
canvas.requestPaint()
}
}
How about something like this:
property var limitStartTime: new Date()
property int refreshesThisSecond: 0
function rateLimitedRefresh(){
// magic to limit to 30 frames per second
if ((new Date()) - limitStartTime >= 33) {
limitStartTime = new Date(limitStartTime.getTime() + 33)
canvas.requestPaint()
}
}
onValueChanged: {
rateLimitedRefresh();
}
I am using extjs 4.1.1a for developing some application.
I had a form consisting of two combo-boxes and an item-selector.
Based on the value selected in first combo-box , the itemselector will load its data from database. This is working fine.
My problem is, if i reselect the first combo-box the new data will be displayed in itemselector along with previous data displayed in itemseletor .That is previous data displayed in itemselector will remain there itself.
for example: name "test1" consists of ids 801,2088,5000. on selecting test1 in firstcombobox itemselector must show output as below.
and if "test2" consists of ids 6090,5040. on selecting test2 in firstcombobox itemselector must show output as below.
problem is. for first time if i select "test1" from firstcombobox , output will come as expected. if i reselect "test2" from firstcombobox , output will come as below.
as you can see, previous data displayed (marked in red rectagle) remains there itself with new data displayed (marked with green rectangle).
I want for every reselection of first combobox, previously displayed data in itemselector to be erased before printing new data on itemselector.
How can I reset the itemselector for every reselection of first combobox?
You should remove all items from the store of the itemselector by the removeAll command. After that you should load the store of the itemselector.
itemselector.store.removeAll();
itemselector.store.load();
Any solutions above solve my problem.
i found solution from Sencha Forum.
https://www.sencha.com/forum/showthread.php?142276-closed-Ext-JS-4.0.2a-itemselector-not-reloading-the-store
in the itemselector.js file, change the line marked below.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
// THIS LINE BELOW MUST BE CHANGED!!!!!!!!!!!!
fromStore.loadData(store.getRange()); //fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
You need to insert...
this.reset();
at the head of the function that is inserting the data.
As an example...
Ext.override( Ext.ux.ItemSelector, {
setValue: function(val) {
this.reset();
if (!val) return;
val = val instanceof Array ? val : val.split(this.delimiter);
var rec, i, id;
for (i = 0; i < val.length; i++) {
var vf = this.fromMultiselect.valueField;
id = val[i];
idx = this.toMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
if (idx != -1) continue;
idx = this.fromMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
rec = this.fromMultiselect.view.store.getAt(idx);
if (rec) {
this.toMultiselect.view.store.add(rec);
this.fromMultiselect.view.store.remove(rec);
}
}
}
});
are u got it?
when u select that combobox frist stoe of item selector is null after store load with ur pass the para meters
for example
store.load(null),
store.proxey.url='jso.php?id='+combobox.getrawvalue(),
store.load();
like that so when ur select a value in ur combobox that time ur used a listeners to ur combobox in that listners ur used above code , select ur some value in combobox that time frist store is get null after ur pass some values to json.php then store load with responce so that time old data is remove and new data load in that store
if u post ur code i will give correct code
I ran into the same issue with ExtJS 4.2.1. I got it to work by calling reload() on the data store and then setValue() with an empty string on the item selector in the data store's reload() callback.
Ext.create("Ext.form.field.ComboBox", {
// Other properties removed for brevity
listeners: {
change: function(field, newValue, oldValue, eOpts) {
Ext.getStore("ExampleStore").reload({
callback: function() {
Ext.getCmp("ExampleItemSelector").setValue("");
}
});
}
}
});
Ext.create("Ext.data.Store", {
storeId: "ExampleStore",
// Other properties removed for brevity
});
Ext.create("Ext.form.FormPanel", {
// Other properties removed for brevity
items:[{
xtype: "itemselector",
id: "ExampleItemSelector",
// Other properties removed for brevity
}]
});
For any folks that are curious, I'm fairly convinced there's a bug in the item selector's populateFromStore() function. When the function is called, it blindly adds all of the values from the bound store (store) to the internal store (fromStore). I suspect there should be a call to fromStore.removeAll() prior to the call to fromStore.add(). Here's the relevant code from ItemSelector.js.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
EDIT 12/18/2013
If you've configured any callback events on the item selector (e.g. change), you may want to disable the events temporarily when you call setValue(""). For example:
var selector = Ext.getCmp("ExampleItemSelector");
selector.suspendEvents();
selector.setValue("");
selector.resumeEvents();
I had the same problem and finally I decided to modify the extjs source code, not considering it a big issue as extjs itself its saying in the start of the file
Note that this control will most likely remain as an example, and not as a core Ext form
control. However, the API will be changing in a future release and so should not yet be
treated as a final, stable API at this time.
Based on that, as jstricker guessed (and sadly I didn't read and took me a while to arrive to the same conclusion), adding fromStore.removeAll() before fromStore.add() solves the problem.
Outside of the problem (but I think it can be interesting as well), additionally, I also added listConfig: me.listConfig in the MultiSelect configuration (inside createList), that way it's possible to format each item additional options (such as images, etc.) setting in the 'itemselector' the option listConfig as it's explained in the (irrealistic) documentation.
Need to reset the store used in ItemSelector that can be done by setting Empty object like below. Also need to call clearValue() method of ItemSelector component.
store.setData({});
ItemSelectorComponent.clearValue();