I am using Flex Action Script.I am facing issue with static array. I have one static array used by two tabs.First time when I login the data is coming fine but if I am going to another tab and coming back to first tab then data from 2nd tab is appended into it and displayed in first.How to get rid of this problem?
//I'm assuming you have a var like this:
public static var sharedArray:Array;
//Then, assuming you're using a Spark TabBar, make sure you have a "change" listener defined for it, and make sure it includes the following code:
public function onTabChange(event:IndexChangeEvent):void {
sharedArray = [];
}
Related
I am using Xamarin and MVVMCross to implement an IOS app. A view I'm working on is displaying correctly, but ONLY when I hard-code the data being bound to inside my ViewModel and NOT when (as by design and necessity) my data arrives late from SQLite, only an indeterminate time after the view is shown.
What I am using and accomplished so far:
Working/Showing storyboard for my View that has a UICollectionView
inside (called: CollectionView in code below)
Custom layout and XIB file for every UICollectionViewCell that also displays correctly in my view
A view that works and displays correctly only if ViewModel data is fully populated the moment ViewDidLoad() is called.
Problem:
My data in my ViewModel is updated by the Model's databases in an uncertain amount of time whilst the view is happily being shown. When I bind the data as shown below (and trying two-way/one-way bindings and the like as well), I don't get updates on my view as the final data comes in later.
What I can't seem to do:
Redraw the UICollectionView or maybe refresh the
MvxCollectionViewSource below to ensure that as the ViewModel's data changes, I can actually redraw the UICollectionView and show my
custom cells with new and updated data.
THE CODE(TM)
The CollectionView cells are implemented as follows. I followed all examples online and from that Stuart Bloke and his Kittens to make sure I implement all the patterns exactly the same:
[Register("MyCell")]
public partial class MyCell : MvxCollectionViewCell
{
public static readonly UINib Nib = UINib.FromName("MyCell", NSBundle.MainBundle);
public static readonly NSString Key = new NSString("MyCell");
public MyCell(IntPtr handle) : base(handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<MyCell, SomeModelClass>();
set.Bind(Label1).To(item => item.Label1);
set.Bind(Label2).To(item => item.Label2);
set.Apply();
});
}
public static MyCell Create()
{
return (MyCell)Nib.Instantiate(null, null)[0];
}
}
My ViewDidLoad() in the View looks something like this:
CollectionView.RegisterNibForCell(MyCell.Nib, MyCell.Key);
var source = new MvxCollectionViewSource(CollectionView, MyCell.Key);
CollectionView.Source = source;
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(source).To(vm => vm.ListOfStuff);
set.Apply();
CollectionView.ReloadData();
NB! The ListOfStuff shown above is really just a List of a custom class containing 2 strings right now.
TL:DR: I don't know ListOfStuff's values the moment I call the above code. When I hard-code them in the ViewModel, I get joy. If I don't, I don't, even as data gets updated correctly later.
I now reach out to you, the neurons of the brain of crowdsourcing...
Instead of using a List<T> use ObservableCollection<T> and new items should be added to the CollectionView.
The UI needs to know when the collection has changed. ObservableCollection<T> implements INotifyCollectionChanged and INotifyPropertyChanged and communicates with the UI when the collection changes.
You shouldn't need ReloadData() anymore if you're using ObservableCollection<T>.
This extension method might be of use when adding range of IEnumerable<T>
public static class ObservableCollectionExtensionMethod
{
public static void AddRange<TSource>(this ObservableCollection<TSource> source, IEnumerable<TSource> collection)
{
foreach (var i in collection) source.Add(i);
}
}
I am new to Flex (got assigned to maintain an old project at work) and am having some trouble getting data binding to work correctly. I have a popup form class, AddOffer.mxml which uses a model AddOfferModel.as. On my popup form, I have the following component:
<mx:FormItem label="{getResource('addOffer.form.OFFER_DATE')}:"
labelWidth="90">
<views:OfferWindowDatesFragment
id="offerWindowField"
start="{model.offerStartDate}"
stop="{model.offerStopDate}" />
</mx:FormItem>
My AddForm.mxml file also has some embedded actionscript where I define my 'model' variable:
[Bindable]
public var model:AddOfferModel;
The model variables I am trying to bind to are standard getters/setters and look like this inside AddOfferModel.as:
[Bindable]
public function set offerStartDate(val:EditableInstant):void
{
_offerStartDate = val;
}
public function get offerStartDate():EditableInstant
{
return _offerStartDate;
}
private var _offerStartDate:EditableInstant;
[Bindable]
public function set offerStopDate(val:EditableInstant):void
{
_offerStopDate = val;
}
public function get offerStopDate():EditableInstant
{
return _offerStopDate;
}
private var _offerStopDate:EditableInstant;
Inside the OfferWindowDatesFragment component class, the start and stop variables look like this:
[Bindable]
public function set start(val:EditableInstant):void
{
_start = val;
}
public function get start():EditableInstant
{
return _start;
}
private var _start:EditableInstant;
[Bindable]
public function set stop(val:EditableInstant):void
{
_stop = val;
}
public function get stop():EditableInstant
{
return _stop;
}
private var _stop:EditableInstant;
Basically, I just want to bind the start and stop variables in my OfferWindowDatesFragment class to the offerStartDate and offerStopDate variables in the AddOfferModel.as file. Whenever I access the start/stop variables in functions inside the OfferWindowDatesFragment class, they are null.
I have an event listener function that gets triggered in OfferWindowDatesFragment anytime a user selects a new date, it looks like this:
private function changeOfferDate():void
{
start.currentValue = offerDateEditor.start;
stop.currentValue = offerDateEditor.stop;
}
Every time I reach this function, it throws up an error because 'start' and 'stop' are both null ... but should have been initialized and bound already. If I look at the variables in the debugger, I can confirm that values on the right side of the assignment expression are valid, and not what is causing the error.
I am not real familiar with how initialization works in Flex, and I assumed as long as I instantiated the component as seen in the first code snippet at the top of my post, it would initialize all the class variables, and setup the bindings. Am I missing something? Perhaps I am not properly initializing the model or class data for AddForm.mxml or AddFormModel.as, thereby binding null references to the start/stop fields in my OfferWindowDatesFragment class?
Any help would be greatly appreciated. Thanks!
EDIT:
I looked into this further and tried using Mate to inject the 'model' variable inside AddOffer.mxml with a valid AddOfferModel object:
<Injectors target="{AddOffer}" debug="{debug}">
<ObjectBuilder generator="{AddOfferModel}" constructorArguments="{scope.dispatcher}" cache="local"/>
<PropertyInjector targetKey="model" source="{lastReturn}" />
</Injectors>
I load the AddOffer.mxml dialog as the result of a button click event on another form. The function that pops it up looks like this:
public function addOffer():void
{
var addOfferDialog:AddOffer = new AddOffer();
addOfferDialog.addEventListener("addOffer", addOfferFromDialog);
modalUtil.popup(addOfferDialog);
}
It doesn't seem to be assigning anything to the 'model' variable in AddOffer.mxml. Does loading a view/dialog this way not trigger an injection from Mate by chance? (I realize this last part might belong in the Mate forums, but I'm hoping somebody here might have some insight on all of this).
In AddOffer.mxml, you have this code:
[Bindable]
public var model:AddOfferModel;
Is there something outside AddOffer.mxml that is setting this to a valid AddOfferModel? There should be. The nature of how the Flex component life cycle means that you can expect that things may be null at times as a View builds. So you should build your components to be able to "right themselves" after receiving bad data, if the data eventually comes good.
Data binding is one way to do this, but it may not paper over everything depending on what else is going on.
Have you verified that the model value you're getting is not null at the point where the user selects the date and that its offerStartDate and offerEndDate properties have been populated with valid EditableInstants? If both of those are correct, I'd start looking for pieces of the Views that expect to have stuff at a given instant and then don't recover if it is provided later.
I am new to vaadin and have a databinding problem. I have posted allready in the vaadin forum, but no answer up to now.
if you answer here, I will of course reward it anyway.
https://vaadin.com/forum/-/message_boards/view_message/1057226
thanks in advance.
greets,
Andreas
Additional information: I tried allready to iterate over the items in the container, after pressing a save button. After deleting all original elements in the model collection, and adding copies from the container, the GUI breaks. Some other GUI elements do not respond anymore.
I have personally never used ListSelect, but I found this from the API docs:
This is a simple list select without, for instance, support for new items, lazyloading, and other advanced features.
I'd recommend BeanItemContainer. You can use it like this:
// Create a list of Strings
List<String> strings = new ArrayList<String>();
strings.add("Hello");
// Create a BeanItemContainer and include strings list
final BeanItemContainer<String> container = new BeanItemContainer<String>(strings);
container.addBean("World");
// Create a ListSelect and make BeanItemContainer its data container
ListSelect select = new ListSelect("", container);
// Create a button that adds "!" to the list
Button button = new Button("Add to list", new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
container.addBean("!");
}
}
// Add the components to a layout
myLayout.addComponent(button);
myLayout.addComponent(select);
The downside (or benefit, it depends :) of this is that you can't add duplicate entries to a BeanItemContainer. In the example above the exclamation mark gets only added once.
You can get a Collection of Strings by calling:
Collection<String> strings = container.getItemIds();
If you need to support duplicate entries, take a look at IndexedContainer. With IndexedContainer you can add a String property by calling myIndexedContainer.addContainerProperty("caption", String.class, ""); and give each Item a unique itemId (or let the container generate the id's automatically).
Im not sure I understand your problem but I belive that it might be that you haven't told the controller to repaint. You do this be setting the datasource like this after the save event has occured.
listSelect.setContainerDataSource(listSelect.getContainerDataSource());
I have a flex app that used to be an internet based app. In it I have a function that creates tooltip error messages when I tell it to create an error message. I pulled this app out verbatim to a desktop app and restructured things a bit to get it to run, but I did not mess with the core fundamentals of the mxml file that utilizes this aside from changing the root tag from a type of 's:Group' to 's:Window'
Everything runs correctly, but the tooltips are not displaying. I can't seem to figure out why, so I thought I'd run this by you guys. Here's the tooltip code (inline mxml code in the fx:script cdata tag):
import mx.controls.ToolTip;
import mx.managers.ToolTipManager;
public var errorTip:ToolTip;
private function createErrorMsg(errorMsg:String, object:Object):void {
if (errorTip){ToolTipManager.destroyToolTip(errorTip);};
errorTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(root).x + object.width,object.getBounds(root).y) as ToolTip;
errorTip.setStyle("styleName", "errorTip");
errorTip.visible = true;
errorTip.enabled = true;
}
Basically, I pass the function a string and an object (text input, checkbox, button, etc...etc...) and it positions it and displays the error message. This fully works in my web version, but not in my desktop version.
Here's the code that instantiates the window:
var window:LoginWindow = new LoginWindow();
Window.systemChrome = NativeWindowSystemChrome.NONE;
Window.transparent = true;
Window.open(true);
Window.maximize()
Any ideas?
On a side note, I check to see if the errorTip exists at the beginning of the function and then destroy it so that the higher scoped variable 'errorTip' always equals the reference to the currently displayed error. This allows me to just destroy that error tip on form validation and then error check again, but it only allows one tooltip to be displayed at a time. Is there a better way to query the tooltip manager for all of it's currently displayed tooltips and destroy them all?
To resolve this you must change the line that calls 'createTooltip' to the following:
errorTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(root).x + object.width,object.getBounds(root).y, null, object as IUIComponent) as ToolTip;
The idea behind this is that you can now just call 'createErrorMsg("myError",myComponent)' and it'll display an error message there, which you can then add another function:
private function clearError():void
{
if(errorTip) errorTip.visible = false;
}
to actually remove the error message. The down side of this is that it only displays one message at a time but I'll tweak it a bit to change that up. So here's my better resolution:
Error messages will (traditionally) only be displayed in a batch set. (Basically you will only have a handful of tooltips displayed at one point in time and they will all be related to the form you are on). This won't always be true, but that's not my concern here. I created a utility class that performs error message handling:
package Utils
{
import mx.controls.ToolTip;
import mx.core.IUIComponent;
import mx.managers.ToolTipManager;
public class ErrorUtils
{
private static var errorCache:Array = new Array();
public function ErrorUtils()
{
}
public static function createErrorMsg(errorMsg:String, object:Object):void {
var errorTip:ToolTip = ToolTipManager.createToolTip(errorMsg,object.getBounds(object.root).x + object.width,object.getBounds(object.root).y, null, object as IUIComponent) as ToolTip
errorTip.setStyle("styleName", "errorTip");
errorTip.visible = true;
errorTip.enabled = true;
var position:int = errorCache.length;
errorCache.push(errorTip);
}
public static function clearAllErrors():void
{
for each(var error:ToolTip in errorCache)
{
ToolTipManager.destroyToolTip(error);
}
}
}
}
To use the class you simply call ErrorUtils.createErrorMsg("my message",invalidComponent), and to clear the errors you simply call ErrorUtils.clearAllErrors()
That way if you have a form and you have a validation function for it, you simply call that function every time it comes back as invalid. The only thing that this is missing is any kind of way to clear a specific error, but to handle that you could place all of the creation calls inside of a master validation function that validates all the fields in your form, then call that each time you gain or loose focus on a field and call 'ErrorUtils.clearAllErrors()' at the beginning of the function.
BTW:
this is the source of the original tutorial I received to do this, I just abstracted it a bit
http://aralbalkan.com/1125
so credits go to the above site for the original code
im trying to getting auto complete working and i can do so fine when i just create an array in my mxml and then just initialize an arrayCollection at the top of the file in the initialize keyword.
However i want to populate the arraycollection from a webservice but i cant seem to get it;
im my application tag i have the following
creationComplete="init()"
initialize="data2 = new ArrayCollection(data1);"
then in my init method;
private function init():void
{
userRequest.loadWSDL(wsdlUrl);
userRequest.getAllCountries();
}
//this is called when i get a result from userRequest.getAllCountries();
private function getAllCountriesResult(e:ResultEvent):void
{
data1 = new Array(e.result);
}
however my text box is not getting any value.
Anyone with ideas?
first off, Array is not Bindable so changing the variable data1 will have no knock on effect.
The arrayCollection is bindable.
So presumming that the result (e.result) is actually an array (you should check this when debugging) then you could do the following
[Bindable]
priavte var ac : ArrayCollection;
then inside you're getAllCountriesResult function.
ac = new ArrayCollection(e.result);
then anything that has is dataprovider set to the var ac will be updated.
If you wish to update a text value inside a textArea or similar then you should listen for the change event in the arrayCollection and take the appropriate action then.
from your additional points below (just edit your original question)
I take it the autocomplete your talking about is the autocomplete text input box from adobe exchange area as a normal text box doesn’t take an arrayCollection.
If you posted some code it may make it easier to help you.
Preinitialize, then initialize, then creationComplete, then applicationComplete (this is the order they get called in).
If your using the component I’m thinking of, check out http://www.websector.de/blog/2008/04/30/quick-tip-avoid-issues-using-adobes-autocomplete-input-component-using-flex-3/
It appears it may have some issues with flex 3, so check out http://blogs.adobe.com/flex/2006/09/component_autocomplete_text_in.html .
Try this:
private function getAllCountriesResult(e:ResultEvent):void
{
data2.source = new Array(e.result); // or data2.source = e.result as Array
}
Make sure data2 is already initialized as a ArrayCollection.
As for AutoComplete, I'm trying to work things out myself.