patchValue of Select with value not in options keeps the form valid
May 21, 2023
Explanation of the problem
When using the patchValue function to set a value to a Select box in Angular, a behavior inconsistency occurs. If the value being set does not exist in the Select options, the model is still updated with that value, and the field is considered valid. This behavior differs from the expected behavior, where setting a value that is not present in the options should either not set the value at all or set an empty value. To ensure that the issue has not already been reported, it is recommended to search GitHub for similar issues or pull requests before submitting a bug report. The following code snippet demonstrates the current behavior:
this.form.patchValue({
selectField: 'd' // Value that doesn't exist in the options
});
To reproduce the problem, a reactive form with a Select box and the Validators.required validation can be set up. The Select box should have several options available, such as “a,” “b,” and “c.” Then, using the patchValue function, set the value of the Select box to a value that is not present in the options, for example, “d.” The issue can be observed in the behavior of the form. The following Plunkr example provides a minimal reproduction of the problem: Plunkr Link.
In the described use case, the motivation for changing the behavior of the patchValue function is to handle the import of previously saved form data. When users import their data, the current form needs to be patched with their supplied data. However, if the available values in a Select box have changed over time and the user has old data that includes values not present in the options, the field and the form remain valid after the import. This leads to a situation where no data is displayed in the Select box. It is unclear whether this behavior should be considered a bug or a feature request. The issue is encountered in an environment using Angular version 4.0.0-beta.6, running on CentOS 6.8 and the Chrome 56 browser.
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.
-
Explanation of the problem
An error occurred while running a TypeScript project. The error message is:
./node_modules/pdfjs-dist/build/pdf.js 3657:145 Module parse failed: Unexpected token (3657:145) File was processed with these loaders: * ./node_modules/babel-loader/lib/index.js You may need an additional loader to handle the result of these loaders. | numPages: this._numPages, | annotationStorage: (annotationStorage === null || annotationStorage === void 0 ? void 0 : annotationStorage.getAll()) || null, > filename: ((_this$_fullReader = this._fullReader) === null || _this$_fullReader === void 0 ? void 0 : _this$_fullReader.filename) ?? null | }).finally(() => { | if (annotationStorage) {The error occurs in a TypeScript project, but the same code works fine in a React JS project. The relevant portions of
tsconfig.jsonandpackage.jsonare included in the description. The TypeScript configuration is set to target ES5 and uses a number of language features such asesnextandjsx. The project has a number of dependencies includingreact,react-dom, andtypescript.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 babel-loader You may need an additional loader to handle the result of these loaders in ansu5555 pdf-viewer-reactjs
The root cause of the problem is that the version of the “pdfjs-dist” package specified in the project’s dependencies uses a caret (^) before the version number. This is a common practice when specifying package versions in npm and it allows npm to install the most recent version of the package that is compatible with the version specified. For example, if the package.json file has the following dependency:
"dependencies": { "pdfjs-dist": "^2.6.347" }When running
npm install, npm will install the most recent version of “pdfjs-dist” that is compatible with version 2.6.347, which could be a version greater than 2.6.347. This can cause issues if the new version is not compatible with the current project.To solve this problem, the user can use the “npm-force-resolutions” package. This package allows to specify which versions of packages should be used during installation, regardless of the versions specified in the dependencies.
"resolutions": { "pdfjs-dist": "2.6.347" }This will force npm to install version 2.6.347 of “pdfjs-dist” and not the most recent compatible version.
To make sure that the specified version of the package is used during installation, the user should add a “preinstall” script in package.json, which will run “npm-force-resolutions” before running the npm install command.
"scripts": { "preinstall": "npx npm-force-resolutions", ... }This way, when running
npm install, it will first run “npm-force-resolutions” and ensure that the specified version of “pdfjs-dist” is installed. This should resolve the issue of the incompatible version of the package causing an error.Other popular problems with pdf-viewer-reactjs
Problem: PDF viewer not rendering in production environment.
When building and deploying a React application that uses the “pdf-viewer-reactjs” package, the PDF viewer may not render correctly in the production environment. This issue is often caused by webpack not correctly handling the file paths of the PDF.js library that is included with the “pdf-viewer-reactjs” package.
Solution:
To fix this issue, the user can use the “copy-webpack-plugin” to copy the PDF.js library from the “pdf-viewer-reactjs” package to the build folder during the webpack build process.
const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ new CopyWebpackPlugin([ { from: 'node_modules/pdfjs-dist/build/pdf.js', to: 'pdf.js' }, ]), // ... ], // ... };Problem: Error “Module not found: Error: Can’t resolve ‘fs’” when running the application
When running the application that uses the “pdf-viewer-reactjs” package, an error may occur saying “Module not found: Error: Can’t resolve ‘fs’”. This is because the PDF.js library that is included with the “pdf-viewer-reactjs” package relies on the Node.js “fs” module, which is not available in the browser.
Solution:
To fix this issue, the user can use the “fs-webpack-plugin” to replace the Node.js “fs” module with a browser-compatible version during the webpack build process.
const FsWebpackPlugin = require('fs-webpack-plugin'); module.exports = { // ... plugins: [ new FsWebpackPlugin(), // ... ], // ... };Problem: Error “TypeError: Cannot read property ‘getDocument’ of undefined” when rendering the PDF viewer component
When rendering the PDF viewer component provided by the “pdf-viewer-reactjs” package, an error may occur saying “TypeError: Cannot read property ‘getDocument’ of undefined”. This is caused by the PDF.js library not being properly initialized before the component is rendered.
Solution:
To fix this issue, the user can use the “pdfjs-dist” package to initialize the PDF.js library before the component is rendered.
import pdfjs from 'pdfjs-dist'; pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`; function App() { return ( <div className="App"> <PdfViewer pdfUrl="example.pdf" /> </div> ); } export default App;It is important to note that the version of pdfjs passed to the workerSrc should match the version of pdfjs-dist that you are using.
A brief introduction to pdf-viewer-reactjs
pdf-viewer-reactjs is a React-based library for displaying PDF documents in web applications. It is built on top of the PDF.js library, which is a JavaScript library for rendering PDF documents in web browsers. pdf-viewer-reactjs provides a set of React components that wrap the functionality provided by PDF.js, making it easy to integrate PDF viewing capabilities into React-based web applications.
The main component provided by pdf-viewer-reactjs is the PdfViewer component, which is a React component that renders a PDF document in a web page. The PdfViewer component can be configured to display the PDF in different ways, such as in a single page or in a continuous scrolling mode. It also provides various other functionalities like displaying thumbnails, searching for text, etc. Additionally, it also allows for annotations and form filling, which makes it very useful for business and legal documents. The library is highly customizable and can be tailored to specific needs of the project. It is also compatible with most modern browsers and has a small footprint, which makes it a great choice for web-based PDF viewing applications.
Most popular use cases for pdf-viewer-reactjs
- Displaying PDF documents in web applications: pdf-viewer-reactjs can be used to display PDF documents within a web application. The library provides a React component that can be integrated into the application to display the PDF document. This can be useful for web-based document management systems, legal document management, and other applications that need to display PDF documents to users.
import { PdfViewer } from 'pdf-viewer-reactjs'; function App() { return ( <div className="App"> <PdfViewer pdfUrl="example.pdf" /> </div> ); } export default App; - Annotation and form filling: pdf-viewer-reactjs provides annotation and form filling functionality, which can be used to add comments, highlights, and other annotations to PDF documents. This can be useful for business and legal documents where users need to add comments and annotations to the document before signing or submitting it.
- Searching for text within the PDF: pdf-viewer-reactjs provides a built-in search feature that allows users to search for specific text within a PDF document. This can be useful for large documents where users need to quickly find specific information. The library provides a search bar component that can be integrated into the application to allow users to search for text within the PDF document.
- 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: patchValue of Select with value not in options keeps the form valid
To solve the issue described, the behavior of the patchValue function in Angular can be modified to align with the expected behavior. One approach is to implement custom validation logic that checks if the value being set exists in the Select options. If the value is not found, the field can be marked as invalid.
Here’s an example of how this can be implemented:
- Modify the form control for the Select box to include custom validation logic:
import { ValidatorFn, AbstractControl } from '@angular/forms';
function validateSelectOption(options: string[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
const value = control.value;
if (options.indexOf(value) === -1) {
return { invalidOption: true };
}
return null;
};
}
// Inside the form creation
this.form = new FormGroup({
selectField: new FormControl('', validateSelectOption(['a', 'b', 'c']))
});
- Update the patchValue function to trigger the custom validation:
const valueToSet = 'd';
this.form.get('selectField')?.setValue(valueToSet);
this.form.get('selectField')?.updateValueAndValidity();
By implementing the custom validation logic, the Select box will correctly handle cases where the value being set is not present in the options. The field will be marked as invalid, ensuring that the behavior aligns with the expected behavior.
Problems with angular
- Problem: “Cannot read property ‘length’ of undefined” error when using the
asyncpipe with aBehaviorSubject. Description: Users have reported an error when using theasyncpipe with aBehaviorSubject. The error message is “Cannot read property ‘length’ of undefined”. This error occurs when theBehaviorSubjecthas not yet emitted any values and theasyncpipe tries to access thelengthproperty of the value. Solution: One solution is to initialize theBehaviorSubjectwith a default value. For example:
private dataSubject = new BehaviorSubject<any[]>([]);
data$ = this.dataSubject.asObservable();
Then, in the component, check if the value is undefined before accessing its length property:
<ng-container *ngIf="data$ | async as data">
<div *ngIf="data.length > 0">
// Display data here
</div>
</ng-container>
- Problem: Error “zone.js has detected that ZoneAwarePromise
(window|global).Promisehas been overwritten” when running Angular tests. Description: When running Angular tests, users have reported an error message that says “zone.js has detected that ZoneAwarePromise(window|global).Promisehas been overwritten”. This error occurs when a third-party library, such as a polyfill, overwrites the globalPromiseobject. Solution: One solution is to use thezone.jslibrary to patch the globalPromiseobject before running the tests. Add the following line to thetest.tsfile:
import 'zone.js/testing';
Then, in the beforeEach function of the test, patch the Promise object:
beforeEach(() => {
(window as any)['Promise'] = Promise;
});
- Problem: Error “Maximum call stack size exceeded” when using
ng serve. Description: Some users have reported an error message that says “Maximum call stack size exceeded” when running theng servecommand. This error occurs when there is a circular dependency in the application. Solution: One solution is to use the Angular dependency injection system to break the circular dependency. For example, ifComponentAdepends onComponentBandComponentBdepends onComponentA, create a new service and move the shared logic to the service. Then, inject the service into both components. For example:
@Injectable({ providedIn: 'root' })
export class SharedService {
// Shared logic here
}
@Component({...})
export class ComponentA {
constructor(private sharedService: SharedService) {}
// Use the shared service here
}
@Component({...})
export class ComponentB {
constructor(private sharedService: SharedService) {}
// Use the shared service here
}
A brief introduction to angular
Angular is a popular open-source web application framework used to build dynamic single-page applications. The Angular framework is written in TypeScript, a superset of JavaScript that allows for static typing and other features. The primary goal of Angular is to provide a robust and efficient framework for building web applications that can be easily maintained and scaled. Angular is known for its powerful features such as two-way data binding, dependency injection, and an extensive library of built-in directives and services.
One of the main benefits of Angular is its component-based architecture, where each component represents a specific feature or view in the application. Components are reusable and can be composed to create complex UIs. Angular also supports reactive programming and provides a robust set of APIs for working with observables, which allows for easier handling of asynchronous operations. The framework has a steep learning curve, but once mastered, developers can build complex and scalable web applications in a relatively short time. Overall, Angular is a versatile framework that offers a lot of power and flexibility to developers.
Most popular use cases for angular
-
- Building dynamic and scalable web applications: Angular provides a comprehensive framework for building modern web applications that are scalable, maintainable, and easy to develop. It offers a wide range of tools and features that make it easy to create dynamic and interactive web applications that can handle large amounts of data and complex workflows.
- Creating reusable UI components: Angular’s component-based architecture allows developers to create reusable UI components that can be easily integrated into different parts of an application. This feature makes it easier to maintain and update applications over time, as changes made to a component are automatically propagated to all instances of that component in the application.
Here’s an example code block for creating a reusable component in Angular:
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-button',
template: `
<button [class]="classes">
{{label}}
</button>
`
})
export class ButtonComponent {
@Input() label: string;
@Input() classes: string;
}
- Developing cross-platform applications: With Angular, developers can build applications that can run on multiple platforms, including web, mobile, and desktop. This is made possible by using technologies such as Angular Universal for server-side rendering, and frameworks like Ionic for mobile app development. By leveraging these tools, developers can create a single codebase that can be used to build applications for multiple platforms.
Here’s an example code block for building a cross-platform application with Angular and Ionic:
# Install Ionic CLI
npm install -g @ionic/cli
# Create a new Ionic app with Angular
ionic start myApp --type=angular
# Add a new page to the app
ionic generate page myPage
# Build and run the app on a mobile device
ionic capacitor add ios
ionic capacitor copy ios
ionic capacitor open ios