I'm building a simple app with appcelerator.
So I have the index view that when the application is started, I want that this index view open Login view.
So I have this:
login.xml
<Alloy>
<View class="container">
<View class="images"></View>
<Label id="loginLable"
class="loginLable">Accedi</Label>
<TextField class="textLogin"></TextField>
</View>
</Alloy>
login.js
// Arguments passed into this controller can be accessed via the `$.args` object directly or:
var args = $.args;
function loginEventListener(e){
Ti.API.info("You clicked the button");
};
index.js
var login = Alloy.createController("login",args).getView();
login.open();
If I try to start this application, I have this error:
[ERROR] : TiExceptionHandler: (main) [19654,21610] ----- Titanium Javascript Runtime Error -----
[ERROR] : TiExceptionHandler: (main) [0,21610] - In alloy/controllers/index.js:35,11
[ERROR] : TiExceptionHandler: (main) [2,21612] - Message: Uncaught TypeError: Object #<View> has no method 'open'
[ERROR] : TiExceptionHandler: (main) [1,21613] - Source: login.open();
[ERROR] : V8Exception: Exception occurred at alloy/controllers/index.js:35: Uncaught TypeError: Object #<View> has no method 'open'
You cannot "open" a view. It needs a container. You can either add it to an already opened window, or you need to make the controller a window. open() doesn't exist for the view.
<Alloy>
<Window>
<View class="container">
<View class="images"></View>
<Label id="loginLable"
class="loginLable">Accedi</Label>
<TextField class="textLogin"></TextField>
</View>
</Window>
</Alloy>
Then, to open it you can do the same as you did already
Related
I am using 'react-native-material-textfield' and it working well, but I need to show error for empty field when clicking on submit button. I have searched lot-of but didn't find any solution.
Put the error message in your state and fill it with a message after clicking on the submit button, if your validation process fails.
render(){
return (
<View>
<TextField
{...props}
error={this.state.error}
errorColor={'red'}
onFocus={() => this.setState({error: ''})}
/>
<Button {...props} />
</View>)}
Check the example on the developers github repository.
According to the module documentation and examples, whenever your this.state.errors for each field is not empty, its error is shown. So your form should look like this:
class Form extends Component {
// ... Some required methods
onSubmit() {
let errors = {};
['firstname'] // This array should be filled with your fields names.
.forEach((name) => {
let value = this[name].value();
if (!value) {
errors[name] = 'Should not be empty'; // The error message when field is empty
}
});
this.setState({ errors });
}
render() {
let { errors = {}, data } = this.state;
return (
<View>
<TextField
value={data.firstname}
onChangeText={this.onChangeText}
error={errors.firstname}
/>
<Text onPress={this.onSubmit}>Submit</Text>
</View>
);
}
}
CUSTOM COMPONENT
// ...
#Output() submit: EventEmitter < any > = new EventEmitter();
// ...
onFilterSubmit($event): void {
this.submit.emit($event);
this.formData = {
minDate: new Date().toISOString(),
maxDate: new Date().toISOString()
};
}
<form (ngSubmit)="onFilterSubmit(formData)">
<!-- -- -->
<button mat-button
mat-raised-button
[disabled]="reqsForm.form.invalid"
type="submit"
color="primary">
{{labels.submit}}
</button>
</form>
OUTER COMPONENT
// ...
onFilterSubmit($event): void {
console.info("FORM SUBMIT", $event);
}
<custom-component (submit)="onFilterSubmit($event)">
<!-- -- -->
</custom-component>
OUTPUT
FORM SUBMIT > Object
FORM SUBMIT > Object
The reason why this was happening is that an event called "submit"
is catchable already from outside the custom component.
I solved by changing the custom event name to filterSubmit
Note also that the type submit on the button - in this use case - is virtually useless, since as default one button in a form will be of type submit.
Is there anyway to do a if condition in the XML file of nativescript? (without angular)
if
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Slider value="18" minValue="5" maxValue="30" />
</Card:CardView>
else
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Label text="Example" class="h3" margin="10" />
</Card:CardView>
What you could do is use a boolean property (which in its get function has the condition you want) and bind it to the visibility of the CardView.
Looks like you can show/hide from the template file using visibility:
In XML file: ie. 'sample-page.xml'
<Button text="{{ isShowing ? 'Hide Me' : 'Show Me' }}" tap="toggleShowing"/> <!-- Notice missing interpolation in Button tag -->
<Label text="Showing Hidden Element" visibility="{{ isShowing ? 'visible' : 'collapsed' }}"/>
In Page file: ie. 'sample-page.ts'
let model = new ViewModel();
// Event handler for Page 'loaded' event attached in main-page.xml
export function pageLoaded(args: observable.EventData) {
const page = <Page>args.object;
page.bindingContext = model;
}
export function toggleShowing() {
model.set('isShowing', !model.get('isShowing'));
}
In View Model: ie. 'sample-view-model.ts'
isShowing:boolean = false;
The only way I found to do to do this was to use the navigatingTo event of the page:
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
var myLayout = <StackLayout>page.getViewById("myLayout");
if (condition) {
let segmentedBar = new SegmentedBar;
...
myLayout.addChild(segmentedBar);
}
else {
let button: Button = new Button;
...
myLayout.addChild(button);
}
No possibility in the template file :-/
It can be done using ng-container.For example
<ng-container *ngIf="true">Your logic here....</ng-container>
I was hoping one of you could help me figure out why my row data keeps writing to the same row and overwriting the previously written data instead of starting on a new row after every iteration. Here's what I have so far:
This alloy.js file below gets the information I need for the app from the server and passes it to the formateData() function in the index.js file.
alloy.js
GetDataRequest("http:somejsondata/data.json");
function GetDataRequest(url)
{
var xhr = Ti.Network.createHTTPClient({
onload: function(e)
{
Alloy.Globals.formateAnimalData(JSON.parse(xhr.responseText));
},
onerror: function(e) {
Ti.API.debug("STATUS: " + this.status);
Ti.API.debug("TEXT: " + this.responseText);
Ti.API.debug("ERROR: " + e.error);
alert('There was an error retrieving the remote data. Try again.');
},
timeout:5000
});
xhr.open("GET", url);
xhr.send();
}
index.js
var appData = [];
Alloy.Globals.formateAnimalData = function(data)
{
console.log("I was also called");
for (var i = 1; i <=8; i++)
{
var row = {
name: data.animals[i].name,
animal: data.animals[i].animal,
food: data.animals[i].food
};
appData.push(Alloy.createController('index', row).getView());
//console.log(appData);
}
};
var args = arguments[0] || {};
$.name.text = args.name || '';
$.animal.text = args.animal || '';
$.food.text = args.food || '';
$.table.setData(appData);
$.index.open();
index.xml
<Alloy>
<TabGroup id='index'>
<Tab title="Tab 1" icon="KS_nav_ui.png">
<Window id='main' class='' title="Zoo">
<TableView>
<TableViewRow id='table'>
<Label id ='name' class ='label'></Label>
<Label id ='animal' class ='label'></Label>
<Label id ='food' class ='label'></Label>
</TableViewRow>
</TableView>
</Window>
</Tab>
</TabGroup>
</Alloy>
I should note that the:
$.table.setData(appData);
line in the index.js file doesn't work. The data still prints to the screen even if this line is not in there. The reason for this I think is because the following line:
appData.push(Alloy.createController('index', row).getView());
is not pushing a row of objects into the appData array and infact when I log the contents of the appData array to the screen I get:
(
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]",
[INFO] : "[object index]"
[INFO] :
)
I am just trying to get it so that it prints the animals: Name, Type and Food Preference to row 1 and the next animals Name, Type and Food Preference to row 2 and so on.
It's an ugly way to do. In this case, your main problem is this line :
appData.push(Alloy.createController('index', row).getView());
using getView() on your index controller will give you the top-level view, in this case the TabGroup of id index.
If you really want to keep it that way, you have to supply 'table' as arguments to ask for the TableViewRow
appData.push(Alloy.createController('index', row).getView('table'));
However, you are assigning to the newly created index controller and not to the original one instantiated by Alloy at starting....
You can try to create another controller named for instance row, and create an associated xml view this way :
row.xml
<Alloy>
<TableViewRow id="row">
<Label id="name" class="label"></Label>
<Label id="animal" class="label"></Label>
<Label id="food" class="label"></Label>
</TableViewRow>
</Alloy>
row.js
var args = arguments[0] || {};
$.row.text = args.name || '';
$.row.text = args.animal || '';
$.row.text = args.food || '';
And, refactor the rest the following way. I'm using events to communicate easily with the original instantiated index controller.
index.xml
<Alloy>
<TabGroup id='index'>
<Tab title="Tab 1" icon="KS_nav_ui.png">
<Window id='main' class='' title="Zoo">
<TableView id="table"/>
</Window>
</Tab>
</TabGroup>
</Alloy>
index.js
/* Store rows somewhere */
$._rows = [];
/* Append row each time it's needed */
$.addEventListener('newrow', function (newrowEvent) {
$._rows.push(newrowEvent.row);
});
/* Refresh only on demand */
$.addEventListener('refreshrows', function () {
$.table.setData($._rows);
});
$.index.open();
alloy.js
/* ...
Your previous function with the call to formateAnimal updated
*/
function formateAnimalData(data) {
for (var i = 1; i <=8; i++) {
var rowContent = {
name: data.animals[i].name,
animal: data.animals[i].animal,
food: data.animals[i].food
};
Ti.app.fireEvent('newrow', {
row: Alloy.createController('row', rowContent).getView()
});
}
Ti.app.fireEvent('refreshrows');
};
This should be a workaround a bit nicer. Keep in mind that, when you create a controller, the code that you put in the file's scope is executed only once; Also, each time you're calling createController, you'll get a new instance of that kind of controller; Do not try to modify other instances from the new instance.. This is weird and inefficient.
Finally, avoid putting bloc scopes above functions or control structures.
for ( ...; ...; ...)
{
}
or
function ()
{
}
are bad ideas. You're in JavaScript and semicolons aren't necessary for each statement or at least, the compiler will not complain and you're likely to obtain strange silent errors ...
In Alloy framework, I want to dynamically add a view defined in an xml file but not bound to any other view to another.
Let's take the following example:
Container I want to fill in index.xml:
<ScrollableView id="scrollableBilan" showPagingControl="true">
</ScrollableView>
The view template question.xml I want to instanciate for each view going into the ScrollableView:
<Alloy>
<Collection src="ReponsePossible">
<View id="questionContainer" class="container">
<Label id="questionText" />
<Button id="buttonNextQuestion">Question suivante</Button>
</View>
</Alloy>
Finally the controller index.js, question being a Collection instance:
for(var i=0; i<questions.length; i++){
$.scrollableBilan.add(Alloy.createController('question', questions.at(i)));
}
This makes my application crash whith following message: 'Unfortunately, your application has stopped'. I already got this error, always when trying to add a view dynamically create using Alloy.createController.
The behavior is ok when creating a view with Ti.UI.createView but I want to use the MVC...
Any help is welcomed!
You are passing it a model object, instead try passing it a JSON object like this:
for(var i=0; i<questions.length; i++){
$.scrollableBilan.add(Alloy.createController('question', questions.at(i).toJSON()));
}
Alternatively you can just do this all inside the xml file by using Data Binding and the dataCollection attribute, put something like this in your index.xml:
<Alloy>
<Collection src="questions">
<ScrollableView id="scrollableBilan" showPagingControl="true" dataCollection="ReponsePossible">
<View id="questionContainer" class="container">
<Label id="questionText" text="{questionText}"/>
<Button id="buttonNextQuestion">Question suivante</Button>
</View>
</ScrollableView>
</Alloy>
The questionText field needs to be an attributes in your questions model.