Saltar al contenido principal
Neural Nativev0.1.0-beta.1Documentación

Models, inputs y outputs

Un input configura un control, por ejemplo su etiqueta o si está desactivado. Un output comunica algo que ha ocurrido, como pulsar un botón. Un model comparte un valor editable en ambas direcciones: la aplicación lo aporta y recibe los cambios del usuario. Las tablas de la API indican el tipo de cada propiedad.

EnlaceSignificado
[(value)]="name"Comparte el valor editable con una signal escribible.
[(checked)]="notifications"Comparte el estado booleano de Switch o Checkbox.
[disabled]="!canSave()"Pasa un valor de la aplicación al control.
(pressed)="save()"Solicita una acción; la aplicación decide qué hace.

Usa la signal escribible directamente en un enlace bidireccional: [(value)]="name". Léela con paréntesis en una expresión: name(). Una signal computed representa estado derivado y sirve para inputs como disabled; no es un model editable.

La pantalla conserva el estado de edición

Coloca el componente siguiente dentro del neu-theme raíz de la aplicación.

src/app/preferences.component.ts
import { Component, computed, signal } from '@angular/core';
import { Text, View } from '@ng-native/components';
import { NeuInputComponent } from '@neural-ui/native/input';
import { NeuSwitchComponent } from '@neural-ui/native/switch';
import { NeuButtonComponent } from '@neural-ui/native/button';

@Component({
  selector: 'app-preferences',
  imports: [Text, View, NeuInputComponent, NeuSwitchComponent, NeuButtonComponent],
  template: `
    <view class="content">
      <neu-input label="Full name" [(value)]="name" hint="Shown to your team" />
      <neu-switch label="Team notifications" [(checked)]="notifications" />
      <neu-button label="Save preferences" [disabled]="!canSave()" (pressed)="save()" />
      <neu-button label="Reset" variant="text" (pressed)="reset()" />
      <text class="feedback">{{ feedback() }}</text>
    </view>
  `,
  styles: `
    .content { padding: 24px; gap: 16px; }
    .feedback { color: var(--neu-text); }
  `,
})
export class PreferencesComponent {
  readonly name = signal('Ada Lovelace');
  readonly notifications = signal(true);
  readonly canSave = computed(() => this.name().trim().length > 0);
  readonly feedback = signal('');

  save(): void {
    if (!this.canSave()) return;
    const preferences = { name: this.name().trim(), notifications: this.notifications() };
    this.feedback.set('Preferences ready for ' + preferences.name);
    // Pass preferences to your application service to persist them.
  }

  reset(): void {
    this.name.set('Ada Lovelace');
    this.notifications.set(true);
    this.feedback.set('');
  }
}

Las signals name y notifications almacenan las ediciones. La validación canSave se deriva del nombre y reset restablece el estado de la pantalla. El handler de guardado realiza la persistencia que necesita la aplicación.

Ediciones locales, datos guardados y tareas asíncronas

Si la edición se puede cancelar, copia el valor guardado en un borrador al abrir el editor. Actualiza el valor guardado solo tras confirmar. No enlaces el editor sin guardar directamente al estado persistido compartido si Cancel debe descartar los cambios.

Este editor completo conserva las ediciones si falla la petición, evita envíos duplicados y cancela la petición al destruir la pantalla. Sustituye el endpoint de ejemplo y aporta la autenticación de tu aplicación. El servidor debe validar y guardar los datos; una acción local correcta por sí sola no guarda nada de forma remota.

Coloca el componente siguiente dentro del neu-theme raíz de la aplicación.

src/app/profile-editor.component.ts
import { Component, DestroyRef, computed, inject, signal } from '@angular/core';
import { Text, View } from '@ng-native/components';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuInputComponent } from '@neural-ui/native/input';

@Component({
  selector: 'app-profile-editor',
  imports: [View, Text, NeuInputComponent, NeuButtonComponent],
  template: `
    <view class="form">
      <neu-input label="Display name" [(value)]="draft" [disabled]="saving()" />
      @if (error()) { <text accessibilityRole="alert">{{ error() }}</text> }
      @if (saved()) { <text accessibilityLiveRegion="polite">Saved: {{ saved() }}</text> }
      <view class="actions">
        <neu-button [label]="error() ? 'Retry' : 'Save'" [loading]="saving()"
          [disabled]="!canSave()" (pressed)="save()" />
        <neu-button label="Cancel edits" variant="text" [disabled]="saving()"
          (pressed)="reset()" />
      </view>
    </view>
  `,
  styles: '.form { gap: 16px; } .actions { gap: 8px; align-items: flex-start; }',
})
export class ProfileEditorComponent {
  readonly draft = signal('Alex Morgan');
  readonly saved = signal('');
  readonly saving = signal(false);
  readonly error = signal('');
  readonly canSave = computed(() => !!this.draft().trim() && !this.saving());
  private readonly lifetime = inject(DestroyRef);
  private request?: AbortController;

  constructor() {
    this.lifetime.onDestroy(() => this.request?.abort());
  }

  reset() {
    this.draft.set(this.saved() || 'Alex Morgan');
    this.error.set('');
  }

  async save() {
    if (!this.canSave()) return;
    const name = this.draft().trim();
    const request = new AbortController();
    this.request = request;
    this.saving.set(true);
    this.error.set('');
    try {
      // Replace with your authenticated API; the server validates and persists.
      const response = await fetch('https://api.example.com/profile', {
        method: 'PATCH', signal: request.signal,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name }),
      });
      if (!response.ok) throw new Error('Save failed');
      if (!this.lifetime.destroyed) this.saved.set(name);
    } catch {
      if (!this.lifetime.destroyed && !request.signal.aborted) {
        this.error.set('Could not save. Your edits are still here; try again.');
      }
    } finally {
      if (!this.lifetime.destroyed) this.saving.set(false);
      if (this.request === request) this.request = undefined;
    }
  }
}

loading pertenece a la pantalla y se restablece en finally. El botón lo recibe; no inicia ni guarda una petición automáticamente. En signals de objetos o arrays, sustituye el valor mediante set o update en vez de mutarlo sin notificar el cambio.