Heim > Web-Frontend > js-Tutorial > Erstellen einer URL-Shortener-App mit Angular und Tailwind CSS

Erstellen einer URL-Shortener-App mit Angular und Tailwind CSS

WBOY
Freigeben: 2024-07-26 18:53:31
Original
1127 Leute haben es durchsucht

Building a URL Shortener App with Angular and Tailwind CSS

In diesem Blog führen wir Sie durch den Prozess der Erstellung einer URL-Shortener-Anwendung mit Angular für das Frontend und Tailwind CSS für das Styling. Ein URL-Shortener ist ein praktisches Tool, das lange URLs in kürzere, besser verwaltbare Links umwandelt. Dieses Projekt wird Ihnen helfen zu verstehen, wie Sie mithilfe moderner Webentwicklungstechnologien eine funktionale und ästhetisch ansprechende Webanwendung erstellen.

Voraussetzungen

Um diesem Tutorial folgen zu können, sollten Sie über grundlegende Kenntnisse von Angular und etwas Vertrautheit mit Tailwind CSS verfügen. Stellen Sie sicher, dass Node.js und Angular CLI auf Ihrem Computer installiert sind.

Projekt-Setup

1. Erstellen eines neuen Angular-Projekts

Erstellen Sie zunächst ein neues Angular-Projekt, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

ng new url-shortener-app
cd url-shortener-app
Nach dem Login kopieren

2. Tailwind CSS installieren

Als nächstes richten Sie Tailwind CSS in Ihrem Angular-Projekt ein. Installieren Sie Tailwind CSS und seine Abhängigkeiten über npm:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
Nach dem Login kopieren

Konfigurieren Sie Tailwind CSS, indem Sie die Datei tailwind.config.js aktualisieren:

module.exports = {
  content: [
    "./src/**/*.{html,ts}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
Nach dem Login kopieren

Fügen Sie die Tailwind-Anweisungen zu Ihrer Datei src/styles.scss hinzu:

@tailwind base;
@tailwind components;
@tailwind utilities;
Nach dem Login kopieren

Erstellen des URL-Shorteners

3. Erstellen des URL-Modells

Erstellen Sie ein URL-Modell, um die Struktur der URL-Daten zu definieren. Fügen Sie eine neue Datei src/app/models/url.model.ts:
hinzu

export type Urls = Url[];

export interface Url {
  _id: string;
  originalUrl: string;
  shortUrl: string;
  clicks: number;
  expirationDate: string;
  createdAt: string;
  __v: number;
}
Nach dem Login kopieren

4. Einrichten des URL-Dienstes

Erstellen Sie einen Dienst zur Verarbeitung von API-Aufrufen im Zusammenhang mit der URL-Verkürzung. Fügen Sie eine neue Datei src/app/services/url.service.ts:
hinzu

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Url, Urls } from '../models/url.model';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root',
})
export class UrlService {
  private apiUrl = environment.apiUrl;

  constructor(private http: HttpClient) {}

  shortenUrl(originalUrl: string): Observable<Url> {
    return this.http.post<Url>(`${this.apiUrl}/shorten`, { originalUrl });
  }

  getAllUrls(): Observable<Urls> {
    return this.http.get<Urls>(`${this.apiUrl}/urls`);
  }

  getDetails(id: string): Observable<Url> {
    return this.http.get<Url>(`${this.apiUrl}/details/${id}`);
  }

  deleteUrl(id: string): Observable<Url> {
    return this.http.delete<Url>(`${this.apiUrl}/delete/${id}`);
  }
}
Nach dem Login kopieren

5. Erstellen der Shorten-URL-Komponente

Generieren Sie eine neue Komponente zum Kürzen von URLs:

ng generate component shorten
Nach dem Login kopieren

Aktualisieren Sie den HTML-Code der Komponente (src/app/shorten/shorten.component.html) wie unten gezeigt:

<div class="max-w-md mx-auto p-4 shadow-lg rounded-lg mt-4">
    <h2 class="text-2xl font-bold mb-2">URL Shortener</h2>
    <form [formGroup]="urlForm" (ngSubmit)="shortenUrl()">
        <div class="flex items-center mb-2">
            <input class="flex-1 p-2 border border-gray-300 rounded mr-4" formControlName="originalUrl"
                placeholder="Enter your URL" required />
            <button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" type="submit">
                Shorten
            </button>
        </div>
        @if (urlForm.get('originalUrl')?.invalid && (urlForm.get('originalUrl')?.dirty ||
        urlForm.get('originalUrl')?.touched)) {
        <div class="text-red-500" role="alert" aria-live="assertive">
            @if (urlForm.get('originalUrl')?.errors?.['required']) {
            URL is required.
            }
            @if (urlForm.get('originalUrl')?.errors?.['pattern']) {
            Invalid URL format. Please enter a valid URL starting with http:// or https://.
            }
        </div>
        }
    </form>
    @if (errorMsg) {
    <div class="p-4 bg-red-100 rounded mt-4">
        <p class="text-red-500">{{ errorMsg }}</p>
    </div>
    }
    @if (shortUrl) {
    <div class="p-4 bg-green-100 rounded">
        <p>Shortened URL: <a class="text-blue-500 hover:text-blue-600" [href]="redirectUrl + shortUrl"
                target="_blank">{{ shortUrl }}</a>
            <button class="ml-2 px-2 py-1 bg-gray-200 text-gray-800 border border-slate-950 rounded hover:bg-gray-300"
                (click)="copyUrl(redirectUrl + shortUrl)">Copy</button>
            @if (copyMessage) {
            <span class="text-green ml-2">{{ copyMessage }}</span>
            }
        </p>
    </div>
    }
</div>

<div class="max-w-md mx-auto mt-4 p-2">
    <h2 class="text-2xl font-bold mb-4">All URLs</h2>
    @if (isloading) {
    <div class="max-w-md mx-auto p-4 shadow-lg rounded-lg">
        <div class="text-center p-4">
            Loading...
        </div>
    </div>
    }
    @else if (error) {
    <div class="max-w-md mx-auto p-4 shadow-lg rounded-lg">
        <div class="text-center p-4">
            <p class="text-red-500">{{ error }}</p>
        </div>
    </div>
    }
    @else {
    @if (urls.length > 0 && !isloading && !error) {
    <ul>
        @for (url of urls; track $index) {
        <li class="p-2 border border-gray-300 rounded mb-2">
            <div class="flex justify-between items-center">
                <div>
                    URL:
                    <a class="text-blue-500 hover:text-blue-600" [href]="redirectUrl + url.shortUrl" target="_blank">{{
                        url.shortUrl }}</a>
                </div>
                <div class="flex justify-between items-center">
                    <button class="px-2 py-1 bg-blue-200 text-blue-800 rounded hover:bg-blue-300"
                        (click)="showDetails(url.shortUrl)">Details</button>
                    <button class="ml-2 px-2 py-1 bg-gray-200 text-gray-800 rounded hover:bg-gray-300"
                        (click)="copyListUrl(redirectUrl + url.shortUrl, $index)">{{
                        copyIndex === $index ? 'Copied' : 'Copy'
                        }}</button>
                    <button class="ml-2 px-2 py-1 bg-red-200 text-red-800 rounded hover:bg-red-300"
                        (click)="prepareDelete(url.shortUrl)">Delete</button>
                </div>
            </div>
        </li>
        }
    </ul>
    }
    @else {
    <div class="max-w-md mx-auto p-4 shadow-lg rounded-lg">
        <div class="text-center p-4">
            No URLs found.
        </div>
    </div>
    }
    }
</div>

@if (showDeleteModal) {
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
    <div class="bg-white p-4 rounded shadow-lg">
        <h3 class="text-xl font-bold mb-2">Confirm Deletion</h3>
        <p class="mb-4">Are you sure you want to delete this URL?</p>
        <button class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600" (click)="confirmDelete()">Yes,
            Delete</button>
        <button class="px-4 py-2 bg-gray-300 text-gray-800 rounded hover:bg-gray-400 ml-2"
            (click)="showDeleteModal = false">Cancel</button>
    </div>
</div>
}

@if (showDetailsModal) {
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
    <div class="bg-white p-4 rounded shadow-lg">
        <h3 class="text-xl font-bold mb-2">URL Details</h3>
        @if (isLoading) {
        <p class="mb-4">Loading...</p>
        }
        @else {
        <p class="mb-4">Short URL: <a class="text-blue-500 hover:text-blue-600"
                [href]="redirectUrl + selectedUrl.shortUrl" target="_blank">{{ selectedUrl.shortUrl }}</a></p>
        <p class="mb-4">Original URL: <a class="text-blue-500 hover:text-blue-600" [href]="selectedUrl.originalUrl"
                target="_blank">{{ selectedUrl.originalUrl }}</a></p>
        <p class="mb-4">Clicks: <span class="text-green-500">{{ selectedUrl.clicks }}</span></p>
        <p class="mb-4">Created At: {{ selectedUrl.createdAt | date: 'medium' }}</p>
        <p class="mb-4">Expires At: {{ selectedUrl.expirationDate | date: 'medium' }}</p>
        <button class="px-4 py-2 bg-gray-300 text-gray-800 rounded hover:bg-gray-400"
            (click)="showDetailsModal = false">Close</button>
        }
    </div>
</div>
}
Nach dem Login kopieren

6. Hinzufügen von Logik zur Komponente

Aktualisieren Sie die TypeScript-Datei der Komponente (src/app/shorten/shorten.component.ts), um Formularübermittlungen und API-Interaktionen zu verarbeiten:

import { Component, inject, OnInit } from '@angular/core';
import { UrlService } from '../services/url.service';
import {
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import { Url } from '../models/url.model';
import { environment } from '../../environments/environment';
import { DatePipe } from '@angular/common';
import { Subject, takeUntil } from 'rxjs';

@Component({
  selector: 'app-shorten',
  standalone: true,
  imports: [DatePipe, ReactiveFormsModule],
  templateUrl: './shorten.component.html',
  styleUrl: './shorten.component.scss',
})
export class ShortenComponent implements OnInit {
  shortUrl: string = '';
  redirectUrl = environment.apiUrl + '/';
  copyMessage: string = '';
  copyListMessage: string = '';
  urls: Url[] = [];
  showDeleteModal = false;
  showDetailsModal = false;
  urlToDelete = '';
  copyIndex: number = -1;
  selectedUrl: Url = {} as Url;
  isLoading = false;
  isloading = false;
  error: string = '';
  errorMsg: string = '';
  urlForm: FormGroup = new FormGroup({});
  private unsubscribe$: Subject<void> = new Subject<void>();

  urlService = inject(UrlService);

  ngOnInit() {
    this.urlForm = new FormGroup({
      originalUrl: new FormControl('', [
        Validators.required,
        Validators.pattern('^(http|https)://.*$'),
      ]),
    });
    this.getAllUrls();
  }

  shortenUrl() {
    if (this.urlForm.valid) {
      this.urlService.shortenUrl(this.urlForm.value.originalUrl).pipe(takeUntil(this.unsubscribe$)).subscribe({
        next: (response) => {
          // console.log('Shortened URL: ', response);
          this.shortUrl = response.shortUrl;
          this.getAllUrls();
        },
        error: (error) => {
          console.error('Error shortening URL: ', error);
          this.errorMsg = error?.error?.message || 'An error occurred!';
        },
      });
    }
  }

  getAllUrls() {
    this.isloading = true;
    this.urlService.getAllUrls().pipe(takeUntil(this.unsubscribe$)).subscribe({
      next: (response) => {
        // console.log('All URLs: ', response);
        this.urls = response;
        this.isloading = false;
      },
      error: (error) => {
        console.error('Error getting all URLs: ', error);
        this.isloading = false;
        this.error = error?.error?.message || 'An error occurred!';
      },
    });
  }

  showDetails(id: string) {
    this.showDetailsModal = true;
    this.getDetails(id);
  }

  getDetails(id: string) {
    this.isLoading = true;
    this.urlService.getDetails(id).subscribe({
      next: (response) => {
        // console.log('URL Details: ', response);
        this.selectedUrl = response;
        this.isLoading = false;
      },
      error: (error) => {
        console.error('Error getting URL details: ', error);
        this.error = error?.error?.message || 'An error occurred!';
      },
    });
  }

  copyUrl(url: string) {
    navigator.clipboard
      .writeText(url)
      .then(() => {
        // Optional: Display a message or perform an action after successful copy
        console.log('URL copied to clipboard!');
        this.copyMessage = 'Copied!';
        setTimeout(() => {
          this.copyMessage = '';
        }, 2000);
      })
      .catch((err) => {
        console.error('Failed to copy URL: ', err);
        this.copyMessage = 'Failed to copy URL';
      });
  }

  copyListUrl(url: string, index: number) {
    navigator.clipboard
      .writeText(url)
      .then(() => {
        // Optional: Display a message or perform an action after successful copy
        console.log('URL copied to clipboard!');
        this.copyListMessage = 'Copied!';
        this.copyIndex = index;
        setTimeout(() => {
          this.copyListMessage = '';
          this.copyIndex = -1;
        }, 2000);
      })
      .catch((err) => {
        console.error('Failed to copy URL: ', err);
        this.copyListMessage = 'Failed to copy URL';
      });
  }

  prepareDelete(url: string) {
    this.urlToDelete = url;
    this.showDeleteModal = true;
  }

  confirmDelete() {
    // Close the modal
    this.showDeleteModal = false;
    // Delete the URL
    this.deleteUrl(this.urlToDelete);
  }

  deleteUrl(id: string) {
    this.urlService.deleteUrl(id).subscribe({
      next: (response) => {
        // console.log('Deleted URL: ', response);
        this.getAllUrls();
      },
      error: (error) => {
        console.error('Error deleting URL: ', error);
        this.error = error?.error?.message || 'An error occurred!';
      },
    });
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

Nach dem Login kopieren

7. Aktualisieren Sie die HTML-Datei der App-Komponente (src/app/app.component.html).

<router-outlet></router-outlet>
Nach dem Login kopieren

8. Aktualisieren Sie die App-Konfigurationsdatei (src/app/app.config.ts)

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient(),
  ],
};
Nach dem Login kopieren

9. Aktualisieren Sie die App-Routen-Datei (src/app/app.routes.ts).

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./shorten/shorten.component').then((m) => m.ShortenComponent),
  },
];
Nach dem Login kopieren

Abschluss

Sie haben erfolgreich eine URL-Shortener-Anwendung mit Angular und Tailwind CSS erstellt. Dieses Projekt zeigt, wie man moderne Frontend-Technologien integriert, um eine funktionale und stilvolle Webanwendung zu erstellen. Mit den leistungsstarken Funktionen von Angular und dem Utility-First-Ansatz von Tailwind CSS können Sie problemlos reaktionsfähige und effiziente Webanwendungen erstellen.

Sie können diese Anwendung gerne erweitern, indem Sie Funktionen wie Benutzerauthentifizierung usw. hinzufügen. Viel Spaß beim Codieren!

Den Code erkunden

Besuchen Sie das GitHub-Repository, um den Code im Detail zu erkunden.


Das obige ist der detaillierte Inhalt vonErstellen einer URL-Shortener-App mit Angular und Tailwind CSS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage