Strange event bubbling issue in Titanium Appcelerator alloy project - ios

I am developing on iOS 9.2 SDK & Titannium SDK v5.1.2.GA.
In my iPad app; there is a product tab page, which has a "Discount" button. When you click it, a Popover with a TextField and Picker is shown like this:
The above is created on the fly. (not using a controller + view).
This works as intended. I wanted to extend this a little further by recording the given discount to a product in alloy.js in a global array variable called Alloy.Globals.ProductDiscounts = []; (so it can be used later).
The way I "capture" the new discount price is by listening to the "hide" event on the picker. Then update the global array.
For debugging purpose, I added a console log to make sure it's getting recorded correctly and then in the Appcelerator Studio console window, I started see this endless output like this:
I had to kill the simulator to stop this weird constant output of nulls.
This is my code so far, any idea why the console window is spazzing out? Also, why isn't my global array isn't getting set? or is it getting set, but I missed the actual console.log entry?
// Subscribe to line discount button click event
lineDiscountButton.addEventListener('click', function(e)
{
// Stop further events
e.cancelBubble = true;
// Create popover
var discountPopover = Titanium.UI.iPad.createPopover({
arrowDirection: Titanium.UI.iPad.POPOVER_ARROW_DIRECTION_RIGHT,
orignalPrice: e.source.orignalPrice,
priceButton: e.source.priceButton
});
var discountPopoverView = Titanium.UI.createView({
width: 250,
height: 210
});
// Create discount popover view wrapper
var discountPopoverViewWrapper = Titanium.UI.createView({
top: 10,
left: 10,
right: 10,
bottom: 10,
layout: 'vertical'
});
discountPopoverViewWrapper.add(Titanium.UI.createLabel({
top: 0,
left: 0,
color: '#5C5C5C',
font: {
fontSize: 12
},
text: 'Enter a new Price'
}));
discountPopoverViewWrapper.add(Titanium.UI.createView({
top: 0,
height: 1,
backgroundColor: '#0088CE',
width: '100%'
}));
var discountPrice = Titanium.UI.createTextField({
top: 0,
width: '100%',
height: Titanium.UI.SIZE,
hintText: discountPopover.orignalPrice,
value: discountPopover.orignalPrice,
backgroundColor: '#FFFFFF',
font: {
fontSize: 18,
fontWeight: 'bold'
},
color: '#5C5C5C'
});
discountPopoverViewWrapper.add(discountPrice);
discountPopoverViewWrapper.add(Titanium.UI.createLabel({
top: 10,
left: 0,
color: '#5C5C5C',
font: {
fontSize: 12
},
text: 'Or Select a Discount Percent'
}));
discountPopoverViewWrapper.add(Titanium.UI.createView({
top: 0,
height: 1,
backgroundColor: '#0088CE',
width: '100%'
}));
var discountPercentPicker = Titanium.UI.createPicker({
top: 0,
width: Titanium.UI.FILL,
height: 112
});
var discountPercentValues = [];
for (var i = 0; i <= 100; i++) {
discountPercentValues.push(Titanium.UI.createPickerRow({
title: i +'%'
}));
}
discountPercentPicker.add(discountPercentValues);
discountPercentPicker.addEventListener('change', function(e) {
if (parseInt(e.rowIndex) === 0) {
discountPrice.value = discountPopover.orignalPrice;
} else {
discountPrice.value = (discountPopover.orignalPrice - (discountPopover.orignalPrice * (parseInt(e.rowIndex) / 100))).toFixed(2);
}
});
discountPopoverViewWrapper.add(discountPercentPicker);
// Add discount popover view wrapper to view
discountPopoverView.add(discountPopoverViewWrapper);
// Set popover content view
discountPopover.contentView = discountPopoverView;
// Subscribe to popover hide event
discountPopover.addEventListener('hide', function(e) {
e.cancelBubble = true;
Alloy.Globals.ProductDiscounts[discountPopover.priceButton.sku] = parseFloat(discountPrice.value).toFixed(2);
Alloy.Globals.LiveBasketCollection.executeQuery("UPDATE live_basket SET Price = "+ discountPrice.value +" WHERE Sku = '"+ discountPopover.priceButton.sku +"'");
discountPopover.priceButton.price = Alloy.Globals.DeviceDefaults.CurrencySymbol + discountPrice.value;
discountPopover.priceButton.title = (discountPopover.priceButton.basketQuantity > 0 ? discountPopover.priceButton.basketQuantity +' x ' : '') + discountPopover.priceButton.price;
Titanium.App.fireEvent('redrawBasket');
discountPopover = discountPopoverView = discountPrice = discountPercentPicker = discountPercentValues = null;
console.log(Alloy.Globals.ProductDiscounts);
});
// Show popover
discountPopover.show({
view: lineDiscountButton,
animated: true
});
});

Just as a Question... Why are you cancelling bubbling.
without more of the code base I can only try and make a suggestion.
1) addeventlistener.
lineDiscountButton.addEventListener('click', setupData);
2) setupData
Function setupData(e) {
lineDiscountButton.removeEventListener('click', setupData);
Your code from the inline function.
}
Basically remove the event listener once activated, so add and remove as required.
I don't want to teach you to suck eggs, but to remove an event listener, you have to use exactly the same parameters as adding it. Thus inline function on event listeners are not ideal, although they work separating the code out into its own function is preferable.
Next Alloy globals.... Not good practice. I am guessing you want to have the data only for the duration of the running of the app, and not for future use.
If you need it for future use, you can store the data in properties.
Hope this helps
T.

Related

Vue-Konva: Is there a way to reorder layers on the fly?

So, I've been working with vue-konva and I have something like this:
<v-container>
<v-stage ref="stage">
<v-layer ref="baseImage">
<v-image>
</v-layer>
<v-layer ref="annotationLayer">
<v-rect ref="eventBox">
<v-rect ref="rubberBox">
<v-rect ref="annotationRect">
</v-layer>
</v-stage>
<v-container>
Currently there are some issues if I want to draw new boxes, when there are other annotationRects already on the image. Because they are technically above the eventBox and rubberbox, they are "blocking" these two layers when the cursor is above an existing annotationRect.
But, I don't want to just constantly have eventBox and rubberBox be on top of annotationRect because I need to be able to interact with annotationRect to move them, resize them ,etc.
Is there a way for me to reorder eventBox, rubberBox, and annotationRect, i.e. changing the order to (bottom to top) annotationRect-eventBox-rubberBox from the original eventBox-rubberBox-annotationRect and back, on the fly, for example when the vue component receives an event from another component?
You need to define your eventBox, rubberBox, and annotationRect inside order array in the state of your app. Then you can use v-for directive to render items from the array:
<template>
<div>
<v-stage ref="stage" :config="stageSize" #click="changeOrder">
<v-layer>
<v-text :config="{text: 'Click me to change order', fontSize: 15}"/>
<v-rect v-for="item in items" v-bind:key="item.id" :config="item"/>
</v-layer>
<v-layer ref="dragLayer"></v-layer>
</v-stage>
</div>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height
},
items: [
{ id: 1, x: 10, y: 50, width: 100, height: 100, fill: "red" },
{ id: 2, x: 50, y: 70, width: 100, height: 100, fill: "blue" }
]
};
},
methods: {
changeOrder() {
const first = this.items[0];
// remove first item:
this.items.splice(0, 1);
// add it to the top:
this.items.push(first);
}
}
};
</script>
DEmo: https://codesandbox.io/s/vue-konva-list-render-l70vs?file=/src/App.vue

Update color of plotline label on change highcharts

is there a way to change color of the plotline label when the movement goes up and change it again in different font color when movement goes down?
yAxis.addPlotLine({
value: 66,
color: 'red',
width: 2,
id: 'plot-line-1',
label: {
text: 66,
align: 'right',
style: {
color: 'red',
fontWeight: 'bold'
},
y: newY,
x: 0
}
});
Here is a working file http://jsfiddle.net/kttfv1h3/9/
thanks for the help
You can add css style options to your plotLabel like to a simple object; docs
var prev_val = 0; // global
plotlabel = yAxis.plotLinesAndBands[0].label;
if(prev_val <= y) {
plotlabel.css({'color':'blue'});
} else {
plotlabel.css({'color':'green'});
}
prev_val = y;
My jsFiddle: http://jsfiddle.net/kttfv1h3/12/
After playing around a bit with the different settings I found a way to achieve what you are seeking.
By delcaring a new variable oldY
And doing the following in setInterval, the label color changes depending on if the value is increasing or decreasing:
if(y < oldY) {
yAxis.plotLinesAndBands["0"].label.element.style.fill = 'red';
} else {
yAxis.plotLinesAndBands["0"].label.element.style.fill = 'green';
}
oldY = y;
See jsfiddle for a working example based on the one you supplied.

how to create an event after an event in Appcelerator Titanium

I am trying to repurpose code to create a custom progress bar - but I am unable to understand how to make the final change.
The current implementation does the progress bar. What I would like is for the progress bar to update the text and then disappear on its own.
var win = Ti.UI.createWindow({
backgroundColor: 'white',
});
var label1 = Ti.UI.createLabel({
text: 'Working on it...',
textAlign:'center',
});
var track = Ti.UI.createView({
width: "60%", height: "20%",
borderRadius:40,
backgroundColor: 'red'
});
var progress = Ti.UI.createView({
borderRadius:40,
left: 0,
width: 5, height: "100%",
backgroundColor : "green"
});
track.add(progress);
track.add(label1);
win.add(track);
win.addEventListener('open', function () {
progress.animate({
width: "100%",
duration: 1000
});
});
win.open();
So when the final green progress is complete - I would like to
a. replace the "working on it" with "complete"
b. after 1000 ms - make the entire progress bar disappear.
No need to add Listener for complete event, you can add anonymous function in animate method itself
e.g
progress.animate({
width: "100%",
duration: 1000
},function(e){
label1.text = "complete";
win.remove(track);
});
You can use complete event listener of Animation object
// create the animation
var animation = Ti.UI.createAnimation({
width: "100%",
duration: 1000
});
animation.addEventListener("complete", function onAnimationComplete(e){
// YOUR CODE HERE
animation.removeEventListener("complete", onAnimationComplete);
});
progress.animate(animation);
More details Titanium.UI.Animation

Titanium ScrollableView height is bigger on iOS

I currently have a scrollable view to scroll through some images.
On Android it's fine, but on iOS the height seems to be a bit bigger as I have some text under the scrollable view and its being pushed down a bit on iOS, while on Android the text is right under where it should be
index.js:
var win = $.index;
if(Alloy.Globals.osname == "android"){
win.backgroundColor = "#000";
}
//If iOS
else{
win.backgroundColor = "#FFF";
}
win.orientationModes = [
Ti.UI.PORTRAIT,
Ti.UI.UPSIDE_PORTRAIT
];
function textColor(){
if(Alloy.Globals.osname == "android"){
return "#FFF";
}
else{
return "#000";
}
}
var button = Titanium.UI.createButton({
title: 'Test Button',
bottom: 30,
width: "75%",
height: "auto",
visible: false
});
var page_view = Titanium.UI.createView({
top: 10,
width: Ti.UI.SIZE,
height: "85%",
layout: "vertical",
visible: false
});
var page_descr = Titanium.UI.createLabel({
text: "This is a description",
width: "75%",
font: { fontSize: 36 },
color: textColor(),
textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
});
var page_image1 = Titanium.UI.createImageView({
image: "/images/screens_android/adsteps_1.jpg",
});
var page_image2 = Titanium.UI.createImageView({
image: "/images/screens_android/adsteps_2.jpg",
});
var page_image3 = Titanium.UI.createImageView({
image: "/images/screens_android/adsteps_3.jpg",
});
var page_image4 = Titanium.UI.createImageView({
image: "/images/screens_android/adsteps_4.jpg",
});
//Change the images to the iOS images
if(Alloy.Globals.osname != "android"){
page_image1.image = "/images/screens_ios/ios_1.jpg";
page_image2.image = "/images/screens_ios/ios_2.jpg";
page_image3.image = "/images/screens_ios/ios_3.jpg";
page_image4.image = "/images/screens_ios/ios_4.jpg";
}
var image_scroller = Titanium.UI.createScrollableView({
width: "100%",
height: "auto",
showPagingControl: false,
backgroundColor: "white",
views: [page_image1, page_image2, page_image3, page_image4],
});
image_scroller.addEventListener('scrollend',function(e)
{
label_step.text = steps(image_scroller.currentPage+1);
});
//Create a view to hold the scrollable view
var view_holder = Ti.UI.createScrollView({
width: "75%",
height: "60%",
top: 5,
});
view_holder.add(image_scroller);
var label_step = Titanium.UI.createLabel({
text: "Test text",
top: 5,
width: "70%",
font: {fontSize: 21 },
color: textColor(),
textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
});
var label_slide = Titanium.UI.createLabel({
text: "(Swipe to view next step)",
font: {fontSize: 19 },
top: 5,
color: textColor(),
textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
width: "70%",
});
page_view.add(page_descr);
page_view.add(label_step);
page_view.add(view_holder);
page_view.add(label_slide);
//Fix fonts
if(Alloy.Globals.osname != "android"){
page_descr.width = "80%";
page_descr.top = 30;
page_descr.font = {fontSize: 19};
label_step.width = "90%";
label_step.font = {fontSize: 15};
label_slide.top = 2;
label_slide.font = {fontSize: 14};
image_scroller.top = -120;
}
else{
page_descr.width = "80%";
page_descr.top = 30;
page_descr.font = {fontSize: 15};
label_step.width = "90%";
label_step.font = {fontSize: 11};
label_slide.top = 5;
label_slide.font = {fontSize: 12};
image_scroller.top = -120;
}
win.add(page_view);
button.addEventListener('click',function(e)
{
alert("I clicked the button!");
});
win.add(button);
win.open();
The android image sizes are all: 768x735px while the iOS images are 475x475px.
Here's a screenshot of what's happening on iOS, it's pushing the text "(Swipe to view next step)" down when it should be directly below the image of the iOS home screen:
http://i.imgur.com/GfDpeDb.jpg
try after commenting the top of label_slide like,
var label_slide = Titanium.UI.createLabel({
text : "(Swipe to view next step)",
font : {
fontSize : 19
},
//top : 5,
color : textColor(),
textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
width : "70%",
});
And tell me either it works or not.
The best and easiest way to do this is to use DP values instead of percentages. If you haven't set it up or modified your tiapp.xml yet your iOS devices use DP and Android uses pixels. This is why you are seeing a difference in the layout.
Dp : Density-independent pixels. A measurement which is translated natively to a corresponding pixel measure using a scale factor based on a platform-specific "default" density, and the device's physical density.
In your tiapp.xml change the default unit to dp.
<property name="ti.ui.defaultunit" type="string">dp</property>
If you then recompile the app, clean and build it will be set up to use DP values. Try using these instead of percentages.
Details on this can be seen here -
http://docs.appcelerator.com/titanium/3.0/#!/guide/Layouts,_Positioning,_and_the_View_Hierarchy

How to add a background image (pattern) to highchart column graph?

i'm using highcharts.js for the first time. seems great and powerful, i've enjoyed it thus far.
i'm trying, however, to get my column graph to look a bit sexier and am having trouble finding the information as to how i can do this, at least in regards to using a repeating background image for the column.
here's what i had designed:
You can add patterns by using this
color: {
pattern: 'https://rawgithub.com/highslide-software/pattern-fill/master/graphics/pattern3.png',
width: 6,
height: 6
}
Modify accordingly to suit your needs in seriesData
Demo - Fiddle Here
Just for the reference, I replied to the same question on Highcharts forum (http://highslide.com/forum/viewtopic.php?f=9&t=25673)
First of all, there was an idea of using pattern fill for columns. Basic demo was posted on Highcharts Uservoice site (http://highcharts.uservoice.com/forums/55896-general/suggestions/2378007-allow-fill-patterns-for-areas-columns-plot-bands).
You can improve that code to create SVG pattern with gradient fill. The main difficulty is support for data updates (and animations). Because of this we can't define fixed width for gradient element. See the sample code below:
Highcharts.Renderer.prototype.color = function (color, elem, prop) {
if (color && color.pattern && prop === 'fill') {
// SVG renderer
if (this.box.tagName == 'svg') {
var patternA,
patternB,
bgColor,
bgPattern,
image,
id;
id = 'highcharts-pattern-' + idCounter++;
patternA = this.createElement('pattern')
.attr({
id: id,
patternUnits: 'userSpaceOnUse',
width: '100%',
height: '100%'
})
.add(this.defs);
patternB = this.createElement('pattern')
.attr({
id: id + '-image',
patternUnits: 'userSpaceOnUse',
width: color.width,
height: color.width
})
.add(this.defs);
image = this.image(color.pattern, 0, 0, 6, 6)
.add(patternB);
bgColor = this.rect(0, 0, 0, 0, 0, 0)
.attr({
fill: color.fill,
width: '100%',
height: '100%'
})
.add(patternA);
bgPattern = this.rect(0, 0, 0, 0, 0, 0)
.attr({
fill: 'url(' + this.url + '#' + id + '-image)',
width: '100%',
height: '100%'
})
.add(patternA);
return 'url(' + this.url + '#' + id + ')';
// VML renderer
} else {
var markup = ['<', prop, ' type="tile" src="', color.pattern, '" />'];
elem.appendChild(
document.createElement(this.prepVML(markup)));
}
} else {
return base.apply(this, arguments);
}
};
Live demo available here: http://jsfiddle.net/Kr82z/

Resources