I want to make a purchase order manager, where a queue is created from a database and then assembled into an accordion. Then, the user can look at requests, and then check the request when it is done. The task will then move to a "completed purchases" list.
I've been using a "notPurchased" datastore with the following server script:
query.filters.purchased._equals = false;
return query.run();
And then when the "submit" button is pressed, I call datastore.load();. However, this doesn't seem to refresh the purchase queue immediately. I have to completely refresh the page in order to see purchase request moved to 'completed'. How do I make this change instantaneous?
I figured out a solution that reduced any lag. Instead of filtering the database with a query, I bound the 'visibility' property to the proper boolean flag. Now items move instantly!
Related
I have a query that is observed on viewWillAppear on View Controller A
let query = FIRDatabase.database().reference().child("tags").
queryOrdered(byChild: "users/uid").queryEqual(toValue: userId)
In the same view controller, in viewDidDisappear I remove this observer.
So let's say I push into another view controller from View Controller A.
When I come back to View Controller A, the observer returns a snapshot, even though my data on the backend hasn't changed.
I only want a snapshot returning if there's been some actual change to my database. How do I solve this problem? Thanks.
One answer is to simply let the observer do it's job and update your data in the background.
So if the user is on controllerA the observer updates a datasource (an array) so then the UI is updated from that array.
When the user switches to controllerB, the observer can still update the array, but don't update the UI in controllerA since there's no need to.
When the user switches back to A you'll have current data available so just reload the tableView (assuming iOS here) from the array.
This solution reduces the 'polling' nature and let's Firebase do the heavy lifting to notify your app when it needs to. You're just reloading the tableViews from an array when that controller becomes active.
Edit
The idea here is to add the observer once - perhaps when the view loads the first time only (viewDidLoad) or maybe in your app delegate. Once you add the observer it will update your dataSource arrays when data changes so when you move from view to view the only action needed will be to reload the tableView from the updated array.
There are times when you may want to remove an observer but it doesn't sound like you need to do that from your question. So - attach the observers once and let them update the dataSource arrays as the data changes. Reload your tableViews when switching views.
You have put the query in viewWillAppear, which means every time you come to viewController A, this query will be executed irrespective of you have removed the observer or not.
Try putting the same query in viewDidLoad which means, the query will be called once and don't remove the observer anywhere. Now the query would be called only when data gets changed in firebase.
I have a template in which a user should be able to click on a button to bring up a modal and in the modal choose a handful of items out of a list of about 10,000 items which are displayed there to search or scroll through.
Since this collection is so big, I don't want to keep it around in memory when I don't absolutely need it.
So I would like to subscribe to this collection only when the modal is being viewed and I would like to ensure that I am unsubscribed if the modal is not being viewed.
Is there a way to explicitly unsubscribe from a collection?
There are a couple of ways you can do this:
Use the subscription handle
subscribe returns a handle you can call stop on. For example:
var handle = Meteor.subscribe('stuff');
handle.stop();
Use an autorun
Because an autorun will automatically start and stop subscriptions when its reactive dependencies change, this will work:
Tracker.autorun(function () {
if (Session.get('showingModal'))
Meteor.subscribe('stuff');
});
Side note - it may make more sense to use a method call for searching such a large data set rather than publishing the entire thing to the client. For example you can set a session variable whenever the user's query changes, then use an autorun to update the result set based on the method's return value.
https://docs.meteor.com/#/full/meteor_subscribe
Quoting the docs :
Meteor.subscribe returns a subscription handle, which is an object
with the following methods:
stop() Cancel the subscription. This will typically result in the
server directing the client to remove the subscription's data from the
client's cache.
So basically what you need to do is storing the subscription handle in a variable and call the stop method when you don't need those published documents anymore.
Note that if you're using iron:router (and you probably should), this is taken care of automatically for you on each route change, which is convenient but has the side effect of provoking a lot of sometimes unnecessary calls to Meteor.publish calls which are non trivial for the server and bandwidth... to address this matter you can use meteorhacks:subs-manager but it's another topic anyway.
Say my application has a list of items of some kind, and users can insert new items in the list.
What Meteor normally does is: when a user inserts an item in the list, it appears in their browser immediately, without waiting for server confirmation.
What I want is: when an item is in this state (submitted but not yet acknowledged by the server), it appears at its correct position in the list, but greyed out.
Is there a way to make Meteor do this?
Sure. Make a method that does the insertion. When the method runs, have it check to see if it is running in simulation, and if so, set a 'temporary' or 'unconfirmed' flag on the inserted item. Use that to decide whether to render the item as greyed out.
Assuming you're using MongoDB:
// Put this in a file that will be loaded on both the client and server
Meteor.methods({
add_item: function (name) {
Items.insert({name: name,
confirmed: !this.isSimulation});
}
});
Calling the method:
Meteor.call("add_item", "my item name");
That's all you need to do. The reason this works is that once the server has finished saving the item, the local (simulated) changes on the client will be backed out and replaced with whatever actually happened on the server (which won't include the 'unconfirmed' flag.)
The above is the simplest way to do it, but it will result in all of the
records in your database having a 'confirmed' attrbiute of true. To avoid this, only set the confirmed attribute if it's false.
Refer to this part of documentation for more information about isSimulation and Meteor.methods
This is what I did added an observer on the server side,
I created a variable called notify false from the client side itself
once the server receives the udpate it will make notify true and the client will be updated on the same.
Collection.find({"notify":false}).observe({
"added" : function(first){
collection.update({"_id":first._id},{$set : {"notify":true}});
}
});
I'm writing a wizard UI based on the QWizard Qt object. There's one particular situation where I want the user to log in to a service using host, username, and password. The rest of the wizard then manipulates this service to do various setup tasks. The login may take a while, especially in error cases where the DNS name takes a long time to resolve -- or perhaps it may not even resolve at all.
So my idea is to make all three fields mandatory using the registerField mechanism, and when the user hits Next, we show a little throbber on the wizard page saying "Connecting to server, please wait..." while we try to connect in the background. If the connection succeeds, we advance to the next page. If not, we highlight the offending field and ask the user to try again.
However, I'm at a loss for how to accomplish this. The options I've thought of:
1) Override validatePage and have it start a thread in the background. Enter a wait inside validatePage() that pumps the Qt event loop until the thread finishes. You'd think this was the ugliest solution, but...
2) Hide the real Next button and add a custom Next button that, when clicked, dispatches my long running function in a thread and waits for a 'validation complete' signal to be raised by something. When that happens, we manually call QWizard::next() (and we completely bypass the real validation logic from validatePage and friends.) This is even uglier, but moves the ugliness to a different level that may make development easier.
Surely there's a better way?
It's not as visually appealing, but you could add a connecting page, and move to that page. If the connection succeeds, call next() on the wizard, and if the connection fails, call previous() and highlight the appropriate fields. It has the advantage of being relatively straightforward to code.
My final choice was #2 (override the Next button, simulate its behavior, but capture its click events manually and do the things I want to with it.) Writing the glue to define the Next button's behavior was minimal, and I was able to subclass QWizardPage with a number of hooks that let me run my task ON the same page, instead of having to switch to an interstitial page and worry about whether to go forwards or backwards. Thanks Caleb for your answer though.
I know this has already been answered (a long time ago!) but in case anyone else is having the same challenge. Another method for this is to create a QLineEdit, initiate it as empty and set it as a mandatory registered field. This will mean that "Next" is not enabled until it is filled with some text.
Run your connection task as normal and when it completes use setText to update the QLineEdit to "True" or "Logged in" or anything other than empty. This will then mean the built in isComplete function will be passed as this previously missing mandatory field is now complete. If you never add it to the layout then it won't be seen and the user won't be able to interact with it.
As an example ...
self.validated_field = QLineEdit("")
self.registerField('validated*', self.validated_field)
and then when your login process completes successfully
self.validated_field.setText("True")
This should do it and is very lightweight. Be sure though that you consider the scenario where a user then goes back to that page and whether you need to reset the field to blank. If that's the case then just add in the initialisePage() function to set it back to blank
self.validated_field.setText("")
Thinking about it you could also add the line edit to the display and disable it so that a user cannot update it and then give it a meaningful completion message to act as a status update...
self.validated_field = QLineEdit("")
self.validated_field.setDisabled(True)
self.validated_field.setStyleSheet("border:0;background-color:none")
self.main_layout.addWidget(self.validated_field)
self.registerField('validated*', self.validated_field)
and then when you update it..
self.validated_field.setText("Logged in")
I have a system set up to lock certain content in a database table so only one user can edit that content at a time. Easy enough and that part is working fine. But now I'm at a road block of how to send a request to "unlock" the content. I have the stored procedure to unlock the content, but how/where would I call it when the user just closes their browser?
You also can't know when the user turns off his computer. You have to do it the other way around.
Require that the lock be renewed periodically. Only the web site would do the periodic renewal. If the user stops using the web site, then the lock expires.
Otherwise, require the user to explicitly unlock the content. Other users who want to edit the content can then go yell at the first user when they can't do their jobs. Not a technological solution, but still a good one. Shame works.
The best thing you can really do is add something to your Session_End in your global.asax. Unfortunately, this won't fire until the session times out.
When the user clicks the "X" in their browser, there isn't anyway to guarantee the browser will send you anything back.
A quick note on the Session_End approaches. If you use this method, then you have to ensure
That sessionstate is InProc, eg. add something like this to your Web.config
<sessionState mode="InProc" timeout="timeout_in_minutes"/>
Make sure that you've setup IIS as to not recycle worker processes during normal operation (see for instance this blog post).
Edit:
Not directly answering the question directly, but another approach would be to use Optimistic concurrency control on the data in question.
There is such event as "user closes browser".
Nevertheless, I can think of two workarounds:
Use Javascript/Ajax to permanently
(lets say every 10 seconds) call a
method in your page. The DateTime of
your last query needs to be stored
somewhere. Now you write a windows
service that checks every second
which session are timed out. Perform
your custom action there.
Use the global.asax Session_End()
-Event. (cannot be used with every SessionState, look up for which ones
it is usable)
Trying to leave a stackoverflow answer page pops up an "are you sure" dialog. Perhaps during the on-page-leave event that SO uses (or however SO does this), you can send a final request with an XmlHttpRequest object. This won't cover if the browser process closes unexpectedly (use session_onend for that), but it will at least send the "I'm closed" event earlier
I think your one stored procedure can do the locking and unlocking (used with "Select #strNewMax As NewMax")...
Here is an example from a system I have:
Declare #strNewMax Char
Select #strNewMax = 'N'
BEGIN TRANSACTION
/* Lock only the rows for this Item ID, and hold those locks throughout the transaction. */
If #BidAmount > (Select Max(AB_Bid_AMT) from AuctionBid With(updlock, holdlock) Where AB_AI_ID = #AuctionItemId)
Begin
Insert Into AuctionBid (AB_AI_ID, AB_Bid_AMT, AB_Emp_ID, AB_Entry_DTM)
Select #AuctionItemId, #BidAmount, #EmployeeId, GetDate()
Select #strNewMax = 'Y'
End
COMMIT TRANSACTION
Select #strNewMax As NewMax
This will insert a record as the next highest bid, all while locking the entire table, so no other bids are processed at the same time. It will return either a 'Y' or 'N' depending on if it worked or not.
Maybe you can take this and adjust it to fit your application.