Troubleshooting Angular: Unidentified property 'clear' error in testing

I've implemented a component as shown below:

   <select #tabSelect (change)="tabLoad($event.target.value)" class="mr-2">
                  <option value="tab1">First tab</option>
                  <option value="tab2">Second tab</option>
                </select>


 <div class="tab-content">
        <div class="tab-pane fade show active">
          <ng-template #tabContent></ng-template>
        </div>
      </div>

The component has two tabs which trigger the tabLoad() function and pass along the clicked tab as a parameter.

export class DemoComponent implements OnInit {

  @ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
  activeTab: any = 'tab1';

  constructor(private resolver: ComponentFactoryResolver) { }

  ngOnInit() {
    this.tabLoad(this.activeTab);
  }

  tabLoad(page) {
    setTimeout(() => {
      this.activeTab = page;
      this.entry.clear();
      if (page == 'tab1') {
        const factory = this.resolver.resolveComponentFactory(Tab1Component);
        console.log(this.entry);
        this.entry.createComponent(factory);
      } else if (page == 'tab2') {
        const factory = this.resolver.resolveComponentFactory(Tab2Component);
        this.entry.createComponent(factory);
      }
    }, 500);
  }

}

In this .ts file, I defined a variable called entry that refers to #tabContent. The tab content loads a component based on the active page.

I wrote a test suite for this functionality as follows:

fdescribe('DemoComponent', () => {
    let component: DemoComponent;
    let fixture: ComponentFixture<DemoComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [RouterModule.forRoot([]), SharedModule],
            declarations: [Tab1Component, Tab2Component],
        }).compileComponents().then(() => {
            fixture = TestBed.createComponent(DemoComponent);
            component = fixture.componentInstance;
        });
    }));
    it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
        component.tabLoad("tab1");
        flush();
        expect(component.activeTab).toBe('tab1');
    }));
});

This test fails with an error stating

Cannot read property 'clear' of undefined
when calling this.entry.clear();. Additionally, console.log(this.entry); prints undefined. I attempted to add fixture.detectChanges() within the scope of .
compileComponents().then(() => {})
, but the issue persists. However, everything works fine when I navigate to the page after serving it using ng serve.

Answer №1

When calling fixture.detectChanges(), the ngOnInit method is invoked and it's important to do this inside a fakeAsync zone in order for flush to work correctly. This is because the timer set by setTimeout is created during the ngOnInit call, so it needs to be within a fakeAsync zone. In your test, move the fixture.detectChanges() inside the test block and remove it from the .then block.

beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [RouterModule.forRoot([]), SharedModule],
            declarations: [Tab1Component, Tab2Component],
        }).compileComponents().then(() => {
            fixture = TestBed.createComponent(DemoComponent);
            component = fixture.componentInstance;
            // !! remove fixture.detectChanges() from here
        });
    }));
    it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
        // !! add it here
        fixture.detectChanges();
        component.tabLoad("tab1");
        flush();
        expect(component.activeTab).toBe('tab1');
    }));

As Petr suggested, instead of using 500ms, you can utilize ngAfterViewInit.

Try updating your code like so:

export class DemoComponent implements OnInit, AfterViewInit { /* add AfterViewInit */

  @ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
  activeTab: any = 'tab1';

  constructor(private resolver: ComponentFactoryResolver) { }

  ngOnInit() {
  }

  ngAfterViewInit() {
    this.tabLoad(this.activeTab);
  }

  tabLoad(page) {
      this.activeTab = page;
      this.entry.clear();
      if (page == 'tab1') {
        const factory = this.resolver.resolveComponentFactory(Tab1Component);
        console.log(this.entry);
        this.entry.createComponent(factory);
      } else if (page == 'tab2') {
        const factory = this.resolver.resolveComponentFactory(Tab2Component);
        this.entry.createComponent(factory);
      }
  }

}

With these changes, you may not need to use fakeAsync and flush anymore.

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

Harnessing the power of data within different components

In my setup, I have two vital components in play. The initial one is responsible for presenting a list of items, while the second dictates the design and layout of these items. These items reside in an array located within the app.vue file. Here lies my p ...

Inserting a pause between a trio of separate phrases

I am dealing with three string variables that are stacked on top of each other without any spacing. Is there a way to add something similar to a tag in the ts file instead of the template? Alternatively, can I input multiple values into my angular compo ...

Angular JS has the capability to toggle the visibility of elements on a per-item basis as well as

I have created a repeater that displays a headline and description for each item. I implemented a checkbox to hide all descriptions at once, which worked perfectly. However, I also wanted to allow users to hide or show each description individually. I almo ...

When using the map function, I am receiving an empty item instead of the intended item based on a condition

Need assistance with my Reducer in ngRx. I am trying to create a single item from an item matching an if condition, but only getting an empty item. Can someone please help me out? This is the code for the Reducer: on(rawSignalsActions.changeRangeSchema, ...

"Exploring the Wonders of Regular Expressions in JavaScript: Embracing the Truth and All

Hey there! I've been working on a JavaScript script to test password field validation. As of now, I have successfully made the script display an alert when the requirements are not met. However, I am now facing an issue regarding what action to take o ...

Parent element with 'overflow: hidden' in CSS is causing child styles to be cropped out

.util-truncate { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } a { text-decoration: none; } a:focus { box-shadow: 0 0 0 3px blue; text-decoration: underline; } a:focus-visible { outline: 0; } <div ...

Is there a way to ensure that the onChange event of ionic-selectable is triggered twice?

I've been working with an ionic app that utilizes the ionic-selectable plugin, and for the most part, it's been running smoothly. However, I encountered a rare scenario where if a user on a slow device quickly clicks on a selection twice in succe ...

Check if the provided arguments in JavaScript are arrays and, if they are, combine them into a single large array

My array variables are as follows: const gumBrands = ['orbit', 'trident', 'chiclet', 'strident']; const mintBrands = ['altoids', 'certs', 'breath savers', 'tic tac']; Presen ...

I need to mass upload a collection of resumes stored in a zip file, then extract and display the content of each resume using a combination of HTML

I recently used a service to extract and retrieve the contents of a zip file. I am trying to read the content of the files and integrate them into the scope of my Angular project. Any suggestions would be greatly appreciated. Below is an outline of my func ...

Is there a way to track the amount a user scrolls once they have reached the bottom of a page using Javascript?

It's a popular UI pattern on mobile devices to have a draggable element containing a scrollable element. Once the user reaches the end of the scrollable content, further scrolling should transition into dragging the outer element. For example, in this ...

Implementing Blob image rendering in Vue.js from a database

In my front-end development, I am utilizing Vue.js. On the backend, I have set up Node.js, Express, and PostgreSQL with Sequelize. One of the challenges I encountered involved storing an item in the database that includes a thumbnail image. Database Model ...

The application of knockoutjs bindings is restricted to specific ids and cannot be used on any

In my project, I have multiple views where each one applies bindings to its own tag individually. Here is a snippet of the code: (Please note that some code has been omitted for brevity. For a more complete example, you can view the full fiddle here: http ...

Encountering a tucked-away issue with the Safari scroll bar?

Encountered an interesting bug where users are unable to scroll down a text nested inside a div. This issue seems to be isolated to Safari, as it doesn't occur in other browsers. I have prepared a sample file to demonstrate the bug and would apprecia ...

Unable to capture user input text using $watch in AngularJS when applied on ng-if condition

I have developed a cross-platform application using AngularJS, Monaca, and OnsenUI. In my login view, I check if the user has previously logged in by querying a SQLite database. Depending on whether there is data in the database, I either display a welcom ...

What are the steps to successfully install OpenCV (javascript edition) on Internet Explorer 11?

I'm currently experiencing issues with getting the OpenCV javascript version to function properly on IE11 for contour detection. While my code runs smoothly on all other up-to-date browsers, I am encountering errors such as: TypeError: Object doesn&a ...

Do we really need to use redux reducer cases?

Is it really necessary to have reducers in every case, or can actions and effects (ngrx) handle everything instead? For instance, I only have a load and load-success action in my code. I use the 'load' action just for displaying a loading spinne ...

Refreshing the list in a Next.js to-do application post-deletion: a step-by-step guide

I am currently developing a basic to-do application using Next.js, TypeScript, and Axios. I have successfully implemented the delete functionality for tasks, but I am facing an issue with refreshing the tasks list after deletion. I would appreciate any s ...

Inserting an item into a list

Looking for assistance with this particular scenario: { "EekvB3cnwEzE":{ "name":"hi", }, "Brv1R4C6bZnD":{ "name":"yup", }, "kRwRXju6ALJZ":{ "name":"okay", } } I'm attempting to store each of these objects in an array. Howe ...

No indication of component statuses in the augury feature

Augury is a useful Chrome extension for debugging Angular applications. However, I have encountered an issue where it is not displaying any states currently. My setup includes Angular version 5.1.0 and Augury version 1.16.0. ...

React automatic scrolling

I am currently working on implementing lazy loading for the product list. I have created a simulated asynchronous request to the server. Users should be able to update the page by scrolling even when all items have been displayed. The issue arises when ...