Does model.getProperty() return a live object for objects that are members of an array? - sapui5

I get an object from within an array in my model (a JSONmodel type) which is
{
"task": [
{
"dbid": 465,
"bk_cnt": 11,
}, {
"dbid": 472,
"bk_cnt": 16,
}
]
}
I bind this model to a table and connect the bk_cnt up to an objectNumber in a cell. No problem so far.
In code I want to change the value of the first bk_cnt value from 11 to 20 on press of a button. Inside the event I have:
var model = this.getView().getModel() // get the model
var tasks = model.getProperty("/task"); // get as a JS object
tasks[0].bk_cnt = 20 // update the model...will it update the view?
// model.refresh() // it will if this is uncommented.
Problem: Though it is bound to the view, the displayed value of bk_cnt does not change. if I add model.refresh() it does. This code is extracted from a larger section and one of the larger features is sorting by column click. When I click a column to re-sort (no change to the model), the value 20 appears.
What gives?
Musings: I have read that the model.getProprty() function returns a javascript object with a live reference back to the model, and that a change to the value of the object will automatically be reflected in the view for any bound controls. Does this statement fall down on array attributes ?
EDIT: Still feeling around the issue I find that
model.setProperty("/task/0/bk_cnt", 20)
Does not require a model.refresh() to update the view. Not a total surprise as this command is directly acting through the model. This leaves me thinking that the 'live' object returned by getProperty() is only live when it is a primitive datatype like a string or integer, but not for a JS object. Or am I missing something ?
EDIT 2: #Ash points out in his answer that there is a further approach which is to access the JS object from the model property, set whatever attributes need to be updated in the JS object, then replace that into the model, e.g.
var tasks = model.getProperty("/task");
tasks[0].bk_cnt = 20
model.setProperty('/task', tasks)
Second edit done to complete the trio of approaches for future readers.

The Model object is an abstraction layer ON TOP of a javascript object. There is no way that a change within an object is notified anywhere. You need to explicitly trigger the notifications through model.refresh() or model.setProperty().
So both of your solutions are valid, another one (which I favor) would be
var tasks = model.getProperty("/task");
tasks[0].bk_cnt = 20
model.setProperty('/task', tasks)
But this actually depends on how you bind your model to your UI objects :)

Related

Running into an issue with a mutation and component flickering with react-query

So, I am making a query everything my context API is updated via a form selection update..
So, order of operation is like so.
User makes a change to a form by selecting (one of possible many) from dropdown.
Change updates "context api" which resaturates the parent component.
Because the form key/values changed, I fire a mutation.
Mutation returns a value. So far, great.
But, when I repeat step #1 - #4, another component flickers with that updated value because at some point the "const" that is expecting a value is undefined... THEN, it has a value..
So, like so..
has a value...
...query api call...
has no value
...returns query
has a value
const ProductPage = (props) => {
const { question } = useContextStateWhatever();
/* Queries */
const { data = {}, isFetched } = useProductUpdatePrice({ questions });
const value = derivePriceFromResponse(data.products);
return (
<SomeComponentRendered value={value} />
)
So, you can see between the "old value" and request in query, that the passed "value" will be undefined. Then query returns, updated value.
I was hoping the query will return any previous value, but the "queryKey" changes with every selection of the form. Deep queryKey.
I was hoping I wouldn't have to then put this value into local state from within a useEffect, or use useRef and create hook to hand back "previous" value until new value is ready.... That's not what react-query is for, right? I mean, shouldn't I be able to make a query call whenever the "context api" changes, and not expect this latency diff of undefined. Any strategies to over come this?
Since the "queryKey" is different (mostly for normal form interaction) for each query, I can see how it can't hand back a previous value until it resolves etc.. any ideas?
Any thoughts?
I think the keepPreviousData: true option is what you are looking for. If the query key changes, you will the get the data from the previous query key, along with an isPreviousData: true flag. The background update will still happen, and then you’ll get the data for the new query key once it arrives. The query will stay in isSuccess status the whole time.

Lookup Edit binding

I have a model written using Entity Framework Code First called Project.
In my UI, there is a page called ProjectEdit as shown in the image below. As seen in the image below, Customer and BOMs are Lookup Edit.
I'm trying to load Customer and BOMs to Lookup Edit but it's not working. Below is my code.
//New
if (entity == null)
{
Entity = new Project();
}
//Edit
else
{
ProjectCodeTextEdit.DataBindings.Add("EditValue", entity, "ProjectCode");
DescriptionTextEdit.DataBindings.Add("EditValue", entity, "Description");
CustomerLookUpEdit.DataBindings.Add("EditValue", entity, "CustomerId");
BOMsLookUpEdit.DataBindings.Add("EditValue", entity, "BOMs");
}
Below is my LookUpEdit Properties.
Generally LookUpEdit object's data binding is not implemented the same way as a TextEdit object's. While in TextEdits's case you just need to assign the variable value to EditValue property (I suppose your TextEdits binding work fine, isn't it?), with LookUp Edit you should assign variables to ValueMember and a DisplayMember properties of the object. That is why we usually display data rows with LookUpEdit objects, where ValueMember is the identification field of the row and DisplayMember is the field of the row whose value you wish to be displayed.
In your case you should be more clear about what you wish to display in your lookupedits. Each Project instance has one Customer property and many BOMs, right? So CustomerLookUpEdit will show one record and BOMsLookUpEdit a list of values according to the Project object that was chosen for edit, correct? I suppose that both your Customer and BOM classes have some kind of ID property and description property of their own. In this case you should bind these values to the LookUpEdits. eg. in your initialization function code add these lines
CustomerLookUpEdit.Properties.DataSource = entity.Customer;
CustomerLookUpEdit.Properties.ValueMember = "someCustomerIDpropertyName" ;
CustomerLookUpEdit.Properties.DisplayMember = "someCustomerDescriptionpropertyName";
BOMsLookUpEdit.Properties.DataSource = entity.BOMs;
BOMsLookUpEdit.Properties.ValueMember = "someBOMIDpropertyName" ;
BOMsLookUpEdit.Properties.DisplayMember = "someBOMDescriptionpropertyName" ;
You can read more in this topic https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraEditorsLookUpEdittopic
When we are adding entities to a List, we have to take care of our DataSource if is a DBContext or a DBSet, each one has implications in the compiler, that was your case, in this case you had to especify your DataSource like a DBSet and get the Entities
Add<TEntity>(TEntity entity)
The type parameter omitted is posible because the compiler will infer it.

How to specify computed property dependency in EmberJS on an array of objects that contain another array

I am using EmberJS and I am trying to setup the dependency on a computed property. A visitor can contain multiple tickets (an array), and I have access to an object containing an array of visitors. This is my computed property. How can the property be recomputed when a ticket is added to any of the visitors in the visitors array?
App.AttendanceController = App.SlideBaseController.extend
eligibleVisitors:
(->
return #getElegibleVisitors()
).property('App.state.session.visitors.#each.type')
allEligibleVisitorsAttending:
(->
return _.all #getElegibleVisitors(), (visitor)->
return visitor.tickets.length > 0 and _.any visitor.tickets, (ticket)->
return ticket.EventID is content.Event.ID
).property('App.state.session.visitors.tickets')
getElegibleVisitors:->
return _.filter App.state.session.visitors, (visitor)->
return visitor.type isnt App.VisitorTypes['Infant']
Something like this should work:
.property('App.state.session.visitors.#each.tickets.#each')
Updates when any ticket is added to any visitor.
AFAIK this is not possible right now and won't be for Ember 1.0. See here: https://github.com/emberjs/ember.js/issues/541
Nested #each properties don't trigger observers correctly.
I faced a similar problem (nested tree structure) -- I got around it by having each child node have a reference property to the parent node, and then adding
Node = Ember.Object.extend
nodeParent: null # to point at parent
nodeChanged: (->
#get('nodeParent')?.notifyPropertyChange 'notifier'
).observes 'various', 'properties
I used 'notifier' as a general key that other properties on the parent can bind to; when things in the child change, it causes a property change on the parent.notifier, which will in turn trigger other properties you want.
This is a hacky work around. Plus side is that it works, downside is you have to keep track of the child-parent relationship; i.e., for you, each Ticket needs to a reference to its Visitor

YUI Datatable: rowsPerPageChage Event: Get Drop Down ID on change

I'm reposting this question from an earlier post because when I originally asked it, it was unclear. I wasn't sure what I actually needed, and that caused the responses I was getting to be not applicable.
Original: YUI Datatable - Get ID of DOM Element after page has loaded and use it in other YUI events
My question is this:
I have a YUI Datatable. I have a rows per page drop down. I need to create an event, or event handler, or utilize one that exists, that will handle the following:
When(ever) the drop down displaying the rows per page changes, the event handler should get the id of that drop down and pass it to another function to use as an argument. My datatable has two rowsPerPageDropDown (aka rppDD) elements. I need this functional for both of them.
Preferably, it would also do this when the page loads, but for now that's extra credit.
I know that the id of "top" rppDD element is currently "yui-pg0-0-rpp12" but my problem extends from the fact that if I add any code to this page, that id will change (to "-rpp13" or something similar). That throws off the rest of my functions. So I want to just grab the id (whatever it may be) at run time and use it that way rather than hard coding it in.
The second function will run immediately after the drop down changes. It will use the id of the drop down to get the currently selected value and assign it to a variable to be used with a third function.
Here's what I have so far: In the earlier thread, it was suggested that I use the event rowsPerPageChange. This works only to a certain degree. Placing a simple "hello world" alert within that event handler proves that it only alerts when the page loads. Whenever I click and change the drop down to something else it no longer alerts.
Below is the function(s) and code I'm using to test this:
var getRppValue = function() {
YAHOO.util.Event.addListener("rowsPerPageChange", getRppValue.prototype.handleOnAvailable());
}
var rppDD_ID = "";
getRppValue.prototype.handleOnAvailable = function() {
alert("hello world");
alert(this.id); //should alert "yui-pg0-0-rpp12"
//rppValue = this.id;
}
var rppEvent = new getRppValue();
Near as I can tell this event only fires/gets handled when the page initially loads. At that time, the first alert alerts "hello world" as expected. The second alert shows "undefined". The intention is that the second alert should alert whatever the id of the rppDD element is ("yui-pg0-0-rpp12").
For reference, the function I plan on passing the id into is as follows. It is intended as I mentioned above to assign the current value of [the|either|both] rppDD element(s) to the variable oRPP. It then calls the endDrag() function (which utilizes oRPP):
If I were to hard code the id it would be:
function getRPP_0(){oRPP = g("yui-pg0-0-rpp12").value;endDrag();};
I want it to by dynamic:
function getRPP_0(){oRPP = g(rppDD_ID).value;endDrag();};
Any help / suggestions are appreciated.
As we saw on the chat, you had this:
var onRPPChange0 = YAHOO.util.Event.addListener("yui-pg0-0-rpp12", "change", getRPP_0);
var onRPPChange1 = YAHOO.util.Event.addListener("yui-pg0-1-rpp24", "change", getRPP_1);
function getRPP_0(){oRPP = g("yui-pg0-0-rpp12").value;endDrag();};
function getRPP_1(){oRPP = g("yui-pg0-1-rpp24").value;endDrag();};
And you wanted to be able to access the element being changed in the function.
Fortunately, event handlers provide the element on which the event is happening in the this object.
So, this works:
var onRPPChange0 = YAHOO.util.Event.addListener(
"yui-pg0-0-rpp12", "change", function() {
// Notice that "this" is the element on which the event was fired
oRPP = this.value;
endDrag();
};
This way, you can generalize your function easily without hardcoding the IDs.

Is there a way to get the original value of a cell in onEdit()?

I'd like to know if there's a way to get the original value of the cell from within the onEdit() event.
For example:
Cell A1's current value is "hello"
I edit A1 it by changing it to "world"
Right now, when I try to get the value from the active range, I'd get "world"
But I would also like to have the possibility to get the original value, i.e. "hello". Is that currently possible?
You can use onEdit(e) and e.oldValue if you're dealing with a single cell. The example below adds the original value to A1 when any cell is edited. 'undefined' if the cell was blank.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getActiveCell();
var origValue = e.oldValue
sheet.getRange('A1').setValue(origValue);
}
The only code that would come close to this is to use an onOpen exit to save
the initial value. The initial value could be saved in several ways. There
are "user properties". I don't know how they work, yet. I am new to this.
You could also use a "hidden" sheet to save the value, in the "onOpen" exit.
There may be other ways, that I don't know about.
The problem becomes more difficult with an increasing number of cells for which
you want to save the original value.
By-the-way, you should understand that the "onOpen" routine fires at the time
the spreadsheet is opened. It so happens, that the end-user also has access and
can change cell values before the onOpen handler finishes its execution. You may
not capture all of your initial values.
One final thing you should know. The onEdit trigger is NOT fired when an UNDO or REDO
event occurs. The cell's contents could change and you will not know it.
I don't know how a validation routine works. If the routine rejects a value, will
the spreadsheet restore the original value? If it does, then this might get around
the onOpen problem. If it only tells the user the value is invalid, it will not be
of much help.
A really round about way that may work, but is very complicated is to save the image
before the spreadsheet closes. You post all the "after" images to a second spreadsheet.
Then in your onEdit handler you look at the corresponding cell in the other spreadsheet.
You then decide to restore the previous image or allow the new image to proceed.
Lastly a wild idea of using a data table in place of the second spreadsheet.
I am just learning about all of these concepts, so don't ask me how to implement them.
I just understand that they MIGHT be possible. For coding and support purposes they
may not be the best options. But since the current script service does not provide
before image access, it is about the best I could do. You have to understand that this
google interface is a client-server application. Your scripts run on the server. The data changes occur in the "clients" (end-users) browser.
One final note: the onEdit trigger does not fire for an UNDO or REDO change to a cell.
So the cell could change and your script is not aware of it.
I don't think that's possible.
I imagine you could get that functionality by having a exact copy of your sheet on a second sheet that updates automatically when your 'onEdit' functions ends.
Until that update, data on the second sheet will have the former value.
A bit tricky but why not ?-)
EDIT : seems to be the 'question of the day', see this post and Henrique Abreu's pertinent comment.
When you change the value of a cell diagrammatically, you can use the setComment method to store the original value as a comment in that cell.
What you basically need to do is to create a shadow sheet (which you can protect and hide or even have it in a totally separate spreadsheet) and use the IMPORTRANGE function to get the values of the original sheet into the shadow sheet (This gives enough delay time to get the old value that was in the cell before it got edited).
=IMPORTRANGE("enter your original sheet's ID","enter the range you wish to get from the sheet")
Please note that using this code, when editing multiple cells at the same time, the function will only work on the first cell.
function onEdit(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var originalSheet = sheet.getSheetByName('Original');
var shadowSheet = sheet.getSheetByName('Shadow');
var editedCell = originalSheet.getActiveCell();
var cellColumn = editedCell.getColumn();
var cellRow = editedCell.getRow();
var oldValue = shadowSheet.getRange(cellRow, cellColumn).getValue();
var cellValue = editedCell.getValue();
var editorMail = Session.getActiveUser().getEmail(); \\getting the editor email
if ("The condition you want to meet for the onEdit function to activate"){
editedCell.setNote(editorMail + "\n" + new Date + "\n" + oldValue + " -> " + cellValue);
}
}