Titanium collections display - titanium

I have two questions about titanium collections :
This is my controller :
var products = Alloy.Collections.products;
products.fetch({
success:function(_m,_r){
var size = Alloy.Globals.half,
width = size;
for(var i = 1; i<=_resultats.length; i++){
width = i%2 == 0 ? size : size-1;
backgroundColor = i%4 == 2 || i%4 == 3 ? Alloy.CFG.colors.lightgrey : "white";
$.item.width = width;
}
});
This is my View :
<ScrollView dataCollection="products" dataTransform="parse_liste" layout='vertical' id="products">
<View id='item'>
<Label text='{itemName}' />
<ImageView image='images/star.png' id='rating' />
<ImageView image='images/star.png' id='rating' />
<ImageView image='images/star.png' id='rating' />
<ImageView image='images/star.png' id='rating' />
</View>
</ScrollView>
All items is displayed well, but i have following error :
[ERROR] : Script Error {
[ERROR] : column = 25;
[ERROR] : line = 269;
[ERROR] : message = "undefined is not an object (evaluating '$.item.width = width')";
[ERROR] : stack = "success\nsuccess\n\nonLoad\nonload";
[ERROR] : }
Secondly how to loop over ratings images from item ratings?, can i do a kind of a loop inside alloy tags?
Thanks for your help.

I think that you are missing a close brace.
But besides that, you could set the width like this, there is no need to create those 2 variables (width & size)
var products = Alloy.Collections.products;
products.fetch({
success:function(_m,_r) {
for(var i = 1; i<=_resultats.length; i++) {
backgroundColor = i%4 == 2 || i%4 == 3 ? Alloy.CFG.colors.lightgrey : "white";
$.item.width = !parseInt(i%2) ? Alloy.Globals.half : Alloy.Globals.half-1;
}
}
});

First you are trying to do collection data binding with a ScrollView and I'm not sure if that's possible out of the box.
Just do it in plain javascript. If you want to keep the item object you can put it in a separate controller and pass appropriate data. Remove xml from the body of the scrollview too.
var products = Alloy.Collections.products;
products.fetch({
success:function(_m,_r) {
var models = _m.models;
models.forEach(function(model){
// In the for loop create an item controller and pass model data
var modelData = model.toJSON();
Ti.API.info(JSON.stringify(modelData));
var item = Alloy.createController('item', modelData) //pass here data for each model
$.products.add(item.getView());
}
}
}
});

Related

How to find find specific Views of the Recycler View and change the background colour?

I am building an app using Kotlin with the MVVM approach, and my Recycler View using multiple view types.
Inside my List Adapter inside the override fun onBindViewHolder, I have a code that detects the first click on the row, the second click to the same row and the first click to the different row.
The click detection works correctly. My goal here is to save the correct view id when I click on the row the first time, then when I click on a different row I would like to find the first row and put back the original background.
I know that this is a recycler view, but I do not scroll the view I just would like to sort the click and put back the original background.
I already saw a lot of examples where someone hardcoded the background colour, but this is not what I am looking for.
I already tried to save the view id, but seems to me I am saving the wrong id because when I try to restore the current view id is the same as the saved view id.
The code that should find the previouse view is this:
val prevConstrainLayoutView = holder.itemView.findViewById<ConstraintLayout>(prevClickedItemViewId)
}
How to save the correct view id or something else and then restore the previously clicked row with the original background colour?
Current Androdi Handheld Screen
onBindViewHolder
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current)
Log.d("onBindViewHolder->", "Views")
// apply the click listener for the item
holder.itemView.setOnClickListener {
// that should check if something was selected, but not sure
if (holder.bindingAdapterPosition != RecyclerView.NO_POSITION) {
onClickListener.onClick(current)
if (clicked == 1 && clickedItem != current.id) {
prevClickedItem = clickedItem
prevClickedItemType = clickedItemType
prevClickedItemViewId = clickedItemViewId
prevClickedItemRootBackgroundDrawable = clickedItemRootBackgroundDrawable
prevClickedItemRootBackgroundColour = clickedItemRootBackgroundColour
prevClickedItemView = clickedItemView
clicked = 1
clickedItemRootBackgroundDrawable = holder.itemView.background.current
clickedItemRootBackgroundColour = holder.itemView.solidColor
clickedItemViewId = holder.itemView.id
clickedItemType = current.orderBy
clickedItem = current.id
clickedItemView = holder.itemView
clickedItemView.tag = 2
if (clickedItem!=prevClickedItem && prevClickedItemViewId!=null && prevClickedItemType!=-1 && clickedConstraintLayout!=null) {
val prevConstrainLayoutView = holder.itemView.findViewById<ConstraintLayout>(prevClickedItemViewId)
Log.d("onBindViewHolder->", "Clicked second time different row")
Log.d("onBindViewHolder->", "$prevConstrainLayoutView and $prevClickedItemType")
when (prevClickedItemType) {
TYPE_ZERO -> {
prevConstrainLayoutView.setBackgroundColor(ContextCompat.getColor(holder.itemView.context, R.color.green_sushi))
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: green_sushi")
}
TYPE_ONE -> {
prevConstrainLayoutView.setBackgroundColor(ContextCompat.getColor(holder.itemView.context, R.color.yellow_background))
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: yellow_background")
}
TYPE_TWO -> {
prevConstrainLayoutView.setBackgroundColor(ContextCompat.getColor(holder.itemView.context, R.color.white_text))
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: white_text")
}
TYPE_THREE -> {
prevConstrainLayoutView.setBackgroundColor(ContextCompat.getColor(holder.itemView.context, R.color.blue_heather))
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: blue_heather")
}
TYPE_FOUR -> {
prevConstrainLayoutView.setBackgroundResource(R.drawable.purple_orange_background)
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: purple_orange_background")
}
else -> {
prevConstrainLayoutView.setBackgroundColor(ContextCompat.getColor(holder.itemView.context, R.color.green_sushi))
Log.d("onBindViewHolder->", "Clicked second time different row, set the prev view to: green_sushi")
}
}
}
holder.itemView.setBackgroundColor(
ContextCompat.getColor(
holder.itemView.context,
R.color.blue_background
)
)
} else if (clicked == 1 && clickedItem == current.id) {
// second click the same row
clicked = 0
clickedItem = current.id
} else if (clicked == 0) {
// first click
clicked = 1
clickedItem = current.id
clickedItemType = current.orderBy
clickedItemViewId = holder.itemView.id
holder.itemView.tag = 1
clickedItemRootBackgroundDrawable = holder.itemView.background.current
clickedItemRootBackgroundColour = holder.itemView.solidColor
clickedItemView = holder.itemView
clickedConstraintLayout = holder.itemView.findViewById<ConstraintLayout>(R.id.root)
Log.d("onBindViewHolder->", "Clicked first time, set the view to: blue_background, " +
"\nconstraint layout:$clickedConstraintLayout")
holder.itemView.setBackgroundColor(
ContextCompat.getColor(
holder.itemView.context,
R.color.blue_background
)
)
}
}
}
}
item view layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/purple_orange_background"
android:paddingLeft="24dp"
android:paddingRight="24dp">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:gravity="center_vertical"
android:text="View 6 TextView"
android:textColor="#color/white"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is what I am using in my ListAdapter:
class WordListAdapter(private val onClickListener: MyRecyclerViewOnClickListener) :
ListAdapter<Word, WordListAdapter.WordViewHolder>(WordsComparator()) {
This is part of the code from View Model:
val allOrderedWords: LiveData<List<Word>> = repository.allOrderedWords.asLiveData()
This is in my Activity:
wordViewModel.allOrderedWords.observe(this, Observer { words ->
// Update the cached copy of the words in the adapter.
words?.let { adapter.submitList(it) }
})
I meant that the view holder should always represent the item and never be a source of information. Single scroll and you will lose it. Do not store "view id", store information about what item it was.
var clickedItem: Word? = null
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
val current = getItem(position)
val setBackgroundColor: (Int) -> Unit = { color ->
viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.context,color)
}
holder.bind(current)
val color = when (current.itemType) {
TYPE_ZERO -> R.color.green_sushi
TYPE_ONE -> R.color.yellow_background
TYPE_TWO -> R.color.white_text
TYPE_THREE -> R.color.blue_heather
TYPE_FOUR -> R.drawable.purple_orange_background
else -> R.color.green_sushi
}
setBackgroundColor(color)
holder.itemView.setOnClickListener {
onClickListener.onClick(current)
when {
clickedItem == null -> {
clickedItem= current
setBackgroundColor(R.color.blue_background)
}
clickedItem == current -> {
// Here I assume that you want to clear the "mark"?
clickedItem= null
notifyItemChanged(position)
}
clicked != current -> {
val oldClicked = clickedItem
clickedItem = current
val indexOfPreviouslyClicked = data.indexOf(oldClicked)
notifyItemChanged(indexOfPreviouslyClicked)
setBackgroundColor(R.color.blue_background)
}
}
}
}
This way you will be able to say RecyclerView(with notifyItemChanged(pos)) itself that a certain item should be updated and it will force onBindViewHolder on that item.
In the end, I find a solution that solves the issue and works exactly as expected.
Step by step how I solve the issue:
Every item layout point to their drawable state.
android:background="#drawable/green_sushi_state"
Every drawable state has two pointers one when you click - select item and second is the default
<item android:drawable="#drawable/pressed_blue_background" android:state_pressed="false" android:state_selected="true" />
<item android:drawable="#drawable/green_sushi_background" />
These two pointers point to the background with the colour that you prefer.
Under my ListAdapter I am using SparseBooleanArray to keep track of what was selected :
private val tvSparseBooleanArraySelectedItems = SparseBooleanArray()
Inside my onBindViewHolder function, I am checking if the position exists in the SparseBooleanArray or not. The default value is false. The value is assigned to my Constraint Layout. The value is assigned whenever you scroll.
val mConstraintLayout = holder.itemView.findViewById<ConstraintLayout>(R.id.root)
val isItSelected = tvSparseBooleanArraySelectedItems.getOrDefault(position, false)
mConstraintLayout.isSelected = isItSelected
Under my ViewHolder inner class, I have multiple types of my views.
private fun bindOther(word: Word) {
//Do your view assignment here from the data model
val mTextView = itemView.findViewById<TextView>(R.id.textView)
mTextView.text = word.word
val mConstraintLayout = itemView.findViewById<ConstraintLayout>(R.id.root)
mTextView.setOnClickListener {
onClickListener.onClick(word)
manageSparseBooleanArrayData(tvSparseBooleanArraySelectedItems, bindingAdapterPosition)
mConstraintLayout.isSelected = true
}
}
fun manageSparseBooleanArrayData manages the SparseBooleanArray by iterating through, finding the key, deleting the key and notifyItemChanged(keyIn). I am iterating through the SparseBooleanArrayData because I want to use notifyItemChanged instead of notifyDataSetChanged.
fun manageSparseBooleanArrayData(_sparseBooleanArrayOnTextView: SparseBooleanArray, _position: Int) {
when (currentTvSelectionMode) {
SINGLE_SELECTION -> {
if (_sparseBooleanArrayOnTextView.isNotEmpty()) {
for (i in 0 until _sparseBooleanArrayOnTextView.size()) {
if (_sparseBooleanArrayOnTextView.valueAt(i)) {
val keyIn = _sparseBooleanArrayOnTextView.keyAt(i)
val value = _sparseBooleanArrayOnTextView.get(keyIn)
_sparseBooleanArrayOnTextView.delete(keyIn)
notifyItemChanged(keyIn)
}
}
}
tvSparseBooleanArraySelectedItems.put(_position, true)
}
MULTIPLE_SELECTION -> {
tvSparseBooleanArraySelectedItems.put(_position, true)
}
else -> {}
}
}
Hope you find my solution helpful.

Razor Page unable to parse the < correctly

Using VS2019 Version 16.6.5 Razor Page Model. Core 3.1
#{
if (Model.Mode != enAccessMode.Read)
{
<text>
$("#mTable tbody tr div").removeClass("TrError").
find("[data-title]").removeAttr("data-title");
var errs = JSAns.CollectData();
if (errs !== null) {
for (var i = 0; i < errs.length; i++) { // Razor doesn't likes the < here!!!!!
var err = errs[i];
var e = $("#mQ_" + err.ErrQID);
e.addClass("TrError");
e.attr("data-title", err.ErrMsg);
}
$("#mQ_" + errs[0].ErrQID)[0].scrollIntoView();
return;
}
</text>
}
}
The compiler doen't like the < symbol at line 9. It gives the error below during compilation. using the < doesn't help at all, since it is a javascript code, not a text string.
End of file or an unexpected character was reached before the "" tag could be parsed. Elements inside markup blocks must be complete. They must either be self-closing ("<br />") or have matching end tags ("<p>Hello</p>"). If you intended to display a "<" character, use the "<" HTML entity.
As the error shows, the razor will regard the < as the uncomplete html tag. To solve this issue, I suggest you could try below work around.
#Html.Raw("<")
Whole codes:
<text>
$("#mTable tbody tr div").removeClass("TrError").
find("[data-title]").removeAttr("data-title");
var errs = JSAns.CollectData();
if (errs !== null) {
for (var i = 0; i #Html.Raw("<") errs.length; i++) {
var err = errs[i];
var e = $("#mQ_" + err.ErrQID);
e.addClass("TrError");
e.attr("data-title", err.ErrMsg);
}
$("#mQ_" + errs[0].ErrQID)[0].scrollIntoView();
return;
}
</text>

realm react-native: how to query correctly an array of strings

can someone show me how to query an array of strings with realm in react-native?
assume i have an array like the following:
const preferences = ["automatic","suv","blue",eco]
What I want is to get realm results where ALL strings in the attribute "specifications" of Cars is in "preferences".
E.g.: If an instance of Cars.specifications contains ["automatic","suv"]
a result should be returned.
But if an instance of Cars.specifications contained ["automatic,"suv","green"] this instance shouldn't be returned.
The length of preferences can vary.
Thank you very much.
Update:
What i tried is the following:
const query = realm.objects("Cars").filtered('specifications = preferences[0] OR specifications = preferences[1]')
As you see it is an OR operator which is surely wrong and it is hardcoded. Looping with realm really confuses me.
This code will work!
const collection = realm.objects('Cars');
const preferences = ["automatic","suv","blue","eco"];
let queryString = 'ANY ';
for (let i = 0; i < preferences.length; i++) {
if (i === 0) {
queryString += `specifications CONTAINS '${preferences[i]}'`;
}
if (i !== 0 && i + 1 <= preferences.length) {
queryString += ` OR specifications CONTAINS '${preferences[i]}'`;
}
}
const matchedResult = collection.filtered(queryString);
example of function to test if a word is inside an array of word
function inArray(word, array) {
var lgth = array.length;
word = word.toLowerCase();
for (var i = 0; i < lgth; i++) {
array[i] = (array[i]).toLowerCase();
if (array[i] == word) return true;
}
return false;
}
const preferences = ["automatic","suv","blue","eco"];
const specifications = ["automatic","suv"] ;
const specifications2 = ["automatic","suv", "boat"] ;
function test(spec,pref){
for (var i in spec){
if(!inArray(spec[i],pref)){
return false ;
}
}
return true;
}
console.log(test(specifications,preferences));
console.log(test(specifications2,preferences));
https://jsfiddle.net/y1dz2gvu/

How to curve the navtitle along the path using wheelnav js

How can I make the navtitle curve along the path of the slice and wrap the text if it's long.
Image of the wheel above
In long text, use '\n' in the title for wrap.
wheel.createWheel(["Long\ntext"]);
Currently, the navtitle curve along the path is an RC feature, so please use the source code instead of the last release.
You can find the new properties in this CodePen: https://codepen.io/softwaretailoring/pen/RQYzWm
var piemenu = new wheelnav("wheelDiv");
// New properties in wheelnav.js v1.8.0
piemenu.titleCurved = true;
piemenu.titleCurvedClockwise = false;
piemenu.titleCurvedByRotateAngle = false;
Unfortunately, the two above properties don't work together. :(
UPDATE: There is a way to achieve your needs. You can use two wheels on each other.
var piemenu = new wheelnav("wheelDiv");
setMenu(piemenu); // Set common properties
piemenu.titleRadiusPercent = 0.65; // Positioning first title
piemenu.markerEnable = true;
piemenu.slicePathFunction = slicePath().DonutSlice;
piemenu.sliceClickablePathFunction = slicePath().DonutSlice;
piemenu.titleHoverAttr = { fill: "#333" };
piemenu.createWheel(["Hello", "world!", "-------"]);
var piemenu2 = new wheelnav("wheelDiv2", piemenu.raphael);
setMenu(piemenu2); // Set common properties
piemenu2.wheelRadius = 520; // Positioning second title
piemenu2.slicePathFunction = slicePath().NullSlice; // There is no slice, only title
piemenu2.createWheel(["Bello", "space!", "*******"]);
// Link navigateFunctions to each other
for (var i = 0; i < piemenu.navItems.length; i++) {
piemenu.navItems[i].navigateFunction = function () {
piemenu2.navigateWheel(Math.abs(this.itemIndex));
}
}
for (var i = 0; i < piemenu2.navItems.length; i++) {
piemenu2.navItems[i].navigateFunction = function () {
piemenu.navigateWheel(Math.abs(this.itemIndex));
}
}
Here is a new CodePen for wrapped and curved text: https://codepen.io/softwaretailoring/pen/eLNBYz

Centering Map around multiple points (annotations) on the map - Titanium mobile

I have multiple annotations on the map - although the user may need to scroll to see them. I want the user to be able to select a button to fit them all on the map - eliminating the need to scroll/manually resize.
I have determined the min. and max. latitudes and longitudes (e.g minLat, maxLat, minLong, maxLong) for the points I want to show on the map.
I'm struggling, however, with the specific formula to use to get the longitude and latitude properties to pass to mapview.setLocation. Any pointers on what formula I can use?
I have made a function for the same requirement. You can test and let me know if it gives some error:
function setMarkersWithCenter(map,latiarray,longiarray)
{
if(latiarray.length != longiarray.length)
return;
var total_locations = latiarray.length;
var minLongi = null, minLati = null, maxLongi = null, maxLati = null;
var totalLongi = 0.0, totalLati = 0.0;
for(var i = 0; i < total_locations; i++)
{
if(minLati == null || minLati > latiarray[i]) {
minLati = latiarray[i];
}
if(minLongi == null || minLongi > longiarray[i]) {
minLongi = longiarray[i];
}
if(maxLati == null || maxLati < latiarray[i]) {
maxLati = latiarray[i];
}
if(maxLongi == null || maxLongi < longiarray[i]) {
maxLongi = longiarray[i];
}
}
var ltDiff = maxLati-minLati;
var lgDiff = maxLongi-minLongi;
var delta = ltDiff>lgDiff ? ltDiff : lgDiff;
if(total_locations>0 && delta>0)
{
map.setLocation({
animate : true,
latitude:((maxLati+minLati)/2),
longitude:((maxLongi+minLongi)/2),
latitudeDelta:delta,
longitudeDelta:delta,
});
}
}
Hope it helps.