对嵌套的observable进行排序

时间:2018-01-28 19:59:03

标签: typescript rxjs observable angular2-nativescript

我这里有一个JSON文件,如下所示:

[
    {
        "question": "What is your age range?",
        "options": ["10-20","20-30","30-40","40-50"]
    },
    {
        "question": "How did you find us?",
        "options": ["Friend recommendation","Google","Other"]
    },
    {
        "question": "Are you interested in etcetc?",
        "options": ["No","Yes","Meh"]
    }
]

在我的项目中,我有一个具有相同结构的模型,如下所示:

export interface Test {
    question: string;
    options: string[];
}

在我的服务文件中,我像这样读取文件(将其转换为可观察文件,因为我希望能够更改数据的顺序,并可能在以后添加/删除/更改问题):

getSurveyQuestion(): Observable<Test[]> {
    return this.http
        .get<Test[]>("/path/to/questions.json")
        .do(data => console.log("All : " + JSON.stringify(data)))
}

在控制台中,以上输出:

  

JS:全部:[{&#34;问题&#34;:&#34;您的年龄范围是什么?,&#34;选项&#34;:[&#34; 10-20&#34;, &#34; 20-30&#34;,&#34; 30-40&#34;,&#34; 40-50&#34;]},{//.....}]

我的组件文件如下所示:

export class TestComponent implements OnInit {
    propertyData: Test[] = [];

    constructor(private router: Router, private testService: TestService) {}

    ngOnInit() {
        this.testService.getSurveyQuestion().subscribe(data => {
            this.propertyData = data;
        }, err => {
            console.log(err);
        })
    }
}

在我的html中,我将其输出到屏幕上,如下所示:

<StackLayout *ngFor="let item of propertyData">
    <Label text="{{item.question}}"></label>
</StackLayout>

现在,我想在html中添加一个按钮或其他东西,然后点击调用一个函数来重新排列用户可以在屏幕上看到的项目。现在,只需按问题(按字母顺序,按升序或降序排列)排列可观察数组的东西就足够了。我已经尝试了好几个小时,但是我在google(和stackoverflow)上找到的任何东西都帮助我完成了这个。

你们有没有人知道如何以我正在寻找的方式对可观察数据进行排序/重新排列?

提前致谢。

(如果有帮助,我会使用NativeScript + Angular)。

1 个答案:

答案 0 :(得分:3)

您可以使用Observable map运算符对列表进行排序。

ngOnInit() {
    this.testService.getSurveyQuestion()
      .map(data => {
        return data.sort((a: Test, b: Test) => {
          const aQ = test.question.toUpperCase();
          const bQ = test.question.toUpperCase();
          return aQ.localeCompare(bQ); // that will sort them alphabetically
        )); 
      })
      .subscribe(data => {
        this.propertyData = data;
      });
  }

现在关于在点击按钮时更改排序方式的问题,这有点棘手。您将希望使用于异步排序的函数。您可以通过在组件上创建属性来实现此目的:

sortFn$ = new BehaviorSubject<SortFnType>(alphabeticalSort // or whatever default sort you want);

在此处详细了解BehaviorSubjecthttp://reactivex.io/rxjs/manual/overview.html#behaviorsubject

单击按钮后,nextBehaviorSubject发挥作用。

onClick() {
  const reverseAlphabeticalSort = (a: Test, b: Test) => {
      const aQ = test.question.toUpperCase();
      const bQ = test.question.toUpperCase();
      return bQ.localeCompare(aQ); 
  });
  this.sortFn$.next(reverseAlphabeticalSort);
}

然后使用combineLatest将其添加到您的信息流中。

ngOnInit() {
  this.testService.getSurveyQuestion()
    .combineLatest(this.sortFn$)
    .map(([data, sortFn]: [Test[], SortFnType]) => {
      return data.sort(sortFn);
    })
    .subscribe(data => {
      this.propertyData = data;
    });
}

另外,我建议您使用async数据管道将数据传递到模板中,这样您就不必乱用Subscription清理工具。

<StackLayout *ngFor="let item of sortedData$ | async">
  <Label text="{{item.question}}"></label>
</StackLayout>

然后在你的组件中:

sortedData$: Observable<Test[]>;

ngOnInit() {
   this.sortedData$ = this.testService.getSurveyQuestion()
    .combineLatest(this.sortFn$)
    .map(([data, sortFn]: [Test[], SortFnType]) => {
      return data.sort(sortFn);
    })
  }

请注意,上面的代码是&#34;草稿&#34;表格,可能需要一些小的调整/编辑才能在您的程序中使用,但这种方法适用于您的用例。