Angular2 drag and drop feature becomes functional only after eliminating an irrelevant section from the template

Currently, I am harnessing the power of Angular2 to incorporate drag and drop functionalities. Specifically, my goal is to enable the movement of "windows" within different "sessions", with each window containing a set of bookmarks.

Upon testing, I have observed that in scenarios where the structure becomes slightly more intricate – for instance, having 4 sessions, each consisting of 2 windows with 5-10 bookmarks per window – the process of moving windows between sessions experiences significant lag, sometimes resulting in up to a minute of waiting time!

Interestingly, the performance vastly improves when the bookmarks are excluded from the template.

To address this issue, my initial approach involved rendering the bookmarks in a separate container located outside the sessions—the area governed by drag-and-drop directives. Then, upon demand, I devised a mechanism using JavaScript (specifically Typescript) to relocate the bookmarks from the external container back into the session windows.

It's worth noting that the bookmarks hold no direct connection to the sessions; they remain isolated entities. And yet, even their presence in the template seems to disrupt the entire functionality. Conversely, removing them results in a noticeable enhancement of responsiveness.

This raises the questions: Why does the inclusion of bookmarks have such a pronounced impact? How can this issue be effectively resolved?

EDIT: For a quick demonstration, you can access the following Plunkers: Plunker with bookmarks - note the delay while moving the "title" sections Plunker without bookmarks - observe the improved performance

One portion of the template responsible for the issue:

<section id="current_bookmarks">
    <div *ngFor="let session_window_name of session_keys;" 
        [bookmark_draggable_target]="{event_type:'moving_sessions',zone:session_window_name}" 
        (drop_here)="onDrop($event)">
        <div class="session_window_name">
            <div class="session_window_name_title"><input class="input_session_names title" [id]="sessions[session_window_name].name+'_input'" type="text" value="{{sessions[session_window_name].title}}"></div>
        </div>
        <div *ngFor="let window_name of sessions[session_window_name].windows_keys; let i = index;" 
            [bookmark_draggable]="{event_type:'moving_sessions',id:session_window_name+'_'+sessions[session_window_name].windows[window_name].id}">
            <session [index]="i" [from]="'stored'" [session]="session_window_name" [window]="sessions[session_window_name].windows[window_name]"></session>
        </div>
    </div>
</section>

If the below segment is omitted from the same template, the issue appears to be resolved. However, it's crucial to note that this section contains no 'bookmark_draggable' or 'bookmark_draggable_target' directives.

<section id="bookmarks_pool">

    <ng-container *ngFor="let session_window_name of session_keys;">
        <div *ngFor="let window_name of sessions[session_window_name].windows_keys; let i = index;">
            <div *ngFor="let bookmark of sessions[session_window_name].windows[window_name].bookmarks; let i = index;" >
                <div>
                    <span class="material-icons list_shown" (click)="bookmark_delete(session_window_name+'_'+sessions[session_window_name].windows[window_name].id+'_'+i+'_bookmark')">label</span>
                    <span class="material-icons clear_hidden" (click)="bookmark_delete(session_window_name+'_'+sessions[session_window_name].windows[window_name].id+'_'+i+'_bookmark')">clear</span>
                    <a target="_blank" [href]="sanitize(bookmark.url)">{{bookmark.url}}</a>
                </div>
            </div>
        </div>
    </ng-container>

</section>

The implemented directives:

import { Output, EventEmitter, Input, HostListener, Directive, HostBinding } from '@angular/core';
.
.
.
. . .

EDIT: adjustments made in tab.config.js, package.json dependencies, etc.

/**
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    . . .
  });
})(this);

System.import('app').catch(function(err){ console.error(err); });
{"dependencies": {...},
 "devDependencies": {...}
}

Answer №1

1) My preference is to utilize a custom pure pipe instead of the sanitize function.

@Pipe({ name: 'safe' })
export class SafePipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}
  transform(url) {
    return this.sanitizer.bypassSecurityTrustUrl(url);
  }
} 

Unlike your sanitize function, Angular only executes a pure pipe when it detects a pure change in the input value.

[href]="bookmark.url | safe">
<img class="bookmark_favicon" [src]="('chrome://favicon/'+bookmark.url) | safe"

https://plnkr.co/edit/7Qnxsh2a4OIODgQX13u0?p=preview

2) For sorting and moving items, I recommend utilizing trackBy within ngFor.

3) An additional enhancement is subscribing to the dragover event outside the angular zone:

drop-target.directive.ts

listener: Function;

constructor(private elRef: ElementRef, private zone: NgZone, private renderer: Renderer2) {
  zone.runOutsideAngular(() => {
    this.listener = this.renderer.listen(this.elRef.nativeElement, 'dragover', (ev) => {
        const { zone = {}, id = {}, event_type = 'default_event' } = this.options; // as defined in "bookmark_draggable_target"
        if (ev.dataTransfer.types.indexOf('application/x-$' + event_type) >= 0) // as sent in the event
            ev.preventDefault();
        else
            console.log(ev.dataTransfer.types)
    });
  })
}

ngOnDestroy() {
    this.listener();
}

https://plnkr.co/edit/NXOZUk8L5bM2k6ceQUpx?p=preview

or

import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
...
zone.runOutsideAngular(() => {
  this.subscription = Observable.fromEvent(this.elRef.nativeElement, 'dragover').subscribe((ev: any) => {
    const { zone = {}, id = {}, event_type = 'default_event' } = this.options; // as defined in "bookmark_draggable_target"
    if (ev.dataTransfer.types.indexOf('application/x-$' + event_type) >= 0) // as sent in the event
      ev.preventDefault();
    else
      console.log(ev.dataTransfer.types)
  });
})

ngOnDestroy() {
    this.subscription.unsubscribe();
}

https://plnkr.co/edit/HrmPPHJ63WjYC1SHNUQp?p=preview

4) To enhance further, consider having a component that renders bookmarks and utilizes the OnPush change detection strategy.

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

Using Javascript, retrieve a custom attribute specific to a checkbox option

How can I access the custom attribute "mem_name" value? <input class="messageCheckbox" type="checkbox" value="3" mem_name='ABC' name="checkmember" > <input class="messageCheckbox" type="checkbox" value="1" mem_name='PQR' name ...

Discover the route of a string within an object or array

Given a specific object or array structure, I am looking to verify the existence of a certain path within it. Example 1: const path = "data/message"; const info = { data: { school: 'yaba', age: 'tolu', message: 'true ...

Azure experiencing issue with MUI Datepicker where selected date is shifted by one day

I have developed a unique custom date selection component that utilizes Material UI and Formik. This component passes the selected date value to a parent form component in the following manner: import React from 'react'; import { useField } from ...

Making a Dialog resizable with jQuery's Resizable Helper

Is there a way to implement the Helper feature from jQuery Resizable, which only shows a frame while resizing the container, in a Dialog? ...

Tips on exporting a TypeScript class while maintaining private related variables

Consider this scenario: const hidden = Symbol() export class Foo { static [hidden] = 'I prefer no one to modify this' } The compiler throws an error TS4028: Public static property '[hidden]' of exported class has or is using privat ...

How to convert a JSON response into a select box using VueJS?

I have a VueJS component where I need to populate a html select box with data from a JSON response. This is my VueJS method: getTaskList() { axios.get('/api/v1/tasklist').then(response => { this.taskList = this.data.taskList; ...

Can you identify the specific error type that occurs in the onError function of react-query's MutationCache when using Typescript

Can someone help me with identifying the type of error in react-query MutationCache onError function when using Typescript? I also need guidance on how to override the type so that I can access and use the fullMessage from the data. const queryClient = new ...

"Encountering a problem with posting API requests using Express

I've encountered a problem where I keep receiving a 404 error when trying to post to a specific route. Here's the code snippet: app.js (code snippet for app.js) .... module.exports = app; api.js (code snippet for api.js) .... module.export ...

What is the best way to generate a new object by combining elements from an array and another object?

Attempting to create a new object by using an existing array. The goal is to generate an object that displays the following: { jack: 'jack', content: 'ocean'}, { marie: 'marie', content: 'pond'}, { james: 'jame ...

What is the safest way to convert a local file path to a file:// URL in Node.js?

In my node.js application, I am faced with the task of converting local file paths into file:// urls. After some research, I came across the File URI scheme on Wikipedia and I believe that there must be a solution out there or perhaps even an npm module t ...

Using Nestjs to inject providers into new instances of objects created using the "new" keyword

Is it possible to inject a provider into objects created by using the new keyword? For instance: @Injectable() export class SomeService { } export class SomeObject { @Inject() service: SomeService; } let obj = new SomeObject(); When I try this in my t ...

The use of D3.js causes 'this' to inherently assume the 'any' type due to the lack of a type annotation

I am working with the 3D.js library and trying to create a graph: d3.select(`#myGraph`) .append('svg') const svg = d3.select(`#myGraph svg`).append('g') const nodeEnter = svg.selectAll("g.node") .data(nodes) .enter() ...

List of Keys in MongoDB: The Ultimate Guide to Retrieving Object Keys

{ "_id": "1", "style": "13123", "category": "dress", "colors": { "Black": { "prestock": 50, "instock": 60, "inactive": 0 }, "Blue": { "prestock": 30, "instock": 0, "inactive": 0 }, ...

Ingesting a PDF stream into a JSP document

How can I display a PDF report inside a JSP? I have the PDFstream available using the following code private void generatePDFReport(OutputStream stream, JasperPrint jasperPrint) throws JRException { JRPdfExporter jrpdfexporter = new JRPdfExporter(); jrp ...

What's the best way to implement asynchronous state updating in React and Redux?

In my React incremental-style game, I have a setInterval function set up in App.ts: useEffect(() => { const loop = setInterval(() => { if (runStatus) { setTime(time + 1); } }, rate); return () => clearInterval(lo ...

Tips for testing the canActivate guard method in Angular2 using Jasmine

Apologies for bringing up this particular inquiry. It seems that no blog or YouTube tutorials exist regarding testing the canActivate guard file. The official documentation also lacks any information on this topic. Any assistance on this matter would be g ...

Encountering a javascript error when clicking on an Ajax link?

Within my partial view, I have included an ajax action link: @foreach (var times in Model.ProvidedDateTimes) { <tr> <td> @times.StartDateTime &nbsp; to &nbsp; @times.EndDateTime </td> <t ...

Issues encountered with JavaScript when trying to use the jQuery API

I'm trying to fetch random quotes using a Mashape API, but when I click the button, nothing happens. I've included the JS and HTML code below. Can anyone spot an issue with the JS code? The quote is not displaying in the div. Thank you! $(' ...

Converting an object to a string and then back to an object in ECMAScript 6Serialization and deserialization in ECM

One of the new features in ES6 (ECMAScript 6) is the introduction of the 'class' keyword. Here's an example of how a class can be defined: class MyClass { constructor() { this.MyProperty = 0; } } var obj = new MyClass(); co ...

Issue with Mat Autocomplete not populating input value when utilized with a formControl

I've encountered an issue with my custom autocomplete component that implements ControlValueAccessor. I'm attempting to set the value from the parent component using form.get('productName').setValue('Product 1');. While this s ...