I have an Angular application that talks to Wordpress REST API. The app needs to create a new post as well as 0-3 new tags with it. I'm saying 0 because in this instance tags are optional. If the user decides to include tags, allow no more than 3 of them. My app works similarly to StackOverflow in that instead of dealing with posts and comments, I will be referring to "questions" and "answers".
The process is as follow:
User types in title, body and optional tags for the new "question" in the template. Tags will be typed like tag-name-1,tag-name2
A button (click)="saveQuestion(title.value, tags.value)"
triggers a function call to the component.
In the component the following piece of code exists:
saveQuestion(title: string, tags: string) {
// value is now an array of strings: ["tag-name-1","tag-name-2"]
let splittedTags = tags.split(",");
let tagData = [];
for (let i = 0; i < splittedTags.length; i++) {
// value is now [{"name": "tag-name-1"}, {"name": "tag-name-2"}]
tagData.push({"name": splittedTags[i]});
}
// component has an empty array variable called tempArray = [];
for (let i = 0; i < 3; i++) {
if (i == 3) { break; }
// use Tag interface to type the code
this.questionService.addTag(tagData[i] as Tag)
.subscribe(tagResponse => {
this.tempArray.push(tagResponse.id)
})
}
// IMPORTANT: this returns an empty array variable when we need an array of numbers
console.log("tempArray: " + JSON.stringify(this.tempArray));
// get title and content from the template
// tempArray should be an array of strings, which we got from previous loop of subscribe() methods
let questionData = {
"date": new Date().toJSON(),
"status": "publish",
"title": title,
"content": this.html,
"tags": this.tempArray
}
this.questionService.addQuestion(questionData as NewQuestion)
.subscribe(response => { console.log(response) })
}
So we start by capturing the tag name string "tag-name-1,tag-name-2"
from the template, after which we put it into an array of objects. Then we call service's addTag() function (as many times as there were tags) which calls the Wordpress REST API and creates a new tag:
private questionsUrl = 'http://localhost/wordpress/wp-json/wp/v2';
httpOptionsAuth = {
headers: new HttpHeaders({
'Authorization': `Basic ${btoa("username:password")}`,
'Content-Type': 'application/json'
})
}
addTag(tag: Tag): Observable<any> {
return this.http.post<Tag>(`${this.questionsUrl}/tags`, tag,
this.httpOptionsAuth)
}
Calling the /tags
API gives us in return the number IDs for the newly created tags. We need to put those number IDs into a an array and then finally call the service's addQuestion()
method:
addQuestion(newquestion: NewQuestion): Observable<NewQuestion> {
return this.http.post<NewQuestion>(`${this.questionsUrl}/posts`, newquestion,
this.httpOptionsAuth);
}
My question in short is: is there a handy RxJS operator that could take care of combining the Observables?
You could do something like this:
saveQuestion(title: string, tags: string) {
// value is now an array of strings: ["tag-name-1","tag-name-2"]
let splittedTags = tags.split(",");
from(splittedTags)
.pipe(
take(3), // <-- take first 3 tags max
map((tag) => ({"name": tag})), // <-- map tag string to Tag
mergeMap( // <-- save tags and extract returned id from responses
(tag) => this.questionService.addTag(tag as Tag).pipe(
map((tagResponse) => tagResponse.id)
)
),
toArray(), // <-- group returned ids into one array
concatMap( // <-- final save request
(tagIds) => {
const questionData = {
"date": new Date().toJSON(),
"status": "publish",
"title": title,
"content": this.html,
"tags": tagIds
}
return this.questionService.addQuestion(questionData as NewQuestion)
}
)
)
.subscribe(console.log);
}
Cheers