bug(Angular Material): Installing Angular Material 14 under Angular 14 after update from 12.x fails: Cannot read properties of undefined (reading 'kind')
  • 24-May-2023
Lightrun Team
Author Lightrun Team
Share
bug(Angular Material): Installing Angular Material 14 under Angular 14 after update from 12.x fails: Cannot read properties of undefined (reading 'kind')

bug(Angular Material): Installing Angular Material 14 under Angular 14 after update from 12.x fails: Cannot read properties of undefined (reading ‘kind’)

Lightrun Team
Lightrun Team
24-May-2023

Explanation of the problem

 

This issue appears to be a regression, as the behavior used to work in the previous version of the software. The bug was not present in version 11, to the best of my knowledge. The error is not limited to an upgrade from Angular 11 to 14; it also occurs when starting a new project with Angular 12 and upgrading to version 14. Despite following the update guide, the error persists.

To reproduce the issue, follow these steps:

  1. Create a new project with Angular 12.x.
  2. Upgrade the project to Angular 13.x.
  3. Further upgrade the project to Angular 14.x.
  4. Install Angular Material with the command ng add @angular/material@14.
  5. The error message “Cannot read properties of undefined (reading ‘kind’)” is encountered.

 

Troubleshooting with the Lightrun Developer Observability Platform

 

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for: bug(Angular Material): Installing Angular Material 14 under Angular 14 after update from 12.x fails: Cannot read properties of undefined (reading ‘kind’)

 

To solve the issue described, there are a few steps you can take:

  1. Review the Closed Issue: Referencing the closed issue (#25215) mentioned in the description can provide valuable insights into similar problems faced by other users. While the issue may not be identical, it could contain relevant information or potential solutions that could be applied to your situation.
  2. Check for Updated Guides and Documentation: Ensure that you have followed the most up-to-date documentation and guides provided by Angular. It’s possible that there are specific instructions or additional steps required for upgrading to Angular 14.x that were missed during the process. Double-check the Angular upgrade guide, release notes, and any relevant documentation to ensure all necessary steps have been followed.
  3. Validate Package Compatibility: Confirm that all packages and dependencies, including Angular Material, are compatible with the Angular version you are upgrading to (in this case, Angular 14.x). Incompatible package versions can lead to runtime errors. Verify that the versions of Angular Material, as well as other relevant packages like @angular/cdk, match the required versions specified in the Angular documentation.

Here’s an example of how you can verify the package versions in your package.json file:

 

{
  "dependencies": {
    "@angular/cdk": "^14.0.4",
    "@angular/material": "^14.0.4",
    // Other dependencies
  }
}

 

By ensuring that the package versions align with the recommended versions, you can mitigate compatibility issues and potential runtime errors.

Remember to consult the Angular community and support channels for additional guidance. They can provide further insights, troubleshooting steps, or specific solutions related to the issue you’re facing.

 

Problems with components

 

Problem 1: Unpredictable Component State Management

One common problem with components is managing their state in a predictable and efficient manner. In complex applications, it can become challenging to keep track of component state changes, leading to unexpected behavior and bugs. Without proper state management, components may exhibit inconsistent rendering, incorrect data binding, or unnecessary re-renders.

One solution to address this issue is to adopt a state management library such as Redux or MobX. These libraries provide a centralized store where component states can be stored and accessed. By decoupling state management from individual components, you achieve better control over state changes and ensure consistency throughout the application. Here’s an example of using Redux for state management:

 

// Define Redux actions and reducer
const incrementAction = { type: 'INCREMENT' };

function counterReducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
}

// Create Redux store
import { createStore } from 'redux';
const store = createStore(counterReducer);

// Connect component to Redux store
import { connect } from 'react-redux';

function Counter({ count, dispatch }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch(incrementAction)}>Increment</button>
    </div>
  );
}

const ConnectedCounter = connect((state) => ({ count: state }))(Counter);

 

By utilizing a state management library, you can achieve more predictable component state management and simplify the process of tracking and updating state across different components.

Problem 2: Performance Bottlenecks in Rendering

Another common problem with components is performance bottlenecks during rendering, especially when dealing with large or frequently updating data. Inefficient rendering can lead to slow UI responsiveness, poor user experience, and unnecessary CPU and memory consumption.

To optimize component rendering, you can implement techniques such as memoization, virtualization, and lazy loading. Memoization involves caching the result of expensive computations to avoid redundant calculations. Virtualization helps render only the visible portion of long lists or tables, reducing the number of DOM elements rendered. Lazy loading enables loading and rendering components on-demand, improving initial page load times. Here’s an example of using the memo and react-virtualized libraries for memoization and virtualization:

 

import React, { memo } from 'react';
import { List } from 'react-virtualized';

const MyListComponent = memo(({ items }) => (
  <List
    width={300}
    height={400}
    rowHeight={50}
    rowRenderer={({ index, key, style }) => (
      <div key={key} style={style}>
        {items[index]}
      </div>
    )}
    rowCount={items.length}
  />
));

// Usage
const items = ['Item 1', 'Item 2', 'Item 3', ...];
<MyListComponent items={items} />;

 

By implementing these optimization techniques, you can significantly enhance the rendering performance of your components and provide a smoother user experience.

Problem 3: Complex Component Interactions and Dependencies

As applications grow in complexity, component interactions and dependencies can become challenging to manage. When components rely heavily on each other or share a complex relationship, making changes to one component may have unintended consequences on others. This tight coupling between components can make the codebase hard to maintain, test, and extend.

To address this problem, you can employ design patterns such as the observer pattern or dependency injection. The observer pattern establishes a loose coupling between components by allowing them to subscribe to and react to changes in each other’s state. Dependency injection helps manage component dependencies by injecting the required dependencies from a higher-level container, reducing direct component-to-component dependencies. Here’s an example of using the observer pattern with MobX:

 

import { observable } from 'mobx';
import { observer } from 'mobx-react';

class Store {
  @observable count = 0;
}

const store = new Store();

const Counter = observer(() => (
  <div>
    <p>Count: {store.count}</p>
    <button onClick={() => store.count++}>Increment</button>
  </div>
));

// Usage
<Counter />;

 

By adopting these design patterns, you can promote better component isolation, reusability, and maintainability, making it easier to manage complex component interactions and dependencies.

Remember that the specific problems you encounter with components may vary based on your application’s requirements and complexity. Analyzing and addressing these challenges can lead to more robust and maintainable component-based architectures.

 

A brief introduction to components

 

Components are a fundamental building block in modern software development, particularly in frameworks like React. They are modular, reusable, and encapsulate both the structure and behavior of user interface elements. Components can be thought of as self-contained entities that accept input in the form of props and produce rendered output. They are designed to be composable, allowing developers to build complex user interfaces by combining smaller, reusable components together.

In technical terms, components in React are typically implemented as classes or functional components. They can have their own internal state, which determines their behavior and appearance. Components can also receive data and functions as props, allowing them to communicate with other components and respond to user interactions. By leveraging the concept of components, developers can achieve a modular and reusable architecture, enabling them to build scalable and maintainable applications.

In frameworks like Angular, components play a similar role. They are the basic building blocks for constructing user interfaces and encapsulate the logic and presentation of a specific part of the application. Components in Angular are typically defined using TypeScript classes that incorporate templates and metadata. They can interact with other components, services, and directives, enabling developers to create powerful and interactive applications. Components in Angular follow the principles of reusability, encapsulation, and separation of concerns, allowing for more manageable and maintainable codebases.

 

Most popular use cases for components

  1. Modularity and Reusability: Components are used to achieve modularity and reusability in software development. They allow developers to break down the user interface into smaller, self-contained units of functionality that can be easily reused across different parts of an application. By encapsulating specific logic and presentation within components, developers can build complex applications by composing and combining these reusable building blocks. Here’s an example of a React component that represents a simple button:
import React from 'react';

const Button = ({ text, onClick }) => {
  return <button onClick={onClick}>{text}</button>;
};

export default Button;
  1. Composition and Hierarchical Structure: Components provide a hierarchical structure for building user interfaces. They can be nested within each other to create complex UI hierarchies. This compositional approach allows developers to manage the complexity of an application by breaking it down into smaller, manageable parts. Components can be combined together to form higher-level components, creating a clear and organized structure. Here’s an example of an Angular component that represents a navigation bar:
import { Component } from '@angular/core';

@Component({
  selector: 'app-navbar',
  template: `
    <nav>
      <a href="/">Home</a>
      <a href="/about">About</a>
      <a href="/contact">Contact</a>
    </nav>
  `,
})
export class NavbarComponent {}
  1. Separation of Concerns: Components facilitate the separation of concerns by allowing developers to separate the logic and presentation aspects of an application. They provide a clear boundary between different parts of the application, making it easier to manage and maintain the codebase. Components encapsulate both the visual representation and the behavior of a specific part of the UI, enabling developers to focus on specific functionalities without worrying about the entire application. This separation of concerns improves code organization, reusability, and testability. Here’s an example of a Vue.js component that represents a form input:
<template>
  <div>
    <label>{{ label }}</label>
    <input v-model="value" type="text" :placeholder="placeholder" />
  </div>
</template>

<script>
export default {
  props: {
    label: String,
    value: String,
    placeholder: String,
  },
};
</script>
Overall, components provide a structured and modular approach to building user interfaces, promoting reusability, composability, and separation of concerns. They enable developers to create scalable and maintainable applications by breaking down the complexity into smaller, manageable parts.
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.