How to update ContextBinding (Element Binding) automatically after the size of its child ListBinding (Aggregation Binding) has changed? - data-binding

Note: In order to demonstrate the below issue, I've created an example in Plunker. In there, I'm using a mock server due to the flaw of the writable service from odata.org. Nevertheless, the issue is reproducible with a real server as well.
Currently, I'm binding a child collection to a list relative to an expanded single entity through a navigation property. Something like this:
<Page title="Products"
binding="{
path: 'odataModel>/BusinessPartnerSet(\'0100000000\')',
parameters: {
expand: 'ToProducts'
}
}"
>
<List items="{odataModel>ToProducts}">
<StandardListItem title="{odataModel>ProductName}" />
</List>
</Page>
Now, if I delete an item from the list, the ListBinding sends a batch request containing DELETE and GET. Here is a screenshot of the batchRequestSent event:
The deleted item is gone from the ListBinding and the list is updated as expected. If I have an additional list with the same bindings, then that additional list gets also updated because of the TwoWay data binding (like the Plunker example above). So far so good.
Problem
But the problem is: The parent ContextBinding (in this case, the element binding of the Page) is not updated. This can be seen if you call bindingContext.getProperty("ToProducts") and it will return a list of binding paths in which the ones of the already deleted items are still there. I guess this is because there was no GET request sent from the ContextBinding but from the ListBinding only.
My question
If the change took place in the child ListBinding (either through DELETE or CREATE), how can the parent ContextBinding get notified about the change and update itself without sending an additional request, so that bindingContext.getProperty("ToProducts").length returns always the correct length?
Or differently, when the change occurs, how can I prevent UI5 from sending a GET request from the child ListBinding and let it send the request from the parent ContextBinding instead, so that the change gets propagated to the child ListBinding afterwards automatically?
Is there any standard approach to solve this kind of problem in UI5?
PS: The same problem does not apply to JSON-based bindings. It seems to be OData only.

If someone encounters the same issue: it turned out to be a bug in UI5 which is fixed in 1.46.7+.
[FIX] ODataListBinding: Update expanded list array
If the fix is not available in your current release, one workaround would be to refresh the parent binding by calling parentControl.getElementBinding("modelName").refresh(false, "yourDeferredGroupId") so that the GET request can be sent together with the DELETE request in a single batch request.
Additionally, we can turn off refreshAfterChange (as krisho suggested) so that UI5 doesn't append yet other GET requests for corresponding ListBindings (relatively bound as well as absolutely bound ones). A disadvantage of this approach is that the absolutely bound ListBinding (which has the same content as the relatively bound one) has to be refreshed manually as well - if there is any.

To stop the framework from making the 'GET' call after Delete, you can use the property 'refreshAfterChange' on sap.ui.model.odata.v2.ODataModel, which can be set to 'false'.
After the success of 'Delete', you can 'refresh' the binding on the page, so that all bindings are consistent.

Related

Unable to get current click value through Adobe Launch

I created a click event in adobe launch which will capture the value of the link and send it to analytics. I have created a data element for saving the value and in my DOM I am saving value in local storage.
Local storage code:
$('.card').click(function() {
var name = $(this).text();
localStorage.setItem('productName', name);
});
My problem is when I click the first link no value got saved, but after when I click second link it saves the value of first link and on third link saves value of second link and so on. I want to save current link value in evar3 variable.
Data element:
Rule:
Set variables:
Thanks,
Harshit
I'm scratching my head a little bit about why your jQuery selector doesn't match your Rule selector, but that's probably not immediately related or relevant, considering you said you are seeing data pop, in general, so at face value, I'm going to ignore that.
But in general, it sounds like your jQuery code is getting executed after the Rule is evaluated, so it's effectively one step behind. I'm not sure there's much if anything you can do about that if you aim to keep two separate click event listeners like this.
You're going to have to restructure to have one chain off the other (which I don't think is actually feasible with jQuery > Launch as-is. Maybe if you write two separate custom code blocks with promise chaining but that kind of sidesteps Launch and IMO is over-complicating things to begin with (see below)). Better yet, merge them into a single click event listener. On that note..
Why do you have two separate click event listeners? Is the sole reason for this to pass off the link text? Because you can reference the clicked element in the Launch Rule itself.
You should be able to reference %this.innerText% in the Set Variables fields. Or you can reference this object in custom code boxes within the Rule.
IOW at face value I don't see why you need or should be creating/using that jQuery listener or pushing to local storage like that.
Update:
You commented the following:
I tried with %this.innerText% it is also not showing current values. I
am pushing the value first to local storage because my link values are
generating on runtime through an API. They are not hardcoded. I am
also trying to figure out why my rule is getting fired before my
jquery is evaluated. But when I check in console using
_satellite.getVar('Product Name'); it shows me the correct value, but in debugger console value is wrong. Can you show me the way you want
to create rule to getting it fired correctly ? Thanks,
Okay so your link values are generated runtime through an API call? Well okay now that sounds like where the (timing) issue is, and it's the same issue in principle I mentioned you prolly had between the jQuery and Launch code. Assuming you can't change this dynamic link functionality, you have two options:
1. Explicitly trigger a launch rule (direct call rule) in a callback from the API code that's generating the link values.
This is the better method, because you won't have race condition issues with your API vs. link tracking code. The biggest caveat about this method though is that it requires requires you to actively add code to the site, which may or may not be feasible for you.
I have no idea what your code for generating the link looks like, but presumably it's some ajax call and generated from the success callback. So in the callback, you'd add a line of code something like this:
_satellite.track('product_image_click', {
text : elem.innerText
});
Again, I don't know what your API code that generates the link looks like, but presumably you have within it some element object you append to or update the DOM, so for this example, I will refer to that as elem.
'product_image_click' - This is the value you use for the direct call rule identifier in the interface, e.g. :
And then _satellite.track() call also includes an object payload in 2nd argument that you can pass to the direct call rule. So in the code above, I set a property named text and give it a value of elem.innerText.
Then, within the direct call rule, where you set variables, the data you passed can be referenced from event.details object within custom code box (e.g. event.details.text), or using % syntax within the interface fields (e.g. %event.details.text%).
2. Make use of setTimeout to delay evaluating the link click.
The one advantage of this method over option #1 is that it is passive. You don't have to add code directly to your site to make it work.
However, this is the shadier option, because you don't really know how long it will take for your link to be generated. But generally speaking, if you can determine it takes on average say 250ms for the link generation API to do its thing, and you set the timeout to be called at say 300-500ms, then you will probably be okay most of the time. However, it's never a 100% guarantee.
In addition, if clicking on the link ultimately redirects the visitor to another page, then this solution will likely not work for you at all, since the browser will almost certainly have navigated before this has a chance to execute. Because of this, I normally I wouldn't even mention this as an option, but since you mentioned this link involves an API that generates the link values when clicked, I figured maybe this isn't a navigation / redirect link, so maybe this is an option you can go for, if you can't do option #1.
First, create a direct call rule the same as shown in option #1. This will be the rule that receives the link text and makes the Adobe Analytics (or whatever other marketing tag) calls.
Then, create a separate rule as a click event, similar to what you are currently trying to do (listening for the link click, based on your css selector). In this rule, you won't be setting any AA variables. Instead, add a custom js box with code like this:
var elem = this;
(function (elem) {
window.setTimeout(function() {
_satellite.track('product_image_click', {
text : elem.innerText
});
}, 500);
})(elem);
So when the rule is triggered, it will create a setTimeout callback to call the first rule, passing the link text in the payload. And.. hopefully the 500ms timeout shown in the code example is enough time for the API to do its thing. You may be able to adjust it up or down.
Rather than defining it in Data Element I would say its better to keep it directly in the Rule itself. Please try with this %this.#text%.
Let me know if this helped.

Plone - How can I setDefaultPage in an event subscriber?

I have a folderish dexterity content type and I have an event subscriber. When the content type is created, I create a Collection, which shows the children in the container according to several parameters. After the collection is created, I try to set the default page of the container to the collection.
def myContainerAdded(my_container, event):
#get container
#set advanced query for collection
#create collection with api.create
my_container.setDefaultPage(new_collection.id)
The subscriber in configure.zcml
<subscriber
for="my.product.my_container.IMyContainer
zope.lifecycleevent.interfaces.IObjectAddedEvent"
handler=".events.myContainerAdded" />
Unfortunately, the default page is not being fully set. It just shows the container page, but the Collection is selected under the 'Display' drop down.
If I click "Change content item as default view" and select the collection, it does change the default page to the collection.
Earlier, I was using a "setuphandler" to setup a folder structure (as opposed to an add event), and setDefaultPage was working. Am I forgetting a step since I'm attempting this through an event?
I am using plone.4.3.
Edit: I also tried:
my_container.default_page = new_collection.id
Edit:
I found something interesting. I temporarily commented out the code related to the collection in the event subscriber. I manually added the collection to the container object and then set the default page of the container to the collection. The container's default page was the collection.
Maybe something isn't getting indexed right?
In fact everything went well, it's just that after creating a Dexterity-based content-type, one will land on the default-view's URL, meaning '/view' is appended to the item's URL, which is an alias to the default-view-method and here resolves to the 'folder_listing'-template.
To overcome this quickly, you can add a redirect to the object's URL in the subscriber's method, without any view-name appended to the URL:
event.REQUEST.RESPONSE.redirect(my_container.absolute_url())

Firebase: Priority set to null on server but not on client when setting grandchild

To reproduce, check out http://jsfiddle.net/3aR7N/16/ . First click "Set child and grandchild," which sets a child to a priority and then sets a grandchild within it. Then click "Show Local Values" and you'll see that the child still seems to have a priority of 123. Click that as many times as you like - the priority always comes back as 123.
Once you're satisfied that your local priority really is 123, refresh the whole page. You'll be dismayed to see that the priority now is null.
I can believe this is a concurrency issue, with certain effects of the set happening in different orders. Is the expected behavior that setting a child would clear the priority of the parent?
Thanks for the easy test case! This is indeed a bug. The client and server are handling this case differently, which is why you're seeing this inconsistent behavior.
Note that this only occurs if you "setWithPriority" a primitive value, and then write deeper in the tree (which deletes that primitive value).
Update: This has now been fixed.

ASPxClientListBox loses values and the first item can not be selected

On a server control I have a DevExpress ASPxClientListBox which is populated dynamically on the client. The server does not need to know anything about it but it is initialized on the server. Normally this server control does not have any problems... however, if I put it in an ASPxCallbackPanel it will work on the first page load but, after a callback, if the ListBox's client side selected index changed event is fired, the selected items values are lost and "undefined" though the keys or "texts" are intact.
On a side note... after a callback the first item in the ListBox can not be selected.... what I mean is that the first item is not highlighted on mouseover or mouseclick.
Links which are slightly relevant but not what I need:
http://www.devexpress.com/Support/Center/p/Q312536.aspx
I had another link that talked about the "unable to select first item" issue and said that it had something to do with adding the items prior to the control hierarchy being established so the solution was to add the items in the ListBox client side Init event as opposed to the page_load event. I am adding the items after the ListBox has been initialized so I don't see this as a solution ... and I can't find the link now anyways...
Edit: I just found that other link again: http://www.devexpress.com/Support/Center/p/Q367021.aspx
Part of the problem is addressed in the second link you provided. What happens is that...
...this function is called immediately when a page is loaded. However,
an ASPxListBox hierarchy is not yet ready. The correct way to call the
getList function is to handle the ASPxClientListBox event...
To cut the long story short, something like the following won't execute properly if you run it e.g. on load...
yourListbox.AddItem("Test1");
yourListbox.AddItem("Test2");
yourListbox.AddItem("Test3");
yourListbox.AddItem("Test4");
...but the following should work...
s.Properties.ClientSideEvents.Init =
#"function(s, e) { yourListbox.AddItem('Test1'); yourListbox.AddItem('Test2'); yourListbox.AddItem('Test3'); yourListbox.AddItem('Test4'); }";
I guess the first selected-items-problem could have happened for the same or similar reason.

ASP.NET 3.5 - Making a field readonly/unmodifiable without "disabling" it

I have a web application with a form that has disabled fields in it. It allows a "Save As" function which basically means the settings can be copied into a new configuration (without being modified) and in the new configuration they can be changed to something else. The problem I am running into with this is that since the fields are disabled, they are not getting posted through and do not appear in the context object on the server side.
When I removed the logic to disable the fields, that part works fine. So the remaining problem is, how to "disable" the fields (not allow any change of the data in any of the entry fields) without really "disabling" them (so that the data gets posted through when saving)?
I was originally looking for a way to do this in CSS but not sure if it exists. The best solution is of course, the simplest one. Thanks in advance!
(Note: by 'disabled' I mean "The textboxes display but none of the text inside of them can be modified at all". It does not matter to me whether the cursor appears when you click inside it, though if I had a preference it would be no cursor...)
http://www.w3schools.com/TAGS/att_input_readonly.asp
readonly attribute is what you want.
i would suggest that instead of using the non-updateable field values from the page's inputs, you retrieve the original object from the DB and copy them from there. It's pretty trivial using something like Firebug to modify the contents of the page whose form will be posted back to modify the values, even if they are marked as readonly. Since you really want the values from the original, I would simply reget the object and copy them. Then you don't need to worry about whether the original (and non-updateable) properties get posted back at all.

Resources