Linking ngModel to a Dynamic List of Checkboxes in Angular 2 Using Typescript

Uncertainty surrounds the correct method for binding and updating a model when dealing with dynamically generated checkboxes in an ASP.NET Core project with Angular 2. The struggle extends to even basic checkbox elements, as observed through personal experience.

Here's a condensed code snippet featuring facilities and timeslots. Each facility can house multiple timeslots, but the issue arises when all timeslots are checked instead of just those associated with the facility. The expected ngModel binding isn't functioning as desired.

  1. The quest to have only relevant timeslots checked within a facility persists. How should this be rectified?
  2. Regarding binding the checked items to the ngModel property 'timeslotids', could the creation of an array property and manual manipulation prove more effective than relying solely on ngModel?
  3. Moreover, struggles persist with simple checkboxes (like 'haselectric') not getting ticked off when necessary. Could there be a missing import that could resolve these issues collectively?

CODE EXPLANATION: Two interfaces ('IFacility' and 'ITimeslot') sourced from an API are simplified here:

export interface IFacility {   
   id: number,
   name: string,
   haselectric: boolean, 
   timeslotids: number[],    
   timeslots: ITimeslot[]
}
export interface ITimeslot {
    id: number,
    timeslotname: string    
}

Timeslots JSON data:

[{"id":1,"timeslotname":"Daily"},{"id":2,"timeslotname":"Hourly"},{"id":4,"timeslotname":"Market"}]

Prior to modification, a single Facility’s JSON data looks like:

{"id":2,"name":"Clements Building","haselectric":true,"timeslotids":[1],"timeslots":[{"id":1,"timeslotname":"Daily"}]}

Facility editing component (facility.component.html):

<form #form="ngForm" (ngSubmit)="submitForm()" *ngIf="formactive">
   <input type="hidden" [(ngModel)]="updatedfacility.id" #id="ngModel" name="id" />
    <div class="form-group">
         <label for="name">Facility Name *</label>
         <input type="text" class="form-control input-lg" [(ngModel)]="updatedfacility.name" #name="ngModel" name="name" placeholder="Facility Name *">
   </div>
   <div class="form-group">
                    <label for="exhibitspaces">Has Electric *</label>
                    <input type="checkbox" class="form-control input-lg" [(ngModel)]="updatedfacility.haselecric" #haselectric="ngModel" name="haselectric">
    </div>
    <div class="form-group">
        <label for="timeslots">Select Timeslots Available for Rent *</label>
        <div *ngIf="dbtimeslots" >
            <span *ngFor="let timeslot of dbtimeslots" class="checkbox">
                <label for="timeslot">
                    <input type="checkbox" [(ngModel)]="updatedfacility.timeslotids" value="{{timeslot.id}}" name="{{timeslot.id}}" [checked]="updatedfacility.timeslotids.indexOf(timeslot.id)" />{{timeslot.timeslotname}}
                 </label>
             </span>
        </div>
    </div>
    <button type="submit" class="btn btn-lg btn-default" [disabled]="form.invalid">Update</button> 
</form>

Component code (facility.component.ts)

import { Component, OnInit, Input, Output, OnChanges, EventEmitter  } from '@angular/core';
// Additional imports…
@Component({
   template: require('./facility.component.html')
})
export class FacilityDetailsComponent {
// Class variables…
  constructor(
    private _route: ActivatedRoute,
    private _router: Router,
    private _facilityservice: FacilityService,
    private _timeslotservice: TimeslotService
  ) {}

  // Relevant methods…

Final Notes: A practice of utilizing separate 'id' arrays for passing information to APIs is explained. This question has brought up some similar inquiries without offering concrete solutions yet.

Returns: ngmodel binding with dynamic array of checkbox in angular2 Get values from a dynamic checkbox list Angular 2: Get Values of Multiple Checked Checkboxes

Answer №1

Thanks to the helpful response from UFO above, I was able to find the solution to my query. Much appreciated!

<div class="form-group">
    <label for="timeslots">Choose Available Timeslots for Rental *</label>
    <div *ngIf="dbtimeslots">
        <span *ngFor="let timeslot of dbtimeslots" class="checkbox">
            <label>
                <input type="checkbox" value="{{timeslot.id}}" name="{{timeslot.id}}"  [checked]="(facility.timeslotids && (-1 !== facility.timeslotids.indexOf(timeslot.id)) ? 'checked' : '')" (change) ="updateSelectedTimeslots($event)" />{{timeslot.timeslotname}}
            </label>
         </span>
    </div>
</div>

Next is the function in the component:

updateSelectedTimeslots(event) {
    if (event.target.checked) {
          if (this.facility.timeslotids.indexOf(parseInt(event.target.name)) < 0) { 
                this.facility.timeslotids.push(parseInt(event.target.name));

          }
     } else {
           if (this.facility.timeslotids.indexOf(parseInt(event.target.name)) > -1) 
            {
                this.facility.timeslotids.splice(this.facility.timeslotids.indexOf(parseInt(event.target.name)), 1);              
            }
    }
     //console.log("TimeslotIDs: ", this.facility.timeslotids);    
}

Answer №2

In my code, I have a similar situation to yours where I am using ngFor to iterate over all the checkboxes with some of them pre-checked. The changes made need to be saved in a separate data structure that is not part of the iteration loop. Here is how I tackled this issue:

<md-checkbox 
    *ngFor="let item of availableItems"
    name="{{ item }}"
    checked="{{ data.items && -1 !== this.data.items.indexOf(item) ? 'checked' : '' }}"
    (change)="updateItem($event)">
    {{ item }}
</md-checkbox>

This is the function updateItem that handles the checkbox updates:

updateItem(event) {
  if (-1 !== this.availableSkills.indexOf(event.source.name)) {
    if (event.checked) {
      this.data.items.push(event.source.name);
    } else {
      this.data.items.splice(this.data.items.indexOf(event.source.name), 1);
    }
  }
}

I must note that it should ideally be event.target, but in this case, it's event.source due to material design conventions. While this solution may seem a bit cumbersome, I believe it will guide you in achieving your desired outcome.

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

When utilizing destructuring in React.js with TypeScript, incorrect prop values are not being alerted as expected

I recently started using TypeScript and I have been enjoying it so far. However, I came across an issue today that I couldn't solve. Imagine a scenario where a parent component A passes a function that expects a numeric value to the child component B ...

I am facing issues with my Angular CRUD application when trying to update data through the API, as it is not functioning properly as a Single

After successfully implementing the CRUD Application In Angular Using API, I encountered a slight issue. When updating values, they do not reflect instantly without reloading the page. Here's a glimpse into my code: Below is my app.component.ts: imp ...

Modify a particular attribute in an array of objects

I am currently working on an Angular project and dealing with the following array object: { "DATA": [ { "CUSTOM1": [ { "value": "Item1", ...

Creating a non-editable form or text field upon clicking the Submit button

<form [formGroup]="calculateForm"> <div class="form-group row"> <p for="inputFrom" class="col-sm-4">Distance traveled ...

Ways to eliminate toggle event in Angular

I've been doing a lot of research online, but all the solutions I find are using jquery. I'm still getting the hang of Angular and Typescript. I found this and this to be unhelpful. I built a monthpicker from scratch, which has a simple and clear ...

Using RouterModule.forRoot() in Angular 2 CLI compiler/ngc command: A step-by-step guide

Assume I have the appRoutingModule shown below: export const routes: Route[] = [ { path: '', component: ApplicationlistComponent } ]; @NgModule( { imports: [ ApplicationsModule, RouterModule.forRoot( routes ) ], exports: [ Route ...

Angular 2+ seems to be failing to detect and update changes in variables within the template

I have a component that includes rendering the user's name from the profile object. The corresponding part of the template looks like this: <button mat-button [matMenuTriggerFor]="userMenu" *ngIf="isAuthenticated()"> {{profile?.name} ...

Adjusting ES2015 Map to accommodate the expected functionality

Exploring the capabilities of ES2015 Maps has been quite exciting, as I'm starting to see its potential. However, I've encountered a use case that has me stumped on whether Maps can handle it. Let's take a look at my class: class A { ...

Switching the keyboard language on the client side of programming languages

I'm interested in altering the keyboard language when an input element changes. Is it possible to modify the keyboard language using client-side programming languages? And specifically, can JavaScript be used to change the keyboard language? ...

Error: Missing provider for MatBottomSheetRef

While experimenting in this StackBlitz, I encountered the following error message (even though the MatBottomSheetModule is imported): ERROR Error: StaticInjectorError(AppModule)[CountryCodeSelectComponent -> MatBottomSheetRef]: S ...

React-Admin error: Attempting to invoke a built-in Promise constructor without using the new keyword is not allowed

I'm currently facing an issue where I am trying to retrieve data using a hook. Strangely, there are no TypeScript errors appearing, but when I run the code, a console error pops up stating "Uncaught TypeError: calling a builtin Promise constructor wit ...

Encountered an issue finding element with Protractor - Error: script timed out after 20 seconds without receiving a response

I recently created a basic website using Angular 6 and I am facing some challenges while writing e2e tests for it. The first script is causing a lot of trouble for me as it throws the following error: C:\Users\user\Documents\workspace- ...

The Angular Material nested Stepper's label position is being forcefully changed

When dealing with a nested Angular Material Stepper, the label position in the child stepper (labelPosition="end") may be overridden by the label position set in the parent stepper (labelPosition="bottom"). Check out the code snippet be ...

Encountering a typescript error while configuring options in an Angular 8 application

When attempting to call a service using REST API with a GET request, I encountered the following error: Argument of type '{ headers: HttpHeaders; responseType: string; }' is not assignable to parameter of type '{ headers?: HttpHeaders | ...

Utilizing global enumerations within VueJS

Is there a way to effectively utilize global enums in Vue or declare them differently? My current setup is as follows: Within my types/auth.d.ts: export {}; declare global { enum MyEnum { some = "some", body = "body", o ...

Adding Dynamic Controls in Angular 4: A Guide

For this illustration, I've put together a compact form that allows me to introduce dynamic controls by simply clicking on the "Add Pre-Phase" button. Once a few pre-phases are added, I will designate a phase type, and if the chosen value is EMS, the ...

The provided argument, which is of type 'RefObject<HTMLDivElement>', cannot be assigned to the parameter of type 'IDivPosition'

Currently, I am implementing Typescript with React. To organize my code, I've created a separate file for my custom function called DivPosition.tsx. In this setup, I am utilizing useRef to pass the reference of the div element to my function. However ...

What is the reason for the allowance of numeric keys in the interface extension of Record<string, ...>

I am currently working on a method to standardize typing for POST bodies and their corresponding responses with API routes in my Next.js application. To achieve this, I have created an interface that enforces the inclusion of a body type and a return type ...

"React Bootstrap column showing gaps on the sides instead of filling the entire

In my current project with React and React Bootstrap 4.0, I am aiming to create a collapsible side-bar of 300px width on the left side of the screen, with the content displayed in the remaining space. The main parent component for the side-bar and content ...

Retrieving results from PostgreSQL database using pagination technique

When I'm pagination querying my data from a PostgreSQL database, each request involves fetching the data in this manner: let lastNArticles: Article[] = await Article.findAll({ limit: +req.body.count * +req.body.page, or ...