Angular HttpClient put()

By Arvind Rai, March 18, 2024
On this page we will learn to execute HTTP PUT request using HttpClient.put() method in our Angular application. HTTP PUT request is used to create or update resource on server.
Angular HttpClient performs HTTP requests. It is an injectable class that has methods to execute HTTP request. These methods are request(), delete(), get(), head(), jsonp(), options(), patch(), post() and put() .
To use HttpClient.put() method, we need to pass url, body and configuration options as arguments. It returns an Observable object. On subscribe to this observable object, HTTP PUT request is performed.
Here we will learn to use HttpClient.put() method with examples in detail.

1. PUT vs POST

1. PUT and POST both contains a body. PUT uses body to update the resource on server and POST uses the body to save resource on the server.
2. In POST request, successful response has always a body but in PUT request, successful response may or may not have body.
3. PUT request is idempotent whereas POST request is not idempotent. Being idempotent means calling more than one time successfully, its has the same effect on the server. As we know that PUT is used to update the data on the server. Suppose we are updating a resource against an id and we hit PUT request more than one time successfully, it is equal to one successful hit and there will be no side effect for multiple calling. In POST request, suppose we are generating an order on the server, so on calling multiple POST request, multiple order can be generated for the same request.
4. PUT and POST both requests can read and write data on server, so both are not safe HTTP methods. HTTP methods that perform read-only operations, are considered as safe HTTP methods.
5. The response of PUT is not cacheable whereas the response of POST is cacheable only if fresh information is included.
6. POST method is allowed in HTML forms but PUT method is not allowed in HTML forms.

2. HttpClient.put() Method Structure

HttpClient.put() method executes HTTP PUT request. put() creates an observable and that on subscribe, hits HTTP PUT request on server.
put() method accepts url, body, options as arguments and returns Observable as following.
put(url: string, body: any, options: {
    headers?: HttpHeaders;
    context?: HttpContext;
    observe?: "body";
    params?: HttpParams;
    reportProgress?: boolean;
    responseType: "json"; 
    withCredentials?: boolean;
}): Observable<T> 
1. Parameters :
url : Endpoint URL.
body : Resource that need to be created or updated.
options : Configures HTTP options. Default value of 'observe' is 'body' and default value of 'responseType' is 'json'.

2. Returns :
put() method returns an Observable object and when we subscribe it, PUT request is executed.

3. options :
headers : Configures request headers.
context : Pass arbitrary user defined values.
observe : It has three values. When the value is “response”, complete response is returned. When the value is “body”, only body of response is returned. When the value is “events”, response with events is returned.
params : Configures query parameters.
responseType : The values of response type can be arraybuffer, blob, json and text.
reportProgress : Boolean value to configure if we want the request progress report, particularly when response type is ‘blob’.
withCredentials : Boolean value to configure if authorization header is being sent.

4. overloads :
put() has following overloads.
1. For observe: 'body' and responseType: 'json', return type is Observable<T>
2. For observe: 'body' and responseType: 'text', return type is Observable<string>
3. For observe: 'body' and responseType: 'arraybuffer', return type is Observable<ArrayBuffer>
4. For observe: 'body' and responseType: 'blob', return type is Observable<Blob>
5. For observe: 'events' and responseType: 'arraybuffer', return type is Observable<HttpEvent<ArrayBuffer>>
6. For observe: 'events' and responseType: 'blob', return type is Observable<HttpEvent<Blob>>
7. For observe: 'events' and responseType: 'text', return type is Observable<HttpEvent<string>>
8. For observe: 'events' and responseType: 'json', return type is Observable<HttpEvent<Object>>
9. For observe: 'events' and responseType: 'json', return type is Observable<HttpEvent<T>>
10. For observe: 'response' and responseType: 'arraybuffer', return type is Observable<HttpResponse<ArrayBuffer>>
11. For observe: 'response' and responseType: 'blob', return type is Observable<HttpResponse<Blob>>
12. For observe: 'response' and responseType: 'text', return type is Observable<HttpResponse<string>>
13. For observe: 'response' and responseType: 'json', return type is Observable<HttpResponse<Object>>
14. For observe: 'response' and responseType: 'json', return type is Observable<HttpResponse<T>>
15. For observe: 'body' and responseType: 'json', return type is Observable<Object>

3. Using HttpClient.put()

1. Configure provideHttpClient() in standalone application to make HttpClient available for dependency injection.
export const APP_CONFIG: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    ------
  ]
}; 
2. Inject HttpClient.
constructor(private http: HttpClient) { } 
3. Create a method to update data on server. HttpClient.put() needs a URL to hit server, data to send, options for request and response configurations.
updateWriter(writer: Writer): Observable<Writer> {
    return this.http.put<Writer>("/api/writers", writer);
} 
options argument is optional. Default value of observe is 'body' and default value of responseType is 'json'.
To get response as text, configure responseType: 'text' as below.
updateWriter(writer: Writer): Observable<string> {
   return this.http.put("/api/writers", writer, {
        responseType: 'text'
   });
} 

4. Pass Headers

1. To configure request herders, we need to configure headers property in options argument.
put(url: string, body: any, options: {
    headers?: HttpHeaders;
}) 
2. HttpHeaders creates headers for HTTP request. Its instances are immutable. It provides methods as append() and set() to add headers. These methods return a cloned instance of HttpHeaders with the change.
append() method appends a new value to the existing set of values.
set() method sets or modifies a value for a given header. If the header already exists, its value is replaced by new value.
We can also add headers using its constructor while creating its instance.
3. Find the code snippet to configure headers using HttpHeaders.
updateBook(book: Book): Observable<Book> {
    const myHeaders = new HttpHeaders()
    .append("Content-Type", "application/json")
    .append("Content-Length", "70");

    return this.http.put<Book>("/api/books", book, {
            headers: myHeaders
    });
} 
4. We can also directly specify headers to headers property.
return this.http.put<Book>("/api/books", book, {
        headers: {
            "Content-Type": "application/json",
            "Content-Length": "70"
        }
}); 

5. Pass Query Parameters

1. To pass query parameters to PUT request, use params property of options argument of put() method.
put(url: string, body: any, options: {
    params?: HttpParams;
}) 
2. HttpParams represents query parameters in HTTP request. It is serialized parameters, per the MIME type application/x-www-form-urlencoded. HttpParams is an immutable class.
We can add parameters to instance of HttpParams using its append(), appendAll and set() methods. These methods return the cloned instance of HttpParams after changing values.
3. Find the code snippet to pass query parameters using HttpParams.
updateBook(book: Book): Observable<Book> {
    const myParams = new HttpParams()
        .set("bookId", "101")
        .set("publisher", "XYZ");

    return this.http.put<Book>("/api/books", book, {
        params: myParams
    });
} 
4. We can also directly specify the query parameters to params property.
return this.http.put<Book>("/api/books", book, {
    params: {
        "bookId": "101",
        "publisher": "XYZ"
    }
}); 


6. Pass Context

1. To configure HTTP context, we need to configure context property in options argument.
put(url: string, body: any, options: {
    context?: HttpContext;
}) 
2. HttpContext stores arbitrary user defined values. It provides set() method to store a value in the context. It replaces the value if already present. We need to pass argument as key/value. Key is a type of HttpContextToken and value can be any object.
3. Suppose we have a HTTP context token as below.
export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false); 
Now specify this context to put() method as below.
updateBook(book: Book): Observable<Book> {
    const myContext = new HttpContext().set(IS_CACHE_ENABLED, true);
    return this.http.put<Book>("/api/books", book, {
        context: myContext
    });
} 

7. Using Credentials

To use credentials with request, we need to enable it in put() request. In options argument, assign true to withCredentials property as below.
updateBook(book: Book): Observable<Book> {
    const myHeaders = new HttpHeaders().set('Authorization', `Bearer ${authToken}`);
    return this.http.put<Book>("/api/books", book, {
        headers: myHeaders,
        withCredentials: true
    });
} 

8. Error Handling

In HTTP requests, there can be client-side error or network error. Server application can also throw error such as '500 Internal Server Error', '501 Not Implemented' etc.
To handle errors in HTTP requests using HttpClient, we need to use Angular HttpErrorResponse. It represents errors of following type.
1. Non-successful HTTP status,
2. An error while executing the request,
3. Error during parsing the response.
HttpErrorResponse provides error property using which we can handle errors.
To handle error, I am creating a function that will accept HttpErrorResponse as argument.
private hadnleError(error: HttpErrorResponse) {
    if (error.status === 0) {
        console.error("Error: A client-side or network error occurred.");
    } else {
        console.error("Error: ", error.error);
    }
    return throwError(() => new Error("An error occurred."));
} 
When HTTP request throws error, we can catch error using RxJS catchError as an object of HttpErrorResponse. We handle the error and then throw the custom error message.
updateBook(book: Book): Observable<Book> {
    const myHeaders = new HttpHeaders()
        .append("Content-Type", "application/json")
        .append("Content-Length", "70");

    return this.http.put<Book>("/api/books", book, {
        headers: myHeaders
    }).pipe(
        catchError(this.hadnleError)
    );
} 
Using retry() :
We can retry failed request. Sometimes it fixes error automatically because if error is network error then next request could be successful. To retry same request we can use RxJS retry() operator.
return this.http.put<Book>("/api/books", book, {
    headers: myHeaders
}).pipe(
    retry(2),
    catchError(this.hadnleError)
); 
retry(2) will attempt failed request two times. If in the first attempt, request is successful, then second attempt will not be made.

9. Subscribe to putHttpClient.put()

HttpClient.put() returns Observable object and when we subscribe this object only then HTTP PUT request is performed.
Here I have a service class and a component. In service class, I am creating methods to update resources on server using HttpClient. In component class, I will subscribe the observable object returned by HttpClient.put() method.
Find the service class containing the methods to execute HTTP PUT request.
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
import { Observable, catchError, retry, throwError } from 'rxjs';
import { Book } from '../book';
import { Writer } from '../writer';

const authToken = 'AUTH_TOKEN';

@Injectable({
    providedIn: 'root'
})
export class BookService {
    constructor(private http: HttpClient) { }
    updateWriter(writer: Writer): Observable<Writer> {
        return this.http.put<Writer>("/api/writers", writer);
    }
    updateBook(book: Book): Observable<HttpResponse<Book>> {
        const myHeaders = new HttpHeaders()
            .append("Content-Type", "application/json")
            .append("Content-Length", "70")
            .append('Authorization', `Bearer ${authToken}`);

        const myParams = new HttpParams()
            .set("bookId", "101")
            .set("publisher", "XYZ");

        return this.http.put<Book>("/api/books", book, {
            headers: myHeaders,
            params: myParams,
            observe: "response",
            responseType: "json",
            withCredentials: true
        }).pipe(
            retry(2),
            catchError(this.hadnleError)
        );
    }
    private hadnleError(error: HttpErrorResponse) {
        if (error.status === 0) {
            console.error("Error: A client-side or network error occurred.");
        } else {
            console.error("Error: ", error.error);
        }
        return throwError(() => new Error("An error occurred."));
    }
} 
Find the component that is calling service methods and subscribing them.
import { Component, OnInit } from '@angular/core';
import { BookService } from './services/book.service';
import { Book } from './book';
import { CommonModule } from '@angular/common';
import { Writer } from './writer';
import { HttpResponse } from '@angular/common/http';

@Component({
   selector: 'app-book',
   standalone: true,
   imports: [CommonModule],
   templateUrl: './book.component.html'
})
export class BookComponent implements OnInit {
   constructor(private bookService: BookService) { }
   ngOnInit() {
      this.updateWriter();
      this.updateBook();
   }
   updateWriter() {
      const writer: Writer = { id: 21, name: 'Ram', city: 'PrayagRaj' };
      this.bookService.updateWriter(writer).subscribe(writer => console.log(writer));
   }
   updateBook() {
      const book: Book = { id: 111, name: 'Hibernate By Mohan', category: 'Hibernate' };
      this.bookService.updateBook(book).subscribe({
         next: (response: HttpResponse<Book>) => {
            console.log(response.body);
         },
         error: (error: any) => {
            console.error(error);
         },
         complete: () => {
            console.log("Request completed")
         }
      });
   }
} 

10. Reference

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE












©2024 concretepage.com | Privacy Policy | Contact Us