2017-08-11 32 views
0

我創建了一個角度爲4的web應用程序,其中包含從firebase(通過angular fire 2)讀取的數據,但在顯示firebase數據的頁面上,我必須刷新頁面一次或數據顯示前兩次。這不是新數據,也不是現有數據。另外,當我將數據寫入Firebase時,在實際寫入任何內容之前,我已經從前端運行了兩次函數。可能是什麼問題呢?刷新以顯示來自Firebase + Angular 4的內容

控制器

import { Component, OnInit } from '@angular/core' 

//import api service 
import { FreeAgentApiService } from '../free-agent-api.service' 

//import list item object 
import { ListItem } from './listitem' 

//import search componenet 
import { SearchComponent } from '../search.component' 

//import router 
import { Router, ActivatedRoute, ParamMap } from '@angular/router' 

//import animations 
import { moveIn, fallIn, moveInLeft } from '../router.animations' 

@Component({ 
    selector: 'app-dashboard', 
    templateUrl: './dashboard.component.html', 
    styleUrls: ['./dashboard.component.css'], 
    animations: [moveIn(), fallIn(), moveInLeft()], 
    //host: {'[@moveIn]': ''} 
}) 


export class DashboardComponent implements OnInit { 

    //user name 
    name: any 

    //state 
    state: string = '' 

    //search string 
    search: String; 

    //declare filter 
    filterOptions = ["Status: Active", "Status: Completed", "Status: Cancelled", "Status: Hidden"] 
    filter = "Status: Active" 

    //projects array 
    projects = []; 

    //inject api service and router into component 
    constructor(private freeAgentApi: FreeAgentApiService, private router: Router){ 

    this.freeAgentApi.afAuth.authState.subscribe(auth => { 
     if(auth){ 
     this.name = auth 
     } 
    }) 
    } 

    ngOnInit(){ 
    //init projects array on page load 
    this.projects = this.freeAgentApi.getAllProjects() 
    //console.log(this.projects) 
    } 

    //function to go to project 
    openProject(projectName, projectUrl, projectEndDate, clientName){ 
    //router with parameters 
    this.router.navigate(['project'], { 
     queryParams: { projectName: projectName, projectUrl: projectUrl, projectEndDate: projectEndDate, clientName} 
    }) 
    } 
} 

查看

<!-- header section --> 
<div class="header"> 
    <div class="headerItem"> 
     <img class="logo" alt="logo" src="../../assets/images/now-boarding-logo.svg"/> 
    </div> 

    <div class="headerItem"> 
     <my-search (onSearchChange)="search = $event"></my-search> 
    </div> 

    <div class="headerItem">  
     <a class="navTop">projects</a> 
     <a class="navTop">reporting</a> 
    </div> 
</div> 

<!-- main section --> 
<div class="main" > 
    <select class="statusfilter" [(ngModel)] = "filter"> 
     <option *ngFor="let f of filterOptions">{{f}}</option> 
    </select> 

    <div class="listentry" 
     *ngFor="let p of projects | statusFilter: filter | searchPipe:'projectName':search" 
     (click) = "openProject(p.projectName,p.projectUrl,p.endsOn, p.clientName)"> 

     <div class="listentryitem"> 
      <div class="userNameCircle">ML</div> 
      <p class="project-title">{{p.projectName}}</p> 
     </div> 

     <div class="listentryitem-2"> 
      <p class="note-grey">{{ p.clientName }} </p> 
     </div> 

     <div class="listentryitem-3"> 
      <input class="dateListItem field" type="text" value="{{p.endsOn | date: 'EEE d MMM'}}" disabled="true"/> 
      <input class="budgetTimeInput" name="budgetTime" type="text" value="{{p.taskBudgetTime}}" disabled="true"> 
      <progress class="budgetProgressBar" value="{{p.totalTimeLogged}}" max="{{p.taskBudgetTime}}"> 
       {{ p.taskBudgetTime - p.totalTimeLogged }} 
      </progress> 
     </div> 
    </div> 
</div> 

提供的API調用

import { Injectable } from '@angular/core'; 

//import http modules 
import { Http, Headers, Response, RequestOptions } from '@angular/http'; 
import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/catch'; 
import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/toPromise'; 
import 'rxjs/add/operator/mergeMap'; 

//import firebase modules 
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database'; 
import { AngularFireAuth } from 'angularfire2/auth' 
import * as firebase from 'firebase/app' 

//import list item component 
import { ListItem } from './dashboard/listitem' 

//import project item object 
import { ProjectItem } from './project/projectItem' 

@Injectable() 
export class FreeAgentApiService { 

    //inject Http 
    constructor(private http: Http, public afAuth: AngularFireAuth, public af: AngularFireDatabase) { 

    } 

    //function to populate authorization header 
    createAuthorizationHeader(headers: Headers) { 
    headers.append('Authorization', 'Bearer 19wUDe8bnIsfuLsa1MhJfblztRj4WIZYCpnc_NQGG'); 
    } 



    //project item 
    //project = new ProjectItem() 

    //array to store projects 
    //projects = [] 

    //declare array to store tasks 
    //tasks = [] 

    //decalare array of expenses 
    //expenses = []; 

    //declare total tracked time * hourly rate 
    //totalTracked = 0 

    //function to get all projects 
    getAllProjects(): ListItem[]{ 

    let projects = [] 

    //declare headers 
    let headers = new Headers(); 

    //add headers 
    this.createAuthorizationHeader(headers); 

    //make api call and return promise array 
    this.http.get('https://api.freeagent.com/v2/projects?sort=-updated_at&per_page=100', { headers: headers }) 
    .map((result: Response) => { 

     //iterate projects list 
     result.json().projects.forEach(element => { 

      //varaible to hold single project 
      let project = new ListItem(); 

      //assign values to list item object, named 'project' here 
      project.projectName = element.name; 
      project.status = element.status; 
      project.endsOn = element.ends_on; 
      project.clientUrl = element.contact; //use string to get client name 
      project.projectUrl = element.url; //use string to get timeslipt 

      //add current project to projects array 
      projects.push(project); 
     }) 
     }) 

     //get contacts 
     .flatMap(() => this.http.get('https://api.freeagent.com/v2/contacts', {headers: headers})) 
     .map((res: Response) => res.json()) 
     .subscribe((res) => { 

      //iterate projects array 
      projects.forEach((project,index) => { 
      //iterate contacts array 
      res.contacts.forEach(r => { 
       //match contact id with project 
       if(project.clientUrl == r.url){ 
       //assign client name to organisation name 
       project.clientName = r.organisation_name 
       } 
      }) 
      }) 
     }) 

     //get timeslips 
     /*this.http.get('https://api.freeagent.com/v2/timeslips', {headers: headers }) 
     .map((res: Response) => res.json()) 
     .subscribe((res) => { 

     //iterate projects array 
     projects.forEach(element => { 
      element.timeLogged = 0 
      //iterate timeslips 
      res.timeslips.forEach(element2 => { 
      //check if project url matches 
      if(element2.project == element.projectUrl){ 
       //assign remaining time slip (increment) 
       element.timeLogged += parseFloat(element2.hours) 
      } 
      }) 
     }) 
     })*/ 

     this.getFromFirebase(projects) 
     return projects 
    } 

    //function to save projects to firebase 
    getFromFirebase(projects: any[]){ 

    //get firebase list 
    let storedProjects = this.af.list('/projects/') 
    .subscribe((res) => { 
     //console.log(res) 
     //check if results array contains values 
     if(res.length != 0){ 
     //iterate firebaselist 
     projects.forEach(p => { 
      //iterate projects arrays 
      res.forEach(r => { 
      //check if project is stored in firebase 
      if(r.projectUrl == p.projectUrl){ 
       //assign time logged 
       if(r.taskBudgetTime != undefined && r.taskBudgetTime != null) { 
       p.taskBudgetTime = r.taskBudgetTime 
       } 

       if(r.totalTimeLogged != undefined && r.totalTimeLogged != null){ 
       p.totalTimeLogged = r.totalTimeLogged 
       } 
      } 
      }) 
     }) 
     } 
    }) 

    return projects; 
    } 

    //function to sync firebase object with local object 
    loadProjectToFromFireBase(project){ 
    //specify key, key is last four characters of url 
    let key = project.projectUrl.substring(project.projectUrl.length - 4) 

    //get firebase object, user project url as query parameter 
    let currentProject = this.af.list('projects/', { 
     query: { 
     orderByChild: 'projectUrl', 
     equalTo: project.projectUrl, 
     limitToFirst: 1, 
     } 
    }) 
    .subscribe((res) => { 
     //check if any fields are undefined and assign value 
     for(let field in project){ 
     if(typeof project[field] == 'undefined'){ 
      console.log(field + " -> " + project[field]) 

      if(field == 'clientName'){ 
      project[field] = 'n/a' 
      } else { 
      project[field] = '' 
      } 
     } 
     } 

     console.log(project) 
     //if no results are returned, write object to firebase 
     if (res.length == 0){ 
     //write project object to firebase 
     let ref = this.af.list(`projects/`) 
     .update(key,project) 

     } else { 

     project.taskBudgetTime = 0 
     //get expenses from firebase 
     //iterate expenses array 
     project.expenses.forEach(expense => { 
      //init expense 
      expense.expenseBudget = 0 
      //iterate expenses array 
      res[0].expenses.forEach(ex => { 
      if(expense.url == ex.url){ 
       expense.expenseBudget = ex.expenseBudget 
      } 
      }) 
     }) 

     //get tasks from firebase 
     //iterate project tasks array 
     project.tasks.forEach(task => { 
      //iterate results array and match task 
      res[0].tasks.forEach(r => { 
      //check if task exists 
      if(task.url == r.url){ 
       //load task budget in time 
       if(r.budgetTime != undefined && r.budgetTime != null){ 
       task.budgetTime = r.budgetTime 
       } 
      } 
      }) 

      //incement billing rate * task budget time 
      project.totalRateBudgetTime += Math.round(task.billing_rate * task.budgetTime) 
      //incement total task budget time 
      project.taskBudgetTime += Math.round(task.budgetTime) 
     } 
    )} 

    }) 
    } 

    //function to get tasks 
    getTasks(projectUrl: string){ 

    let project = new ProjectItem() 

    //declare headers 
    let headers = new Headers() 
    //add headers 
    this.createAuthorizationHeader(headers) 

    //get tasks from project 
    this.http.get('https://api.freeagent.com/v2/tasks?project=' + projectUrl, {headers: headers }) 
    .map((res: Response) => { 
     res.json().tasks.forEach(t => { 
     //map to local variable 
     if(t.is_billable){ 
      let task 
      task = t 
      project.tasks.push(task) 
     } 
     }) 
    }) 
    .flatMap(() => this.http.get('https://api.freeagent.com/v2/timeslips?project=' + projectUrl, {headers: headers })) 
    .map((res: Response) => res.json()) 
    .subscribe((res) => { 
     //init task budget time 
     project.taskBudgetTime = 0 

     //init total budget rate * total budget time 
     project.totalRateBudgetTime = 0 


     //iterate tasks array 
     project.tasks.forEach((task, index) => { 
     //add time logged field 
     task.timeLogged = 0 
     //load budget time from firebase or init to zero if not exists 
     task.budgetTime = 0 
     //iterate timeslips 
     res.timeslips.forEach(timeslip => { 
      //check if task url mathes 
      if(task.url == timeslip.task){ 
      //increment time logged 
      task.timeLogged += parseFloat(timeslip.hours) 
      } 
     }) 

     //increment total tracked time * task billing rate 
     project.totalTracked += Math.round(task.timeLogged * task.billing_rate) 

     //increment total time logged for all tasks 
     project.totalTimeLogged += Math.round(task.timeLogged) 

     }) 
    }) 

    //console.log(project) 
    return project 
    } 

    //function to get bills (not expenses) 
    getExpenses(projectUrl: string){ 

    //declare headers 
    let headers = new Headers(); 

    //add headers 
    this.createAuthorizationHeader(headers) 

    //get expenses from project 
    return this.http.get('https://api.freeagent.com/v2/bills?project=' + projectUrl, {headers: headers }) 
    .map((res) => res.json()) 

    } 

    //function to get invoices 
    getInvoices(projectUrl: string){ 

    //declare total invoices 
    let invoiceTotal = 0; 

    //declare headers 
    let headers = new Headers(); 

    //add headers 
    this.createAuthorizationHeader(headers) 

    //get invoices from project 
    return this.http.get('https://api.freeagent.com/v2/invoices?project=' + projectUrl, {headers: headers }) 
    .map((res) => res.json()) 
    } 

    //funtion to get estimates 
    getEstimates(projectUrl: string){ 

    //declare headers 
    let headers = new Headers() 
    //add headers 
    this.createAuthorizationHeader(headers) 

    //get approved estimates 
    return this.http.get('https://api.freeagent.com/v2/estimates?project=' + projectUrl, {headers: headers}) 
    .map((res) => res.json()) 

    } 

    //function to extract promise data 
    private extractData(res: Response) { 
    let body = res.json();   
    return body; 
    } 

    //function to handle promise errors 
    private handleErrorPromise (error: Response | any) { 
    console.error(error.message || error); 
    return Promise.reject(error.message || error); 
    } 
} 
+3

當您詢問有關由您的代碼引起的問題的問題時,如果您提供可用於重現問題的代碼,您將得到更好的答案。該代碼應該是...... **最小** - 儘可能使用盡可能少的代碼,仍然會產生相同的問題。要經歷的代碼越多,人們發現問題的可能性就越小。請參見[如何創建最小,完整和可驗證的示例](https://stackoverflow.com/help/mcve)。 – georgeawg

回答

2

親瑕疵是你期待異步資源是同步的。 Basicallt這是發生了什麼:

getAllProject(){ 
    let projects = []; 

    doAsyncStuffThatTakes30Seconds.subscribe(result => projects = result) 

    return projects; 
} 

在您返回項目的時候,異步的東西還沒有完成。這就是爲什麼你沒有看到數據。這可能讓你想知道爲什麼你有時會看到數據。這是因爲firebase緩存結果直到它發生變化。所以異步的東西在處理之前完成。

解決的辦法是返回一個observable並完成流中的所有處理。現在您可以在您的組件中訂閱以獲取數據。

+0

我明白你在說什麼,但我不知道如何構造代碼。有一個http請求從'free agent api'獲得信息,我必須在調用firebase之前先返回這些信息,因爲要加載的firebase信息與此信息相關聯。我將如何在可觀察的情況下實現這一目標? –

+0

您可以使用'switchMap'。例如:'http.get('someUrl1).switchMap(someValue => http.get(someValue))' –

+0

謝謝,讓我試試這個並且回覆你 –