问题
I have here a JSON file which looks like this:
[
{
"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"]
}
]
In my project I've got a model with the same structure which looks like this:
export interface Test {
question: string;
options: string[];
}
in my service file, I read the file like so (turning it into an observable, because I want to be able to change the order of the data and possibly later add/remove/change questions):
getSurveyQuestion(): Observable<Test[]> {
return this.http
.get<Test[]>("/path/to/questions.json")
.do(data => console.log("All : " + JSON.stringify(data)))
}
In the console, the above outputs:
JS: All : [{"question":"What is your age range?,"options":["10-20","20-30","30-40","40-50"]}, { //.....}]
My component file looks as follows:
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);
})
}
}
In my html, I output it to the screen like so:
<StackLayout *ngFor="let item of propertyData">
<Label text="{{item.question}}"></label>
</StackLayout>
now, I want to add a button or something to the html, which on tap calls a function to rearrange the items the user can see on screen. For now, something that simply arranges the array of observables by question (alphabetically, ascending or descending) is good enough. I've been trying to accomplish this for hours, but nothing I found on google (and stackoverflow) has helped me accomplish this yet.
Does any of you know how to sort/rearrange the data of an observable in the way I'm looking for?
Thanks in advance.
(in case it helps, I'm working with NativeScript + Angular).
回答1:
You can use the Observable map operator to sort the list.
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;
});
}
Now as for the question of changing the way they are sorted when you click a button, that's a bit trickier. You will want to make the function being used for sorting asynchronous. You could do that by making a property on your component:
sortFn$ = new BehaviorSubject<SortFnType>(alphabeticalSort // or whatever default sort you want);
Read more about BehaviorSubject
s here: http://reactivex.io/rxjs/manual/overview.html#behaviorsubject
Then next
functions to that BehaviorSubject
when the button is clicked.
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);
}
And then use combineLatest to get it into your stream.
ngOnInit() {
this.testService.getSurveyQuestion()
.combineLatest(this.sortFn$)
.map(([data, sortFn]: [Test[], SortFnType]) => {
return data.sort(sortFn);
})
.subscribe(data => {
this.propertyData = data;
});
}
Also, I'd recommend using the async
pipe to pass your data into your template so you don't have to mess with Subscription cleanup.
<StackLayout *ngFor="let item of sortedData$ | async">
<Label text="{{item.question}}"></label>
</StackLayout>
Then in your component:
sortedData$: Observable<Test[]>;
ngOnInit() {
this.sortedData$ = this.testService.getSurveyQuestion()
.combineLatest(this.sortFn$)
.map(([data, sortFn]: [Test[], SortFnType]) => {
return data.sort(sortFn);
})
}
Note that the code above is in "rough draft" form and will probably require some minor tweaks / edits to work in your program, but the approach there will work for your use case.
来源:https://stackoverflow.com/questions/48490703/sort-nested-observable