Tapestry: Events from Palette component - ajax

I'm using palette components on a page and I want the available elements in two of them to change depending on what is selected in the first.
What is the best way to achieve this? Which events are thrown by the palette component, that I could listen to, adapt the palette's model and perform a zone update? I thought it would work the same way as for select components doing something like this:
void onValueChanged() {
// do something
}
Unfortunately that doesn't work for palettes.
I'm using Tapestry 5.4-beta-6, but I guess things haven't changed that much since earlier versions.

I'd probably do this with a mixin.
public class PaletteChange {
#Parameter
private String zone;
#InjectContainer
private Palette palette;
public void afterRender() {
Link eventLink = componentResources.createEventLink("change");
JSONObject args = new JSONOBject(
"id", pallete.getClientId(),
"url", eventLink,
"zone", zone
);
javascriptSupport.addScript("palleteChange(%s)", args);
}
Object onChange(#RequestParameter("value") String value) {
CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
resources.triggerEvent("change", new String[] { value }, callback);
return callback.getResult();
}
}
Javascript
function palleteChange(spec) {
var field = $('#' + spec.id + '/select[1]');
field.on('change', function() {
var zoneManager = Tapestry.findZoneManagerForZone(spec.zone);
var params = { value: field.val() };
zoneManager.updateFromURL(spec.url, params);
});
}
Then use the mixin in your code
<t:palette t:id="myPalette" t:mixins="paletteChange" zone="myZone" ... />
<t:zone t:id="myZone">
...
</t:zone>
Page
#Inject
private Zone myZone;
Block onChangeFromMyPalette(String value) {
doStuff(value);
return myZone.getBody();
}
See here for a similar mixin.

I finally used the didChange element together with a similar mixin like the Observe mixin. I put a demo on Github for anyone, who is interested.
Just a few notes:
I used 5.4 beta 6, it already has the necessary client side events.
I couldn't make it work using a Tapestry javascript module, so I still use javascriptSupport.addInitializerCall.
The remaining problem is, that updating the second palette with a zone update will reset any changes the user has made in this palette, since they are only kept on the client side (in a hidden field). I will still need to look into that, but it is not part of the original question.

Related

Event each time component becomes visible

Is there a way in Angular2 to have an event fired when my component becomes visible?
It is placed in a tabcontrol and I want to be notified when the user switches. I'd like my component to fire an event.
What I finally did (which is not very beautiful but works while I don't have a better way to do it...) is to use the ngAfterContentChecked() callback and handle the change myself.
#ViewChild('map') m;
private isVisible: boolean = false;
ngAfterContentChecked(): void
{
if (this.isVisible == false && this.m.nativeElement.offsetParent != null)
{
console.log('isVisible switched from false to true');
this.isVisible = true;
this.Refresh();
}
else if (this.isVisible == true && this.m.nativeElement.offsetParent == null)
{
console.log('isVisible switched from true to false');
this.isVisible = false;
}
}
There is no such event, but if you're using a tab control, the proper way to do this would be to create a tab change #Output for your tab control if it's custom, otherwise, most tab controls (like ng-bootstrap) have some tab change event as well.
If your component has to be aware of this, you can use this tab change event to detect which tab is visible, and if you know which tab is visible, you also know if your component is visible or not. So you can do something like this:
onTabChange(event) {
this.currentTab = /** Get current tab */;
}
And then you can send it to your component itself if you have an input:
#Input() activated: boolean = false;
And then you can apply it with:
<my-component [activated]="currentTab == 'tabWithComponent'"></my-component>
Now you can listen to OnChanges to see if the model value activated changed to true.
You can also refactor this to use a service with an Observable like this:
#Injectable()
export class TabService {
observable: Observable<any>;
observer;
constructor() {
this.observable = Observable.create(function(observer) {
this.observer = observer;
});
}
}
When a component wishes to listen to these changes, it can subscribe to tabService.observable. When your tab changes, you can push new items to it with tabService.observer.next().
You can use the ngAfterViewInit() callback
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
Update
The new Intersection Observer API can be used for that
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
See also https://stackoverflow.com/a/44670818/217408
For those watching at home, you can now use ngAfterContentInit() for this, at least on Ionic anyway.
https://angular.io/guide/lifecycle-hooks
Best way to work around this limitation of Angular is to use a shared service that provides a Subject your component can subscribe to. That way new values could be pushed onto the Observable and the components which subscribe get the newest data and can act accordingly.
Fyi: The difference between a normal Observable and a Subject is that a Subject is multicast whereas an Observable could only be subscribed to by one Subscriber.
As a small example I show you a possible implementation of a shared-service and following the subscription inside the component that needs this new data.
Shared-service:
// ...
private actualNumberSubject = new Subject<number>()
public actualNumber$ = this.actualNumberSubject.asObservable()
/**
* #info CONSTRUCTOR
*/
constructor() {}
/**
* #info Set actual number
*/
setActualNumber(number: number) {
this.actualNumberSubject.next(internalNumber)
}
// ...
Push new value onto the subject from anywhere where shared.service is imported:
// ...
this.sharedService.setActualNumber(1)
Subscribe to sharedService.actualNumber$ in component to process/display that new data:
// ...
this.sharedService.actualNumber$.subscribe(number => {
console.log(number)
// e.g. load data freshly, etc.
})
// ...
I have the same purpose and cannot get a satisfy approach to it. The first answer will call so many times.
There is a compromised way I used, of course, not elegant either.
In parent component, I set a method:
parentClick() {
setTimeout(() => {
// TO-DO
This.commonService.childMethod();
}, time);
}
Maybe the method not accurate in time, but in some way, you reach the destiny.

JavaFX binding image property to image (MVC)

I stumbled upon this question while searching for an answer. But it doesn't seem to be a solution for my case.
In my viewcontroller I've the following:
public void setModel(CarcassonneModel model) {
this.model = model;
ivHoveringTile.imageProperty().bind(getImage(model.board.getActiveTile().getFilename()));
}
private ObjectProperty<Image> getImage(String filename) {
File file = new File("src/carcassonneapplicatie/resources/tiles/" + filename + ".png");
Image image = new Image(file.toURI().toString());
ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>(image);
return imageProperty;
}
However, the displayed image doesn't change when I change the filename in my model using an action event. I've got other bindings for my labels and they seem to work perfectly, except for this one.
If you do
someProperty.bind(someOtherProperty);
then someProperty is updated automatically whenever someOtherProperty.set(...) is invoked.
In your code someOtherProperty is the ObjectProperty<Image> you create in your getImage() method. Since you don't even retain a reference to this property, there's no possible way you can ever call set(...) on it. So the image in ivHoveringTile never updates.
You need to bind to an observable object in the model, representing the actual value that may change.

Conditional AJAX confirm with a new value in Wicket

I need to solve a simple problem, but yet I have not been able to found out any solution yet.
I have a simple DropDownChoice with AJAX onChange() JS event. I need to add a confirm box before the onUpdate() action is done - this is not difficult, BUT I need to display the confirm box only if the new selected value of the DropDownChoice is X (one certain value), and do not display the confirm box in any other case. Is it doable?
Short example snippet:
DropDownChoice<Integer> choice = new DropDownChoice<Integer>("id", new Model<Integer>(0));
choice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
// do some stuff
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new AjaxCallListener() {
#Override
public CharSequence getPrecondition(Component component) {
return "return confirm('Really?')"; // I NEED THIS DISPLAYED CONDITIONALLY
}
}
}
}
I don't know how to access the "choice" model object (converted input...) with the proposed value to add it to a condition in updateAjaxAttributes() method.
Thank you.
I think you should go for a JavaScript-based solution. The code of AJAX call listener is executed in a scope where you can use variable attrs. This variable contains the parameters used to perform AJAX call, including the id of the component. In this way you could check for selected value.
See more at http://wicket.apache.org/guide/guide/ajax.html#ajax_5

Triggering event in Activity using MvvmCross

I have an MvxFragmentActivity which loads a google map and places markers on the map. The code to create the map and markers is very Droid specific so it is in the Activity. The markers are created based on objects in the ViewModel which each contain lat/long coordinates. This worked fine as long as I loaded the objects in my Init method. I have since moved the load objects method to a service and call it on a different thread. This way the UI is responsive. However, how do I call the method in the Activity when the load is completed?
Here is my current code in the Activity (this code shouldn't change, just how it is called):
private void InitMapFragment()
{
foreach (var item in viewModel.Items)
{
var icon = BitmapDescriptorFactory.FromResource(Resource.Drawable.place_img);
var markerOptions = new MarkerOptions()
.SetPosition(new LatLng(item.Latitude, item.Longitude))
.InvokeIcon(icon)
.SetSnippet(item.DistanceText)
.SetTitle(item.Name);
var marker = _map.AddMarker(markerOptions);
_markerIds.Add(marker.Id, item.Id);
}
}
Code in my viewModel:
private void BeginLoadItems()
{
_loadItemsService.Load();
}
// This is triggered by a message
private void OnLoadItemsComplete(LoadCompleteMessage message)
{
Items = message.Items;
}
Code in my Service:
public void Load()
{
ThreadPool.QueueUserWorkItem(state =>
{
var results = _repository.Retrieve();
_messenger.Publish(new LoadCompleteMessage(this, results));
});
}
You're already triggering an event when you set:
Items = message.Items;
This triggers PropertyChanged with a property name of "Items"
For more on map binding, see Using MvvmCross how can I bind a list of annotations to a MapView? - although with Droid you'll need to use markers instead of annotations.

Automatically detect when storing an object with ServiceStack.Redis

I am looking for a way to subscribe to events like Storing a specific object type to ServiceStack.Redis.
For example I may
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.Store(myObject);//<-- I want this to trigger an event somehow
}
Is there anything like a OnStore event which I can hook too, anything out of the box? if not, is there any recommendation about how this should be done?
I don't think there is anything you can hook into (could be wrong).
Two options that came to mind:
1 - Make an extension method
2 - Publish a message to store your object and have a handler that listens for a response and does something. This is probably overkill since it's heading into the publish/subscribe realm. But, I think, worth looking into. (Basic example here and see Pub/Sub here).
Extension Method
public static class RedisClientExtensions
{
public static void StoreWithTrigger<T>(this IRedisTypedClient<T> redisClient, T value, Action<T> trigger)
{
redisClient.Store(value);
trigger(value);
}
}
Using ExtensionMethod
public void MyMethod()
{
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.StoreWithTrigger<MyObject>(new MyObject(), TriggerEvent);//<-- I want this to trigger an event somehow
}
}
private void TriggerEvent<T>(T value)
{
//dosomething
}
Hope this gives you some ideas.

Resources