I am currently trying to use the new approach of using FlexGlobals to access an ArrayCollection that I have on the Default view of a mobile application I am developing. Below is the code that makes up the creation and population of that array on the Default view which happens as soon as the app is initiated:
private var ids:ArrayCollection = new ArrayCollection();
private function loop():void
{
var index:int;
for( index = 0; index < compsCollection.length; index++ )
{
trace( "Element " + index + " is " + compsCollection[index].comp_id );
trace( ids.length);
ids.addItem(compsCollection[index].comp_id);
}
}
Now when this code is run i can clearly see from the console that the "ids" ArrayCollection is being populated correctly. Now on a different view within the app I want to access this data and use it for various things. I have used the code below to try and access the data for the ArrayCollection:
protected var ids_list:ArrayCollection = new ArrayCollection();
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
var obj:Object = FlexGlobals.topLevelApplication.parameters;
ids_list.source = obj.ids;
trace(ids_list.length);
}
When i tried this i do not get an error however the Trace statement returns "0". So I also tried:
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
ids_list.source = FlexGlobals.topLevelApplication.parameters.ids;
trace(ids_list.length);
}
Which again returned "0" in the trace statement. I finally tried this to see if it would work:
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
ids_list.source = FlexGlobals.topLevelApplication.ids;
trace(ids_list.length);
}
When I tried this and navigated to the view where this code would be initated I got this error:
Error #1069: Property ids not found on Main and there is no default value.
I could only assume that the ArrayCollection I had created in my default view has no value once I navigate away from it. Is there anyway anyone can please help me with this as there seems to be next to no documentation on how to do these types of things with FlexGlobals? Thanks
You can't access a private property, try it
/*
Replace
private var ids:ArrayCollection = new ArrayCollection();
by
*/
private var _ids:ArrayCollection = new ArrayCollection();
public set ids(value:ArrayCollection):void{
_ids = value;
}
public get ids():ArrayCollection{
return _ids
}
It's extremely bad practice for a child component to know anything at all about the structure of its parent/ancestor. That being said, the reason you can't see the value of the Application's member variable is because you've made it private. If you want it to be visible outside its own scope, you need to make it public.
Pass the ArrayCollection as a property to your view from the main application.
Related
I used the lifecycle callback onCreate to fetch data like below
mWeOutViewModel.getPlaceListLiveData()
.observe(this, weOutItemViewModels -> {
AppLogger.i(getCustomTag() + "adding items " + weOutItemViewModels.size());
if (weOutItemViewModels != null && weOutItemViewModels.size() > 0)
mWeOutListAdapter.addToExisting(weOutItemViewModels);
});
As you can see the AppLogger output the initial size which is 0 when the fragment is displayed, then I fetch the data and call postValue (setValue crashes the app and it expected because I fetch data from the internet using a background thread). So I call post value like below :
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> oldList = placeMutableLiveData.getValue();
oldList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+oldList.size());
placeMutableLiveData.postValue(oldList);
}
As you can see the other AppLogger before postValue, the size of the list is displayed(not empty), but nothing happens until the app crashes and nothing is shown in the logs. I have no ways of debugging since even on debug mode nothing happens. The post value doesn't trigger the observer.
I initialize the mutableLivedata like this :
private final MutableLiveData<List<WeOutGroupedViewModels>> placeMutableLiveData = new MutableLiveData<>();
and access like this :
public LiveData<List<WeOutGroupedViewModels>> getPlaceListLiveData() {
return placeMutableLiveData;
}
Event when I make the livedata public to access directly the livedata, there is no change (just in case someone thinks that's is where the issue comes from)
Instead of placeMutableLiveData.postValue(oldList);
I recommend using
placeMutableLiveData.postValue(Collections.unmodifiableList(new ArrayList<>(newList));
That way, the next time you access this list, you won't be able to mutate it in place, which is a good thing. You're not supposed to mutate the list inside a reactive state holder (MutableLiveData).
So theoretically it should look like this:
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> newList = new ArrayList<>(placeMutableLiveData.getValue());
newList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+newList.size());
placeMutableLiveData.postValue(Collections.unmodifiableList(newList));
}
I'm trying to create an app for my Samsung Gear S3 that takes data from an API, dynamically creates a Listview, and propagates it using the data returned via the API. I've got as far as putting the items in the list, but when the page is hidden I want to remove everything from the list. Since I'm doing it programmatically in the first place, there's no easy way such as removing all of the elements using a querySelector and refreshing the Listview.
That said, much how I'm using .addItem - is there a .removeItem or equivalent that isn't documented?
At the moment, I'm listening to the pagebeforehide event and calling .destroy() on the created Listview - which works in the sense that the _items property is cleared but the Listview is still displaying all of the items from before...
The code I have at the moment can be seen below.
(function() {
var page = document.getElementById('search.results'),
listView;
var elems = [ ];
page.addEventListener("pagebeforeshow", function() {
if(sessionStorage.length && sessionStorage.getItem("currentResults")) {
var currentResults = JSON.parse(sessionStorage.getItem("currentResults"));
var elem = document.getElementById("results-list");
listView = tau.widget.Listview(elem, { dataLength: currentResults.length, bufferSize: 10 });
listView.setListItemUpdater(function(listElement, newIndex) {
var data = currentResults[newIndex];
listElement.innerHTML = data.Description;
listElement.id = data.EAN;
});
}
$("li").click(function(e) {
var li = $(e.target);
if(li.attr("id")) {
var EAN = li.attr("id");
tau.changePage("product.html", { name: li.innerHTML, ean: EAN });
}
});
});
page.addEventListener("pagebeforehide", function() {
console.log("page before hide");
if(sessionStorage.length && sessionStorage.getItem("currentResults")) {
console.log(sessionStorage);
sessionStorage.removeItem("currentResults");
console.log(sessionStorage);
}
listView.destroy();
console.log(listView);
});
}());
This list does not actually have an API for removing items or binding to data.
I see you have solved this problem practically by yourself.
I think it is enough to clear the base element of the list now, eg. listView.element.innerHTML = "" or document.getElementById("results-list").innerHTML = "".
I think you can also create issue on https://github.com/Samsung/TAU/issues
because the .destroy() method should remove items which was not attached to widget before widget build.
It's been a while that I just started to learn how to develop in Kotlin.
There is this thing that I am working on, I am trying to parse a list into another type of list. Basically they are the same thing but with different names. But when I try to populate the new list with the data that I get from the list given as parameter in the function the list only gets populated with the first object.
Here is my function:
fun convertRoomClass(course: List<Course>) : List<Courses> {
lateinit var list : List<Courses>
course.forEach {
val id = it.pathID
val name = it.pathName
val desc = it.pathDescription
val crs : Courses = Courses(id, name!!, desc!!)
list = listOf(crs)
}
return list
}
The error in your code is that you are making a list in every iteration of the loop. You should make the list first and then add every item from the loop to it!
fun convertRoomClass(courses: List<Course>) : List<AnotherCourseClass> {
val newList = mutableListOf<AnotherCourseClass>()
courses.forEach {
newList += AnotherCourseClass(it.pathID, it.pathName, it.pathDescription)
}
return newList
}
A better solution is to use the map function
fun convertRoomClass(courses: List<Course>) = courses.map {
AnotherCourseClass(it.pathID, it. pathName, it.pathDescription)
}
You might be looking for Kotlin Map
Example:
course.map { Courses(it.pathID, it.pathName,it.pathDescription) }
You're getting the list with only on object, cause the function listOf(crs) returns a list of all objects that are passed as a parameters. Saying the same thing in Java you're doing something like this:
for (course: Courses) {
Course course = new Course(...);
List<Course> list = new ArrayList<>();
list.add(course);
return list;
}
As you can see the it created new list with a single object per iteration.
What you're trying to achieve, can be done with operator map{...} which simply transforms every object in the initial list using code passed inside map and returns list of transformed objects
course.map{ Courses(...) }
Also, I've noticed that you're using the !! operator when creating a Courses object. Probably because the Course can have nullable name, while Courses can't. I'm considering this as a bad practice, cause in this case you're saying
Please throw an Exception if the name is null.
I think that a much better approach is to provide an alternative, like:
val name = course.name ?: "default", saying
Please use name or "default" if the name is null.
or skip objects without name, or any other approach that suits your situation.
You could use MutableList instead of List. That enable you to append new element at the end of your list instead of replace the entire list by doing : list = listOf(crs)
So replace the type of your var lateinit var list : List<Courses> by lateinit var list : MutableList<Courses> then replace list = listOf(crs) by list.add(crs)
Hope it helps and have fun :)
Why Android Studio show error when I use No.2 script.
I found no different between 1 and 2.
class Adapter {
var nameList : ArrayList<String>? = null
}
class Program {
private fun send() {
val list: ArrayList<String> = ArrayList()
val adapter = Adapter()
// Case 1
var otherList = adapter.nameList
if (otherList != null) {
list.addAll(otherList) // <--- no error
}
// Case 2
if (adapter.nameList!=null) {
list.addAll(adapter.nameList) // <--- Error here
// Smart cast to 'kotlin.collections.ArrayList<String> /* = java.util.ArrayList<String> */' is impossible, because 'adapter.nameList' is a mutable property that could have been changed by this time
}
}
}
Please explain this case
The IDE should give you a warning, explaining that after the null check, it's possible that adapter.nameList was changed by another thread, and that when you call list.addAll(adapter.nameList), adapter.nameList could actually be null by that point (again, because a different thread could have changed the value. This would be a race condition).
You have a few solutions:
Make nameList a val, which makes its reference final. Since it's final, it's guaranteed another thread can't change it. This probably doesn't fit your use case.
class Adapter {
val nameList : ArrayList<String>? = null
}
Create a local copy of name list before you do the check. Because it's a local copy, the compiler knows that another thread can't access it, and thus it can't be changed. The local copy could be defined with either a var or a val in this case, but I recommend val.
val nameList = adapter.nameList
if (nameList != null) {
list.addAll(nameList)
}
Use one of the utility functions that Kotlin provides for just such a case as this. The let function copies the reference it's called on as a parameter using an inline function. This means that it effectively compiles down to be the same as #2, but it's a bit more terse. I prefer this solution.
adapter.nameList?.let { list.addAll(it) }
Your adapter.nameList is mutable property so please convert it to immutable.
Use this
val nameList : ArrayList<String>? = null
Instead of this
var nameList : ArrayList<String>? = null
Or you can also solve this problem by assert of non null Assert
list.addAll(adapter.nameList!!)
Note :- !! is evaluated at runtime, it's just an operator.
The expression (x!!)
throws a KotlinNullPointerException if x == null,
otherwise, it returns x cast to the corresponding non-nullable type (for example, it returns it as a String when called on a variable with type String?).
adapter.nameList is a mutable property that could have been changed`
The reason for this check and error message is threads. What you have is called a race-condition. In many similar cases it is possible for another thread to change the value of adapter.namelist between the nullity check and the list.addAll call. Clearly this can not happen in your case as the adapter is not leaked from the send function, but I guess the compiler isn't smart enough to know that.
In contrast there is no race condition in case 1 as the namelist is only accessed once.
Also this can not happen if namelist is val rather than var - since the compiler then knows it can not change - so it can not change from non-null to null.
I'm working with Titanium SDK 2.1.3 and developing for both iOS 6.1 and Android 4.0.
My application has a TableView, each TableViewRow represents a file and has a custom property called lockedStatus that can be either true or false, when a row is clicked, depending on the value of lockedStatus, a window is opened indicating the state of the element of the row. In this window I can modify the lockedStatus from true to false and viceversa.
The problem I'm having is that in Android the changes done to this property are ignored, unless a completely force the application to stop, this doesn't happen in iOS.
To change the lockedStatus value in the window, I fire an event like this:
Ti.App.fireEvent('updateLockedStatus', {
lockedStatus : true //this can be true or false
});
Then the listener is like this:
Ti.App.addEventListener('updateLockedStatus', function(e){
var rows = table.data[0].rows;
for (var i = 0; i < rows.length; i++) //iterate through the rows to look for the file
{
if (rows[i].fileID == currentlyEditingFile)
{
rows[i].updateLockedStatus(e.lockedStatus);
files[currentlyEditingFile].lockedStatus = e.lockedStatus; //update the file lockedStatus
rows[i].fileObject = files[currentlyEditingFile];
saveFilesToDatabase();
}
}
});
Each row is generated with a updateLockedStatus function, that has the following definition:
row.updateLockedStatus = function(lockedStatus)
{
row.lockedStatus = lockedStatus;
}
The saveFilesToDatabase function looks like this:
function saveFilesToDatabase()
{
var tempFilesArray = [];
for(var i=0;i<filesIds.length;i++)
{
tempFilesArray.push(files[filesIds[i]]);
}
Ti.App.Properties.setString('filesApp', JSON.stringify(tempFilesArray)); //save the file values in the application properties
}
What I mean by being ignored is that when I exit the window and return to the view with the table of files, if I click again on the file I just modified, the window will be opened as if no change had been done.
How can I force an update for all of the contents in the TableView? In iOS there's no problem, but with Android this isn't updated unless I close the application.
Any help will be appreciated.
try using Ti.API.fireEvent & Ti.API.addEventListener instead of Ti.App
you would take care when doing this :
Each row is generated with a updateLockedStatus function, that has the following definition:
row.updateLockedStatus = function(lockedStatus)
{
row.lockedStatus = lockedStatus;
};
If the code above is put in a loop then executing the row.updateLockedStatus may affect only the last row because the row variable gets reused and contains at the end only the last row object. Take a look at this
You can simply use this code :
row.updateLockedStatus = function(lockedStatus)
{
this.lockedStatus = lockedStatus; // this is the current row
};
Or using closures, something like that :
row.updateLockedStatus = (function(r){
return function(lockedStatus){
r.lockedStatus = lockedStatus; // r is the current row
};
})(row);
the local r variable should retain the right row object.
BTW, when you listen events using Ti.App, you should use variables for handlers. Instead of doing this :
Ti.App.addEventListener('updateLockedStatus', function(e){
use this way
var updateLockedStatusHandler = function(e){
//...
};
Ti.App.addEventListener('updateLockedStatus', updateLockedStatusHandler);
Then this could be useful to remove the handler and avoid a potential memory leak :
Ti.App.removeEventListener('updateLockedStatus', updateLockedStatusHandler);