Jennifer S
Jennifer S

Reputation: 1439

rxjs How to deal with null values in map operation?

I have an angular service that gets values from an http get, and then maps the values to an Observable of a specific object type as it returns. Sometimes the value of one of the properties is a string, and sometimes it is a null value that I want to set to a default empty string.

Is there a more elegant way to do this than what I have?

I have tried passing in a boolean value for the specific field that is of interest to me, the template.AreaId attribute, but I am concerned as more attributes may have this issue over time as there are more template objects created.

getTaskFromTemplate(extraChildTask:string, hasArea: boolean) : Observable<Task>{
    if (hasArea){
      return this.templateService.getTemplateByName(extraChildTask).pipe(
        map(template => {
          return {
            WorkItemId:'',
            WorkItemType:'Task',
            Title: template.Title,
            Description: template.Description,
            AssignedTo: '',
            TeamProject: template.TeamProjectName,
            AreaPathId: template.AreaId.toString(),
            IterationPathId: '',
            Application:'',
            State:'',
            TargetDate: null,
            OriginalEstimate: 0,
            CompletedWork: 0,
            RemainingWork:0,
            BusinessPriority: '',
            CreatedBy:'',
            CreatedDate: null,
            Priority:'',
            Notes:'',
            AreaPathName:'',
            IterationPathName:'',
          }; 
        }),
        catchError((error: HttpErrorResponse) => { return throwError(error); })
      )
    }
    return this.templateService.getTemplateByName(extraChildTask).pipe(
      map(template => {
        return {
          WorkItemId:'',
          WorkItemType:'Task',
          Title: template.Title,
          Description: template.Description,
          AssignedTo: '',
          TeamProject: template.TeamProjectName,
          AreaPathId: '',
          IterationPathId: '',
          Application:'',
          State:'',
          TargetDate: null,
          OriginalEstimate: 0,
          CompletedWork: 0,
          RemainingWork:0,
          BusinessPriority: '',
          CreatedBy:'',
          CreatedDate: null,
          Priority:'',
          Notes:'',
          AreaPathName:'',
          IterationPathName:'',
        }; 
      }),
      catchError((error: HttpErrorResponse) => { return throwError(error); })
    )
  }

}

This works, but I would prefer to have a way to better handle situations where a field is null, or does not have a string value.

Here is what I had before I had an issue with the template.AreaId value being null.

getTaskFromTemplate(extraChildTask:string) : Observable<Task>{
return this.templateService.getTemplateByName(extraChildTask).pipe(
    map(template => {
      return {
        WorkItemId:'',
        WorkItemType:'Task',
        Title: template.Title,
        Description: template.Description,
        AssignedTo: '',
        TeamProject: template.TeamProjectName,
        AreaPathId: template.AreaId.toString(),
        IterationPathId: '',
        Application:'',
        State:'',
        TargetDate: null,
        OriginalEstimate: 0,
        CompletedWork: 0,
        RemainingWork:0,
        BusinessPriority: '',
        CreatedBy:'',
        CreatedDate: null,
        Priority:'',
        Notes:'',
        AreaPathName:'',
        IterationPathName:'',
      }; 
    }),
    catchError((error: HttpErrorResponse) => { return throwError(error); })
  )
}

Upvotes: 0

Views: 3508

Answers (1)

miqh
miqh

Reputation: 3664

Expanding out from my comment, my suggestion about using the ternary operator would look like the following:

getTaskFromTemplate(extraChildTask:string) : Observable<Task>{
return this.templateService.getTemplateByName(extraChildTask).pipe(
    map(template => {
      return {
        WorkItemId:'',
        WorkItemType:'Task',
        Title: template.Title,
        Description: template.Description,
        AssignedTo: '',
        TeamProject: template.TeamProjectName,
        // Just use a ternary to prevent the `toString()` call if the property
        // does not exist on the template
        AreaPathId: template.AreaId != null ? template.AreaId.toString() : '',
        IterationPathId: '',
        Application:'',
        State:'',
        TargetDate: null,
        OriginalEstimate: 0,
        CompletedWork: 0,
        RemainingWork:0,
        BusinessPriority: '',
        CreatedBy:'',
        CreatedDate: null,
        Priority:'',
        Notes:'',
        AreaPathName:'',
        IterationPathName:'',
      }; 
    }),
    catchError((error: HttpErrorResponse) => { return throwError(error); })
  )
}

Also expanding on what I said about the potential to dynamically map properties (i.e. to avoid explicitly specifying the mapping of each property in involved), a demonstration of a possible solution might look like the following:

const { of } = rxjs;
const { map } = rxjs.operators;

const baseTask = {
    Title: '',
    Description: '',
    TeamProjectName: '',
    AreaPathId: '',
    WorkItemId: '',
    WorkItemType: 'Task',
    // ...
};

function getTaskFromTemplate(extraChildTask, hasArea) {
  return getTemplateByName(extraChildTask).pipe(
    map(template => {
      // Merges properties the acquired template with those in the base task
      // with the properties in the template taking precedence
      return Object.assign({}, baseTask, template);
    }),
  );
}

// Stub implementation that just returns a fixed template
function getTemplateByName(extraChildTask) {
  return of({
    Title: 'Title',
    Description: 'Description',
    TeamProjectName: 'Team Project Name',
    AreaPathId: 'Area Path ID',
    // ...
  });
}

// Example usage in action
getTaskFromTemplate('something').subscribe(task => {
  console.log(task);
});
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>

As I noted, if some task property names don't match names from the template you need to map from, this can be solved by setting up some additional mapping steps.

Upvotes: 1

Related Questions