简体   繁体   English

在 Angular 中返回未定义的服务数据获取器

[英]Service data getter returning undefined in Angular

I'm having trouble finding out why my ShopManagerService keeps returning undefined when the data is accessed by a pipe.当 pipe 访问数据时,我无法找出为什么我的ShopManagerService一直返回undefined Here is the ShopManagerService :这是ShopManagerService

@Injectable({ providedIn: 'root' })
export class ShopManagerService {
    private shopPreferences: ShopPreferences = null;

    setPreferences(shopPreferences: ShopPreferences) {
        this.shopPreferences = shopPreferences;
    }

    getDateFormat(){
        if(this.shopPreferences == null || this.shopPreferences.time_format.date == null) return;
        return this.shopPreferences.time_format.date;
    }
    ...
    // more data getters
}

The previous field shopPreferences is set in the service ApiManagerService , like so:前一个字段shopPreferences在服务ApiManagerService中设置,如下所示:

@Injectable({ providedIn: 'root' })
export class ApiManagerService {

    private token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MjcwMjA3MiwidGltZXN0YW1wIjoiMjAyMS0wNC0wOSAwOToxNToxNS4';
    private webzine_id: string = "2702072";

    constructor(private http: HttpClient, private shopManager: ShopManagerService) {
        // We want to keep shop preferences updated throughout execution
        timer(0, 5000).pipe(
            filter(() => this.webzine_id && this.webzine_id !== ""),
            switchMap(() => this.fetchShopPreferences().pipe(first()))
        )
        .subscribe(preferences => this.shopManager.setPreferences(preferences)); 
    }

    fetchShopPreferences() {
        const url = "https://commerce.ww-api.com/commerceapi/v1/front/front_url/" + this.webzine_id + "/";
        return this.http
            .get<ShopPreferences>(url, {
                headers: new HttpHeaders({
                    token: this.token,
                }),
            });
    }
    ...
    // more api requests
}

The component looks like this:该组件如下所示:

@Component({
  selector: 'app-order-details',
  templateUrl: './order-details.component.html',
  styleUrls: ['./order-details.component.css']
})
export class OrderDetailsComponent {

      constructor(private apiManager: ApiManagerService, private shopPreferencesService: ShopManagerService){ }

      closeDetails(){
          /* implement close details functionality */
      }
}

The pipe is called in the.html like so: pipe 在.html 中调用如下:

<div id="details-container">
    <div id="details-header">
        <div class="header-text">
            <!-- hard coded for now -->
            <label>#3172</label>
            <label>{{"01/18/2021" | addOrderDateHourFormat:"date"}}</label>
        </div>
        <div class="close-button" (click)="closeDetails()">
            <img src="../../../assets/my-orders/close-icon.svg">
        </div>
    </div>
   <app-order-properties></app-order-properties>
   <app-order-channel></app-order-channel>
</div>

Inside the pipe, the code looks like this:在 pipe 内部,代码如下所示:

@Pipe({
    name: 'addOrderDateHourFormat',
})
export class OrderDateFormatPipe implements PipeTransform {

    constructor(private formatService: FormatManagerService){}

    transform(value: string, type: string, trigger: number) {
        if(type === "hour"){
            return this.formatService.formatTime(value);
        }
        else if(type === "date"){
            return this.formatService.formatDate(value);
        }
        else if(type === "date+hour"){
            return this.formatService.formatDateAndTime(value)
        }
    }

}

Finally, this is the look inside the FormatManagerService :最后,这是FormatManagerService内部的外观:

@Injectable({ providedIn: 'root' })
export class FormatManagerService {

    constructor(private datePipe: DatePipe, private shopPrefencesService: ShopManagerService) {}

    formatDate(date: string){
        let dateFormat = this.shopPrefencesService.getDateFormat(); // returning undefined
        if(dateFormat === "GBCOMMERCE_DATEFORMAT_1"){
            return this.datePipe.transform(date, 'EEEE, LLLL d yyyy');
        }
        else if(dateFormat === "GBCOMMERCE_DATEFORMAT_2"){
            return this.datePipe.transform(date, 'LLLL d yyyy');
        }
        else if(dateFormat === "GBCOMMERCE_DATEFORMAT_3"){
            return this.datePipe.transform(date, 'MM/d/yyyy');
        }
        else if(dateFormat === "GBCOMMERCE_DATEFORMAT_4"){
            return this.datePipe.transform(date, 'MM.d.yyyy');
        }
        else if(dateFormat === "GBCOMMERCE_DATEFORMAT_5"){
            return this.datePipe.transform(date, 'MM-d-yyyy');
        }
    }
    ...
    // more format methods
}

How come the call this.shopPrefencesService.getDateFormat() inside the formatDate() method keeps returning undefined ?为什么在formatDate()方法中调用this.shopPrefencesService.getDateFormat()一直返回undefined What am I missing here?我在这里想念什么?

Thanks in advance for the help.在此先感谢您的帮助。

EDIT: added.html and pipe code.编辑:添加。html 和 pipe 代码。

I'd say this happens because of asynchrony.我会说这是因为异步而发生的。 I'd reckon you call the date pipe long before the REST-call returns the value.我认为您在 REST 调用返回值之前很久就调用了日期 pipe。 And the pipe gets only fired once. pipe 只被触发一次。

My suggestion is the following.我的建议如下。

Make your ShopPreferences an Observable of Type BehaviorSubject and wait for the value to be set in the OrderDetailsComponent.将 ShopPreferences 设为 BehaviorSubject 类型的 Observable,然后等待在 OrderDetailsComponent 中设置值。 Only if the value is present render the div.仅当值存在时才呈现 div。

Your ShopManagerService您的 ShopManagerService

@Injectable({ providedIn: 'root' })
export class ShopManagerService {
  private shopPreferences: BehaviorSubject<ShopPreferences> = new BehaviorSubject<ShopPreferences>(null);

  setPreferences(shopPreferences: ShopPreferences): void {
    this.shopPreferences.next(shopPreferences);
  }

  getPreferencesAsObservable(): Observable<ShopPreferences> {
    return this.shopPreferences.asObservable();
  }

  getDateFormat(): Date{
    if (this.shopPreferences.getValue() === null || this.shopPreferences.getValue().time_format.date === null) {
      return;
    }
    
    return this.shopPreferences.getValue().time_format.date;
  }

  // more data getters
}

Your OrderDetailsComponent您的 OrderDetailsComponent

@Component({
  selector: 'app-order-details',
  templateUrl: './order-details.component.html',
  styleUrls: ['./order-details.component.css']
})
export class OrderDetailsComponent {
    shopPreferencesArePresent = false;        

      constructor(private apiManager: ApiManagerService, private shopPreferencesService: ShopManagerService){
          this.shopPreferencesService.getPreferencesAsObservable().subscribe( value => {
              if (value) {
                  this.shopPreferencesArePresent = true;
              } 
          });

      }

      closeDetails(){
          /* implement close details functionality */
      }
}

And the HTML of your OrderDetailsComponent以及 OrderDetailsComponent 的 HTML

    <div id="details-container">
    <div id="details-header">
        <div *ngIf="shopPreferencesArePresent" class="header-text">
            <!-- hard coded for now -->
            <label>#3172</label>
            <label>{{"01/18/2021" | addOrderDateHourFormat:"date"}}</label>
        </div>
        <div class="close-button" (click)="closeDetails()">
            <img src="../../../assets/my-orders/close-icon.svg">
        </div>
    </div>
   <app-order-properties></app-order-properties>
   <app-order-channel></app-order-channel>
</div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM