Titanium Appcelerator (Alloy): I want to add and delete from tableview - titanium

i fill data in the textfield when i press done on android keyboard it will enter the data in the tableviewrow but i dont know how?
i did it on button click
and second question is : i want to delete the data on button click whose
row has
hasCheck=true
i mean to say these rows on button click
index.xml
<TableView id="table">
<TableViewRow title="Capsicum" onClick="select"></TableViewRow>
<TableViewRow title="Beans" onClick="select"></TableViewRow>
<TableViewRow title="Spinach" onClick="select"></TableViewRow>
<TableViewRow title="Pizza" onClick="select"></TableViewRow>
<TableViewRow title="Burger" onClick="select"></TableViewRow>
</TableView>
<TextField id="textfield" class="insertField" hintText="Add ingredients"></TextField>
<Button id="addButton" title="Add" onClick="addRow" ></Button>
<Button id="removeButton" title="Remove" onClick="removeRow" ></Button>
</Window>
index.js file
function addRow(){
var myTextFieldValue = $.textfield.getValue();
var row=Ti.UI.createTableViewRow({title:myTextFieldValue});
$.table.appendRow(row);
}
function select(e) {
if (e.row.hasCheck) {
e.row.hasCheck = false;
} else {
e.row.hasCheck= true;
}
}

It's pretty simple if you properly follow the Appc Docs:
Answer for Query 1:
There's an event called return for TextField proxies which is called when you press Done button or Return button on iOS or Android. But the title of this Return button can be anything as specified here: Return Key Title
So, you have to make below changes in TextField node to make it work on pressing enter on keyboard, like this:
<TextField id="textfield" returnKeyType="Ti.UI.RETURNKEY_DONE" onReturn="addRow" class="insertField" hintText="Add ingredients" />
Answer for Query 2:
You will have to fetch all rows from table which is a bit lengthy because you cannot directly fetch rows from TableView, rather you will first need to fetch the first section of TableView & then rows from section.
Note: If you do not add any section in TableView, then by default Titanium adds a single section in TableView and add rows in this section. This is why you need to take care of getting the first section first.
Here's the code to delete all checked-rows on Remove button click.
function removeRow() {
// first get all sections of table which will be first section in your case
var sections = $.table.sections;
// perform this check to make your code error free when there are no rows added to tableview.
if (sections.length !== 0) {
var rows = sections[0].rows;
var totalRows = rows.length;
if (totalRows !== 0) {
// now delete all rows which has uncheck=true
for (var i=0; i<totalRows; i++) {
var tempCurrentRow = rows[i];
if (tempCurrentRow.hasCheck) {
$.table.deleteRow(tempCurrentRow);
}
}
}
}
}
Just a minor change in your adding code so that you don't accidentally add empty title rows:
function addRow(){
var myTextFieldValue = $.textfield.value.trim();
if (myTextFieldValue != "") {
$.table.appendRow( Ti.UI.createTableViewRow({title:myTextFieldValue}) );
}
}

Related

Vue binding is sometimes putting "\" \"" around the strings, but v-html makes it always say Object object?

I am trying to make a quiz game and after getting the answers to shuffle around, they sometimes are surrounded by "\" \"" marks. When just displaying the correct answer, it will not have these marks. I have tried using v-html which normally gets rid of this issue, however then all the answers are [Object object]. I have tried trying to retrieve just part of the arrays and objects with [0] [2] and so one but that didn't work either.
Here is my html:
Style binded so that if that button is selected, the background colour of the button will change. Colour is a variable changed when answer is checked -->
<button class="answer" :disabled="answered" #click="checkAnswer(shuffled[0])" :style="selected == shuffled[0].value && {backgroundColor: colour}">{{shuffled[0]}}</button>
<button class="answer" :disabled="answered" #click="checkAnswer(shuffled[1])" :style="selected == shuffled[1].value && {backgroundColor: colour}">{{shuffled[1]}}</button>
<button class="answer" :disabled="answered" #click="checkAnswer(shuffled[2])" :style="selected == shuffled[2].value && {backgroundColor: colour}">{{shuffled[2]}}</button>
<button class="answer" :disabled="answered" #click="checkAnswer(shuffled[3])" :style="selected == shuffled[3].value && {backgroundColor: colour}">{{shuffled[3]}}</button>
<!-- Button running function to end game with parameters of current correctCount and totalQuestions -->
<button type="button" #click="finish(correctCount, totalQuestions)" class="btn button done">Finish</button> <div class="correct"><p v-if="answered"><strong>Correct Answer:</strong> {{correctAnswer}}</p></div><button type="button" :disabled="!nextAvailable" #click="getQ(selectedDifficulty)" #click="shuffle(options)" class="btn button next">Next</button>
<!-- Button which is disabled when a question loads and runs functions to shuffle the answers and get a new question -->
</div>
<h3>Debugging</h3>
<p><strong>Correct Answer: </strong>{{correctAnswer}}</p>
</div>
And here is my relevant JS:
let getQ = async function() {
let response = await fetchURL('https://the-trivia-api.com/api/questions?categories=film_and_tv&limit=1&region=AU'); // api source link
this.correctAnswer = response[0].correctAnswer;
this.incorrectAnswerOne = response[0].incorrectAnswers[0];
this.incorrectAnswerTwo = response[0].incorrectAnswers[1];
this.incorrectAnswerThree = response[0].incorrectAnswers[2];
this.question = response[0].question;
this.shuffle(options); // shuffles the answers in options variable
this.answered = false; // resets the disabled buttons
this.nextAvailable = false; // disables 'next' button
this.totalQuestions++; // increases number of total questions
}
// function to shuffle the answers
let shuffle = function(array) {
this.selected = null; // resets selected variable
let num = array.length, t, raInt;
// while there are remaining elements to shuffle
while (num) {
// choose random
raInt = Math.floor(Math.random() * num--);
// swap with current element
t = array[num]
array[num] = array[raInt];
array[raInt] = t;
}
this.shuffled = array.value; // saves to shuffled variable
}
All variables are declared and returned in the component. What I want is for it to always have every answer shuffled and without these markings.
There are some error messages but I am not quite sure what they mean:
If I haven't provided enough information or code, please feel free to say if you need more.

Ionic4 sliding item problems

I am building a chat messaging app with Ionic4. The expected behavior is to have a sliding item with the picture and name of a friend and upon sliding left, add and remove friend buttons are revealed to add or remove a friend from the chat. Further, if a friend is added to the chat the add button is disabled and the remove button is enabled using the [disabled] tag. Finally if any friends exist that have been added, a button on the bottom appears, to take us to the chat page and initialize a conversation.
The problem is that I initially swipe left on the bottom most friend, and then we see the add and remove buttons with add disabled, So far so good. I then click add and the remove button becomes undisabled, and the add button becomes disabled. I then click remove and it works great!
I then try the same thing on the friend above the bottom and everything fails. The buttons do not enable/disable, but when the remove button still appears disabled.
So far I have tested the custom pipe(s) I created and they have all shown true when a friend appears within chatfriends, so that condition is ok.
//chats.html
<ion-list-header>
Friends
</ion-list-header>
<ion-list>
<ion-item-sliding #slidingitem *ngFor="let key of myfriends" >
<ion-item >
<ion-avatar item-left>
<img src="{{key.photoURL}}">
</ion-avatar>
<h2>{{key.displayName}}</h2>
</ion-item>
<ion-item-options slide="left">
<button ion-button [disabled]="(chatfriends | contains:key)" color="primary" (click)="addfriendtochat(key)" >
Add
</button>
<button ion-button [disabled]="!(chatfriends | contains:key)" color="danger" (click)="removefriendfromchat(key,slidingitem)" >
Remove
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
//**************************
//chats.ts
ionViewWillEnter() {
//chats.ts has to listen to chatconnect node as it does with the nodes below
this.requestservice.getmyrequests();
this.requestservice.getmyfriends();
this.requestservice.getmychatconnects();
//this.contains.transform(localfriends, "Eddie");
// this.myfriends = [];
this.events.subscribe('gotrequests', () => {
this.myrequests = [];
this.myrequests = this.requestservice.userdetails;
});
this.events.subscribe('friends', () => {
this.myfriends = [];
this.myfriends = this.requestservice.myfriends;
});
this.events.subscribe('gotchatconnects', () => {
this.mychatconnects = [];
this.mychatconnects = this.requestservice.chatconnects;
});
}
ionViewDidLeave() {
this.events.unsubscribe('gotrequests');
this.events.unsubscribe('friends');
this.events.unsubscribe('gotchatconnects');
}
//this function adds a friend to the chat and a friend to the chatfriend array, and also sets the flag that determines whether or not to show the start chat button
addfriendtochat(friend){
if(this.myfriends.includes(friend)){
this.chatservice.addfriendtochat(friend);
if(this.chatservice.getfriendcount() > 0){
this.friendsinarray = true;
}else{
this.friendsinarray = false;
}
}
this.chatfriends = this.chatservice.getfriends();
}
//this function removes a friend from the chat and removes a friend from the chatfriend array, and also sets the flag that determines whether or not to show the start chat button
removefriendfromchat(friend, slidingitem: ItemSliding){
if(this.myfriends.includes(friend)){
this.chatservice.removefriendfromchat(friend);
if(this.chatservice.getfriendcount() > 0){
this.friendsinarray = true;
}else{
this.friendsinarray = false;
}
}
this.chatfriends = this.chatservice.getfriends();
slidingitem.close();
}
There is no error message, the one notable thing is that upon the steps above when it fails, the items are not reloaded.
I wasn't able to get the exact sliding action I wanted, but the problem was really in the rendering of buttons. After removing the slider the buttons did not hide and unhide or [enable/disable] the add and remove from chat buttons, exactly as previously described. As I posted before, the page did not seem to re-render after objects were updated in the providers. I believe that the problem was just that, subscribing to events from two providers. I switched the tracking of added friends to my requests.ts. This was for two reasons.
1. I pulled my list of friends, called myfriends, from my firebase rest api using that provider, so my object to track whether one of my friends friends was added to the chat, called friendobjs, were there as well. Further I had to use events from both objects to fill in info on my chats.ts page.
2. Everything else on my page that was subscribing to events from that provider so I didn' think conflicts would arise.
Here is the working code, which hides and unhides the add and remove buttons for each friend displayed.
//chats.ts
ionViewWillEnter() {
//chats.ts has to listen to chatconnect node as it does with the nodes below
this.requestservice.getmyfriends();
this.events.subscribe('friends', () => {
this.myfriends = [];
this.friendobjs = [];
this.myfriends = this.requestservice.myfriends;
this.friendobjs = this.requestservice.friendobjs;
this.singleArray = [];
for (var _i = 0; _i < this.myfriends.length; _i++) {
this.singleArray.push({
myfriends: this.myfriends[_i],
friendobjs: this.friendobjs[_i]
});
}
});
this.events.subscribe('addedfriendtochat', () => {
this.friendobjs = [];
this.friendobjs = this.requestservice.friendobjs;
this.singleArray = [];
for (var _i = 0; _i < this.myfriends.length; _i++) {
this.singleArray.push({
myfriends: this.myfriends[_i],
friendobjs: this.friendobjs[_i]
});
}
});
this.events.subscribe('removedfriendfromchat', () => {
this.friendobjs = [];
this.friendobjs = this.requestservice.friendobjs;
this.singleArray = [];
for (var _i = 0; _i < this.myfriends.length; _i++) {
this.singleArray.push({
myfriends: this.myfriends[_i],
friendobjs: this.friendobjs[_i]
});
}
}
removefriendfromchat(friendobj){
if(!friendobj.canbeadded){
this.requestservice.removefriendfromchat(friendobj);
if(!this.requestservice.canchat){
this.friendsinarray = true;
}else{
this.friendsinarray = false;
}
}
addfriendtochat(friendobj){
if(friendobj.canbeadded){
this.requestservice.addfriendtochat(friendobj);
if(!this.requestservice.canchat){
this.friendsinarray = true;
}else{
this.friendsinarray = false;
}
}
}
//chats.html
<ion-item *ngFor="let single of singleArray" >
<ion-avatar item-left>
<img src="{{single.myfriends.photoURL}}">
</ion-avatar>
<h2>{{single.myfriends.displayName}}</h2>
<h2>{{single.friendobjs.canbeadded}}</h2>
<button ion-button round item-right color="primary" [hidden]="!single.friendobjs.canbeadded" (click)="addfriendtochat(single.friendobjs)" >
<ion-icon name="checkmark"></ion-icon>
Add
</button>
<button ion-button round item-right color="danger" [hidden]="single.friendobjs.canbeadded" (click)="removefriendfromchat(single.friendobjs)" >
<ion-icon name="trash"></ion-icon>
Remove
</button>
</ion-item>

Unable to get id from TableView in Appcelerator

I attached some eventListeners to a number of elements, like this:
$.someButton.addEventListener('click', function(evt) { eventHandling(evt); });
$.someImageView.addEventListener('click', function(evt) { eventHandling(evt); });
$.someWindow.addEventListener('click', function(evt) { eventHandling(evt); });
$.someTableView.addEventListener('click', function(evt) { eventHandling(evt); });
Then, I catch them like this:
function eventHandling(evt) {
switch (evt.source.id) {
case "someButton":
console.log("button clicked");
break;
case "someImageView":
console.log("imageview clicked");
break;
case "someWindow":
console.log("window clicked");
break;
case "someTableView":
console.log("tableview clicked");
break;
};
};
They all log their id's, except the TableView which logs undefined.
The TableView is set like this (minimised):
<Alloy>
<View id="main">
<TableView bottom="100" zIndex="0" id="someTableView" dataCollection="inspection" dataFilter="statusFilter">
...
</TableView>
</View>
</Alloy>
Why isn't this working?
To tell you more clearly on why this error is happening, we need more UI/XML code to know how TableView is linked with other UI elements.
The only guess right now is that any of the TableView Row's child element(s) can also fire that click event. Can you also share the code where you are getting undefined printed on console ?
Apart from this, I observe a less useful code pattern here - Why are you using switch statement just to know the id of clicked element?
I would prefer to use below code instead of above code.
$.someButton.addEventListener('click', buttonClick);
$.someImageView.addEventListener('click', imageViewClick);
$.someWindow.addEventListener('click', windowClick);
$.someTableView.addEventListener('click', tableClick);
function buttonClick(e) {
// .....
}
function windowClick(e) {
// .....
}
function imageViewClick(e) {
// .....
}
function tableClick(e) {
// .....
}
The point here is that running the code by assuming the id of source-view which fired that event is not a good-idea, because your layout might change in future or you can rename the IDs of those elements or could be any other reason.

Titanium alloy: I want to add and delete rows from one window and it will automatically update in second window

i have two xml files..index.xmland win2.xml and both windows have tableview with same content in the rows i mean to say:
index.xml
<Alloy>
<Window backgroundColor="red" >
<ScrollView id="scrollview" >
<TableView id="table" >
<TableViewRow title="Capsicum" onClick="select"></TableViewRow>
<TableViewRow title="Beans" onClick="select" ></TableViewRow>
<TableViewRow title="Spinach" onClick="select"></TableViewRow>
<TableViewRow title="Pizza" onClick="select"></TableViewRow>
<TableViewRow title="Burger" onClick="select"></TableViewRow>
</TableView>
</View>
</ScrollView>
</Window>
index.js
function select(e) {
if (e.row.hasCheck) {
e.row.hasCheck = false;
} else {
e.row.hasCheck= true;
}
}
$.index.open();
win2.xml
<Alloy>
<Window backgroundColor="red" id="win2">
<TableView id="tableView" >
<TableViewRow title="Capsicum" onClick="select"></TableViewRow>
<TableViewRow title="Beans" onClick="select" ></TableViewRow>
<TableViewRow title="Spinach" onClick="select"></TableViewRow>
<TableViewRow title="Pizza" onClick="select"></TableViewRow>
<TableViewRow title="Burger" onClick="select"></TableViewRow>
</TableView>
<TextField id="textfield" returnKeyType="Ti.UI.RETURNKEY_DONE" onReturn="addRow" class="insertField" hintText="Add ingredients"></TextField>
<Button id="removeButton" onClick="removeRow"></Button>
</Window>
</Alloy>
win2.js
function select(e) {
if (e.row.hasCheck) {
e.row.hasCheck = false;
} else {
e.row.hasCheck= true;
}
}
function addRow(){
var myTextFieldValue = $.textfield.value.trim();
if (myTextFieldValue != "") {
$.tableView.appendRow( Ti.UI.createTableViewRow({title:myTextFieldValue}) );
}
}
function removeRow() {
// first get all sections of table which will be first section in your case
var sections = $.tableView.sections;
// perform this check to make your code error free when there are no rows added to tableview.
if (sections.length !== 0) {
var rows = sections[0].rows;
var totalRows = rows.length;
if (totalRows !== 0) {
// now delete all rows which has uncheck=true
for (var i=0; i<totalRows; i++) {
var tempCurrentRow = rows[i];
if (tempCurrentRow.hasCheck) {
$.tableView.deleteRow(tempCurrentRow);
}
}
}
}
}
Now if i add or delete any element from win2.xml or second window it will also delete from or add in index.xml or first window...
Note:
i only want to delete the checked elements i.e hasCheck=true from second window and when i add some new element then i will be able to check them like old elementsLike This
can we create a single tableview and access in both windows?
Like #Rene Pot is saying you could use data binding or if it is too much you could just share the data via a file or property.
You would need to create a JSON object out of your data and the parse this data to create the table. Once you change the data inside the table you apply all the changes to the JSON object. When you open your windows you create the table out of that data.
initial data:
var myData = [{"name":"Item name 1", "checked": true,..}, {"name":"Item name 2", "checked": false,..}, ...];
Ti.App.Properties.setObject("myData", myData);
now in your two other controllers:
var myData = Ti.App.Properties.getObject("myData", []); // this will receiv myData or {}
Now myData has all elements and you would create the table out of this array.
When you change row 0 to be checked you change myData[0].checked=true/false;. At the end you do a Ti.App.Properties.setObject("myData", myData) again to store the data.
If you have much data or want to keep it even if the users clears the app cache/storage you could save myData into a file and read it again instead of getObject.
For the future:
Using data binding/models will help you to get rid of all these manual updates. When you change data all connected elements will update

Titanium scrollable view index

My project is to list products with a views, then when click on a product i have to go to another page and re-list the products with scrollableview and target the clicked view to be displayed first (sorry for bad english):
Let's say this is my list (Grabbed from a collection) :
<Alloy>
<Window id="products" onClose="cleanup" title="All products" class="container" >
<ScrollView dataCollection="list_products" dataTransform="parse_liste" layout='vertical' id="results" class="resultats">
<View product_id="{ID}"
product_photo="{photo}"
product_name="{product_name}" lauyout='horizontal' class='heightAuto' width='Ti.UI.FILL' backgroundColor='#eee' bottom='1'>
<ImageView touchEnabled="false" left='10' image='{photo}' />
<View touchEnabled="false" layout='vertical' height='Ti.UI.SIZE'>
<Label touchEnabled="false" left='100' class='nom' text='{product_name}' />
<Label touchEnabled="false" left='100' class='nom' text='{product_price} €/h' />
</View>
</View>
</ScrollView>
<ActivityIndicator color="white" id="activityIndicator" message="Chargement des candidats ..."/>
</Window>
When click on product (the controller) :
var products = Alloy.Collections.liste_recherche;
$.activityIndicator.show();
products.fetch({
success:function(model,results){
Alloy.Globals.results = results;
$.activityIndicator.hide();
}
});
//// CLICK ON A PRODUCT
$.list_products.addEventListener('click', function(e){
if( Alloy.Globals.results ){
///////// GO TO PRODUCT PAGE AND PASS ARGS
var xpng = require('xpng');
xpng.openWin(Alloy.CFG.nav, 'product', Alloy.Globals.products);
}
});
The product page :
var args = $.args;
function getProfil(){
var products_array = [];
var c = ['gray','green','blue','red'];
_.each(args, function(item, key, value){
/* Créer la vue */
var product_container = Ti.UI.createView({
id:item.ID,
layout:'vertical',
backgroundColor:c[Math.floor(Math.random() * c.length)]
});
var product_image = Ti.UI.createImageView({ image : item.photo});
var product_name = Ti.UI.createLabel({
text: item.champs_candidat.nom
});
product_container.add(product_image);
product_container.add(product_name);
products_array.push(product_container);
});
var style = $.createStyle({
classes: ['listeProfiles'],
apiName: 'ScrollableView'
});
var product_scroller = Ti.UI.createScrollableView({
className: 'listeProfiles',
views : products_array,
showPagingControl:true
});
product_scroller.applyProperties(style);
$.product_win.add(product_scroller);
}
Those codes works fine, just i want to display clicked view (from page A) first.
Thank you for help.
I think you need to capture which view was clicked on ScrollView click event:
$.list_products.addEventListener('click', function(e){
// e.source will give you the clicked view and will behave like $.VIEW_ID
// so you can access any property or method of the clicked view using e.source here
Ti.API.info("Clicked View ID = " + e.source.product_id);
if( Alloy.Globals.results ){
///////// GO TO PRODUCT PAGE AND PASS ARGS
var xpng = require('xpng');
xpng.openWin(Alloy.CFG.nav, 'product', Alloy.Globals.products);
}
});
As per your code, you have already disabled the touches of child-views of the view with this property product_id, so the above modified click event code will be able to provide you the correct product ID of the clicked View inside the ScrollView.
Now:
You have the product_id of the view clicked.
Write code on your collection data to know the index of that
product_id.
Use the index from step 2 to set this property currentPage of ScrollableView
Here is the summary of overall process:
Get the product_id of the clicked view using e.source
Use this product_id to know its index in data collection.
Finally, index from step 2 will be the value of currentPage property of ScrollableView. (it's your preference how you pass this index to product page.)