Building a Reusable Login Form with Appcelerator Titanium Alloy MVC
Learn how to create a reusable login form in Appcelerator Titanium by separating view, style and controller with Alloy MVC, then verify the implementation with a simple build and test cycle.
05 Jul 2025, 06:00 UTC

Useful answer
Appcelerator Titanium Alloy’s MVC pattern lets you create a login form that is easy to reuse, test, and maintain by separating the user‑interface markup (XML), styling (TSS) and behavior (JavaScript). Once the three files are in place you can instantiate the form anywhere in your app with a single call to Alloy.createController('login').getView().open().
Mechanism – worked configuration
The example below shows a minimal but complete login screen. All file paths are relative to the project root of a Titanium Alloy application.
View – app/views/login.xml
<Alloy>
<TextField id="username" hintText="Username" />
<TextField id="password" hintText="Password" passwordMask="true" />
<Button id="login" title="Log In" />
</Alloy>
Style – app/styles/login.tss
"#username, #password" {
height: 40;
width: Ti.UI.FILL;
left: 20;
right: 20;
borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED;
color: #000;
backgroundColor: #fff;
}
"#login" {
height: 44;
width: 200;
top: 20;
backgroundColor: #0066cc;
color: #fff;
font: {fontSize:16, fontWeight:'bold'}
}
Controller – app/controllers/login.js
function doLogin(e) {
// Retrieve values from the view via the generated $ object
var user = $.username.value;
var pass = $.password.value;
// In a real app you would validate and call a service here
Ti.API.info('Login attempt – username: %s', user);
// Example: clear fields after attempt
$.username.value = '';
$.password.value = '';
}
// Attach the click handler
$.login.addEventListener('click', doLogin);
// Export a cleanup function to be called when the window closes
exports.destroy = function() {
$.login.removeEventListener('click', doLogin);
};
Instantiating the form
From any other controller (e.g., app/controllers/index.js) you can open the login window:
var loginWin = Alloy.createController('login').getView();
loginWin.open();
Verification steps
- Copy the three files above into a fresh Titanium Alloy project (ensure you have the Titanium SDK and CLI installed).
- Open a terminal in the project root and run the build command for your target platform. For iOS simulator:
For Android emulator or device:ti build -p ios -s simulator
Required permission: you must have execute rights on theti build -p android -s emulatortiCLI and the SDK must be on your PATH. - When the app launches, the login window should appear with the styled fields and button as defined in the TSS.
- Enter text in the username and password fields, tap Log In, and check the Studio console (or
ti info -p ioslog) for a line similar to:
This confirms that the controller correctly read the view values via the[INFO] Login attempt – username: testuser$object. - Close the window and reopen it several times. Use Xcode Instruments (iOS) or Android Profiler to watch for memory growth; there should be no increase attributable to the login form if the
destroyfunction is called automatically when the window closes (Alloy does this for top‑level windows).
Limits and trade‑offs
- Build step overhead: Alloy introduces an XML‑to‑JavaScript compilation phase, which adds a few seconds to each build compared with a pure classic Titanium project.
- UI declaration constraint: Within a single Alloy view you must declare all UI components in the XML; you cannot mix programmatic creation in the same file without breaking the automatic
$binding. - Platform‑specific quirks: Certain behaviors, such as Android soft‑keyboard adjustments or iOS status‑bar handling, may require extra code in the controller or a native module because the TSS does not expose every platform property.
Common mistakes and how to avoid them
| Mistake | Why it happens | How to prevent / fix |
|---|---|---|
Failing to export the controller function or call .getView().open() | The controller is instantiated but the view is never shown. | Always keep the pattern Alloy.createController('name').getView().open() and verify that the window appears. |
| Mismatched IDs between XML, TSS and controller | Typing errors lead to undefined variables (e.g., $.username is null). | Use a consistent naming convention and run a quick lint check; the Alloy compiler will warn about unused IDs. |
| Leaving event listeners attached after the window closes | Listeners keep a reference to the controller, preventing garbage collection and causing memory leaks. | Implement a destroy function (as shown) and remove all custom listeners there; Alloy calls it automatically for top‑level windows. |
| Hard‑coding credentials or tokens in the view/controller | Security risk if the app is reverse‑engineered. | Store secrets in Ti.App.Properties with encryption or use a dedicated keychain module; never place them in XML/TSS. |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.