How to Transfer Deleted List Items from one Unordered List to another in Angular 9 using Event Binding

Greetings to all =) I've recently delved into Angular 9 and I'm really enjoying the component-based approach. To sharpen my skills in property and event binding, I embarked on a project involving building a deck with two lists. English isn't my first language, but I'll do my best to explain clearly.

IMPORTANT: Since I'm keen on mastering this tool, could you please provide detailed explanations in your responses? If you have hints or solutions, I'm open to exploring them independently for learning purposes.

GOAL:

The aim is to have two lists (CardlistComponent and DecklistComponent) where clicking on an item in CardListComponent (or DecklistComponent) removes it from that list and adds it to the other list.

COMPONENTS :

  1. DeckbuilderComponent containing the two other components (the two lists)
  2. CardlistComponent (the first list)
  3. DecklistComponent (the second list)

ISSUE:

I can successfully remove an element from CardlistComponent, but I'm struggling to add the same element to DecklistComponent. I believe the problem lies in how I handle arrays in JavaScript.

DeckbuilderComponent (HTML) (Parent component)

<app-cardlist></app-cardlist>
<app-decklist
  (cardAdded)="onCardAdded($event)">
</app-decklist>

*DeckbuilderComponent (TS)* (Parent component) The issue might be related here as I've duplicated the logic of adding an item in the parent AND in DecklistComponent.

@Output() cardAdded = new EventEmitter<{name: string}>();

decklist: Card[] = [
new Card('CardC'),
new Card('CardD')
]

onCardAdded(element : HTMLLIElement){
this.decklist.push({
name: element.textContent
})
}

CardlistComponent

@Output() cardRemoved = new EventEmitter<{cardName: string}>();

cardlist: Card[] = [
new Card('CardA'),
new Card('CardB')
]

removeCard(indexCard: number, element: HTMLLIElement){
this.cardlist.splice(indexCard, 1)
this.cardRemoved.emit({
cardName: element.textContent
});

DecklistComponent - this list should receive the element removed from the first list

@Input() cardName: {name: string};

decklist: Card[] = [
new Card('CardC'),
new Card('CardD')
]

onCardAdded(element : HTMLLIElement){ 
this.decklist.push({
name: element.textContent
})
}

Here's the HTML code for my two components just in case.

DecklistComponent (HTML)

<div class="container">
<div class="col-xs-12">
    <ul class="list-group">
        <li
        *ngFor="let card of decklist; let indexCard=index"
        class="list-group-item"
        #cardName
        >
            <a href="#"  class="list-group-item list-group-item-action" >{{ card.name }}</a>
        </li>
    </ul>
</div>

CardlistComponent (HTML)

<div class="container">
<div class="col-xs-12">
    <ul class="list-group">
        <li *ngFor="let card of cardlist; let indexCard=index" class="list-group-item">
            <a href="#"
            (click)="removeCard(indexCard, card)"
            class="list-group-item list-group-item-action">{{ card.name }}</a>
        </li>
    </ul>
</div>

If there's anything else important that I missed mentioning, please feel free to share, and may your day be filled with enthusiastic coding =D

Answer №1

To enhance your application, consider structuring DeckbuilderComponent as a Container Component and

CardlistComponent, DecklistComponent
as Presentational Components.

source

Container Components: These components are responsible for fetching data from the service layer. Typically, the top-level component of a route serves as a Container Component, hence the naming convention

Presentational Components - these components simply receive data as input and handle its display on the UI. They also have the ability to emit custom events

<deck-list (remove)="removeAndMoveTo($event, deckList, cardList)" [items]="deckList"></deck-list>
<card-list (remove)="removeAndMoveTo($event, cardList, deckList)" [items]="cardList"></card-list>
<div class="container">
<div class="col-xs-12">
    <ul class="list-group">
        <li *ngFor="let card of cardlist; let indexCard=index" class="list-group-item">
            <a href="#"
            (click)="removeCard(card)"
            class="list-group-item list-group-item-action">{{ card.name }}</a>
        </li>
    </ul>
</div>
class CardlistComponent {

  @Output("remove") removeCardEmitter: EventEmitter<Card>;

  constructor() {
    this.removeCardEmitter = new EventEmitter();
  }

  removeCard(card: Card){
    this.removeCardEmitter.emit(card);
  };

}
class DeckbuilderComponent {

  cardList: Card[];
  decklist: Card[];

  constructor() {
    this.cardList = [
      new Card('CardA'),
      new Card('CardB')
    ];

    this.decklist = [
      new Card('CardC'),
      new Card('CardD')
    ];
  }

  removeAndMoveTo(card: Card, sourceList: Card[], targetList: Card[]) {
    this.sourceList = this.sourceList.filter(pr => pr.id === card.id);
    this.targetList.push(card);
  }
}

There are various methods to transfer elements between lists. Alternatively, each card could contain information on which group it belongs to, resulting in a simple filter '

[items]="list.filter(pr => pr.isInCardList)
'.

Another approach involving a single-list setup might utilize rxjs with an observable that can be divided using the partition operator.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is searching for duplicate entries in an array using a specific key?

Below is an array structure: [ { "Date": "2020-07", "data": [ { "id": "35ebd073-600c-4be4-a750-41c4be5ed24a", "Date": "2020-07-03T00:00:00.000Z", ...

Compelled to utilize unfamiliar types in TypeScript generics

When working with a Typescript React form builder, I encountered a situation where each component had different types for the value and onChange properties. To tackle this issue, I decided to utilize generics so that I could define the expected types for e ...

What is the process for incorporating the jsnetworkx library into an ionic or angular 4 project?

When using any Ionic3 app service import * as jsnx from 'jsnetworkx'; The output error message is: Uncaught (in promise): Error: Cannot find module "lodash/lang/isPlainObject" Error: Cannot find module "lodash/lang/isPlainObject" at webpackMis ...

"Unraveling the depths of a multidimensional array in JavaScript: a comprehensive guide

Seeking assistance from this wonderfully helpful community :) It seems like I might be declaring and creating my arrays incorrectly, as I am trying to add content to an array of arrays but unable to retrieve anything from it. Here's the code snippet ...

Issue: AuthInterceptor Provider Not Found

I've been attempting to set up an http interceptor with the new HttpClient in Angular 4.3, but I'm continuously encountering this error message: ERROR Error: Uncaught (in promise): Error: No provider for AuthInterceptor! In my app.module.ts f ...

What is the best way to pass a state within a route component in react-router?

... import { useNavigate, NavigateFunction } from "react-router"; ... function Form(): JSX.Element { const navigateToCountry = (country: string) => { // Code to navigate to country page with the given country } const [selectedCount ...

Transform the array into an associative array

Below is the array I am working with: print_r($data); When printed in PHP, the result looks like this: $data=Array( [0] => stdClass Object ( [name] => location [value] =>lko ) [1] => stdClass Object ( [n ...

Ways to import a library in JavaScript/TypeScript on a web browser?

I'm currently working on a project that involves a TypeScript file and an HTML page. Right now, I am loading the necessary libraries for the TypeScript file in the HTML Page using script tags like <script src="https://unpkg.com/<a href="/cd ...

Customize the appearance of Angular components by altering their color schemes

When working on my project, I often utilize pre-made Angular components. However, some of these components come with colors that do not align with the overall aesthetic of my project (refer to image above). To rectify this issue, I am looking to replace t ...

Adjusting the selection in the Dropdown Box

I've been attempting to assign a value to the select box as shown below: <dx-select-box [items]="reportingProject" id="ReportingProj" [text]="reportingProject" [readOnly]="true" > ...

An object may be null when its type is A or undefined, but we are certain it is not undefined

Since the release of version 4.8.4, the TypeScript compiler has been flagging an issue with the following code: type A = {v: number} function get_the_first<T>(xs: T[]): T | undefined { if (xs.length > 1) return xs[0]; else ...

Can an excessive amount of classes cause my Angular application to run sluggishly?

Within my Angular 7 application, I have generated approximately 200 to 300 classes for model types (e.g. component.model.ts) solely for type checking purposes. I have not instantiated any objects from these classes. As I navigate through the application, ...

Basic HTML Audio Player Featuring Several Customizable Variables

I have a unique API that manages music playback. Instead of playing audio in the browser, it is done through a Discord bot. Achievement Goal https://i.stack.imgur.com/w3WUJ.png Parameters: current: indicates the current position of the track (e.g. 2:3 ...

Utilize TypeScript function types in React for enhanced functionality

I have made the decision to refactor a project that was originally created with vanilla JavaScript and now I want to transition it to TypeScript. One issue I am facing is how to pass a function as a type on an interface. Although I referred to the TypeScr ...

The service fails to recognize the ActivatedRoute

Using ActivatedRoute in Services The Challenge Attempting to utilize ActivatedRoute within a service, I encountered an issue where it was not tracking the current route accurately. It seemed unable to detect any route at all. After spending considerable ...

Finding a solution for duplicate date selections in NextJS using react-calendar

I am currently working on a calendar component using NextJS, typescript, tailwindcss, and the react-calendar library. I have encountered an issue with duplicate dates appearing in the calendar when selecting a date range. Although I have managed to handle ...

Can TypeScript restrict a callback parameter based on the type of another parameter using generics?

I am currently developing an event manager system. The main objective is to allow users to subscribe to events by providing an event type and a callback function. In my implementation, events are represented as classes, where AwesomeEventType in the exampl ...

Creating custom disabled button styles using TailwindUI in a NextJS application

I had a NextJS application that utilized Atomic CSS and featured a button which becomes disabled if a form is left unfilled: <Button className="primary" onClick={handleCreateCommunity} disabled={!phone || !communi ...

Mongoose failing to persist subdocument

After trying to insert into my collection, I noticed that the sub-document is not being saved along with it. This issue has left me puzzled. This is the scheme/model I am working with: import { Schema, Document, Model, model } from 'mongoose' ...

Just made the switch to Mongoose 5.12 and hit a snag - unable to use findOneAndUpdate with the $push operator

After upgrading to Mongoose 5.12 from 5.11 and incorporating Typescript, I encountered an issue with my schema: const MyFileSchema = new Schema<IMyFile>({ objectID: { type: String, required: true }, attachments: { type: Array, required: false ...