Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
67 views
in Technique[技术] by (71.8m points)

javascript - Make angular service return class field as a observable

Here is what I want to achieve: There is a component that has a UserService in it. After log in I want to call that service to retrieve user data from backend and store it in a service field. Then after I reload main page I want that service to do not call backend again, just return previously stored data. Additionally I want some divs to do not load before a data is loaded. How can I achieve that? Should I make getAllUSerInfoNormal() method to return observable, if its possible how should I do that? Or there is some other way?

Here is what I have got right now:

Service class:

@Injectable()
export class AccountService {

    userAccount : UserAccount;
    constructor(private _httpClient: HttpClient) {
    }


     getAllUSerInfoNormal(): UserAccount {
    if (!this.userAccount) {
        this._httpClient.get<UserAccount>(Constants.apiRoot + 'account/accountInfo').subscribe(user => {
            this.userAccount = user;
            return this.userAccount
        })
    } else {
        return this.userAccount;
    }
}



Component class:

@Component({
  selector: 'app-main-view',
  templateUrl: './main-view.component.html',
  styleUrls: ['./main-view.component.scss']
})
export class MainViewComponent implements OnInit {

  private account: UserAccount;
  private isDataLoaded = false;

  constructor(private _router: Router,
              private _accountService: AccountService) {
    setInterval(() => {
      this.updateResources();
    }, 1);
  }

  ngOnInit() {
    this._accountService.getAllUSerInfoNormal();
  }

  updateResources(){

  }
}



Component template:

<div class="ingame-menu">
    <div *ngIf="isDataLoaded">
        <div class="resources">
            <div class="resource"><img src="../../../assets/images/resources/gold.png" title="Gold"/>
                <br/> {{account.gold}}</div>
            <div class="resource"><img src="../../../assets/images/resources/wood.png" title="Wood"/>
                <br/> {{account.wood}}</div>
            <div class="resource"><img src="../../../assets/images/resources/stone.png" title="Stone"/>
                <br/> {{account.stone}}</div>
            <div class="resource"><img src="../../../assets/images/resources/meat.png" title="Meat"/>
                <br/> {{account.people}} </div>
        </div>
    </div>
    <div class="ingame-nav">
        <ul>
            <li><a routerLink="overview">Overview</a></li>
            <li><a routerLink="population">Population</a></li>
            <li><a routerLink="resources">Resources</a></li>
            <li><a routerLink="research">Research</a></li>
            <li><a routerLink="buildings">Buildings</a></li>
            <li><a routerLink="barracks">Barracks</a></li>
            <li><a routerLink="tavern">Tavern</a></li>
            <li><a routerLink="defence">Defence</a></li>
        </ul>
    </div>
    <div id="content">
        <router-outlet></router-outlet>
    </div>
</div>
question from:https://stackoverflow.com/questions/65937349/make-angular-service-return-class-field-as-a-observable

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can save the loaded user in the lcoalStorage after the first load and then just read it on the following accesses.

This is a simple solution to learn. But in a real application scenario you shouldn't have "infinite time" cache like this. And must have a way to update the cache sometimes.

Change the service class to :

@Injectable()
export class AccountService {

    get userAccount() {
      return JSON.parse(localStorage.getItem('user'));
    };

    constructor(private _httpClient: HttpClient) {
    }


    getAllUSerInfoNormal(forceBanckendCall: boolean): Observable<UserAccount> {
        if (this.userAccount && !forceBackendCall) {
           return of(this.userAccount);
        }

        return this._httpClient.get<UserAccount>(Constants.apiRoot + 'account/accountInfo')
           .pipe(
             tap(user => localStorage.setItem('user', JSON.stringify(user)))
           );

    }

then the component class to:

@Component({
  selector: 'app-main-view',
  templateUrl: './main-view.component.html',
  styleUrls: ['./main-view.component.scss']
})
export class MainViewComponent implements OnInit {

  private account: UserAccount;
  private isDataLoaded = false;

  constructor(private _router: Router,
              private _accountService: AccountService) {
    setInterval(() => {
      this.updateResources();
    }, 1);
  }

  ngOnInit() {
    this._accountService.getAllUSerInfoNormal().subscribe(account => this.userAccount = account);
  }

  updateResources(){

  }
}

then in your component template place a *ngIf="userAccount" in the elements that you want to be loaded just after the user is present.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...