Asynchronously populate/pre-fill multiple, user mutable `#State` values when using SwiftUI? - swiftui

I have some TextField(s) with “.text” values that are supposed to be populated by a method called within “init()” (can be moved) that asynchronously calls a completion handler with a struct of data from the network.
The thing is... the user can also begin typing into them manually in the meantime and the completion callback shouldn’t overwrite the manually edited values.
I’m not sure how to:
Update the values of the #State String variables to replace the existing values only if they haven’t been modified by the user yet... while also having them be mutable by the end-user.
Update N number of the #State values from the same request at once. One request is used to pull all of the data in, so mapping into a single value while having it be mutable as noted above is a head scratcher at the moment.
It seems like a job for Combine, just not sure where to start yet.

I'm assuming the user-edit is per-field and all other fields should be left alone.
Move all state inside an ObservableObject, make the state variables #Published, take a publisher from each and create a sink from each setting a variable if stateX != initialStateXValue { stateXUserModified = true } inside each sink and have a method networkUpdate(loadedData:) on the ObservableObject which only sets the state if the associated stateXUserModified is false.

Related

Emberjs inside of get computed making request to backend multiple times cause infinite loop

I have a table basically in every row i have get function that makes a backend request with store service. But somehow when there is one row it works expect, but when there is multiple rows it always try to recalculate get function which makes sending infinite request to backend. I am using glimmer component
I cannot use model relation on ember side at this point, there is deep chain on backend side. Thats why i am making backend request.
get <function_name>() {
return this.store.query('<desired_model_name>', { <dependent1_id>: <dependent1_id_from_args>, <dependent2_id>: <dependent2_id_from_args> });
}
I fixed this problem with using constructor. But do you have any idea why this get function re-calculate all the time? Dependent_ids are constant.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
But do you have any idea why this get function re-calculate all the time?
When something like this happens, it's because you're reading #tracked data that is changed later (maybe when the query finishes).
because getters are re-ran every access, you'll want to throw #cached on top of it,
// cached is available in ember-source 4.1+
// or as early as 3.13 via polyfill:
// https://github.com/ember-polyfills/ember-cached-decorator-polyfill
import { cached } from '#glimmer/tracking';
// ...
#cached
get <function_name>() {
return this.store.query(/* ... */);
}
this ensures a stable object reference on the getter that the body of the getter only re-evaluates if tracked data accessed within the getter is changed.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
Given this observation, it's possible that when query finishes, that it's changing tracked data that it, itself is consuming during initial render -- in which case, you'd still have an infinite loop, even with #cached (because tracked-data is changing that was accessed during render).
To get around that is fairly hard in a getter.
Using a constructor is an ok solution for getting your initial data, but it means you opt out of reactive updates with your query (if you need those, like if the query changes or anything).
If you're using ember-source 3.25+ and you're wanting something a little easier to work with, maybe ember-data-resourecs suits your needs
the above code would be:
import { query } from 'ember-data-resources';
// ...
// in the class body
data = query(this, 'model name', () => ({ query stuff }));
docs here
This builds off some primitives from ember-resources which implement the Resource pattern, which will be making a strong appearance in the next edition of Ember.

ember.js ember-data initiale state of new created model record

Ember.data 2.2.0
The state of just created object is dirty (get('hasDirtyAttributes' return true), cause the new ID is set every time.
I need to know when the record is created, not saved and the "user" not modified it. So, I can't use the dirty state cause the store change it.
If I modified the internal state just after create the record, I will broke somthing inner the record?
My real need, is when I create the record I need a initiale state and I want detect when a user change it. So, I saw in the record source code, it's use the "setProperties" methode to set the ID and optionaly the data passed to the createRecord method.
So, I want override the createRecord store metod to set the dirty state to false after created it. And the principal, how I can do that?
I saw the doc of DS.RootState Class and it just talk about that states : (deleted, saved, uncommitted, inFlight, empty, loaded, created, updated, loading) and the method translateTo but nothing to change the dirty state.
In the doc say :
Flags are Boolean values that can be used to introspect a record's
current state in a more user-friendly way than examining its state
path
So... I set currentState.parentState.isDirty to false and that it
Edit: After set the flag directly, the record doesn't change state, stay in no dorty. So, how what I can do?
The only solution I found is test if the object is new. If is it, re-create a new one.
If not, I call the rollbackAttributes().
And use the that method on model for detect a change :
isChanged : function() {
return this._internalModel.hasChangedAttributes();
}

SFDC Apex Code: Access class level static variable from "Future" method

I need to do a callout to webservice from my ApexController class. To do this, I have an asycn method with attribute #future (callout=true). The webservice call needs to refeence an object that gets populated in save call from VF page.
Since, static (future) calls does not all objects to be passed in as method argument, I was planning to add the data in a static Map and access that in my static method to do a webservice call out. However, the static Map object is getting re-initalized and is null in the static method.
I will really appreciate if anyone can give me some pointeres on how to address this issue.
Thanks!
Here is the code snipped:
private static Map<String, WidgetModels.LeadInformation> leadsMap;
....
......
public PageReference save() {
if(leadsMap == null){
leadsMap = new Map<String, WidgetModels.LeadInformation>();
}
leadsMap.put(guid,widgetLead);
}
//make async call to Widegt Webservice
saveWidgetCallInformation(guid)
//async call to widge webserivce
#future (callout=true)
public static void saveWidgetCallInformation(String guid) {
WidgetModels.LeadInformation cachedLeadInfo =
(WidgetModels.LeadInformation)leadsMap.get(guid);
.....
//call websevice
}
#future is totally separate execution context. It won't have access to any history of how it was called (meaning all static variables are reset, you start with fresh governor limits etc. Like a new action initiated by the user).
The only thing it will "know" is the method parameters that were passed to it. And you can't pass whole objects, you need to pass primitives (Integer, String, DateTime etc) or collections of primitives (List, Set, Map).
If you can access all the info you need from the database - just pass a List<Id> for example and query it.
If you can't - you can cheat by serializing your objects and passing them as List<String>. Check the documentation around JSON class or these 2 handy posts:
https://developer.salesforce.com/blogs/developer-relations/2013/06/passing-objects-to-future-annotated-methods.html
https://gist.github.com/kevinohara80/1790817
Side note - can you rethink your flow? If the starting point is Visualforce you can skip the #future step. Do the callout first and then the DML (if needed). That way the usual "you have uncommitted work pending" error won't be triggered. This thing is there not only to annoy developers ;) It's there to make you rethink your design. You're asking the application to have open transaction & lock on the table(s) for up to 2 minutes. And you're giving yourself extra work - will you rollback your changes correctly when the insert went OK but callout failed?
By reversing the order of operations (callout first, then the DML) you're making it simpler - there was no save attempt to DB so there's nothing to roll back if the save fails.

Asynchronous network calls

I made a class that has an asynchronous OpenWebPage() function. Once you call OpenWebPage(someUrl), a handler gets called - OnPageLoad(reply). I have been using a global variable called lastAction to take care of stuff once a page is loaded - handler checks what is the lastAction and calls an appropriate function. For example:
this->lastAction == "homepage";
this->OpenWebPage("http://www.hardwarebase.net");
void OnPageLoad(reply)
{
if(this->lastAction == "homepage")
{
this->lastAction = "login";
this->Login(); // POSTs a form and OnPageLoad gets called again
}
else if(this->lastAction == "login")
{
this->PostLogin(); // Checks did we log in properly, sets lastAction as new topic and goes to new topic URL
}
else if(this->lastAction == "new topic")
{
this->WriteTopic(); // Does some more stuff ... you get the point
}
}
Now, this is rather hard to write and keep track of when we have a large number of "actions". When I was doing stuff in Python (synchronously) it was much easier, like:
OpenWebPage("http://hardwarebase.net") // Stores the loaded page HTML in self.page
OpenWebpage("http://hardwarebase.net/login", {"user": username, "pw": password}) // POSTs a form
if(self.page == ...): // now do some more checks etc.
// do something more
Imagine now that I have a queue class which holds the actions: homepage, login, new topic. How am I supposed to execute all those actions (in proper order, one after one!) via the asynchronous callback? The first example is totally hard-coded obviously.
I hope you understand my question, because frankly I fear this is the worst question ever written :x
P.S. All this is done in Qt.
You are inviting all manner of bugs if you try and use a single member variable to maintain state for an arbitrary number of asynchronous operations, which is what you describe above. There is no way for you to determine the order that the OpenWebPage calls complete, so there's also no way to associate the value of lastAction at any given time with any specific operation.
There are a number of ways to solve this, e.g.:
Encapsulate web page loading in an immutable class that processes one page per instance
Return an object from OpenWebPage which tracks progress and stores the operation's state
Fire a signal when an operation completes and attach the operation's context to the signal
You need to add "return" statement in the end of every "if" branch: in your code, all "if" branches are executed in the first OnPageLoad call.
Generally, asynchronous state mamangment is always more complicated that synchronous. Consider replacing lastAction type with enumeration. Also, if OnPageLoad thread context is arbitrary, you need to synchronize access to global variables.

How to refresh an Infragistics UltraGrid?

I am using Infragistics UltraGrid with datasouce Windows Bindingsouce.
On change, I provide datasouce to Bindingsouce and call DataBinding of UltraGrid. Value in the datasouce of Bindingsouce changes, but that is not reflected in the UltraGrid.
Your binding source must raise some event to trigger grid refresh. For example, if you are using BindingList it should raise the ListChanged event.
Also, make sure that whatever class that you are using as your Binding Object implements INotifyPropertyChanged so that when you update the BindingObject at run time it gets channeled to BindingSource which eventually gets picked up by Grid.
i.e.:
BindingList<Foo> lstItems = new BindingList<Foo>;
BindingSource bso = ;
bso.DataSource = lstItems;
Grid.DataSource = bso;
public class Foo : INotifyPropertyChanged
see MDSN article here
Also depends if you changing the collection outside Grid (at runtime, because if you do, you need to use BindingList<T> and assign it to BindingSource