角度更新视图

时间:2018-02-13 19:38:10

标签: javascript angular observable

我正在开发一个带有分页的角度应用程序。我的组件包含所有员工的列表。初始化时,它从服务中获取数据。一旦这些数据返回,列表就会更新,我在我的分页方法中使用它。此时,我的应用程序状态已更改,因此我将要更新视图。一切都很好,但有延迟。我只看到F5之后的变化。我不明白,我怎么能告诉Angular在特定时刻更新视图。为了确保在激活路径之前执行任务,我使用解析器,但它仅对第一次渲染有帮助。我明白,问题在于异步数据,但我不明白,如何修复它。请帮我解决这个问题。

我的组件:

@Component({
    selector: "paTable",
    moduleId: module.id,
    templateUrl: "table.component.html",
    styleUrls: ["table.component.css"]
})
export class TableComponent implements OnInit{
    private allEmployees: Employee[];
    pager: any =  {};
    pagedItems: any[];
    constructor(private model: Model, private paginationService: PaginationService){
        console.log('constructor');
    }

    ngOnInit(){
        this.allEmployees = this.model.getAllEmployees();
        console.log(this.allEmployees);
        this.setPage(1);
    }

    deleteEmployee(key: number) {
        this.model.deleteEmployee(key);
    }

    setPage(page: number){
        console.log(page)
        if (page < 1 || page > this.pager.totalPages) {
            return;
        }

        // get pager object from service
        this.pager = this.paginationService.getPagination(this.allEmployees.length, page);
        console.log(this.allEmployees.length);
        console.log(this.pager.startIndex);
        // get current page of items
        this.pagedItems = (this.model.getAllEmployees()).slice(this.pager.startIndex, this.pager.pageSize + 1);
        console.log(this.pagedItems);
    }
}

我的服务:

@Injectable()
export class Model{
    private employees: Employee[] = new Array<Employee>();
    private departments: Department[] = new Array<Department>();

    constructor(private dataSource: DataSource){
        console.log('model');
        this.dataSource.getEmployeesData().subscribe(data => this.employees = data);
        this.dataSource.getDepsData().subscribe(data => this.departments = data);
    }

    getAllEmployees(): Employee[]{
        return this.employees;
    }

    getAllDeps(): Department[]{

        return this.departments;
    }

    getEmployee(id: number): Employee{
        return this.employees.find(e => e.id == id);
    }


    saveEmployee(employee: Employee){
        if(employee.id == 0 || employee.id == null){
            this.dataSource.saveEmployee(employee).subscribe(e => {employee.id = e.insertId; this.employees.push(employee)});
        }else{
            this.dataSource.updateEmployee(employee).subscribe(() => {
                let index = this.employees.findIndex(e => e.id == employee.id);
                this.employees.splice(index,1,employee);
            });
        }
    }
}

我的模板:

<tbody>    
        <tr *ngFor="let item of pagedItems; let i = index">
            <td>{{i+1}}</td>
            <td>{{item.firstName}}</td>
            <td>{{item.lastName}}</td>
            <td [ngSwitch] = "item.isActive">
                <span *ngSwitchCase ="1">Yes</span>
                <span *ngSwitchDefault>No</span>
            </td>
            <td [ngSwitch] = "item.emp_depID">
                <span *ngSwitchCase = "1">HR</span>
                <span *ngSwitchCase = "2">Tech</span>
                <span *ngSwitchCase = "3">Finance</span>
                <span *ngSwitchDefault>No data</span>
            </td>
            <td>
                <button class="btn btn-danger btn-xs" (click)="deleteEmployee(item.id)">
                    Delete
                </button>
            </td>
            <td>    
                <button class="btn btn-primary btn-xs" [routerLink]="['/form', 'edit', item.id]">
                    Edit
                </button>
            </td>
        </tr>
    </tbody>

我的解析器:

@Injectable()
export class ModelResolver{

    constructor(private model: Model, private dataSource: DataSource, 
                private messages: MessageService){

    }
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Employee[]>{
        if (this.model.getAllEmployees().length == 0){
            this.messages.reportMessage(new Message("Loading data..."));
            return this.dataSource.getEmployeesData(); 
        }
    }
}

app.routing:

const routes: Routes = [
                            { path: "form/:mode/:id", component:FormComponent }, 
                            { path: "form/:mode", component:FormComponent},
                            { path: "", component: TableComponent, resolve:{model:ModelResolver} },
                        ]
export const routing = RouterModule.forRoot(routes);

数据源:

@Injectable()
export class DataSource{

    constructor(private http: Http, @Inject(URL) private url: string){
        console.log('datasource')
    }

    getEmployeesData(): Observable<Employee[]>{
        return this.sendRequest(RequestMethod.Get, `${this.url}employeeslist`);
    }
    getDepsData(): Observable<Department[]>{
        return this.sendRequest(RequestMethod.Get, `${this.url}depslist`);
    }
    saveEmployee(employee: Employee): Observable<Response>{
        return this.sendRequest(RequestMethod.Post, `${this.url}save`, employee);
    }
    updateEmployee(employee:Employee): Observable<Employee>{
        return this.sendRequest(RequestMethod.Put, `${this.url}${employee.id}`, employee);
    }
    deleteEmployee(id: number): Observable<Employee>{
        return this.sendRequest(RequestMethod.Delete, `${this.url}${id}`);
    }

    private sendRequest(method: RequestMethod, url: string, body?: Employee){
        return this.http.request(new Request({
            method: method,
            url: url,
            body: body
        })).map(response => response.json());
    }
}

4 个答案:

答案 0 :(得分:0)

我的“模特”服务看起来更像是这样:

private products: IProduct[];

getProducts(): Observable<IProduct[]> {
    if (this.products) {
        return of(this.products);
    }
    return this.http.get<IProduct[]>(this.productsUrl)
                    .pipe(
                        tap(data => console.log(JSON.stringify(data))),
                        tap(data => this.products = data),
                        catchError(this.handleError)
                    );
}

当第一个请求进入时,products属性尚未设置,因此它将获取数据。对于将来的请求,它将返回先前检索的列表。

以这种方式执行,该方法返回一个Observable,因此解析器可以工作。否则该组件可以订阅。

所以:

1)更改您的服务以返回可观察的内容。

订阅组件中的observable。

在检索到数据后,将要执行的代码移动到订阅内。

OR

2)更改您的服务以返回可观察的

使用解析器获取数据

使用组件中的解析器数据。这样,只要组件初始化,数据就可用。

例如,请考虑按如下方式更改代码:

getAllEmployees(): Observable<Employee[]>{
    if (this.employees) {
       return of(this.employees);
    }
   return this.dataSource.getEmployeesData();
}

从构造函数中删除调用。

然后你的组件(或你的解析器......不是两者)都可以调用getAllEmployees方法并取回observable。

例如:

ngOnInit(){
    this.model.getAllEmployees().subscribe(
          employees => { 
             console.log(employees);
             this.allEmployees = employees;
             this.setPage(1);
          }
    );
}

答案 1 :(得分:0)

我将此添加到单独的答案中,因为先前的答案太长了。除了我之前的答案中概述的模型服务更改,您可以使用解析器的数据,如下所示:

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

import { IProduct } from './product';

@Component({
    templateUrl: './app/products/product-detail.component.html'
})
export class ProductDetailComponent implements OnInit {
    pageTitle: string = 'Product Detail';
    product: IProduct;
    errorMessage: string;

    constructor(private route: ActivatedRoute) { }

    ngOnInit(): void {
        this.product = this.route.snapshot.data['product'];
    }
}

请注意,我正在注入ActivatedRoute。然后使用this.route.snapshot.data读取解析器检索到的数据。

在路由配置中,您将数据命名为model,因此语法为:

ngOnInit(){
    this.allEmployees = this.route.snapshot.data['model'];
    console.log(this.allEmployees);
    this.setPage(1);
}

答案 2 :(得分:0)

在我的代码中,解析器用于确保在激活路由之前执行任务。在ModelResolver类中调用resolve方法时,它会检查数据模型中的对象数,以确定对服务器的HTTP请求是否已完成。如果数据模型中没有对象,则resolve方法从DataSource返回Observable。因此,在模型填充来自服务器的数据之前,它会阻止用户查看表。我认为问题在于ngOnInit()方法,因为this.model.getAllEmployees();返回一组员工,我将其分配给变量并在我的代码中使用它。当调用ngOnInit()时,异步请求尚未完成,并且我的变量从该方法获取旧数据。新数据到达有一些延迟,所以只有在F5之后,我才能看到我的新结果。我删除了ngOnInit()方法并仅使用我的方法。 这是我的代码。

@Component({
        selector: "paTable",
        moduleId: module.id,
        templateUrl: "table.component.html",
        styleUrls: ["table.component.css"]
    })
    export class TableComponent{
        employeesPerPage = 10;
        selectedPage = 1;
        totalPages: number;

        constructor(private model: Model){

        }

        getAllEmployees(): Employee[]{
           let pageIndex = (this.selectedPage - 1)*this.employeesPerPage; 
           return this.model.getAllEmployees().slice(pageIndex, pageIndex + this.employeesPerPage);
        }

        getKey(index: number, employee: Employee){
            return employee.id;
        }

        deleteEmployee(key: number) {
            this.model.deleteEmployee(key);
        }
        changePage(newPage: number){
            this.selectedPage = newPage;
        }
        get pageNumbers(): number[]{
            this.totalPages = Math.ceil(this.model.getAllEmployees().length/this.employeesPerPage);
            let startPage: number;
            let endPage: number;
            if(this.totalPages <= 10){
                startPage = 1;
                endPage = this.totalPages;            
            }else{
                if(this.selectedPage <= 6){
                    startPage = 1;
                    endPage = 10;
                }else if(this.selectedPage + 4 >= this.totalPages){
                    startPage = this.totalPages-9;
                    endPage = this.totalPages;
                }else{
                    startPage = this.selectedPage - 5;
                    endPage = this.selectedPage + 4;
                }
            }
            return new Array((endPage+1) - startPage).fill(0).map((x,i) => i + startPage);
        }
    }

模板:

<tr *ngFor="let item of getAllEmployees(); let i = index; trackBy:getKey">
            <td>{{i+1}}</td>
            <td>{{item.firstName}}</td>
            <td>{{item.lastName}}</td>
            <td [ngSwitch] = "item.isActive">
                <span *ngSwitchCase ="1">Yes</span>
                <span *ngSwitchDefault>No</span>
            </td>
            <td [ngSwitch] = "item.emp_depID">
                <span *ngSwitchCase = "1">HR</span>
                <span *ngSwitchCase = "2">Tech</span>
                <span *ngSwitchCase = "3">Finance</span>
                <span *ngSwitchDefault>No data</span>
            </td>
            <td>
                <button class="btn btn-danger btn-xs" (click)="deleteEmployee(item.id)">
                    Delete
                </button>
            </td>
            <td>    
                <button class="btn btn-primary btn-xs" [routerLink]="['/form', 'edit', item.id]">
                    Edit
                </button>
            </td>
        </tr>

答案 3 :(得分:0)

如果模型中有对象,我的ModelResolver类中的resolve方法返回null,因为它既不是Observable也不是Promise,Angular会立即激活新路由。所以,在变量this.allEmployees = this.route.snapshot.data [&#39; model&#39;];在我的deleteEmployee或saveEmployee请求之后,我将拥有一系列具有旧数据的员工,因为在初始化时,对服务器的HTTP请求尚未完成。刷新页面时,会显示新结果。