Angular 5: dynamic component swapping

I am facing a challenge with dynamically placing components inside the view and need to switch their positions.

For instance, I have dynamically created components with ids 1 and 2, as shown in the figure linked below.

https://i.sstatic.net/hgxd2.jpg

Now, the task at hand is to swap the positions of these two components. But how can this be achieved?

Theoretically, I know that the location can be altered using the 'move' method within the ViewContainerRef class through an object like so:

viewContainerRef.move(componentRef.hostView, index)
.

I attempted to implement this method but unfortunately, the positions did not actually swap.

@ViewChild(ContainerRefDirective) location: ContainerRefDirective;

let componentFactory = this.factoryResolver.resolveComponentFactory(EntryComponent);
let componentRef = this.location.viewContainerRef.createComponent(componentFactory);

let entryComponent: EntryComponent = componentRef.instance;
// Assuming the counter variable increments with each new component creation.
entryComponent.Id = ++counter; 

// The move method is invoked immediately after creating and placing a new component.
// This snippet of code resides within the same method responsible for dynamic component creation.
this.location.viewContainerRef.move(componentRef.hostView, 1);

I have thoroughly reviewed the documentation on ViewContainerRef provided on angular.io and also looked into similar questions related to this matter. However, I seem to be struggling to comprehend or resolve this issue.

Answer №1

Have you considered binding your two dynamic components to an array instead? Here's an example:

this.dynamicComponents.push(dynamicComponentA);
..
this.dynamicComponents.push(dynamicComponentB);

Your HTML structure would then look like this:

<ng-container *ngFor="let component of dynamicComponents">
   <<You can reference dynamicComponentA and dynamicComponentB by using "component.value" etc.>>
</ng-container>

If you ever need to switch the positions of these components, simply manipulate the array and Angular will take care of the rest:

this.dynamicComponents = this.dynamicComponents.reverse();

This will automatically re-render the containers in a new order. You can check out a working example here.

Answer №2

Solved the issue successfully.

My Solution Technique

To address the problem, I opted to change the values instead of moving the components around.

In other words, I swapped the values 1 <=> 2 which gave the illusion that the components had been interchanged.

Implementation Steps

// initializing an array to store the newly created component instances
entryComponents: EntryComponent[] = new Array<EntryComponent>();

Add the newly created component to this array

let factoryResolver = this.factoryResolver.resolveComponentFactory(EntryComponent);    
let componentRef = this.location.viewContainerRef.createComponent(factoryResolver);

this.entryComponents.push(componentRef.instance); // retrieving components' instances

Finally, swap the values in order to create the appearance of component interchange.

Credits to @ForestG for providing this helpful suggestion

Answer №3

Although I have not experimented with this myself, it seems like the move method may not be the appropriate approach based on what I've read in the documentation. It appears that the move method is intended for "adding" an existing component to the view.

According to the documentation for detach:

Use along with insert to move a View within the current container.

Given that description, my suggestion would be...

let ref = this.location.viewContainerRef;
ref.detach(ref.indexOf(componentRef.hostView));
ref.insert(componentRef.hostView, 0);

Edit I successfully implemented an example using Angular's live example of Dynamic Component Loader.

Here is the relevant code:

// ShaneCoder - don't clear so we get multiple ads to demonstrate move.
// viewContainerRef.clear();

// ShaneCoder - only create 4 ads
if (viewContainerRef.length<4) {

    let componentRef = viewContainerRef.createComponent(componentFactory);
    (<AdComponent>componentRef.instance).data = adItem.data;

    // ShaneCoder ad is inserted at the bottom (no index parameter).  Move it up if there are multiple ads.
    if (viewContainerRef.length>=1) {
        // we're not the first ad
        // move new ad to top using detach and insert (the next two lines answer the original question)
        viewContainerRef.detach(viewContainerRef.indexOf(componentRef.hostView));
        viewContainerRef.insert(componentRef.hostView, 0);
        // comment out the previous two lines to see that ads are inserted at the bottom.
    }
}

Edit 2

After posting the last edit realized I hadn't tried move. Switched the detach and insert out for a move and it works. Example here. Relevant code:

viewContainerRef.move(componentRef.hostView, 0);
// comment out the previous two lines to see that ads are inserted at the bottom.

Now I'm wondering if the problem you were fighting with move was its zero-index. To make a component the first in a container view you need to pass a zero for the index. To make it second pass a one and so on. So your code would change like such:

this.location.viewContainerRef.move(componentRef.hostView, 0);

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 there a method to initiate a 'simple' action when dispatching an action in ngrx?

Here's the scenario I'm facing: When any of the actions listed below are dispatched, I need to update the saving property in the reducer to true. However, currently, I am not handling these actions in the reducer; instead, I handle them in my ef ...

There was an issue encountered while trying to execute npm install, resulting in the error message: "This is a glitch

I am diving into the world of Angular and Node for the first time. I'm currently attempting to run a project that combines Angular with Neo4j, but encountering some issues along the way. When I initially tried to launch the project as is, after openin ...

Issue with Ionic Capacitor React & Typescript build process for Firebase Functions

Recently, I've been working on a cutting-edge Ionic Capacitor React application that utilizes Typescript with a Firebase backend. While everything has been running smoothly so far, I encountered some challenges when trying to build Firebase Functions. ...

Is it possible for the input change event to not function properly on an input form control?

<div class="start-time-wrapper"> <input class="form-control" type="text" placeholder="From" [ngxTimepicker]="start" [format]="24" formControlName="startTime" (change)=&q ...

ts1109: An error occurred as there was an expectation for an angular

I am encountering an error while creating a simple form with Angular using a reactive form. I'm puzzled as to why it's indicating that something is missing: Although I have created forms numerous times before, this is the first instance of such ...

Utilizing Tick formatting in Chart.js with Typescript: A step-by-step guide

When setting Chart.js to use the en-US locale, the scale numbers are formatted optimally. https://i.sstatic.net/fzjpQmM6.png If I try using a tick callback as shown in the documentation: ticks: { callback: function(value) { return value.toStr ...

What steps should be taken to find a particular route when a user logs in for the first time?

I am currently working on an application using Angular 2+, Node.js, and Express.js that includes registration and login authentication functionality. How can I direct first-time users to a specific route upon login, but for all subsequent logins have them ...

What are the best ways to resolve the warning from webpack in Express?

I have set up webpack to bundle both the server and client side code... Below is my webpack configuration: const webpack = require('webpack'); const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin&a ...

Showing the Enum name rather than the value in an Angular HTML template for a bound Typescript Interface

After retrieving an array of objects with various properties from a .NET Controller, I am utilizing the following Typescript code: import { Component, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Co ...

The compilation of Angular8 ngx-bootstrap fails when using the --prod flag

Utilizing angular cli 8.3.8, bootstrap 4.4.4 ngx-bootstrap 5.6.1, During development, everything is functioning properly. However, when I compile with --prod, the errors below occur. It is crucial that we resolve the issues with --prod. The datepicker ...

Tips for displaying an object's properties using ngfor?

<div *ngFor="let article of articleList; let i = index"> <div class="item"> <div class="image-holder" style="background-image: url(https://uniqueblog.com/images/ninja-1.jpg)"&g ...

It is not possible to alter the styles of the ng-daterangepicker Angular plugin

After installing the angular2 plugin "ng-daterangepicker", I attempted to resize a div within it by modifying the .sass file. However, despite clearing the cache, the changes did not reflect in my browser. It seems that I may need to make adjustments to .s ...

Consolidating repeated data objects found in the response

After receiving an API response, the data looks like this: response = [ { id: 1, val: 'A', date: '28/03/2021', versions: [] }, { id: 1, val: 'B', date: '29/03/2021', versions: [] }, { id: 1, val: 'C', ...

What could be causing the error in Angular 2 when using multiple conditions with ng-if?

My aim is to validate if the length of events is 0 and the length of the term is greater than 2 using the code below: <li class="more-result" *ngIf="events?.length == 0 && term.value.length > 2"> <span class="tab-content- ...

Issues with implementing Dark mode in TailwindCSS with Nuxt.js

After spending a couple of days on this, I'm still struggling to get the dark mode working with Tailwind CSS in Nuxt.js. It seems like there might be an issue with the CSS setup rather than the TypeScript side, especially since I have a toggle that sw ...

The most effective method for transferring a JavaScript object between a TypeScript frontend and a Node.js backend

I need some advice on how to effectively share a JavaScript object between my Angular2 with Typescript frontend and NodeJS backend in an application I'm working on. Currently, I am using a .d.ts file for the frontend and adding a module.exports in the ...

Typescript fails to detect class constructor mismatches

Why is TypeScript unable to detect that the MadeFromString class is missing a second boolean argument in its constructor, making it incompatible with the StringConstructable interface in this code snippet: interface ComesFromString { name: string; } cla ...

Determine data type using the generic type of a related property in Typescript

I am seeking a method to specify the type of a property based on the generic type of another property within the same context. For instance, consider the following: type Person = { id: number; name: string; } type Select<Value=unknown> = (props ...

Explanation on How to utilize the $( document ).ready() jQuery function within the ngAfterViewInit() on a Component class using Angular 2

This is the code snippet: constructor(private el: ElementRef) { } ngAfterViewInit() { this.loadScript('app/homepage/template-scripts.js'); } ...

Ways to cite a vendor in static class functions?

Is there a method to access a service provided at bootstrap within static class methods? I don't have any code to share right now, but I've been experimenting with the standard syntax using Injector (you can find more information here). Whenever ...