Zum Inhalt

NativeScript Cheatsheet

NativeScript - Native Mobile Apps mit JavaScript

NativeScript ist ein Open-Source-Framework für den Aufbau von wirklich nativen mobilen Anwendungen mit JavaScript, TypeScript, Angular, Vue.js oder React. Es bietet direkten Zugriff auf native APIs und UI Komponenten ohne WebViews. < p>

generieren

Inhaltsverzeichnis

  • [Installation](#installation
  • (#getting-started)
  • [Projektstruktur](#project-structure_
  • CLI-Befehle
  • Core Concepts
  • UI Komponenten
  • (#layouts_)
  • [Navigation](LINK_7__
  • [Datenbindung](LINK_8__
  • (Styling)(LINK_9_)
  • Platform APIs
  • Plugins
  • [Angular Integration](#angular-integration_
  • [Vue.js Integration](#vuejs-integration
  • (#react-integration_)
  • Test
  • [Performance](#performance_
  • [Bestellung](LINK_17__
  • Beste Praktiken

Installation

Voraussetzungen

# Install Node.js (version 14 or later)
# Download from nodejs.org

# Verify Node.js installation
node --version
npm --version

# Install Android Studio (for Android development)
# Download from developer.android.com

# Install Xcode (for iOS development, macOS only)
# Download from Mac App Store

# Set environment variables
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/platform-tools
```_

### NativeScript CLI Installation
```bash
# Install NativeScript CLI globally
npm install -g @nativescript/cli

# Verify installation
ns --version
tns --version  # Alternative command

# Check environment setup
ns doctor
ns doctor android
ns doctor ios

# Install platform tools
ns install
```_

### Entwicklung Umwelt Setup
```bash
# For Android development
# 1. Install Android Studio
# 2. Install Android SDK
# 3. Set ANDROID_HOME environment variable
# 4. Add platform-tools to PATH

# For iOS development (macOS only)
# 1. Install Xcode
# 2. Install Xcode Command Line Tools
xcode-select --install

# 3. Install CocoaPods
sudo gem install cocoapods

# Verify setup
ns doctor
```_

## Erste Schritte

### Neues Projekt erstellen
```bash
# Create new NativeScript project
ns create MyApp

# Create with specific template
ns create MyApp --template @nativescript/template-hello-world
ns create MyApp --template @nativescript/template-tab-navigation
ns create MyApp --template @nativescript/template-drawer-navigation

# Create with framework
ns create MyApp --ng  # Angular
ns create MyApp --vue # Vue.js
ns create MyApp --react # React

# Create with TypeScript
ns create MyApp --ts

# Navigate to project directory
cd MyApp

# Run the app
ns run android
ns run ios
```_

### Projektvorlagen
```bash
# JavaScript templates
ns create MyApp --template @nativescript/template-hello-world
ns create MyApp --template @nativescript/template-tab-navigation
ns create MyApp --template @nativescript/template-drawer-navigation
ns create MyApp --template @nativescript/template-master-detail

# TypeScript templates
ns create MyApp --template @nativescript/template-hello-world-ts
ns create MyApp --template @nativescript/template-tab-navigation-ts

# Angular templates
ns create MyApp --template @nativescript/template-hello-world-ng
ns create MyApp --template @nativescript/template-tab-navigation-ng
ns create MyApp --template @nativescript/template-drawer-navigation-ng

# Vue.js templates
ns create MyApp --template @nativescript/template-hello-world-vue
ns create MyApp --template @nativescript/template-tab-navigation-vue

# React templates
ns create MyApp --template @nativescript/template-hello-world-react
```_

## Projektstruktur

### Grundstruktur
MyApp/ ├── app/ # Application source code │ ├── app.js # Application entry point │ ├── app.css # Global styles │ ├── main-page.js # Main page logic │ ├── main-page.xml # Main page markup │ └── main-view-model.js # Main page view model ├── platforms/ # Platform-specific code (generated) │ ├── android/ │ └── ios/ ├── node_modules/ # Dependencies ├── package.json # Project configuration ├── nsconfig.json # NativeScript configuration ├── webpack.config.js # Webpack configuration └── App_Resources/ # Platform resources ├── Android/ │ ├── src/main/res/ │ └── app.gradle └── iOS/ ├── Info.plist └── build.xcconfig
### App.js (Eingabepunkt)
```javascript
import { Application } from '@nativescript/core';

// Start the application
Application.run({ moduleName: 'app-root' });

// Alternative with navigation
Application.run({ moduleName: 'main-page' });
```_

### Hauptseite.xml (UI Markup)
```xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page">
    <Page.actionBar>
        <ActionBar title="My App" icon="" class="action-bar">
        </ActionBar>
    </Page.actionBar>

    <GridLayout class="page">
        <Label text="Hello, NativeScript!" class="h1 text-center" />
        <Button text="Tap me!" tap="{{ onTap }}" class="btn btn-primary" />
    </GridLayout>
</Page>
```_

### Hauptseite.js (Seitenlogik)
```javascript
import { fromObject } from '@nativescript/core';

export function onNavigatingTo(args) {
    const page = args.object;
    const viewModel = fromObject({
        message: 'Hello, NativeScript!',
        onTap: function() {
            this.set('message', 'Button tapped!');
        }
    });

    page.bindingContext = viewModel;
}
```_

## CLI Befehle

### Projektleitung
```bash
# Create new project
ns create <app-name> [options]

# Add platform
ns platform add android
ns platform add ios

# Remove platform
ns platform remove android
ns platform remove ios

# List platforms
ns platform list

# Clean project
ns platform clean android
ns platform clean ios

# Update platform
ns platform update android
ns platform update ios
```_

### Entwicklungskommandos
```bash
# Run on device/emulator
ns run android
ns run ios

# Run with specific options
ns run android --device
ns run ios --device

# Run on specific device
ns run android --device <device-id>
ns run ios --device <device-id>

# Debug mode
ns debug android
ns debug ios

# Preview in browser (limited functionality)
ns preview

# Serve for hot reload
ns run android --hmr
ns run ios --hmr
```_

### Befehle erstellen
```bash
# Build for platform
ns build android
ns build ios

# Build for release
ns build android --release
ns build ios --release --for-device

# Build with specific configuration
ns build android --env.production
ns build ios --env.production

# Clean build
ns clean

# Prepare platform
ns prepare android
ns prepare ios
```_

### Plugin Management
```bash
# Install plugin
ns plugin add <plugin-name>

# Remove plugin
ns plugin remove <plugin-name>

# List installed plugins
ns plugin list

# Update plugin
ns plugin update <plugin-name>

# Search for plugins
npm search nativescript-plugin
```_

## Kernkonzepte

### Beobachtbar und Datenbindung
```javascript
import { Observable, fromObject } from '@nativescript/core';

// Create observable object
const viewModel = new Observable();
viewModel.set('name', 'John Doe');
viewModel.set('age', 30);

// Listen to property changes
viewModel.on(Observable.propertyChangeEvent, (args) => {
    console.log(`Property ${args.propertyName} changed to ${args.value}`);
});

// Create from object
const user = fromObject({
    firstName: 'John',
    lastName: 'Doe',
    fullName: function() {
        return `${this.firstName} ${this.lastName}`;
    },
    updateName: function(first, last) {
        this.set('firstName', first);
        this.set('lastName', last);
    }
});

// Observable Array
import { ObservableArray } from '@nativescript/core';

const items = new ObservableArray([
    { name: 'Item 1', value: 1 },
    { name: 'Item 2', value: 2 },
    { name: 'Item 3', value: 3 }
]);

// Listen to array changes
items.on(ObservableArray.changeEvent, (args) => {
    console.log('Array changed:', args);
});

// Add items
items.push({ name: 'Item 4', value: 4 });

// Remove items
items.splice(0, 1);
```_

### Seite Navigation
```javascript
import { Frame, topmost } from '@nativescript/core';

// Navigate to page
export function navigateToDetails() {
    const frame = Frame.topmost();
    frame.navigate({
        moduleName: 'details-page',
        context: { id: 123, name: 'Sample Item' }
    });
}

// Navigate with animation
export function navigateWithAnimation() {
    topmost().navigate({
        moduleName: 'details-page',
        animated: true,
        transition: {
            name: 'slide',
            duration: 300,
            curve: 'ease'
        }
    });
}

// Navigate back
export function goBack() {
    topmost().goBack();
}

// Clear history and navigate
export function navigateAndClearHistory() {
    topmost().navigate({
        moduleName: 'home-page',
        clearHistory: true
    });
}

// Modal navigation
export function showModal() {
    const frame = topmost();
    frame.showModal('modal-page', {
        context: { data: 'modal data' },
        fullscreen: false,
        animated: true
    });
}

// Close modal
export function closeModal() {
    const frame = topmost();
    frame.closeModal();
}
```_

### Anwendung Lebenszyklus
```javascript
import { Application, on as applicationOn, launchEvent, suspendEvent, resumeEvent, exitEvent } from '@nativescript/core';

// Application launch
applicationOn(launchEvent, (args) => {
    console.log('Application launched');
    if (args.android) {
        // Android-specific code
        console.log('Launched Android application with the following intent: ' + args.android.getIntent());
    } else if (args.ios) {
        // iOS-specific code
        console.log('Launched iOS application with options: ' + args.ios);
    }
});

// Application suspend
applicationOn(suspendEvent, (args) => {
    console.log('Application suspended');
    // Save application state
});

// Application resume
applicationOn(resumeEvent, (args) => {
    console.log('Application resumed');
    // Restore application state
});

// Application exit
applicationOn(exitEvent, (args) => {
    console.log('Application exit');
    // Cleanup resources
});

// Low memory warning
applicationOn('lowMemory', (args) => {
    console.log('Low memory warning');
    // Free up memory
});

// Orientation change
applicationOn('orientationChanged', (args) => {
    console.log('Orientation changed to:', args.newValue);
});
```_

## UI Komponenten

### Grundkomponenten
```xml
<!-- Label -->
<Label text="Hello World" class="label" />
<Label text="{{ message }}" textWrap="true" />

<!-- Button -->
<Button text="Click Me" tap="{{ onTap }}" class="btn btn-primary" />
<Button text="Disabled" isEnabled="false" />

<!-- TextField -->
<TextField text="{{ username }}" hint="Enter username" />
<TextField secure="true" hint="Password" />

<!-- TextView -->
<TextView text="{{ description }}" editable="false" />
<TextView hint="Enter description" />

<!-- Image -->
<Image src="~/images/logo.png" stretch="aspectFit" />
<Image src="{{ imageUrl }}" loadMode="async" />

<!-- ActivityIndicator -->
<ActivityIndicator busy="{{ isLoading }}" />

<!-- Progress -->
<Progress value="{{ progressValue }}" maxValue="100" />

<!-- Slider -->
<Slider value="{{ sliderValue }}" minValue="0" maxValue="100" />

<!-- Switch -->
<Switch checked="{{ isEnabled }}" />

<!-- DatePicker -->
<DatePicker date="{{ selectedDate }}" />

<!-- TimePicker -->
<TimePicker time="{{ selectedTime }}" />

<!-- SearchBar -->
<SearchBar text="{{ searchQuery }}" hint="Search..." submit="{{ onSearch }}" />
```_

### Liste Komponenten
```xml
<!-- ListView -->
<ListView items="{{ items }}" itemTap="{{ onItemTap }}">
    <ListView.itemTemplate>
        <Label text="{{ name }}" class="list-item" />
    </ListView.itemTemplate>
</ListView>

<!-- ListView with complex template -->
<ListView items="{{ users }}" separatorColor="transparent">
    <ListView.itemTemplate>
        <GridLayout columns="auto, *" rows="auto, auto" class="list-item">
            <Image src="{{ avatar }}" row="0" col="0" rowSpan="2" class="avatar" />
            <Label text="{{ name }}" row="0" col="1" class="name" />
            <Label text="{{ email }}" row="1" col="1" class="email" />
        </GridLayout>
    </ListView.itemTemplate>
</ListView>

<!-- Repeater -->
<Repeater items="{{ items }}">
    <Repeater.itemTemplate>
        <StackLayout class="item">
            <Label text="{{ title }}" class="title" />
            <Label text="{{ description }}" class="description" />
        </StackLayout>
    </Repeater.itemTemplate>
</Repeater>
```_

### Erweiterte Komponenten
```xml
<!-- WebView -->
<WebView src="{{ url }}" />
<WebView src="~/html/index.html" />

<!-- HtmlView -->
<HtmlView html="{{ htmlContent }}" />

<!-- Placeholder -->
<Placeholder creatingView="{{ onCreatingView }}" />

<!-- ContentView -->
<ContentView content="{{ dynamicContent }}" />

<!-- Frame -->
<Frame defaultPage="main-page" />

<!-- TabView -->
<TabView selectedIndex="{{ selectedTab }}">
    <TabViewItem title="Tab 1">
        <Label text="Content of Tab 1" />
    </TabViewItem>
    <TabViewItem title="Tab 2">
        <Label text="Content of Tab 2" />
    </TabViewItem>
</TabView>

<!-- SegmentedBar -->
<SegmentedBar items="{{ segments }}" selectedIndex="{{ selectedSegment }}" />

<!-- ActionBar -->
<ActionBar title="My App" class="action-bar">
    <ActionItem text="Settings" tap="{{ openSettings }}" ios.position="right" android.position="actionBar" />
    <ActionItem icon="res://ic_menu" tap="{{ openMenu }}" ios.position="left" android.position="actionBar" />
</ActionBar>
```_

## Layouts

### Auf der Karte anzeigen
```xml
<!-- Vertical stack (default) -->
<StackLayout>
    <Label text="First" />
    <Label text="Second" />
    <Label text="Third" />
</StackLayout>

<!-- Horizontal stack -->
<StackLayout orientation="horizontal">
    <Button text="Button 1" />
    <Button text="Button 2" />
    <Button text="Button 3" />
</StackLayout>

<!-- With spacing and padding -->
<StackLayout spacing="10" padding="20">
    <Label text="Item 1" />
    <Label text="Item 2" />
</StackLayout>
```_

### GridLayout
```xml
<!-- Basic grid -->
<GridLayout columns="*, *, *" rows="auto, auto">
    <Label text="(0,0)" row="0" col="0" />
    <Label text="(0,1)" row="0" col="1" />
    <Label text="(0,2)" row="0" col="2" />
    <Label text="(1,0)" row="1" col="0" />
    <Label text="(1,1)" row="1" col="1" />
    <Label text="(1,2)" row="1" col="2" />
</GridLayout>

<!-- Spanning cells -->
<GridLayout columns="*, *" rows="auto, auto, auto">
    <Label text="Header" row="0" col="0" colSpan="2" class="header" />
    <Label text="Left" row="1" col="0" />
    <Label text="Right" row="1" col="1" />
    <Label text="Footer" row="2" col="0" colSpan="2" class="footer" />
</GridLayout>

<!-- Complex layout -->
<GridLayout columns="100, *, 50" rows="auto, *, auto">
    <Label text="Fixed 100" row="0" col="0" />
    <Label text="Flexible" row="0" col="1" />
    <Label text="Fixed 50" row="0" col="2" />
    <ScrollView row="1" col="0" colSpan="3">
        <StackLayout>
            <!-- Content -->
        </StackLayout>
    </ScrollView>
    <Button text="Footer" row="2" col="0" colSpan="3" />
</GridLayout>
```_

### FlexboxLayout
```xml
<!-- Basic flexbox -->
<FlexboxLayout flexDirection="row" justifyContent="space-between">
    <Label text="Start" />
    <Label text="Center" />
    <Label text="End" />
</FlexboxLayout>

<!-- Vertical flexbox -->
<FlexboxLayout flexDirection="column" alignItems="center">
    <Label text="Centered" />
    <Button text="Button" />
</FlexboxLayout>

<!-- Flex wrap -->
<FlexboxLayout flexDirection="row" flexWrap="wrap">
    <Label text="Item 1" flexGrow="1" />
    <Label text="Item 2" flexGrow="1" />
    <Label text="Item 3" flexGrow="1" />
    <Label text="Item 4" flexGrow="1" />
</FlexboxLayout>

<!-- Complex flexbox -->
<FlexboxLayout flexDirection="column" height="100%">
    <Label text="Header" class="header" />
    <FlexboxLayout flexDirection="row" flexGrow="1">
        <Label text="Sidebar" class="sidebar" />
        <ScrollView flexGrow="1">
            <StackLayout>
                <!-- Main content -->
            </StackLayout>
        </ScrollView>
    </FlexboxLayout>
    <Label text="Footer" class="footer" />
</FlexboxLayout>
```_

### Absolutes Layout
```xml
<!-- Absolute positioning -->
<AbsoluteLayout>
    <Label text="Top Left" left="0" top="0" />
    <Label text="Top Right" right="0" top="0" />
    <Label text="Bottom Left" left="0" bottom="0" />
    <Label text="Bottom Right" right="0" bottom="0" />
    <Label text="Center" left="50%" top="50%" />
</AbsoluteLayout>

<!-- Overlapping elements -->
<AbsoluteLayout>
    <Image src="~/images/background.jpg" width="100%" height="100%" />
    <Label text="Overlay Text" left="20" top="50" class="overlay-text" />
    <Button text="Action" right="20" bottom="50" />
</AbsoluteLayout>
```_

### DockLayout
```xml
<!-- Basic dock layout -->
<DockLayout>
    <Label text="Top" dock="top" class="dock-top" />
    <Label text="Bottom" dock="bottom" class="dock-bottom" />
    <Label text="Left" dock="left" class="dock-left" />
    <Label text="Right" dock="right" class="dock-right" />
    <Label text="Fill" class="dock-fill" />
</DockLayout>

<!-- App layout with dock -->
<DockLayout>
    <ActionBar dock="top" title="My App" />
    <GridLayout dock="bottom" columns="*, *, *" class="tab-bar">
        <Button text="Home" col="0" />
        <Button text="Search" col="1" />
        <Button text="Profile" col="2" />
    </GridLayout>
    <ScrollView>
        <StackLayout>
            <!-- Main content -->
        </StackLayout>
    </ScrollView>
</DockLayout>
```_

### WrapLayout
```xml
<!-- Horizontal wrap -->
<WrapLayout orientation="horizontal" itemWidth="100" itemHeight="100">
    <Label text="1" class="item" />
    <Label text="2" class="item" />
    <Label text="3" class="item" />
    <Label text="4" class="item" />
    <Label text="5" class="item" />
</WrapLayout>

<!-- Vertical wrap -->
<WrapLayout orientation="vertical" itemWidth="150" itemHeight="50">
    <Button text="Button 1" />
    <Button text="Button 2" />
    <Button text="Button 3" />
    <Button text="Button 4" />
</WrapLayout>
```_

## Navigation

### Bild Navigation
```javascript
import { Frame, topmost } from '@nativescript/core';

// Basic navigation
export function navigateToPage() {
    topmost().navigate('details-page');
}

// Navigation with context
export function navigateWithData() {
    topmost().navigate({
        moduleName: 'details-page',
        context: {
            id: 123,
            title: 'Sample Item',
            data: { key: 'value' }
        }
    });
}

// Navigation with transition
export function navigateWithTransition() {
    topmost().navigate({
        moduleName: 'details-page',
        animated: true,
        transition: {
            name: 'slideLeft',
            duration: 300,
            curve: 'easeIn'
        }
    });
}

// Back navigation
export function goBack() {
    if (topmost().canGoBack()) {
        topmost().goBack();
    }
}

// Clear history
export function navigateAndClearHistory() {
    topmost().navigate({
        moduleName: 'home-page',
        clearHistory: true
    });
}
```_

### Modische Navigation
```javascript
// Show modal
export function showModal() {
    const frame = topmost();
    frame.showModal({
        moduleName: 'modal-page',
        context: { message: 'Hello from modal' },
        fullscreen: false,
        animated: true,
        stretched: false
    }).then((result) => {
        console.log('Modal closed with result:', result);
    });
}

// Close modal with result
export function closeModal() {
    const frame = topmost();
    frame.closeModal('Modal result data');
}

// Modal page setup
// modal-page.js
export function onShownModally(args) {
    const page = args.object;
    const context = args.context;

    page.bindingContext = fromObject({
        message: context.message,
        closeModal: function() {
            page.closeModal('Data from modal');
        }
    });
}
```_

### Zurück zur Übersicht
```xml
<!-- TabView navigation -->
<TabView selectedIndex="{{ selectedTab }}" selectedIndexChanged="{{ onTabChanged }}">
    <TabViewItem title="Home" iconSource="res://ic_home">
        <Frame defaultPage="home-page" />
    </TabViewItem>
    <TabViewItem title="Search" iconSource="res://ic_search">
        <Frame defaultPage="search-page" />
    </TabViewItem>
    <TabViewItem title="Profile" iconSource="res://ic_profile">
        <Frame defaultPage="profile-page" />
    </TabViewItem>
</TabView>
```_

### Drawer Navigation
```xml
<!-- RadSideDrawer (requires plugin) -->
<nsDrawer:RadSideDrawer xmlns:nsDrawer="@nativescript/ui-sidedrawer">
    <nsDrawer:RadSideDrawer.drawerContent>
        <StackLayout class="drawer-content">
            <Label text="Menu" class="drawer-header" />
            <Button text="Home" tap="{{ navigateToHome }}" />
            <Button text="Settings" tap="{{ navigateToSettings }}" />
            <Button text="About" tap="{{ navigateToAbout }}" />
        </StackLayout>
    </nsDrawer:RadSideDrawer.drawerContent>

    <nsDrawer:RadSideDrawer.mainContent>
        <Frame defaultPage="main-page" />
    </nsDrawer:RadSideDrawer.mainContent>
</nsDrawer:RadSideDrawer>
```_

## Datenbindung

### Ein-Way Binding
```xml
<!-- Text binding -->
<Label text="{{ message }}" />
<Label text="{{ 'Hello ' + name }}" />

<!-- Property binding -->
<Button isEnabled="{{ canSubmit }}" />
<Image src="{{ imageUrl }}" />
<ActivityIndicator busy="{{ isLoading }}" />

<!-- Conditional binding -->
<Label text="{{ isLoggedIn ? 'Welcome' : 'Please login' }}" />
<Button visibility="{{ hasData ? 'visible' : 'collapsed' }}" />
```_

### Zwei-Wege-Bindung
```xml
<!-- TextField two-way binding -->
<TextField text="{{ username, mode=twoWay }}" />
<Slider value="{{ volume, mode=twoWay }}" />
<Switch checked="{{ isEnabled, mode=twoWay }}" />
<DatePicker date="{{ selectedDate, mode=twoWay }}" />
```_

### Veranstaltungsbindung
```xml
<!-- Event handlers -->
<Button text="Click Me" tap="{{ onButtonTap }}" />
<TextField text="{{ username }}" textChange="{{ onTextChange }}" />
<ListView items="{{ items }}" itemTap="{{ onItemTap }}" />
<SearchBar text="{{ query }}" submit="{{ onSearch }}" clear="{{ onClear }}" />
```_

### Liste Bindung
```javascript
// View model with observable array
import { Observable, ObservableArray } from '@nativescript/core';

export function createViewModel() {
    const viewModel = new Observable();

    const items = new ObservableArray([
        { id: 1, name: 'Item 1', description: 'First item' },
        { id: 2, name: 'Item 2', description: 'Second item' },
        { id: 3, name: 'Item 3', description: 'Third item' }
    ]);

    viewModel.set('items', items);

    viewModel.set('addItem', function() {
        const newItem = {
            id: items.length + 1,
            name: `Item ${items.length + 1}`,
            description: `Item number ${items.length + 1}`
        };
        items.push(newItem);
    });

    viewModel.set('removeItem', function(index) {
        items.splice(index, 1);
    });

    viewModel.set('onItemTap', function(args) {
        const item = items.getItem(args.index);
        console.log('Tapped item:', item.name);
    });

    return viewModel;
}
```_

```xml
<!-- List with binding -->
<StackLayout>
    <Button text="Add Item" tap="{{ addItem }}" />
    <ListView items="{{ items }}" itemTap="{{ onItemTap }}">
        <ListView.itemTemplate>
            <GridLayout columns="*, auto" class="list-item">
                <StackLayout col="0">
                    <Label text="{{ name }}" class="item-name" />
                    <Label text="{{ description }}" class="item-description" />
                </StackLayout>
                <Button text="Delete" col="1" tap="{{ $parents['Page'].removeItem, $index }}" />
            </GridLayout>
        </ListView.itemTemplate>
    </ListView>
</StackLayout>
```_

## Styling

### CSS Styling
```css
/* app.css - Global styles */

/* Import NativeScript theme */
@import '@nativescript/theme/css/core.css';
@import '@nativescript/theme/css/default.css';

/* Global styles */
.page {
    background-color: #f0f0f0;
}

.action-bar {
    background-color: #3f51b5;
    color: white;
}

/* Typography */
.h1 {
    font-size: 24;
    font-weight: bold;
    color: #333;
}

.h2 {
    font-size: 20;
    font-weight: bold;
    color: #666;
}

.body {
    font-size: 16;
    color: #333;
}

/* Buttons */
.btn {
    font-size: 16;
    padding: 12;
    margin: 8;
    border-radius: 4;
}

.btn-primary {
    background-color: #3f51b5;
    color: white;
}

.btn-secondary {
    background-color: #f0f0f0;
    color: #333;
    border-width: 1;
    border-color: #ccc;
}

/* Layout helpers */
.text-center {
    text-align: center;
}

.text-left {
    text-align: left;
}

.text-right {
    text-align: right;
}

.m-10 {
    margin: 10;
}

.p-10 {
    padding: 10;
}

/* List styles */
.list-item {
    padding: 16;
    border-bottom-width: 1;
    border-bottom-color: #e0e0e0;
}

.list-item:active {
    background-color: #f5f5f5;
}

/* Form styles */
.form-field {
    margin: 8;
    padding: 12;
    border-width: 1;
    border-color: #ccc;
    border-radius: 4;
}

.form-field:focus {
    border-color: #3f51b5;
}

/* Platform-specific styles */
.android .action-bar {
    height: 56;
}

.ios .action-bar {
    height: 44;
}

/* Responsive styles */
@media (orientation: landscape) {
    .container {
        flex-direction: row;
    }
}
```_

### SCSS Unterstützung
```bash
# Install SCSS support
npm install --save-dev sass

# Create SCSS files
# app.scss
```_

```scss
// app.scss
@import '@nativescript/theme/scss/variables/blue';
@import '@nativescript/theme/scss/core';
@import '@nativescript/theme/scss/default';

// Variables
$primary-color: #3f51b5;
$secondary-color: #f0f0f0;
$text-color: #333;
$border-color: #e0e0e0;

// Mixins
@mixin button-style($bg-color, $text-color) {
    background-color: $bg-color;
    color: $text-color;
    padding: 12;
    border-radius: 4;
    font-size: 16;
}

@mixin card-style {
    background-color: white;
    border-radius: 8;
    elevation: 2;
    margin: 8;
    padding: 16;
}

// Styles
.page {
    background-color: $secondary-color;
}

.btn-primary {
    @include button-style($primary-color, white);
}

.btn-secondary {
    @include button-style($secondary-color, $text-color);
    border-width: 1;
    border-color: $border-color;
}

.card {
    @include card-style;
}

// Nested styles
.list-container {
    .list-item {
        padding: 16;
        border-bottom-width: 1;
        border-bottom-color: $border-color;

        .item-title {
            font-size: 18;
            font-weight: bold;
            color: $text-color;
        }

        .item-subtitle {
            font-size: 14;
            color: lighten($text-color, 20%);
        }
    }
}
```_

### Plattform-Specific Styling
```css
/* Platform-specific CSS */
.android .action-bar {
    background-color: #3f51b5;
    color: white;
    height: 56;
}

.ios .action-bar {
    background-color: #007aff;
    color: white;
    height: 44;
}

/* Device-specific styles */
.phone .container {
    padding: 16;
}

.tablet .container {
    padding: 32;
    max-width: 600;
    horizontal-align: center;
}

/* Orientation-specific styles */
.portrait .layout {
    flex-direction: column;
}

.landscape .layout {
    flex-direction: row;
}
```_

### Dynamische Styling
```javascript
// Dynamic styling in code
import { Color } from '@nativescript/core';

export function applyDynamicStyles(view, theme) {
    if (theme === 'dark') {
        view.backgroundColor = new Color('#333');
        view.color = new Color('#fff');
    } else {
        view.backgroundColor = new Color('#fff');
        view.color = new Color('#333');
    }
}

// CSS class manipulation
export function toggleTheme(view) {
    if (view.className.includes('dark-theme')) {
        view.className = view.className.replace('dark-theme', 'light-theme');
    } else {
        view.className = view.className.replace('light-theme', 'dark-theme');
    }
}

// Style animation
import { Animation } from '@nativescript/core';

export function animateButton(button) {
    const animation = new Animation([{
        target: button,
        scale: { x: 1.2, y: 1.2 },
        duration: 200
    }, {
        target: button,
        scale: { x: 1, y: 1 },
        duration: 200
    }]);

    animation.play();
}
```_

## Plattform APIs

### Informationen Ã1⁄4ber das Gerät
```javascript
import { Device, Screen, isAndroid, isIOS } from '@nativescript/core';

// Device information
console.log('Device Type:', Device.deviceType);
console.log('OS:', Device.os);
console.log('OS Version:', Device.osVersion);
console.log('Model:', Device.model);
console.log('Manufacturer:', Device.manufacturer);
console.log('UUID:', Device.uuid);
console.log('Language:', Device.language);
console.log('Region:', Device.region);

// Screen information
console.log('Screen Width:', Screen.mainScreen.widthDIPs);
console.log('Screen Height:', Screen.mainScreen.heightDIPs);
console.log('Screen Scale:', Screen.mainScreen.scale);

// Platform detection
if (isAndroid) {
    console.log('Running on Android');
    // Android-specific code
} else if (isIOS) {
    console.log('Running on iOS');
    // iOS-specific code
}

// Platform-specific API access
if (isAndroid) {
    const context = Application.android.context;
    const packageManager = context.getPackageManager();
    // Use Android APIs
} else if (isIOS) {
    const currentDevice = UIDevice.currentDevice;
    // Use iOS APIs
}
```_

### Dateisystem
```javascript
import { File, Folder, knownFolders, path } from '@nativescript/core';

// Known folders
const documentsFolder = knownFolders.documents();
const tempFolder = knownFolders.temp();
const appFolder = knownFolders.currentApp();

// Create file
const file = documentsFolder.getFile('data.txt');

// Write to file
file.writeText('Hello, NativeScript!')
    .then(() => {
        console.log('File written successfully');
    })
    .catch((error) => {
        console.error('Error writing file:', error);
    });

// Read from file
file.readText()
    .then((content) => {
        console.log('File content:', content);
    })
    .catch((error) => {
        console.error('Error reading file:', error);
    });

// Create folder
const folder = documentsFolder.getFolder('myFolder');

// List folder contents
folder.getEntities()
    .then((entities) => {
        entities.forEach((entity) => {
            console.log('Entity:', entity.name, entity.path);
        });
    });

// File operations
const sourceFile = documentsFolder.getFile('source.txt');
const targetFile = documentsFolder.getFile('target.txt');

// Copy file
sourceFile.copy(targetFile.path)
    .then(() => {
        console.log('File copied');
    });

// Move file
sourceFile.move(targetFile.path)
    .then(() => {
        console.log('File moved');
    });

// Delete file
file.remove()
    .then(() => {
        console.log('File deleted');
    });

// Check if file exists
if (file.exists) {
    console.log('File exists');
}

// Get file info
console.log('File size:', file.size);
console.log('Last modified:', file.lastModified);
```_

### HTTP-Anfragen
```javascript
import { Http } from '@nativescript/core';

// GET request
Http.getJSON('https://api.example.com/users')
    .then((result) => {
        console.log('Users:', result);
    })
    .catch((error) => {
        console.error('Error:', error);
    });

// POST request
Http.request({
    url: 'https://api.example.com/users',
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token'
    },
    content: JSON.stringify({
        name: 'John Doe',
        email: 'john@example.com'
    })
})
.then((response) => {
    console.log('Response:', response.content.toJSON());
})
.catch((error) => {
    console.error('Error:', error);
});

// File upload
Http.request({
    url: 'https://api.example.com/upload',
    method: 'POST',
    headers: {
        'Content-Type': 'multipart/form-data'
    },
    content: {
        file: file,
        description: 'Uploaded file'
    }
})
.then((response) => {
    console.log('Upload successful');
})
.catch((error) => {
    console.error('Upload failed:', error);
});

// Download file
Http.getFile('https://example.com/file.pdf', documentsFolder.path + '/downloaded.pdf')
    .then((file) => {
        console.log('File downloaded:', file.path);
    })
    .catch((error) => {
        console.error('Download failed:', error);
    });
```_

### Lokale Speicherung
```javascript
import { ApplicationSettings } from '@nativescript/core';

// Store data
ApplicationSettings.setString('username', 'john_doe');
ApplicationSettings.setNumber('userId', 123);
ApplicationSettings.setBoolean('isLoggedIn', true);

// Retrieve data
const username = ApplicationSettings.getString('username', 'default_user');
const userId = ApplicationSettings.getNumber('userId', 0);
const isLoggedIn = ApplicationSettings.getBoolean('isLoggedIn', false);

// Remove data
ApplicationSettings.remove('username');

// Check if key exists
if (ApplicationSettings.hasKey('username')) {
    console.log('Username exists');
}

// Clear all data
ApplicationSettings.clear();

// Store complex objects
const user = {
    id: 123,
    name: 'John Doe',
    email: 'john@example.com'
};

ApplicationSettings.setString('user', JSON.stringify(user));

// Retrieve complex objects
const storedUser = JSON.parse(ApplicationSettings.getString('user', '{}'));
```_

## Plugins

### Core Plugins
```bash
# Camera plugin
ns plugin add @nativescript/camera

# Geolocation plugin
ns plugin add @nativescript/geolocation

# Local notifications
ns plugin add @nativescript/local-notifications

# Social share
ns plugin add @nativescript/social-share

# Email plugin
ns plugin add @nativescript/email

# Phone plugin
ns plugin add @nativescript/phone

# Fingerprint authentication
ns plugin add @nativescript/fingerprint-auth

# Secure storage
ns plugin add @nativescript/secure-storage

# Background HTTP
ns plugin add @nativescript/background-http
```_

### Verwendung von Camera Plugin
```javascript
import { Camera, requestPermissions } from '@nativescript/camera';

export function takePicture() {
    requestPermissions()
        .then(() => {
            const options = {
                width: 300,
                height: 300,
                keepAspectRatio: true,
                saveToGallery: false
            };

            return Camera.takePicture(options);
        })
        .then((imageAsset) => {
            console.log('Picture taken:', imageAsset);
            // Use the image
            const image = new Image();
            image.src = imageAsset;
        })
        .catch((error) => {
            console.error('Camera error:', error);
        });
}

export function selectFromGallery() {
    const options = {
        width: 300,
        height: 300,
        keepAspectRatio: true
    };

    Camera.requestPermissions()
        .then(() => {
            return Camera.selectFromGallery(options);
        })
        .then((imageAsset) => {
            console.log('Image selected:', imageAsset);
        })
        .catch((error) => {
            console.error('Gallery error:', error);
        });
}
```_

### Verwendung von Geolocation Plugin
```javascript
import { Geolocation, CoreTypes } from '@nativescript/geolocation';

export function getCurrentLocation() {
    const options = {
        desiredAccuracy: CoreTypes.Accuracy.high,
        maximumAge: 5000,
        timeout: 10000
    };

    Geolocation.getCurrentLocation(options)
        .then((location) => {
            console.log('Location:', location);
            console.log('Latitude:', location.latitude);
            console.log('Longitude:', location.longitude);
            console.log('Altitude:', location.altitude);
            console.log('Speed:', location.speed);
        })
        .catch((error) => {
            console.error('Location error:', error);
        });
}

export function watchLocation() {
    const options = {
        desiredAccuracy: CoreTypes.Accuracy.high,
        updateDistance: 10,
        minimumUpdateTime: 1000
    };

    const watchId = Geolocation.watchLocation(
        (location) => {
            console.log('Location update:', location);
        },
        (error) => {
            console.error('Location error:', error);
        },
        options
    );

    // Stop watching
    setTimeout(() => {
        Geolocation.clearWatch(watchId);
    }, 30000);
}
```_

### Benutzerdefinierte Plugin Entwicklung
```javascript
// Create custom plugin
// my-plugin/index.js
export class MyPlugin {
    static getMessage() {
        return 'Hello from custom plugin!';
    }

    static performAction(data) {
        return new Promise((resolve, reject) => {
            // Plugin logic here
            setTimeout(() => {
                resolve(`Action completed with: ${data}`);
            }, 1000);
        });
    }
}

// my-plugin/index.android.js
export class MyPlugin {
    static getMessage() {
        // Android-specific implementation
        return 'Hello from Android plugin!';
    }

    static performAction(data) {
        return new Promise((resolve, reject) => {
            // Android-specific logic
            const context = Application.android.context;
            // Use Android APIs
            resolve(`Android action completed with: ${data}`);
        });
    }
}

// my-plugin/index.ios.js
export class MyPlugin {
    static getMessage() {
        // iOS-specific implementation
        return 'Hello from iOS plugin!';
    }

    static performAction(data) {
        return new Promise((resolve, reject) => {
            // iOS-specific logic
            // Use iOS APIs
            resolve(`iOS action completed with: ${data}`);
        });
    }
}

// Usage
import { MyPlugin } from './my-plugin';

console.log(MyPlugin.getMessage());

MyPlugin.performAction('test data')
    .then((result) => {
        console.log('Plugin result:', result);
    })
    .catch((error) => {
        console.error('Plugin error:', error);
    });
```_

## Angly Integration

### Setup Angular Project
```bash
# Create Angular project
ns create MyAngularApp --ng

# Add Angular dependencies
cd MyAngularApp
npm install @angular/core @angular/common @angular/forms @angular/router

# Generate components
ng generate component home
ng generate component details
ng generate service data
```_

### App Modul
```typescript
// app.module.ts
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { NativeScriptModule } from '@nativescript/angular';
import { NativeScriptRouterModule } from '@nativescript/angular';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { DetailsComponent } from './details/details.component';

@NgModule({
    bootstrap: [AppComponent],
    imports: [
        NativeScriptModule,
        AppRoutingModule
    ],
    declarations: [
        AppComponent,
        HomeComponent,
        DetailsComponent
    ],
    providers: [],
    schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }
```_

### Routing
```typescript
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { NativeScriptRouterModule } from '@nativescript/angular';

import { HomeComponent } from './home/home.component';
import { DetailsComponent } from './details/details.component';

const routes: Routes = [
    { path: '', redirectTo: '/home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'details/:id', component: DetailsComponent }
];

@NgModule({
    imports: [NativeScriptRouterModule.forRoot(routes)],
    exports: [NativeScriptRouterModule]
})
export class AppRoutingModule { }
```_

### Komponente
```typescript
// home.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { DataService } from '../data.service';

@Component({
    selector: 'ns-home',
    templateUrl: './home.component.html',
    styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
    items: any[] = [];
    isLoading = false;

    constructor(
        private router: Router,
        private dataService: DataService
    ) { }

    ngOnInit(): void {
        this.loadData();
    }

    loadData(): void {
        this.isLoading = true;
        this.dataService.getItems()
            .subscribe(
                (data) => {
                    this.items = data;
                    this.isLoading = false;
                },
                (error) => {
                    console.error('Error loading data:', error);
                    this.isLoading = false;
                }
            );
    }

    onItemTap(item: any): void {
        this.router.navigate(['/details', item.id]);
    }

    onRefresh(): void {
        this.loadData();
    }
}
```_

```html
<!-- home.component.html -->
<ActionBar title="Home" class="action-bar">
    <ActionItem text="Refresh" (tap)="onRefresh()" ios.position="right"></ActionItem>
</ActionBar>

<GridLayout rows="*">
    <ActivityIndicator [busy]="isLoading" *ngIf="isLoading"></ActivityIndicator>

    <ListView [items]="items" (itemTap)="onItemTap($event.object.bindingContext)" *ngIf="!isLoading">
        <ng-template let-item="item">
            <GridLayout columns="auto, *" rows="auto, auto" class="list-item">
                <Label [text]="item.title" row="0" col="1" class="item-title"></Label>
                <Label [text]="item.description" row="1" col="1" class="item-description"></Label>
            </GridLayout>
        </ng-template>
    </ListView>
</GridLayout>
```_

### Service
```typescript
// data.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Http } from '@nativescript/core';

@Injectable({
    providedIn: 'root'
})
export class DataService {
    private apiUrl = 'https://api.example.com';

    constructor() { }

    getItems(): Observable<any[]> {
        return new Observable((observer) => {
            Http.getJSON(`${this.apiUrl}/items`)
                .then((data: any[]) => {
                    observer.next(data);
                    observer.complete();
                })
                .catch((error) => {
                    observer.error(error);
                });
        });
    }

    getItem(id: number): Observable<any> {
        return new Observable((observer) => {
            Http.getJSON(`${this.apiUrl}/items/${id}`)
                .then((data: any) => {
                    observer.next(data);
                    observer.complete();
                })
                .catch((error) => {
                    observer.error(error);
                });
        });
    }

    createItem(item: any): Observable<any> {
        return new Observable((observer) => {
            Http.request({
                url: `${this.apiUrl}/items`,
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                content: JSON.stringify(item)
            })
            .then((response) => {
                observer.next(response.content.toJSON());
                observer.complete();
            })
            .catch((error) => {
                observer.error(error);
            });
        });
    }
}
```_

## Vue.js Integration

### Setup Vue Project
```bash
# Create Vue project
ns create MyVueApp --vue

# Add Vue dependencies
cd MyVueApp
npm install vue@next @vue/runtime-core
```_

### Haupt App
```javascript
// app.js
import { createApp } from 'nativescript-vue';
import Home from './components/Home.vue';

const app = createApp(Home);

app.start();
```_

### Vue Component
```vue
<!-- components/Home.vue -->
<template>
    <Frame>
        <Page>
            <ActionBar title="Home" />

            <GridLayout rows="auto, *">
                <StackLayout row="0" class="form">
                    <TextField v-model="newItem" hint="Enter new item" />
                    <Button text="Add Item" @tap="addItem" class="btn btn-primary" />
                </StackLayout>

                <ListView row="1" :items="items" @itemTap="onItemTap">
                    <template #default="{ item, index }">
                        <GridLayout columns="*, auto" class="list-item">
                            <Label col="0" :text="item.name" class="item-name" />
                            <Button col="1" text="Delete" @tap="deleteItem(index)" class="btn btn-secondary" />
                        </GridLayout>
                    </template>
                </ListView>
            </GridLayout>
        </Page>
    </Frame>
</template>

<script>
import { ref, reactive } from 'vue';

export default {
    setup() {
        const newItem = ref('');
        const items = reactive([
            { id: 1, name: 'Item 1' },
            { id: 2, name: 'Item 2' },
            { id: 3, name: 'Item 3' }
        ]);

        const addItem = () => {
            if (newItem.value.trim()) {
                items.push({
                    id: Date.now(),
                    name: newItem.value
                });
                newItem.value = '';
            }
        };

        const deleteItem = (index) => {
            items.splice(index, 1);
        };

        const onItemTap = (event) => {
            const item = event.item;
            console.log('Tapped item:', item.name);
        };

        return {
            newItem,
            items,
            addItem,
            deleteItem,
            onItemTap
        };
    }
};
</script>

<style scoped>
.form {
    padding: 20;
}

.list-item {
    padding: 16;
    border-bottom-width: 1;
    border-bottom-color: #e0e0e0;
}

.item-name {
    font-size: 16;
    vertical-align: center;
}

.btn {
    margin: 8;
}
</style>
```_

### Vue Router
```javascript
// router.js
import { createRouter } from 'nativescript-vue';
import Home from './components/Home.vue';
import Details from './components/Details.vue';

const router = createRouter({
    routes: [
        { path: '/', component: Home },
        { path: '/details/:id', component: Details }
    ]
});

export default router;
```_

## React Integration

### Setup React Project
```bash
# Create React project
ns create MyReactApp --react

# Add React dependencies
cd MyReactApp
npm install react react-nativescript
```_

### Komponenten
```jsx
// app.jsx
import React from 'react';
import { StackNavigator } from 'react-nativescript-navigation';
import { Home } from './components/Home';
import { Details } from './components/Details';

const AppContainer = StackNavigator(
    {
        Home: {
            screen: Home
        },
        Details: {
            screen: Details
        }
    },
    {
        initialRouteName: 'Home'
    }
);

export default AppContainer;
```_

### React Component
```jsx
// components/Home.jsx
import React, { useState, useEffect } from 'react';

export function Home({ navigation }) {
    const [items, setItems] = useState([]);
    const [newItem, setNewItem] = useState('');
    const [isLoading, setIsLoading] = useState(false);

    useEffect(() => {
        loadData();
    }, []);

    const loadData = async () => {
        setIsLoading(true);
        try {
            // Simulate API call
            const data = [
                { id: 1, name: 'Item 1', description: 'First item' },
                { id: 2, name: 'Item 2', description: 'Second item' },
                { id: 3, name: 'Item 3', description: 'Third item' }
            ];
            setItems(data);
        } catch (error) {
            console.error('Error loading data:', error);
        } finally {
            setIsLoading(false);
        }
    };

    const addItem = () => {
        if (newItem.trim()) {
            const item = {
                id: Date.now(),
                name: newItem,
                description: `Description for ${newItem}`
            };
            setItems([...items, item]);
            setNewItem('');
        }
    };

    const deleteItem = (id) => {
        setItems(items.filter(item => item.id !== id));
    };

    const navigateToDetails = (item) => {
        navigation.navigate('Details', { item });
    };

    return (
        <page>
            <actionBar title="Home" />

            <gridLayout rows="auto, *">
                <stackLayout row={0} className="form">
                    <textField
                        text={newItem}
                        onTextChange={(args) => setNewItem(args.value)}
                        hint="Enter new item"
                    />
                    <button
                        text="Add Item"
                        onTap={addItem}
                        className="btn btn-primary"
                    />
                </stackLayout>

                {isLoading ? (
                    <activityIndicator row={1} busy={true} />
                ) : (
                    <listView
                        row={1}
                        items={items}
                        onItemTap={(args) => navigateToDetails(args.item)}
                        cellFactory={(item) => (
                            <gridLayout columns="*, auto" className="list-item">
                                <stackLayout col={0}>
                                    <label text={item.name} className="item-name" />
                                    <label text={item.description} className="item-description" />
                                </stackLayout>
                                <button
                                    col={1}
                                    text="Delete"
                                    onTap={() => deleteItem(item.id)}
                                    className="btn btn-secondary"
                                />
                            </gridLayout>
                        )}
                    />
                )}
            </gridLayout>
        </page>
    );
}

React Hooks

// hooks/useApi.js
import { useState, useEffect } from 'react';
import { Http } from '@nativescript/core';

export function useApi(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                setLoading(true);
                const result = await Http.getJSON(url);
                setData(result);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        fetchData();
    }, [url]);

    return { data, loading, error };
}

// Usage in component
export function DataComponent() {
    const { data, loading, error } = useApi('https://api.example.com/data');

    if (loading) return <activityIndicator busy={true} />;
    if (error) return <label text={`Error: ${error.message}`} />;

    return (
        <listView
            items={data}
            cellFactory={(item) => (
                <label text={item.name} />
            )}
        />
    );
}
```_

## Prüfung

### Einheitsprüfung
```bash
# Install testing dependencies
npm install --save-dev @nativescript/unit-test-runner
npm install --save-dev jasmine
npm install --save-dev karma
npm install --save-dev karma-jasmine
npm install --save-dev karma-nativescript-launcher

# Run tests
ns test android
ns test ios
```_

### Testkonfiguration
```javascript
// karma.conf.js
module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: [
            'app/**/*.spec.js'
        ],
        exclude: [],
        preprocessors: {},
        reporters: ['progress'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: ['NativeScript'],
        singleRun: true,
        concurrency: Infinity
    });
};
```_

### Prüfbeispiel
```javascript
// tests/example.spec.js
describe('Calculator', function() {
    let calculator;

    beforeEach(function() {
        calculator = new Calculator();
    });

    it('should add two numbers correctly', function() {
        const result = calculator.add(2, 3);
        expect(result).toBe(5);
    });

    it('should subtract two numbers correctly', function() {
        const result = calculator.subtract(5, 3);
        expect(result).toBe(2);
    });

    it('should multiply two numbers correctly', function() {
        const result = calculator.multiply(4, 3);
        expect(result).toBe(12);
    });

    it('should divide two numbers correctly', function() {
        const result = calculator.divide(10, 2);
        expect(result).toBe(5);
    });

    it('should throw error when dividing by zero', function() {
        expect(function() {
            calculator.divide(10, 0);
        }).toThrow();
    });
});

// Calculator class
class Calculator {
    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    multiply(a, b) {
        return a * b;
    }

    divide(a, b) {
        if (b === 0) {
            throw new Error('Division by zero');
        }
        return a / b;
    }
}
```_

### E2E Prüfung
```bash
# Install Appium
npm install --save-dev appium
npm install --save-dev wd

# Install test framework
npm install --save-dev mocha
npm install --save-dev chai
```_

```javascript
// e2e/test.spec.js
const wd = require('wd');
const { expect } = require('chai');

describe('App E2E Tests', function() {
    let driver;

    before(async function() {
        this.timeout(60000);

        driver = wd.promiseChainRemote('localhost', 4723);

        const desiredCaps = {
            platformName: 'Android',
            platformVersion: '10',
            deviceName: 'emulator-5554',
            app: './platforms/android/app/build/outputs/apk/debug/app-debug.apk',
            automationName: 'UiAutomator2'
        };

        await driver.init(desiredCaps);
    });

    after(async function() {
        if (driver) {
            await driver.quit();
        }
    });

    it('should display home screen', async function() {
        const title = await driver.elementByAccessibilityId('home-title');
        const titleText = await title.text();
        expect(titleText).to.equal('Home');
    });

    it('should navigate to details screen', async function() {
        const button = await driver.elementByAccessibilityId('details-button');
        await button.tap();

        const detailsTitle = await driver.elementByAccessibilityId('details-title');
        const titleText = await detailsTitle.text();
        expect(titleText).to.equal('Details');
    });

    it('should add new item', async function() {
        const textField = await driver.elementByAccessibilityId('new-item-field');
        await textField.sendKeys('Test Item');

        const addButton = await driver.elementByAccessibilityId('add-button');
        await addButton.tap();

        const listItem = await driver.elementByAccessibilityId('list-item-0');
        const itemText = await listItem.text();
        expect(itemText).to.contain('Test Item');
    });
});
```_

## Leistung

### Speicherverwaltung
```javascript
// Proper cleanup
export function onNavigatingFrom(args) {
    const page = args.object;

    // Remove event listeners
    if (page.bindingContext && page.bindingContext.cleanup) {
        page.bindingContext.cleanup();
    }

    // Clear timers
    if (page._timer) {
        clearInterval(page._timer);
        page._timer = null;
    }

    // Clear references
    page.bindingContext = null;
}

// Observable cleanup
class ViewModel extends Observable {
    constructor() {
        super();
        this._subscriptions = [];
    }

    addSubscription(subscription) {
        this._subscriptions.push(subscription);
    }

    cleanup() {
        this._subscriptions.forEach(sub => {
            if (sub && sub.unsubscribe) {
                sub.unsubscribe();
            }
        });
        this._subscriptions = [];
    }
}
```_

### Bildoptimierung
```javascript
// Lazy loading images
export function onImageLoading(args) {
    const image = args.object;
    const listView = image.parent;

    // Only load images for visible items
    if (listView && listView.isItemAtIndexVisible) {
        const index = listView.getItemAtIndex(args.index);
        if (listView.isItemAtIndexVisible(index)) {
            image.src = args.item.imageUrl;
        }
    }
}

// Image caching
class ImageCache {
    constructor() {
        this.cache = new Map();
        this.maxSize = 50; // Maximum number of cached images
    }

    get(url) {
        return this.cache.get(url);
    }

    set(url, imageSource) {
        if (this.cache.size >= this.maxSize) {
            // Remove oldest entry
            const firstKey = this.cache.keys().next().value;
            this.cache.delete(firstKey);
        }
        this.cache.set(url, imageSource);
    }

    clear() {
        this.cache.clear();
    }
}

const imageCache = new ImageCache();

// Optimized image loading
export function loadOptimizedImage(imageView, url) {
    // Check cache first
    const cachedImage = imageCache.get(url);
    if (cachedImage) {
        imageView.imageSource = cachedImage;
        return;
    }

    // Load and cache image
    ImageSource.fromUrl(url)
        .then((imageSource) => {
            imageCache.set(url, imageSource);
            imageView.imageSource = imageSource;
        })
        .catch((error) => {
            console.error('Error loading image:', error);
        });
}
```_

### Leistung
```javascript
// Virtual scrolling for large lists
export function createVirtualList(container, items, itemHeight, visibleItems) {
    let startIndex = 0;
    let endIndex = Math.min(visibleItems, items.length);

    const updateList = () => {
        container.removeChildren();

        for (let i = startIndex; i < endIndex; i++) {
            const item = items[i];
            const itemView = createItemView(item, i);
            container.addChild(itemView);
        }
    };

    const onScroll = (scrollY) => {
        const newStartIndex = Math.floor(scrollY / itemHeight);
        const newEndIndex = Math.min(newStartIndex + visibleItems, items.length);

        if (newStartIndex !== startIndex || newEndIndex !== endIndex) {
            startIndex = newStartIndex;
            endIndex = newEndIndex;
            updateList();
        }
    };

    updateList();

    return { updateList, onScroll };
}

// Efficient list item recycling
export function createRecyclingListView(items, itemTemplate) {
    const recycledViews = [];
    const activeViews = new Map();

    const getRecycledView = () => {
        return recycledViews.pop() || null;
    };

    const recycleView = (view) => {
        recycledViews.push(view);
    };

    const createItemView = (item, index) => {
        let view = getRecycledView();

        if (!view) {
            view = itemTemplate();
        }

        // Bind data to view
        view.bindingContext = item;
        activeViews.set(index, view);

        return view;
    };

    const removeItemView = (index) => {
        const view = activeViews.get(index);
        if (view) {
            activeViews.delete(index);
            recycleView(view);
        }
    };

    return { createItemView, removeItemView };
}
```_

## Bereitstellung

### Android Bereitstellung
```bash
# Build debug APK
ns build android

# Build release APK
ns build android --release --key-store-path myapp.keystore --key-store-password password --key-store-alias myapp --key-store-alias-password password

# Generate keystore
keytool -genkey -v -keystore myapp.keystore -alias myapp -keyalg RSA -keysize 2048 -validity 10000

# Build AAB (Android App Bundle)
ns build android --release --aab --key-store-path myapp.keystore --key-store-password password --key-store-alias myapp --key-store-alias-password password

# Upload to Google Play Console
# Use the generated AAB file in platforms/android/app/build/outputs/bundle/release/
```_

### iOS Bereitstellung
```bash
# Build for device
ns build ios --for-device --release

# Build with provisioning profile
ns build ios --for-device --release --provision "MyApp Distribution Profile"

# Create IPA
ns build ios --for-device --release --provision "MyApp Distribution Profile" --team-id TEAM_ID

# Upload to App Store
# Use Xcode or Application Loader to upload the IPA
```_

### Kontinuierliche Integration
```yaml
# .github/workflows/build.yml
name: Build and Test

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v2

    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16'

    - name: Install dependencies
      run: npm install

    - name: Install NativeScript CLI
      run: npm install -g @nativescript/cli

    - name: Add platforms
      run: |
        ns platform add android
        ns platform add ios

    - name: Build Android
      run: ns build android --release

    - name: Build iOS
      run: ns build ios --for-device --release

    - name: Run tests
      run: ns test android --justlaunch

    - name: Upload artifacts
      uses: actions/upload-artifact@v2
      with:
        name: builds
        path: |
          platforms/android/app/build/outputs/apk/release/
          platforms/ios/build/Release-iphoneos/
```_

## Best Practices

### Projektstruktur
app/ ├── core/ # Core functionality │ ├── services/ # Business logic services │ ├── models/ # Data models │ ├── utils/ # Utility functions │ └── constants/ # App constants ├── shared/ # Shared components │ ├── components/ # Reusable UI components │ ├── directives/ # Custom directives │ └── pipes/ # Custom pipes ├── features/ # Feature modules │ ├── home/ # Home feature │ ├── profile/ # Profile feature │ └── settings/ # Settings feature ├── assets/ # Static assets │ ├── images/ │ ├── fonts/ │ └── sounds/ └── styles/ # Global styles ├── variables.scss ├── _mixins.scss └── app.scss ```

Code Organisation

```javascript // Use consistent naming conventions // PascalCase for classes and constructors class UserService { constructor() { this.users = []; } }

// camelCase for variables and functions const userName = 'john_doe'; const getUserById = (id) => { return users.find(user => user.id === id); };

// UPPER_CASE for constants const API_BASE_URL = 'https://api.example.com'; const MAX_RETRY_ATTEMPTS = 3;

// Use meaningful names // Bad const d = new Date(); const u = users.filter(x => x.a);

// Good const currentDate = new Date(); const activeUsers = users.filter(user => user.isActive);

// Use consistent error handling class ApiService { async fetchData(url) { try { const response = await Http.getJSON(url); return { success: true, data: response }; } catch (error) { console.error('API Error:', error); return { success: false, error: error.message }; } } }

// Use proper async/await patterns // Bad function loadUserData(userId) { getUserById(userId) .then(user => { return getProfileData(user.id); }) .then(profile => { return getPreferences(profile.id); }) .then(preferences => { console.log('User data loaded'); }) .catch(error => { console.error('Error:', error); }); }

// Good async function loadUserData(userId) { try { const user = await getUserById(userId); const profile = await getProfileData(user.id); const preferences = await getPreferences(profile.id); console.log('User data loaded'); return { user, profile, preferences }; } catch (error) { console.error('Error loading user data:', error); throw error; } } ```_

Performance Best Practices

```javascript // Optimize data binding // Use one-time binding for static data // XML: text="{{ message, mode=oneTime }}"

// Minimize property changes class OptimizedViewModel extends Observable { constructor() { super(); this._batchUpdates = false; this._pendingUpdates = {}; }

startBatch() {
    this._batchUpdates = true;
}

endBatch() {
    this._batchUpdates = false;
    Object.keys(this._pendingUpdates).forEach(key => {
        super.set(key, this._pendingUpdates[key]);
    });
    this._pendingUpdates = {};
}

set(name, value) {
    if (this._batchUpdates) {
        this._pendingUpdates[name] = value;
    } else {
        super.set(name, value);
    }
}

}

// Use efficient list operations // Bad items.forEach((item, index) => { if (item.shouldRemove) { items.splice(index, 1); } });

// Good const itemsToKeep = items.filter(item => !item.shouldRemove); items.splice(0, items.length, ...itemsToKeep);

// Optimize image loading const optimizeImageLoading = (imageView, url, placeholder) => { // Show placeholder immediately imageView.src = placeholder;

// Load actual image asynchronously
ImageSource.fromUrl(url)
    .then(imageSource => {
        imageView.imageSource = imageSource;
    })
    .catch(error => {
        console.error('Image loading failed:', error);
        // Keep placeholder or show error image
    });

};

// Use proper cleanup class ComponentManager { constructor() { this.components = new Map(); this.timers = new Set(); this.subscriptions = new Set(); }

addTimer(timer) {
    this.timers.add(timer);
}

addSubscription(subscription) {
    this.subscriptions.add(subscription);
}

cleanup() {
    // Clear timers
    this.timers.forEach(timer => clearTimeout(timer));
    this.timers.clear();

    // Unsubscribe from observables
    this.subscriptions.forEach(sub => {
        if (sub && sub.unsubscribe) {
            sub.unsubscribe();
        }
    });
    this.subscriptions.clear();

    // Clear component references
    this.components.clear();
}

} ```_

--

Zusammenfassung

NativeScript bietet eine leistungsstarke Plattform für den Aufbau wirklich nativer mobiler Anwendungen mit Web-Technologien:

Key Vorteile: - True Native Performance*: Direkter Zugriff auf native APIs ohne WebViews - ** Code Sharing: Teilen Sie Business-Logik über Plattformen, während native UI - Framework Flexibilität*: Unterstützung für Angular, Vue.js, React und Vanille JavaScript - **Native UI: Plattformspezifisch UI Komponenten, die aussehen und fühlen native - Plugin Ecosystem: Reiches Ökosystem von Plugins für native Funktionalität

Best Use Cases: - Anwendungen, die native Leistung und UI erfordern - Cross-Plattform-Apps mit komplexer Geschäftslogik - Teams mit Web-Entwicklungskompetenz - Anwendungen, die umfangreichen nativen API-Zugriff benötigen - Enterprise-Anwendungen mit kundenspezifischen Anforderungen

Verbleib: - Steeper Lernkurve als hybride Frameworks - Größere App-Größe im Vergleich zu webbasierten Lösungen - Erforderliche plattformspezifische Prüfung - Notwendigkeit, native Plattformkonzepte zu verstehen

NativeScript ist ideal für Entwickler, die Web-Technologien nutzen möchten, während Sie wirklich native mobile Anwendungen mit hervorragender Performance und Plattformintegration aufbauen möchten.

<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll('code'); alle Befehle = ''; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + '\n'); navigator.clipboard.writeText (allCommands); Alarm ('Alle Befehle, die in die Zwischenablage kopiert werden!'); }

Funktion generierenPDF() { Fenster.print(); }