Using NgModel with a custom element

I am currently using a basic component within my form as shown below:

<app-slider [min]="field.min" [max]="field.max" [value]="field.min"></app-slider>

This component consists of the following code:

HTML:

<input #mySlider
       class="slider"
       type="text"
       name="slider"
       data-provide="slider"
       data-slider-min="1"
       data-slider-max="3"
       [attr.data-slider-min]="min"
       [attr.data-slider-max]="max"
       data-slider-step="1"
       [attr.data-slider-value]="value"
       data-slider-tooltip="show"/>

TypeScript (ts):

import {Component, ViewChild, Input} from '@angular/core';

declare var $ : any;

@Component({
    selector: 'app-slider',
    templateUrl: './slider.component.html',
    styleUrls: ['./slider.component.css']
})
export class SliderComponent{
    @ViewChild('mySlider') slider: any; // can be ElementRef;
    @Input() min: number;
    @Input() max: number;
    @Input() value: number;

    constructor() { }

    ngAfterViewInit() {
        // slider is available
        $(this.slider.nativeElement).slider();
        let value = $(this.slider.nativeElement).attr("data-slider-value");
        $(this.slider.nativeElement).slider('setValue', value);
    }
}

I would like to implement NgModel on my component in order to access and modify its value:

<app-slider [min]="field.min" [max]="field.max" [value]="field.min" [(ngModel)]="currentValue"></app-slider>

Is there a way for me to accomplish this?

Answer №1

Introduction: it appears that a jQuery UI slider-like tool is being utilized, albeit with some modifications. In instances where assumptions had to be made, the API of this tool was employed.

The main objective achieved here is through the implementation of ControlValueAccessor, allowing the component to be recognized by Angular as a form control or, in simpler terms, a custom form control. While I am unsure if the syntax aligns with the latest Angular practices, my approach usually looked something like this:

import { Component, ViewChild, Input, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

declare var $: any;

export const SLIDER_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SliderComponent),
    multi: true
};

@Component({
    selector: 'app-slider',
    templateUrl: './slider.component.html',
    styleUrls: ['./slider.component.css'],
    providers: [
        SLIDER_VALUE_ACCESSOR
    ]
})
export class SliderComponent implements ControlValueAccessor {
    @ViewChild('mySlider') slider: any;
    @Input() min: number;
    @Input() max: number;

    propagateChange = (_: any) => { };
    propagateTouched = () => { };

    constructor() { }

    ngAfterViewInit() {
        $(this.slider.nativeElement).slider();
    }

    onChange() {
        this.propagateChange($(this.slider.nativeElement).attr("data-slider-value"););
    }

    writeValue(value: any): void {
        $(this.slider.nativeElement).slider('setValue', value);
    }

    registerOnChange(fn: any): void {
        this.propagateChange = fn;
    }

    registerOnTouched(fn: any): void {
        this.propagateTouched = fn;
    }

    setDisabledState?(isDisabled: boolean): void {
        $(this.slider.nativeElement).slider(isDisabled ? "disable" : "enable");
    }
}

Template structure:

<input #mySlider
    class="slider"
    type="text"
    name="slider"
    data-provide="slider"
    data-slider-min="1"
    data-slider-max="3"
    [attr.data-slider-min]="min"
    [attr.data-slider-max]="max"
    data-slider-step="1"
    data-slider-tooltip="show"
    (change)="onChange" /> <!-- change event resembling jQuery UI slider  -->

Implementation example:

<app-slider [min]="field.min" [max]="field.max" [(ngModel)]="currentValue"></app-slider>

In this scenario, currentValue must be initialized with the minimum value for proper functionality based on the given example.

Answer №2

Give this a shot:

<app-slider [min]="field.min" [max]="field.max" [(value)]="currentValue"></app-slider>

Update the value of currentValue using changeCurrentValue

import {Component, ViewChild, Input, Output, EventEmitter} from '@angular/core';

declare var $ : any;

@Component({
    selector: 'app-slider',
    templateUrl: './slider.component.html',
    styleUrls: ['./slider.component.css']
})
export class SliderComponent{
    @ViewChild('mySlider') slider: any; // can be ElementRef;
    @Input() min: number;
    @Input() max: number;
    @Input() value: number;
    @Output() valueChange = new EventEmitter();
    constructor() { }

    ngAfterViewInit() {
         let self = this;
        // slider is ready
        $(this.slider.nativeElement).slider();
        let value = $(this.slider.nativeElement).attr("data-slider-value");
        $(this.slider.nativeElement).slider('setValue', value);

       $(this.slider.nativeElement).slider().on('change', function(event) {
          self.value = event.value.newValue; self.valueChange.emit(self.value); });
          }

   changeCurrentValue(){
      this.value= //insert desired value;
      this.valueChange.emit(this.value);
   } 
}

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

What steps are needed to develop a TypeScript component within Angular framework?

I've been attempting to develop an Angular Component in TypeScript. I'm trying to utilize document.createElement to build a toolbar within my component, but it's not appearing. Below is my Component code: import {Directive, Component, boot ...

Typescript may fall short in ensuring type safety for a basic reducer

I have been working on a simple reducer that uses an object to accumulate values, aiming to maximize TS inference. However, I am facing difficulties in achieving proper type safety with TypeScript. The issue arises when the empty object does not contain an ...

What could be causing my for loop to not function properly within the ngOnInit lifecycle hook?

I am attempting to create a nested loop structure in order to access an array that is inside an object within an array of objects, and then store this data into a new array. My issue arises as the first loop executes successfully but the second one does no ...

Unable to execute dockerfile on local machine

I'm currently attempting to run a Dockerfile locally for a Node TypeScript project. Dockerfile FROM node:20-alpine EXPOSE 5000 MAINTAINER Some Dev RUN mkdir /app WORKDIR /app COPY ./backend/* /app RUN npm i CMD ["npm","start"] However, I encoun ...

Is there a way to alter the date format for input elements within formGroups using Angular 7?

When the input is of type 'Date', the date format is dd/MM/yyyy. I need to convert the date format from MM/dd/yyyy to dd/MM/yyyy (Turkish Format and Turkish Calendar). Below is the code snippet. <form [formGroup]="opportunityForm" (ngSubmit ...

Is there a way to bypass the "Error: Another application is currently displaying over Chrome" message using Javascript or Typescript?

Can the "Another app is displaying over chrome error" be bypassed using JavaScript or TypeScript? Error Message: https://i.stack.imgur.com/iSEuk.png ...

What is the best way to call a method within a TypeScript class using its name as a string while already inside the class itself?

Currently, I am developing a class that automates the creation of routes for Express and invokes a function in a controller. However, upon trying to execute this[methodName](req, res), I come across an error message stating: 'Element implicitly has an ...

Difficulties in Networking Requests Following Event Emitter Notification in an Angular Application

Within my Angular application, a network request is sent to retrieve filtered data based on user-selected filters. The function responsible for handling the filter values and executing the request is outlined as follows: public onFilterReceived(values) { ...

Having trouble debugging localhost because the cookie for a specific domain is not being written

In a particular scenario, I needed to write a cookie upon logging in with a code for a specific domain, for example, let's say the domain is "uat.example.com." The backend API will generate this cookie after authenticating the user, and then the appl ...

Unlock the power of asynchronous dependencies on a global scale with Inversify

I'm looking to resolve an asynchronous dependency at the top level without relying on top-level awaits. Currently, I've implemented a temporary solution by defining an asynchronous function getService() in the controller file. However, this appr ...

Utilizing TypeScript to Retrieve the Parameter Types of a Method within a Composition Class

Greetings to the TS community! Let's delve into a fascinating problem. Envision having a composition interface structured like this: type IWorker = { serviceTask: IServiceTask, serviceSomethingElse: IServiceColorPicker } type IServiceTask = { ...

Setting up ng-bootstrap for your project

I'm currently in the process of trying to incorporate the ng-bootstrap package into my application. To ensure I have all the necessary packages, I've executed npm i and updated both the @angular/core and @angular/cli modules to their latest vers ...

Update not reflecting in Angular Reactive Form custom component value

I've developed a custom input component that extends the primeng spinner component. However, I'm facing an issue with Angular Reactive Form where the model value of my component is not being updated. To make it easier to debug, I've created ...

Is it possible to encounter an unusual token export while trying to deactivate Vue with veevalidate

Utilizing Nuxt with server side rendering. Incorporating Typescript along with vee-validate version 3.4.9. The following code has been validated successfully extend('positive', value => { return value >= 0; }); Upon adding the default, ...

Drizzle-ORM provides the count of items in a findMany query result

Hello there, I'm currently experimenting with the Drizzle ORM and imagine I have this specific query const members = await trx.query.memberTable.findMany({ with: { comments:true } }) I'm wondering how I can retrieve the total count of me ...

Guide on navigating to a specific step within a wizard using Vue and TypeScript

In this wizard, there are 6 steps. The last step includes a button that redirects the user back to step 4 when clicked. The user must then complete steps 5 and 6 in order to finish the wizard. step6.ts <router-link to="/stepFour" ...

Removing an object from an array when a certain key value already exists in TypeScript

I'm currently facing an issue with my function that adds objects to an array. The problem arises when a key value already exists in the array - it still gets added again, but I want it to only add if it doesn't exist yet. Here's what I have: ...

What is the process to activate/deactivate a drop-down menu once a radio button has been chosen

JSON Data: radio1Data: any[] = [ { value: 'col-1', viewValue: 'Col-1' }, { value: 'col-2', viewValue: 'Col-2' } ]; radio2Data: any[] = [ { value: 'col-1', viewValue: 'Col-1' }, ...

Getting js.map Files to Function Properly with UMD Modules

I am experiencing an issue with debugging TypeScript files in Chrome and Firefox. Specifically, when trying to debug the MapModuleTest.ts file, the debugger seems to be out of sync with the actual JavaScript code by two lines. This discrepancy makes settin ...

"Enhance your user experience with seamless drag and drop functionality for transferring items between

I am currently working on transferring items between a list and table using the drag-and-drop feature of PrimeNG. Both elements should be able to act as both drag sources and drop destinations. My project is based on PrimeNG-9.0.0 and Angular 9.0.2 https ...