In this episode, we build a five-star review system with Angular and the Firestore NoSQL document database. Learn how to model data relationships and manage them from an Angular service. https://angularfirebase.com/lessons/star-ratings-system-with-firestore/

CodePen CSS Stars: https://codepen.io/jamesbarnett/pen/vlpkh
Firestore Data Model: https://firebase.google.com/docs/firestore/data-model
2024-02-04T08:00:44.452Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
3c131061-3e3a-4658-b9ac-ac1e4b0cdc2c
9a7df03a405b89d8d7fb9fbe8d269243cecb75cfa7c770b55ac5235d5a29eae5
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
Modeling data relationships is one of the single most important concerns for an app developer.
In this episode, I'm going to show you how to build a 5 star review from scratch using the new Firestore document database and Angular.
My goal is to give you a good foundation for building many to many relationships in a NoSQL backend environment.
The reason I chose a 5 star review system for this episode is because the underlying concepts can applied to many different features such as Facebook likes, Reddit votes, Instagram hearts, and many other concepts similar to those.
One of the most important best practices with Firestore is to create big collections and small documents.
To build this feature, we're going to have 3 collections in the root of the database.
System.
So this first collection is a user's collection and the only attribute on the document itself is the display name.
I won't have time to cover user authentication in this video, but if you're new to my channel, make sure to check out my previous Firestore OAuth video to get up to speed on that.
So back to the database, our primary goal here is to create a relationship between users and movies through the Stars collection.
System.
So if you look at our movies document, it just has the image, title, and plot of the movie.
If we jump over to the stars collection, you'll see that's where all the relational data is kept.
Each star document will save the movie ID as well as the user ID and the value of the rating.
And then because a user can only post one review for each movie.
I've also created a custom ID here to ensure that there's no accidental data duplication.
So we create the document ID by concatenating the movie ID to user ID separated by an underscore, and you'll see this more once we get into Angular.
If you come from a SQL background, this is very similar to a many to many through relationship.
So now I'm jumping into my Angular project.
I'm assuming you have AngularFire 2 version 5 installed.
What I'm going to be doing today is creating a very generic middleman service that can connect relationships between any two documents.
To focus on the relationship aspect of this feature, I'm going to just hard code, a user document, and movie document directly in the app component.
So I'll set up a variable for the user document and movie document, which are both Angular Firestore document types and then we'll also set another variable for the user and the movie as an observable.
Then during n g on init, we'll go ahead and load the documents.
System.
So in this case, I'm just going to hard code a user ID in here, but in the real world, you would get this from an auth service or some other auth mechanism.
And I'll do the exact same thing for a movie document.
But I'd like to point out that these two documents never contain any information about the other party.
It's all handled through the middle man star document that we're going to create later.
Then to turn these references into observables, we call value changes on each of the documents.
System.
We can get the movie ID or user ID on the AngularFire document itself.
So we can set up a getter here system, and then on the Angular Firestore document, we can call ref ID.
More than anything, setting up the getter just helps clean up the code once we get to the HTML.
Sys.
So inside the app component HTML, I'm going to unwrap the movies observable, and I do that by just calling movie async and then set it as a template variable of m.
With that, we can display a title and display the image URL as well as the movie plot.
And we'll come back to this later after we create the star review component.
But before we do that, we're first going to build our service.
So run ng generate service star and make sure to specify the app module.
The goal of our service is to communicate with Firestore to build that middle relationship in the star collection.
System.
So after importing AngularFirestore, I'm going to create an interface here just to reinforce what a star is.
So a star is just going to be the sys user ID, movie ID, and then a value for the star, which ranges from 0.5 to 5.
System.
From there, we can add AngularFirestore to the constructor.
With that done, I wanna show you why I have stars as a root collection and not a sub collection under users or movies.
Sis.
So let's say we wanted to get all of the movies that a user has starred.
We could do that by calling collection stars, and use the query callback with where to filter them by the user ID.
Firestore will automatically index by the user ID, so this will be a fast query even if it's a massive collection.
And then in this case here, we want to return the observable of this query, so we just call value changes on it.
Now the reason it's a root collection is because we want to query stars by their movie ID as well.
So we can do the exact same thing, just switching out the user ID for the movie ID.
If stars were nested as a sub collection, we'd be able to make one of these queries but not both of them.
The last thing we need from our service is to set the actual relationship.
So we'll create a method here that say user ID, movie ID, and value of the star review.
So we'll go ahead and set that to a JavaScript object typed to our star interface.
Then we can use this data to also create a custom ID for our document.
So I'm going to set that up as user ID_movieid.
This technique will ensure that you don't have any data duplication, assuming you have unique user IDs and movie IDs.
It also provides a quick way to query a star command if you already have a movie ID and user ID somewhere else in your code.
So the only thing left to do is make a reference to this path and then call set with, uh, star data.
That's all we need for now, but you might also want to add a delete method if you want users to be able to remove their past stars.
That's it for the service.
Now we can move on to the star review component.
So after it's been generated, let's go ahead and first add it to the app component.
Sys.
So we're going to pass it a movie ID and a user ID from the parent component.
If you wanna learn more about this, check out my sharing data with components video.
But all we're doing here is passing the movie ID and user ID strings from the parent to the child.
And we'll set that up with the input decorator here in the TypeScript in just a second.
Inside the star review typescript, we'll go ahead and import the service as well as the input decorator from Angular core.
System, then we can declare our user ID and movie ID variables with the decorator.
This just tells Angular to allow these variables to be passed in directly through the HTML.
Before we go any further, let's look a little closer at how this app works.
So we have a current user here on the left and then a movie on the right.
And if a movie has no reviews, it just says not reviewed here.
The user can then hover over the stars, and when they click it, it will actually update the data in Firebase.
And it will also calculate the average rating among all users that have starred this movie.
So the next step will be to set up a variable for the stars observable.
Sys then I'm going to set a separate observable for the average rating.
Then we can use our star service during n g on a net to retrieve the actual observable list.
So in this case, we wanna show all of the movie stars, so we just call get movie stars with the movie ID.
Then we can use the RXJS map operator to system, break this down into an average rating.
After calling map, we're going to have an observable array of objects.
So first, we'll map these objects down to just their number values.
In other words, the value that users have rated them.
And then with that array of numbers, we can then reduce it down to its list average.
This code might look kind of ugly, but this is the standard way that you would get the average of an array in plain JavaScript.
And if that array is empty, we're just going to say not reviewed.
That takes care of the observables, but now we need an event handler that will update Firestore when the user clicks on the star icon.
So we already have the method set up in the service so we just have to pass the event handler a value and then it will use the user ID, movie ID, and that value to update Firestore.
So let's jump over to the HTML and put this all together.
System.
So for the average rating, we can just call async, and we're good to go.
It's already been mapped down to the value that we want.
To show other users that have start a movie.
We can loop over the star observable with the async pipe and then just display some data about that particular star.
So we'll say this user ID gave this movie x amount of stars.
So now we can actually build that form where the user updates their star rating.
System.
A lot of this is CSS that I'm not going to cover but you can get the full code at the lesson link in the description.
So we set up an NG container here which basically system allows you to loop over something without affecting the DOM.
In this case, I'm just going to loop over the integers of 5 through 1.
I set up this loop so we only have to create 2 input fields rather than 10.
So the first input is going to represent a full star.
So what we can do is pass the number down to our star handler.
So if the user clicks the 5th star, it'll send a 5 star review up to Firestore.
And then we need to set up an ID which is different for each one.
So we'll just say star plus whatever number we're looping over.
And these are just HTML radio buttons with some fancy CSS styling.
Each input also has a label, and it's important that the 4 attribute that matches the ID on the input exactly.
To create a half star, we can duplicate this process.
We just have to subtract 0.5 from each number.
So So when we call the star handler, we'll subtract 0.5.
Then we also have different CSS styles on the half star, so that's why we have them separated as 2 different inputs like this.
Sys the CSS to make this look good on the front end is actually based on a code pen, so I'll have a link for that in the description and you can also get the full code at angularfirebase.com.
So if we check it out on the front end, you can see we have the movie here with just one average rating of 2.5.
If we had another 5 star rating, that average to changes in real time to 3.75.
Then if we go ahead and add another review, this time with Firestore pulled up, You can see that the values are being saved in the back end in real time as well.
That's it for star ratings with Angular and Firestore.
If this video helped you, please like and subscribe.
And if you wanna take your app to the next level with exclusive pro content, consider becoming a pro member at angularfirebase.com.
Thanks for watching, and I'll see See you next time.

Speaker 0: Modeling data relationships is one of the single most important concerns for an app developer. In this episode, I'm going to show you how to build a 5 star review from scratch using the new Firestore document database and Angular. My goal is to give you a good foundation for building many to many relationships in a NoSQL backend environment. The reason I chose a 5 star review system for this episode is because the underlying concepts can applied to many different features such as Facebook likes, Reddit votes, Instagram hearts, and many other concepts similar to those. One of the most important best practices with Firestore is to create big collections and small documents.

To build this feature, we're going to have 3 collections in the root of the database. System. So this first collection is a user's collection and the only attribute on the document itself is the display name. I won't have time to cover user authentication in this video, but if you're new to my channel, make sure to check out my previous Firestore OAuth video to get up to speed on that. So back to the database, our primary goal here is to create a relationship between users and movies through the Stars collection.

System. So if you look at our movies document, it just has the image, title, and plot of the movie. If we jump over to the stars collection, you'll see that's where all the relational data is kept. Each star document will save the movie ID as well as the user ID and the value of the rating. And then because a user can only post one review for each movie.

I've also created a custom ID here to ensure that there's no accidental data duplication. So we create the document ID by concatenating the movie ID to user ID separated by an underscore, and you'll see this more once we get into Angular. If you come from a SQL background, this is very similar to a many to many through relationship. So now I'm jumping into my Angular project. I'm assuming you have AngularFire 2 version 5 installed.

What I'm going to be doing today is creating a very generic middleman service that can connect relationships between any two documents. To focus on the relationship aspect of this feature, I'm going to just hard code, a user document, and movie document directly in the app component. So I'll set up a variable for the user document and movie document, which are both Angular Firestore document types and then we'll also set another variable for the user and the movie as an observable. Then during n g on init, we'll go ahead and load the documents. System.

So in this case, I'm just going to hard code a user ID in here, but in the real world, you would get this from an auth service or some other auth mechanism. And I'll do the exact same thing for a movie document. But I'd like to point out that these two documents never contain any information about the other party. It's all handled through the middle man star document that we're going to create later. Then to turn these references into observables, we call value changes on each of the documents.

System. We can get the movie ID or user ID on the AngularFire document itself. So we can set up a getter here system, and then on the Angular Firestore document, we can call ref ID. More than anything, setting up the getter just helps clean up the code once we get to the HTML. Sys.

So inside the app component HTML, I'm going to unwrap the movies observable, and I do that by just calling movie async and then set it as a template variable of m. With that, we can display a title and display the image URL as well as the movie plot. And we'll come back to this later after we create the star review component. But before we do that, we're first going to build our service. So run ng generate service star and make sure to specify the app module.

The goal of our service is to communicate with Firestore to build that middle relationship in the star collection. System. So after importing AngularFirestore, I'm going to create an interface here just to reinforce what a star is. So a star is just going to be the sys user ID, movie ID, and then a value for the star, which ranges from 0.5 to 5. System.

From there, we can add AngularFirestore to the constructor. With that done, I wanna show you why I have stars as a root collection and not a sub collection under users or movies. Sis. So let's say we wanted to get all of the movies that a user has starred. We could do that by calling collection stars, and use the query callback with where to filter them by the user ID.

Firestore will automatically index by the user ID, so this will be a fast query even if it's a massive collection. And then in this case here, we want to return the observable of this query, so we just call value changes on it. Now the reason it's a root collection is because we want to query stars by their movie ID as well. So we can do the exact same thing, just switching out the user ID for the movie ID. If stars were nested as a sub collection, we'd be able to make one of these queries but not both of them.

The last thing we need from our service is to set the actual relationship. So we'll create a method here that say user ID, movie ID, and value of the star review. So we'll go ahead and set that to a JavaScript object typed to our star interface. Then we can use this data to also create a custom ID for our document. So I'm going to set that up as user ID_movieid.

This technique will ensure that you don't have any data duplication, assuming you have unique user IDs and movie IDs. It also provides a quick way to query a star command if you already have a movie ID and user ID somewhere else in your code. So the only thing left to do is make a reference to this path and then call set with, uh, star data. That's all we need for now, but you might also want to add a delete method if you want users to be able to remove their past stars. That's it for the service.

Now we can move on to the star review component. So after it's been generated, let's go ahead and first add it to the app component. Sys. So we're going to pass it a movie ID and a user ID from the parent component. If you wanna learn more about this, check out my sharing data with components video.

But all we're doing here is passing the movie ID and user ID strings from the parent to the child. And we'll set that up with the input decorator here in the TypeScript in just a second. Inside the star review typescript, we'll go ahead and import the service as well as the input decorator from Angular core. System, then we can declare our user ID and movie ID variables with the decorator. This just tells Angular to allow these variables to be passed in directly through the HTML.

Before we go any further, let's look a little closer at how this app works. So we have a current user here on the left and then a movie on the right. And if a movie has no reviews, it just says not reviewed here. The user can then hover over the stars, and when they click it, it will actually update the data in Firebase. And it will also calculate the average rating among all users that have starred this movie.

So the next step will be to set up a variable for the stars observable. Sys then I'm going to set a separate observable for the average rating. Then we can use our star service during n g on a net to retrieve the actual observable list. So in this case, we wanna show all of the movie stars, so we just call get movie stars with the movie ID. Then we can use the RXJS map operator to system, break this down into an average rating.

After calling map, we're going to have an observable array of objects. So first, we'll map these objects down to just their number values. In other words, the value that users have rated them. And then with that array of numbers, we can then reduce it down to its list average. This code might look kind of ugly, but this is the standard way that you would get the average of an array in plain JavaScript.

And if that array is empty, we're just going to say not reviewed. That takes care of the observables, but now we need an event handler that will update Firestore when the user clicks on the star icon. So we already have the method set up in the service so we just have to pass the event handler a value and then it will use the user ID, movie ID, and that value to update Firestore. So let's jump over to the HTML and put this all together. System.

So for the average rating, we can just call async, and we're good to go. It's already been mapped down to the value that we want. To show other users that have start a movie. We can loop over the star observable with the async pipe and then just display some data about that particular star. So we'll say this user ID gave this movie x amount of stars.

So now we can actually build that form where the user updates their star rating. System. A lot of this is CSS that I'm not going to cover but you can get the full code at the lesson link in the description. So we set up an NG container here which basically system allows you to loop over something without affecting the DOM. In this case, I'm just going to loop over the integers of 5 through 1.

I set up this loop so we only have to create 2 input fields rather than 10. So the first input is going to represent a full star. So what we can do is pass the number down to our star handler. So if the user clicks the 5th star, it'll send a 5 star review up to Firestore. And then we need to set up an ID which is different for each one.

So we'll just say star plus whatever number we're looping over. And these are just HTML radio buttons with some fancy CSS styling. Each input also has a label, and it's important that the 4 attribute that matches the ID on the input exactly. To create a half star, we can duplicate this process. We just have to subtract 0.5 from each number.

So So when we call the star handler, we'll subtract 0.5. Then we also have different CSS styles on the half star, so that's why we have them separated as 2 different inputs like this. Sys the CSS to make this look good on the front end is actually based on a code pen, so I'll have a link for that in the description and you can also get the full code at angularfirebase.com. So if we check it out on the front end, you can see we have the movie here with just one average rating of 2.5. If we had another 5 star rating, that average to changes in real time to 3.75.

Then if we go ahead and add another review, this time with Firestore pulled up, You can see that the values are being saved in the back end in real time as well. That's it for star ratings with Angular and Firestore. If this video helped you, please like and subscribe. And if you wanna take your app to the next level with exclusive pro content, consider becoming a pro member at angularfirebase.com. Thanks for watching, and I'll see See you next time.
Modeling data relationships is one of the single most important concerns for an app developer. In this episode, I'm going to show you how to build a 5 star review from scratch using the new Firestore document database and Angular. My goal is to give you a good foundation for building many to many relationships in a NoSQL backend environment.
modeling
databases
data management
data analysis
research
The reason I chose a 5 star review system for this episode is because the underlying concepts can applied to many different features such as Facebook likes, Reddit votes, Instagram hearts, and many other concepts similar to those.
One of the most important best practices with Firestore is to create big collections and small documents. To build this feature, we're going to have 3 collections in the root of the database. System. So this first collection is a user's collection and the only attribute on the document itself is the display name. I won't have time to cover user authentication in this video, but if you're new to my channel, make sure to check out my previous Firestore OAuth video to get up to speed on that. So back to the database, our primary goal here is to create a relationship between users and movies through the Stars collection. System. So if you look at our movies document, it just has the image, title, and plot of the movie. If we jump over to the stars collection, you'll see that's where all the relational data is kept.
streaming
databases
film
video gaming
data management
commerce
Each star document will save the movie ID as well as the user ID and the value of the rating.
And then because a user can only post one review for each movie. I've also created a custom ID here to ensure that there's no accidental data duplication. So we create the document ID by concatenating the movie ID to user ID separated by an underscore, and you'll see this more once we get into Angular.
film
If you come from a SQL background, this is very similar to a many to many through relationship. So now I'm jumping into my Angular project. I'm assuming you have AngularFire 2 version 5 installed. What I'm going to be doing today is creating a very generic middleman service that can connect relationships between any two documents. To focus on the relationship aspect of this feature, I'm going to just hard code, a user document, and movie document directly in the app component.
databases
software
So I'll set up a variable for the user document and movie document, which are both Angular Firestore document types and then we'll also set another variable for the user and the movie as an observable.
Then during n g on init, we'll go ahead and load the documents. System. So in this case, I'm just going to hard code a user ID in here, but in the real world, you would get this from an auth service or some other auth mechanism. And I'll do the exact same thing for a movie document. But I'd like to point out that these two documents never contain any information about the other party. It's all handled through the middle man star document that we're going to create later. Then to turn these references into observables, we call value changes on each of the documents.
documentation
programming
System. We can get the movie ID or user ID on the AngularFire document itself. So we can set up a getter here system, and then on the Angular Firestore document, we can call ref ID. More than anything, setting up the getter just helps clean up the code once we get to the HTML. Sys. So inside the app component HTML, I'm going to unwrap the movies observable, and I do that by just calling movie async and then set it as a template variable of m. With that, we can display a title and display the image URL as well as the movie plot. And we'll come back to this later after we create the star review component.
But before we do that, we're first going to build our service. So run ng generate service star and make sure to specify the app module. The goal of our service is to communicate with Firestore to build that middle relationship in the star collection. System. So after importing AngularFirestore, I'm going to create an interface here just to reinforce what a star is.
software development
So a star is just going to be the sys user ID, movie ID, and then a value for the star, which ranges from 0.5 to 5. System. From there, we can add AngularFirestore to the constructor. With that done, I wanna show you why I have stars as a root collection and not a sub collection under users or movies. Sis. So let's say we wanted to get all of the movies that a user has starred. We could do that by calling collection stars, and use the query callback with where to filter them by the user ID.
Firestore will automatically index by the user ID, so this will be a fast query even if it's a massive collection. And then in this case here, we want to return the observable of this query, so we just call value changes on it. Now the reason it's a root collection is because we want to query stars by their movie ID as well.
commerce
databases
So we can do the exact same thing, just switching out the user ID for the movie ID. If stars were nested as a sub collection, we'd be able to make one of these queries but not both of them. The last thing we need from our service is to set the actual relationship. So we'll create a method here that say user ID, movie ID, and value of the star review. So we'll go ahead and set that to a JavaScript object typed to our star interface. Then we can use this data to also create a custom ID for our document. So I'm going to set that up as user ID_movieid. This technique will ensure that you don't have any data duplication, assuming you have unique user IDs and movie IDs. It also provides a quick way to query a star command if you already have a movie ID and user ID somewhere else in your code. So the only thing left to do is make a reference to this path and then call set with, uh, star data. That's all we need for now, but you might also want to add a delete method if you want users to be able to remove their past stars.
That's it for the service. Now we can move on to the star review component. So after it's been generated, let's go ahead and first add it to the app component. Sys. So we're going to pass it a movie ID and a user ID from the parent component. If you wanna learn more about this, check out my sharing data with components video. But all we're doing here is passing the movie ID and user ID strings from the parent to the child.
film
television
And we'll set that up with the input decorator here in the TypeScript in just a second. Inside the star review typescript, we'll go ahead and import the service as well as the input decorator from Angular core. System, then we can declare our user ID and movie ID variables with the decorator. This just tells Angular to allow these variables to be passed in directly through the HTML. Before we go any further, let's look a little closer at how this app works. So we have a current user here on the left and then a movie on the right. And if a movie has no reviews, it just says not reviewed here. The user can then hover over the stars, and when they click it, it will actually update the data in Firebase. And it will also calculate the average rating among all users that have starred this movie. So the next step will be to set up a variable for the stars observable. Sys then I'm going to set a separate observable for the average rating. Then we can use our star service during n g on a net to retrieve the actual observable list. So in this case, we wanna show all of the movie stars, so we just call get movie stars with the movie ID. Then we can use the RXJS map operator to system, break this down into an average rating. After calling map, we're going to have an observable array of objects. So first, we'll map these objects down to just their number values. In other words, the value that users have rated them. And then with that array of numbers, we can then reduce it down to its list average. This code might look kind of ugly, but this is the standard way that you would get the average of an array in plain JavaScript. And if that array is empty, we're just going to say not reviewed. That takes care of the observables, but now we need an event handler that will update Firestore when the user clicks on the star icon. So we already have the method set up in the service so we just have to pass the event handler a value and then it will use the user ID, movie ID, and that value to update Firestore.
So let's jump over to the HTML and put this all together. System. So for the average rating, we can just call async, and we're good to go. It's already been mapped down to the value that we want. To show other users that have start a movie. We can loop over the star observable with the async pipe and then just display some data about that particular star.
programming
So we'll say this user ID gave this movie x amount of stars. So now we can actually build that form where the user updates their star rating.
System. A lot of this is CSS that I'm not going to cover but you can get the full code at the lesson link in the description. So we set up an NG container here which basically system allows you to loop over something without affecting the DOM. In this case, I'm just going to loop over the integers of 5 through 1.
programming
I set up this loop so we only have to create 2 input fields rather than 10. So the first input is going to represent a full star. So what we can do is pass the number down to our star handler. So if the user clicks the 5th star, it'll send a 5 star review up to Firestore. And then we need to set up an ID which is different for each one. So we'll just say star plus whatever number we're looping over. And these are just HTML radio buttons with some fancy CSS styling. Each input also has a label, and it's important that the 4 attribute that matches the ID on the input exactly. To create a half star, we can duplicate this process. We just have to subtract 0.5 from each number. So So when we call the star handler, we'll subtract 0.5. Then we also have different CSS styles on the half star, so that's why we have them separated as 2 different inputs like this. Sys the CSS to make this look good on the front end is actually based on a code pen, so I'll have a link for that in the description and you can also get the full code at angularfirebase.com. So if we check it out on the front end, you can see we have the movie here with just one average rating of 2.5.
If we had another 5 star rating, that average to changes in real time to 3.75. Then if we go ahead and add another review, this time with Firestore pulled up, You can see that the values are being saved in the back end in real time as well. That's it for star ratings with Angular and Firestore. If this video helped you, please like and subscribe. And if you wanna take your app to the next level with exclusive pro content, consider becoming a pro member at angularfirebase.com. Thanks for watching, and I'll see See you next time.
youtube
revenue
Modeling data relationships is one of the single most important concerns for an app developer. In this episode, I'm going to show you how to build a 5 star review from scratch using the new Firestore document database and Angular. My goal is to give you a good foundation for building many to many relationships in a NoSQL backend environment. The reason I chose a 5 star review system for this episode is because the underlying concepts can applied to many different features such as Facebook likes, Reddit votes, Instagram hearts, and many other concepts similar to those. One of the most important best practices with Firestore is to create big collections and small documents. To build this feature, we're going to have 3 collections in the root of the database. System. So this first collection is a user's collection and the only attribute on the document itself is the display name. I won't have time to cover user authentication in this video, but if you're new to my channel, make sure to check out my previous Firestore OAuth video to get up to speed on that. So back to the database, our primary goal here is to create a relationship between users and movies through the Stars collection. System. So if you look at our movies document, it just has the image, title, and plot of the movie. If we jump over to the stars collection, you'll see that's where all the relational data is kept. Each star document will save the movie ID as well as the user ID and the value of the rating. And then because a user can only post one review for each movie. I've also created a custom ID here to ensure that there's no accidental data duplication. So we create the document ID by concatenating the movie ID to user ID separated by an underscore, and you'll see this more once we get into Angular. If you come from a SQL background, this is very similar to a many to many through relationship. So now I'm jumping into my Angular project. I'm assuming you have AngularFire 2 version 5 installed. What I'm going to be doing today is creating a very generic middleman service that can connect relationships between any two documents. To focus on the relationship aspect of this feature, I'm going to just hard code, a user document, and movie document directly in the app component. So I'll set up a variable for the user document and movie document, which are both Angular Firestore document types and then we'll also set another variable for the user and the movie as an observable. Then during n g on init, we'll go ahead and load the documents. System. So in this case, I'm just going to hard code a user ID in here, but in the real world, you would get this from an auth service or some other auth mechanism. And I'll do the exact same thing for a movie document. But I'd like to point out that these two documents never contain any information about the other party. It's all handled through the middle man star document that we're going to create later. Then to turn these references into observables, we call value changes on each of the documents. System. We can get the movie ID or user ID on the AngularFire document itself. So we can set up a getter here system, and then on the Angular Firestore document, we can call ref ID. More than anything, setting up the getter just helps clean up the code once we get to the HTML. Sys. So inside the app component HTML, I'm going to unwrap the movies observable, and I do that by just calling movie async and then set it as a template variable of m. With that, we can display a title and display the image URL as well as the movie plot. And we'll come back to this later after we create the star review component. But before we do that, we're first going to build our service. So run ng generate service star and make sure to specify the app module. The goal of our service is to communicate with Firestore to build that middle relationship in the star collection. System. So after importing AngularFirestore, I'm going to create an interface here just to reinforce what a star is. So a star is just going to be the sys user ID, movie ID, and then a value for the star, which ranges from 0.5 to 5. System. From there, we can add AngularFirestore to the constructor. With that done, I wanna show you why I have stars as a root collection and not a sub collection under users or movies. Sis. So let's say we wanted to get all of the movies that a user has starred. We could do that by calling collection stars, and use the query callback with where to filter them by the user ID. Firestore will automatically index by the user ID, so this will be a fast query even if it's a massive collection. And then in this case here, we want to return the observable of this query, so we just call value changes on it. Now the reason it's a root collection is because we want to query stars by their movie ID as well. So we can do the exact same thing, just switching out the user ID for the movie ID. If stars were nested as a sub collection, we'd be able to make one of these queries but not both of them. The last thing we need from our service is to set the actual relationship. So we'll create a method here that say user ID, movie ID, and value of the star review. So we'll go ahead and set that to a JavaScript object typed to our star interface. Then we can use this data to also create a custom ID for our document. So I'm going to set that up as user ID_movieid. This technique will ensure that you don't have any data duplication, assuming you have unique user IDs and movie IDs. It also provides a quick way to query a star command if you already have a movie ID and user ID somewhere else in your code. So the only thing left to do is make a reference to this path and then call set with, uh, star data. That's all we need for now, but you might also want to add a delete method if you want users to be able to remove their past stars. That's it for the service. Now we can move on to the star review component. So after it's been generated, let's go ahead and first add it to the app component. Sys. So we're going to pass it a movie ID and a user ID from the parent component. If you wanna learn more about this, check out my sharing data with components video. But all we're doing here is passing the movie ID and user ID strings from the parent to the child. And we'll set that up with the input decorator here in the TypeScript in just a second. Inside the star review typescript, we'll go ahead and import the service as well as the input decorator from Angular core. System, then we can declare our user ID and movie ID variables with the decorator. This just tells Angular to allow these variables to be passed in directly through the HTML. Before we go any further, let's look a little closer at how this app works. So we have a current user here on the left and then a movie on the right. And if a movie has no reviews, it just says not reviewed here. The user can then hover over the stars, and when they click it, it will actually update the data in Firebase. And it will also calculate the average rating among all users that have starred this movie. So the next step will be to set up a variable for the stars observable. Sys then I'm going to set a separate observable for the average rating. Then we can use our star service during n g on a net to retrieve the actual observable list. So in this case, we wanna show all of the movie stars, so we just call get movie stars with the movie ID. Then we can use the RXJS map operator to system, break this down into an average rating. After calling map, we're going to have an observable array of objects. So first, we'll map these objects down to just their number values. In other words, the value that users have rated them. And then with that array of numbers, we can then reduce it down to its list average. This code might look kind of ugly, but this is the standard way that you would get the average of an array in plain JavaScript. And if that array is empty, we're just going to say not reviewed. That takes care of the observables, but now we need an event handler that will update Firestore when the user clicks on the star icon. So we already have the method set up in the service so we just have to pass the event handler a value and then it will use the user ID, movie ID, and that value to update Firestore. So let's jump over to the HTML and put this all together. System. So for the average rating, we can just call async, and we're good to go. It's already been mapped down to the value that we want. To show other users that have start a movie. We can loop over the star observable with the async pipe and then just display some data about that particular star. So we'll say this user ID gave this movie x amount of stars. So now we can actually build that form where the user updates their star rating. System. A lot of this is CSS that I'm not going to cover but you can get the full code at the lesson link in the description. So we set up an NG container here which basically system allows you to loop over something without affecting the DOM. In this case, I'm just going to loop over the integers of 5 through 1. I set up this loop so we only have to create 2 input fields rather than 10. So the first input is going to represent a full star. So what we can do is pass the number down to our star handler. So if the user clicks the 5th star, it'll send a 5 star review up to Firestore. And then we need to set up an ID which is different for each one. So we'll just say star plus whatever number we're looping over. And these are just HTML radio buttons with some fancy CSS styling. Each input also has a label, and it's important that the 4 attribute that matches the ID on the input exactly. To create a half star, we can duplicate this process. We just have to subtract 0.5 from each number. So So when we call the star handler, we'll subtract 0.5. Then we also have different CSS styles on the half star, so that's why we have them separated as 2 different inputs like this. Sys the CSS to make this look good on the front end is actually based on a code pen, so I'll have a link for that in the description and you can also get the full code at angularfirebase.com. So if we check it out on the front end, you can see we have the movie here with just one average rating of 2.5. If we had another 5 star rating, that average to changes in real time to 3.75. Then if we go ahead and add another review, this time with Firestore pulled up, You can see that the values are being saved in the back end in real time as well. That's it for star ratings with Angular and Firestore. If this video helped you, please like and subscribe. And if you wanna take your app to the next level with exclusive pro content, consider becoming a pro member at angularfirebase.com. Thanks for watching, and I'll see See you next time.
Modeling
modeling
data
data
relationships
relationships
is
is
one
one
of
of
the
the
single
single
most
most
important
important
concerns
concerns
for
for
an
an
app
app
developer.
developer
In
in
this
this
episode,
episode
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
build
build
a
a
5
5
star
star
review
review
from
from
scratch
scratch
using
using
the
the
new
new
Firestore
firestore
document
document
database
database
and
and
Angular.
angular
My
my
goal
goal
is
is
to
to
give
give
you
you
a
a
good
good
foundation
foundation
for
for
building
building
many
many
to
to
many
many
relationships
relationships
in
in
a
a
NoSQL
nosql
backend
backend
environment.
environment
The
the
reason
reason
I
i
chose
chose
a
a
5
5
star
star
review
review
system
system
for
for
this
this
episode
episode
is
is
because
because
the
the
underlying
underlying
concepts
concepts
can
can
applied
applied
to
to
many
many
different
different
features
features
such
such
as
as
Facebook
facebook
likes,
likes
Reddit
reddit
votes,
votes
Instagram
instagram
hearts,
hearts
and
and
many
many
other
other
concepts
concepts
similar
similar
to
to
those.
those
One
one
of
of
the
the
most
most
important
important
best
best
practices
practices
with
with
Firestore
firestore
is
is
to
to
create
create
big
big
collections
collections
and
and
small
small
documents.
documents
To
to
build
build
this
this
feature,
feature
we're
we're
going
going
to
to
have
have
3
3
collections
collections
in
in
the
the
root
root
of
of
the
the
database.
database
System.
system
So
so
this
this
first
first
collection
collection
is
is
a
a
user's
user's
collection
collection
and
and
the
the
only
only
attribute
attribute
on
on
the
the
document
document
itself
itself
is
is
the
the
display
display
name.
name
I
i
won't
won't
have
have
time
time
to
to
cover
cover
user
user
authentication
authentication
in
in
this
this
video,
video
but
but
if
if
you're
you're
new
new
to
to
my
my
channel,
channel
make
make
sure
sure
to
to
check
check
out
out
my
my
previous
previous
Firestore
firestore
OAuth
oauth
video
video
to
to
get
get
up
up
to
to
speed
speed
on
on
that.
that
So
so
back
back
to
to
the
the
database,
database
our
our
primary
primary
goal
goal
here
here
is
is
to
to
create
create
a
a
relationship
relationship
between
between
users
users
and
and
movies
movies
through
through
the
the
Stars
stars
collection.
collection
System.
system
So
so
if
if
you
you
look
look
at
at
our
our
movies
movies
document,
document
it
it
just
just
has
has
the
the
image,
image
title,
title
and
and
plot
plot
of
of
the
the
movie.
movie
If
if
we
we
jump
jump
over
over
to
to
the
the
stars
stars
collection,
collection
you'll
you'll
see
see
that's
that's
where
where
all
all
the
the
relational
relational
data
data
is
is
kept.
kept
Each
each
star
star
document
document
will
will
save
save
the
the
movie
movie
ID
id
as
as
well
well
as
as
the
the
user
user
ID
id
and
and
the
the
value
value
of
of
the
the
rating.
rating
And
and
then
then
because
because
a
a
user
user
can
can
only
only
post
post
one
one
review
review
for
for
each
each
movie.
movie
I've
i've
also
also
created
created
a
a
custom
custom
ID
id
here
here
to
to
ensure
ensure
that
that
there's
there's
no
no
accidental
accidental
data
data
duplication.
duplication
So
so
we
we
create
create
the
the
document
document
ID
id
by
by
concatenating
concatenating
the
the
movie
movie
ID
id
to
to
user
user
ID
id
separated
separated
by
by
an
an
underscore,
underscore
and
and
you'll
you'll
see
see
this
this
more
more
once
once
we
we
get
get
into
into
Angular.
angular
If
if
you
you
come
come
from
from
a
a
SQL
sql
background,
background
this
this
is
is
very
very
similar
similar
to
to
a
a
many
many
to
to
many
many
through
through
relationship.
relationship
So
so
now
now
I'm
i'm
jumping
jumping
into
into
my
my
Angular
angular
project.
project
I'm
i'm
assuming
assuming
you
you
have
have
AngularFire
angularfire
2
2
version
version
5
5
installed.
installed
What
what
I'm
i'm
going
going
to
to
be
be
doing
doing
today
today
is
is
creating
creating
a
a
very
very
generic
generic
middleman
middleman
service
service
that
that
can
can
connect
connect
relationships
relationships
between
between
any
any
two
two
documents.
documents
To
to
focus
focus
on
on
the
the
relationship
relationship
aspect
aspect
of
of
this
this
feature,
feature
I'm
i'm
going
going
to
to
just
just
hard
hard
code,
code
a
a
user
user
document,
document
and
and
movie
movie
document
document
directly
directly
in
in
the
the
app
app
component.
component
So
so
I'll
i'll
set
set
up
up
a
a
variable
variable
for
for
the
the
user
user
document
document
and
and
movie
movie
document,
document
which
which
are
are
both
both
Angular
angular
Firestore
firestore
document
document
types
types
and
and
then
then
we'll
we'll
also
also
set
set
another
another
variable
variable
for
for
the
the
user
user
and
and
the
the
movie
movie
as
as
an
an
observable.
observable
Then
then
during
during
n
n
g
g
on
on
init,
init
we'll
we'll
go
go
ahead
ahead
and
and
load
load
the
the
documents.
documents
System.
system
So
so
in
in
this
this
case,
case
I'm
i'm
just
just
going
going
to
to
hard
hard
code
code
a
a
user
user
ID
id
in
in
here,
here
but
but
in
in
the
the
real
real
world,
world
you
you
would
would
get
get
this
this
from
from
an
an
auth
auth
service
service
or
or
some
some
other
other
auth
auth
mechanism.
mechanism
And
and
I'll
i'll
do
do
the
the
exact
exact
same
same
thing
thing
for
for
a
a
movie
movie
document.
document
But
but
I'd
i'd
like
like
to
to
point
point
out
out
that
that
these
these
two
two
documents
documents
never
never
contain
contain
any
any
information
information
about
about
the
the
other
other
party.
party
It's
it's
all
all
handled
handled
through
through
the
the
middle
middle
man
man
star
star
document
document
that
that
we're
we're
going
going
to
to
create
create
later.
later
Then
then
to
to
turn
turn
these
these
references
references
into
into
observables,
observables
we
we
call
call
value
value
changes
changes
on
on
each
each
of
of
the
the
documents.
documents
System.
system
We
we
can
can
get
get
the
the
movie
movie
ID
id
or
or
user
user
ID
id
on
on
the
the
AngularFire
angularfire
document
document
itself.
itself
So
so
we
we
can
can
set
set
up
up
a
a
getter
getter
here
here
system,
system
and
and
then
then
on
on
the
the
Angular
angular
Firestore
firestore
document,
document
we
we
can
can
call
call
ref
ref
ID.
id
More
more
than
than
anything,
anything
setting
setting
up
up
the
the
getter
getter
just
just
helps
helps
clean
clean
up
up
the
the
code
code
once
once
we
we
get
get
to
to
the
the
HTML.
html
Sys.
sys
So
so
inside
inside
the
the
app
app
component
component
HTML,
html
I'm
i'm
going
going
to
to
unwrap
unwrap
the
the
movies
movies
observable,
observable
and
and
I
i
do
do
that
that
by
by
just
just
calling
calling
movie
movie
async
async
and
and
then
then
set
set
it
it
as
as
a
a
template
template
variable
variable
of
of
m.
m
With
with
that,
that
we
we
can
can
display
display
a
a
title
title
and
and
display
display
the
the
image
image
URL
url
as
as
well
well
as
as
the
the
movie
movie
plot.
plot
And
and
we'll
we'll
come
come
back
back
to
to
this
this
later
later
after
after
we
we
create
create
the
the
star
star
review
review
component.
component
But
but
before
before
we
we
do
do
that,
that
we're
we're
first
first
going
going
to
to
build
build
our
our
service.
service
So
so
run
run
ng
ng
generate
generate
service
service
star
star
and
and
make
make
sure
sure
to
to
specify
specify
the
the
app
app
module.
module
The
the
goal
goal
of
of
our
our
service
service
is
is
to
to
communicate
communicate
with
with
Firestore
firestore
to
to
build
build
that
that
middle
middle
relationship
relationship
in
in
the
the
star
star
collection.
collection
System.
system
So
so
after
after
importing
importing
AngularFirestore,
angularfirestore
I'm
i'm
going
going
to
to
create
create
an
an
interface
interface
here
here
just
just
to
to
reinforce
reinforce
what
what
a
a
star
star
is.
is
So
so
a
a
star
star
is
is
just
just
going
going
to
to
be
be
the
the
sys
sys
user
user
ID,
id
movie
movie
ID,
id
and
and
then
then
a
a
value
value
for
for
the
the
star,
star
which
which
ranges
ranges
from
from
0.5
0.5
to
to
5.
5
System.
system
From
from
there,
there
we
we
can
can
add
add
AngularFirestore
angularfirestore
to
to
the
the
constructor.
constructor
With
with
that
that
done,
done
I
i
wanna
wanna
show
show
you
you
why
why
I
i
have
have
stars
stars
as
as
a
a
root
root
collection
collection
and
and
not
not
a
a
sub
sub
collection
collection
under
under
users
users
or
or
movies.
movies
Sis.
sis
So
so
let's
let's
say
say
we
we
wanted
wanted
to
to
get
get
all
all
of
of
the
the
movies
movies
that
that
a
a
user
user
has
has
starred.
starred
We
we
could
could
do
do
that
that
by
by
calling
calling
collection
collection
stars,
stars
and
and
use
use
the
the
query
query
callback
callback
with
with
where
where
to
to
filter
filter
them
them
by
by
the
the
user
user
ID.
id
Firestore
firestore
will
will
automatically
automatically
index
index
by
by
the
the
user
user
ID,
id
so
so
this
this
will
will
be
be
a
a
fast
fast
query
query
even
even
if
if
it's
it's
a
a
massive
massive
collection.
collection
And
and
then
then
in
in
this
this
case
case
here,
here
we
we
want
want
to
to
return
return
the
the
observable
observable
of
of
this
this
query,
query
so
so
we
we
just
just
call
call
value
value
changes
changes
on
on
it.
it
Now
now
the
the
reason
reason
it's
it's
a
a
root
root
collection
collection
is
is
because
because
we
we
want
want
to
to
query
query
stars
stars
by
by
their
their
movie
movie
ID
id
as
as
well.
well
So
so
we
we
can
can
do
do
the
the
exact
exact
same
same
thing,
thing
just
just
switching
switching
out
out
the
the
user
user
ID
id
for
for
the
the
movie
movie
ID.
id
If
if
stars
stars
were
were
nested
nested
as
as
a
a
sub
sub
collection,
collection
we'd
we'd
be
be
able
able
to
to
make
make
one
one
of
of
these
these
queries
queries
but
but
not
not
both
both
of
of
them.
them
The
the
last
last
thing
thing
we
we
need
need
from
from
our
our
service
service
is
is
to
to
set
set
the
the
actual
actual
relationship.
relationship
So
so
we'll
we'll
create
create
a
a
method
method
here
here
that
that
say
say
user
user
ID,
id
movie
movie
ID,
id
and
and
value
value
of
of
the
the
star
star
review.
review
So
so
we'll
we'll
go
go
ahead
ahead
and
and
set
set
that
that
to
to
a
a
JavaScript
javascript
object
object
typed
typed
to
to
our
our
star
star
interface.
interface
Then
then
we
we
can
can
use
use
this
this
data
data
to
to
also
also
create
create
a
a
custom
custom
ID
id
for
for
our
our
document.
document
So
so
I'm
i'm
going
going
to
to
set
set
that
that
up
up
as
as
user
user
ID_movieid.
id_movieid
This
this
technique
technique
will
will
ensure
ensure
that
that
you
you
don't
don't
have
have
any
any
data
data
duplication,
duplication
assuming
assuming
you
you
have
have
unique
unique
user
user
IDs
ids
and
and
movie
movie
IDs.
ids
It
it
also
also
provides
provides
a
a
quick
quick
way
way
to
to
query
query
a
a
star
star
command
command
if
if
you
you
already
already
have
have
a
a
movie
movie
ID
id
and
and
user
user
ID
id
somewhere
somewhere
else
else
in
in
your
your
code.
code
So
so
the
the
only
only
thing
thing
left
left
to
to
do
do
is
is
make
make
a
a
reference
reference
to
to
this
this
path
path
and
and
then
then
call
call
set
set
with,
with
uh,
uh
star
star
data.
data
That's
that's
all
all
we
we
need
need
for
for
now,
now
but
but
you
you
might
might
also
also
want
want
to
to
add
add
a
a
delete
delete
method
method
if
if
you
you
want
want
users
users
to
to
be
be
able
able
to
to
remove
remove
their
their
past
past
stars.
stars
That's
that's
it
it
for
for
the
the
service.
service
Now
now
we
we
can
can
move
move
on
on
to
to
the
the
star
star
review
review
component.
component
So
so
after
after
it's
it's
been
been
generated,
generated
let's
let's
go
go
ahead
ahead
and
and
first
first
add
add
it
it
to
to
the
the
app
app
component.
component
Sys.
sys
So
so
we're
we're
going
going
to
to
pass
pass
it
it
a
a
movie
movie
ID
id
and
and
a
a
user
user
ID
id
from
from
the
the
parent
parent
component.
component
If
if
you
you
wanna
wanna
learn
learn
more
more
about
about
this,
this
check
check
out
out
my
my
sharing
sharing
data
data
with
with
components
components
video.
video
But
but
all
all
we're
we're
doing
doing
here
here
is
is
passing
passing
the
the
movie
movie
ID
id
and
and
user
user
ID
id
strings
strings
from
from
the
the
parent
parent
to
to
the
the
child.
child
And
and
we'll
we'll
set
set
that
that
up
up
with
with
the
the
input
input
decorator
decorator
here
here
in
in
the
the
TypeScript
typescript
in
in
just
just
a
a
second.
second
Inside
inside
the
the
star
star
review
review
typescript,
typescript
we'll
we'll
go
go
ahead
ahead
and
and
import
import
the
the
service
service
as
as
well
well
as
as
the
the
input
input
decorator
decorator
from
from
Angular
angular
core.
core
System,
system
then
then
we
we
can
can
declare
declare
our
our
user
user
ID
id
and
and
movie
movie
ID
id
variables
variables
with
with
the
the
decorator.
decorator
This
this
just
just
tells
tells
Angular
angular
to
to
allow
allow
these
these
variables
variables
to
to
be
be
passed
passed
in
in
directly
directly
through
through
the
the
HTML.
html
Before
before
we
we
go
go
any
any
further,
further
let's
let's
look
look
a
a
little
little
closer
closer
at
at
how
how
this
this
app
app
works.
works
So
so
we
we
have
have
a
a
current
current
user
user
here
here
on
on
the
the
left
left
and
and
then
then
a
a
movie
movie
on
on
the
the
right.
right
And
and
if
if
a
a
movie
movie
has
has
no
no
reviews,
reviews
it
it
just
just
says
says
not
not
reviewed
reviewed
here.
here
The
the
user
user
can
can
then
then
hover
hover
over
over
the
the
stars,
stars
and
and
when
when
they
they
click
click
it,
it
it
it
will
will
actually
actually
update
update
the
the
data
data
in
in
Firebase.
firebase
And
and
it
it
will
will
also
also
calculate
calculate
the
the
average
average
rating
rating
among
among
all
all
users
users
that
that
have
have
starred
starred
this
this
movie.
movie
So
so
the
the
next
next
step
step
will
will
be
be
to
to
set
set
up
up
a
a
variable
variable
for
for
the
the
stars
stars
observable.
observable
Sys
sys
then
then
I'm
i'm
going
going
to
to
set
set
a
a
separate
separate
observable
observable
for
for
the
the
average
average
rating.
rating
Then
then
we
we
can
can
use
use
our
our
star
star
service
service
during
during
n
n
g
g
on
on
a
a
net
net
to
to
retrieve
retrieve
the
the
actual
actual
observable
observable
list.
list
So
so
in
in
this
this
case,
case
we
we
wanna
wanna
show
show
all
all
of
of
the
the
movie
movie
stars,
stars
so
so
we
we
just
just
call
call
get
get
movie
movie
stars
stars
with
with
the
the
movie
movie
ID.
id
Then
then
we
we
can
can
use
use
the
the
RXJS
rxjs
map
map
operator
operator
to
to
system,
system
break
break
this
this
down
down
into
into
an
an
average
average
rating.
rating
After
after
calling
calling
map,
map
we're
we're
going
going
to
to
have
have
an
an
observable
observable
array
array
of
of
objects.
objects
So
so
first,
first
we'll
we'll
map
map
these
these
objects
objects
down
down
to
to
just
just
their
their
number
number
values.
values
In
in
other
other
words,
words
the
the
value
value
that
that
users
users
have
have
rated
rated
them.
them
And
and
then
then
with
with
that
that
array
array
of
of
numbers,
numbers
we
we
can
can
then
then
reduce
reduce
it
it
down
down
to
to
its
its
list
list
average.
average
This
this
code
code
might
might
look
look
kind
kind
of
of
ugly,
ugly
but
but
this
this
is
is
the
the
standard
standard
way
way
that
that
you
you
would
would
get
get
the
the
average
average
of
of
an
an
array
array
in
in
plain
plain
JavaScript.
javascript
And
and
if
if
that
that
array
array
is
is
empty,
empty
we're
we're
just
just
going
going
to
to
say
say
not
not
reviewed.
reviewed
That
that
takes
takes
care
care
of
of
the
the
observables,
observables
but
but
now
now
we
we
need
need
an
an
event
event
handler
handler
that
that
will
will
update
update
Firestore
firestore
when
when
the
the
user
user
clicks
clicks
on
on
the
the
star
star
icon.
icon
So
so
we
we
already
already
have
have
the
the
method
method
set
set
up
up
in
in
the
the
service
service
so
so
we
we
just
just
have
have
to
to
pass
pass
the
the
event
event
handler
handler
a
a
value
value
and
and
then
then
it
it
will
will
use
use
the
the
user
user
ID,
id
movie
movie
ID,
id
and
and
that
that
value
value
to
to
update
update
Firestore.
firestore
So
so
let's
let's
jump
jump
over
over
to
to
the
the
HTML
html
and
and
put
put
this
this
all
all
together.
together
System.
system
So
so
for
for
the
the
average
average
rating,
rating
we
we
can
can
just
just
call
call
async,
async
and
and
we're
we're
good
good
to
to
go.
go
It's
it's
already
already
been
been
mapped
mapped
down
down
to
to
the
the
value
value
that
that
we
we
want.
want
To
to
show
show
other
other
users
users
that
that
have
have
start
start
a
a
movie.
movie
We
we
can
can
loop
loop
over
over
the
the
star
star
observable
observable
with
with
the
the
async
async
pipe
pipe
and
and
then
then
just
just
display
display
some
some
data
data
about
about
that
that
particular
particular
star.
star
So
so
we'll
we'll
say
say
this
this
user
user
ID
id
gave
gave
this
this
movie
movie
x
x
amount
amount
of
of
stars.
stars
So
so
now
now
we
we
can
can
actually
actually
build
build
that
that
form
form
where
where
the
the
user
user
updates
updates
their
their
star
star
rating.
rating
System.
system
A
a
lot
lot
of
of
this
this
is
is
CSS
css
that
that
I'm
i'm
not
not
going
going
to
to
cover
cover
but
but
you
you
can
can
get
get
the
the
full
full
code
code
at
at
the
the
lesson
lesson
link
link
in
in
the
the
description.
description
So
so
we
we
set
set
up
up
an
an
NG
ng
container
container
here
here
which
which
basically
basically
system
system
allows
allows
you
you
to
to
loop
loop
over
over
something
something
without
without
affecting
affecting
the
the
DOM.
dom
In
in
this
this
case,
case
I'm
i'm
just
just
going
going
to
to
loop
loop
over
over
the
the
integers
integers
of
of
5
5
through
through
1.
1
I
i
set
set
up
up
this
this
loop
loop
so
so
we
we
only
only
have
have
to
to
create
create
2
2
input
input
fields
fields
rather
rather
than
than
10.
10
So
so
the
the
first
first
input
input
is
is
going
going
to
to
represent
represent
a
a
full
full
star.
star
So
so
what
what
we
we
can
can
do
do
is
is
pass
pass
the
the
number
number
down
down
to
to
our
our
star
star
handler.
handler
So
so
if
if
the
the
user
user
clicks
clicks
the
the
5th
5th
star,
star
it'll
it'll
send
send
a
a
5
5
star
star
review
review
up
up
to
to
Firestore.
firestore
And
and
then
then
we
we
need
need
to
to
set
set
up
up
an
an
ID
id
which
which
is
is
different
different
for
for
each
each
one.
one
So
so
we'll
we'll
just
just
say
say
star
star
plus
plus
whatever
whatever
number
number
we're
we're
looping
looping
over.
over
And
and
these
these
are
are
just
just
HTML
html
radio
radio
buttons
buttons
with
with
some
some
fancy
fancy
CSS
css
styling.
styling
Each
each
input
input
also
also
has
has
a
a
label,
label
and
and
it's
it's
important
important
that
that
the
the
4
4
attribute
attribute
that
that
matches
matches
the
the
ID
id
on
on
the
the
input
input
exactly.
exactly
To
to
create
create
a
a
half
half
star,
star
we
we
can
can
duplicate
duplicate
this
this
process.
process
We
we
just
just
have
have
to
to
subtract
subtract
0.5
0.5
from
from
each
each
number.
number
So
so
So
so
when
when
we
we
call
call
the
the
star
star
handler,
handler
we'll
we'll
subtract
subtract
0.5.
0.5
Then
then
we
we
also
also
have
have
different
different
CSS
css
styles
styles
on
on
the
the
half
half
star,
star
so
so
that's
that's
why
why
we
we
have
have
them
them
separated
separated
as
as
2
2
different
different
inputs
inputs
like
like
this.
this
Sys
sys
the
the
CSS
css
to
to
make
make
this
this
look
look
good
good
on
on
the
the
front
front
end
end
is
is
actually
actually
based
based
on
on
a
a
code
code
pen,
pen
so
so
I'll
i'll
have
have
a
a
link
link
for
for
that
that
in
in
the
the
description
description
and
and
you
you
can
can
also
also
get
get
the
the
full
full
code
code
at
at
angularfirebase.com.
angularfirebase.com
So
so
if
if
we
we
check
check
it
it
out
out
on
on
the
the
front
front
end,
end
you
you
can
can
see
see
we
we
have
have
the
the
movie
movie
here
here
with
with
just
just
one
one
average
average
rating
rating
of
of
2.5.
2.5
If
if
we
we
had
had
another
another
5
5
star
star
rating,
rating
that
that
average
average
to
to
changes
changes
in
in
real
real
time
time
to
to
3.75.
3.75
Then
then
if
if
we
we
go
go
ahead
ahead
and
and
add
add
another
another
review,
review
this
this
time
time
with
with
Firestore
firestore
pulled
pulled
up,
up
You
you
can
can
see
see
that
that
the
the
values
values
are
are
being
being
saved
saved
in
in
the
the
back
back
end
end
in
in
real
real
time
time
as
as
well.
well
That's
that's
it
it
for
for
star
star
ratings
ratings
with
with
Angular
angular
and
and
Firestore.
firestore
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
take
take
your
your
app
app
to
to
the
the
next
next
level
level
with
with
exclusive
exclusive
pro
pro
content,
content
consider
consider
becoming
becoming
a
a
pro
pro
member
member
at
at
angularfirebase.com.
angularfirebase.com
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
see
see
See
see
you
you
next
next
time.
time
success
The speakers discuss how to build a five star review from scratch using the new Firatera document database and Angular, emphasizing the importance of setting up a root collection and creating a method to return the observable of a query. They explain how to use the app component to create a middle relationship between users and movies, and how to set up a method to update Fir throughput. They also discuss how to use the star review component in the app and create a half star with a different input ID, and provide a link for the full code at angularQuad.com.
Star Review System from Scratch with Firestore + Angular
Modeling data relationships is one of the single most important concerns for an app developer. In this episode, I'm going to show you how to build a 5 star review from scratch using the new Firestore document database and Angular. My goal is to give you a good foundation for building many to many relationships in a NoSQL backend environment. The reason I chose a 5 star review system for this episode is because the underlying concepts can applied to many different features such as Facebook likes, Reddit votes, Instagram hearts, and many other concepts similar to those. One of the most important best practices with Firestore is to create big collections and small documents. To build this feature, we're going to have 3 collections in the root of the database. System. So this first collection is a user's collection and the only attribute on the document itself is the display name. I won't have time to cover user authentication in this video, but if you're new to my channel, make sure to check out my previous Firestore OAuth video to get up to speed on that. So back to the database, our primary goal here is to create a relationship between users and movies through the Stars collection. System. So if you look at our movies document, it just has the image, title, and plot of the movie. If we jump over to the stars collection, you'll see that's where all the relational data is kept. Each star document will save the movie ID as well as the user ID and the value of the rating. And then because a user can only post one review for each movie. I've also created a custom ID here to ensure that there's no accidental data duplication. So we create the document ID by concatenating the movie ID to user ID separated by an underscore, and you'll see this more once we get into Angular. If you come from a SQL background, this is very similar to a many to many through relationship. So now I'm jumping into my Angular project. I'm assuming you have AngularFire 2 version 5 installed. What I'm going to be doing today is creating a very generic middleman service that can connect relationships between any two documents. To focus on the relationship aspect of this feature, I'm going to just hard code, a user document, and movie document directly in the app component. So I'll set up a variable for the user document and movie document, which are both Angular Firestore document types and then we'll also set another variable for the user and the movie as an observable. Then during n g on init, we'll go ahead and load the documents. System. So in this case, I'm just going to hard code a user ID in here, but in the real world, you would get this from an auth service or some other auth mechanism. And I'll do the exact same thing for a movie document. But I'd like to point out that these two documents never contain any information about the other party. It's all handled through the middle man star document that we're going to create later. Then to turn these references into observables, we call value changes on each of the documents. System. We can get the movie ID or user ID on the AngularFire document itself. So we can set up a getter here system, and then on the Angular Firestore document, we can call ref ID. More than anything, setting up the getter just helps clean up the code once we get to the HTML. Sys. So inside the app component HTML, I'm going to unwrap the movies observable, and I do that by just calling movie async and then set it as a template variable of m. With that, we can display a title and display the image URL as well as the movie plot. And we'll come back to this later after we create the star review component. But before we do that, we're first going to build our service. So run ng generate service star and make sure to specify the app module. The goal of our service is to communicate with Firestore to build that middle relationship in the star collection. System. So after importing AngularFirestore, I'm going to create an interface here just to reinforce what a star is. So a star is just going to be the sys user ID, movie ID, and then a value for the star, which ranges from 0.5 to 5. System. From there, we can add AngularFirestore to the constructor. With that done, I wanna show you why I have stars as a root collection and not a sub collection under users or movies. Sis. So let's say we wanted to get all of the movies that a user has starred. We could do that by calling collection stars, and use the query callback with where to filter them by the user ID. Firestore will automatically index by the user ID, so this will be a fast query even if it's a massive collection. And then in this case here, we want to return the observable of this query, so we just call value changes on it. Now the reason it's a root collection is because we want to query stars by their movie ID as well. So we can do the exact same thing, just switching out the user ID for the movie ID. If stars were nested as a sub collection, we'd be able to make one of these queries but not both of them. The last thing we need from our service is to set the actual relationship. So we'll create a method here that say user ID, movie ID, and value of the star review. So we'll go ahead and set that to a JavaScript object typed to our star interface. Then we can use this data to also create a custom ID for our document. So I'm going to set that up as user ID_movieid. This technique will ensure that you don't have any data duplication, assuming you have unique user IDs and movie IDs. It also provides a quick way to query a star command if you already have a movie ID and user ID somewhere else in your code. So the only thing left to do is make a reference to this path and then call set with, uh, star data. That's all we need for now, but you might also want to add a delete method if you want users to be able to remove their past stars. That's it for the service. Now we can move on to the star review component. So after it's been generated, let's go ahead and first add it to the app component. Sys. So we're going to pass it a movie ID and a user ID from the parent component. If you wanna learn more about this, check out my sharing data with components video. But all we're doing here is passing the movie ID and user ID strings from the parent to the child. And we'll set that up with the input decorator here in the TypeScript in just a second. Inside the star review typescript, we'll go ahead and import the service as well as the input decorator from Angular core. System, then we can declare our user ID and movie ID variables with the decorator. This just tells Angular to allow these variables to be passed in directly through the HTML. Before we go any further, let's look a little closer at how this app works. So we have a current user here on the left and then a movie on the right. And if a movie has no reviews, it just says not reviewed here. The user can then hover over the stars, and when they click it, it will actually update the data in Firebase. And it will also calculate the average rating among all users that have starred this movie. So the next step will be to set up a variable for the stars observable. Sys then I'm going to set a separate observable for the average rating. Then we can use our star service during n g on a net to retrieve the actual observable list. So in this case, we wanna show all of the movie stars, so we just call get movie stars with the movie ID. Then we can use the RXJS map operator to system, break this down into an average rating. After calling map, we're going to have an observable array of objects. So first, we'll map these objects down to just their number values. In other words, the value that users have rated them. And then with that array of numbers, we can then reduce it down to its list average. This code might look kind of ugly, but this is the standard way that you would get the average of an array in plain JavaScript. And if that array is empty, we're just going to say not reviewed. That takes care of the observables, but now we need an event handler that will update Firestore when the user clicks on the star icon. So we already have the method set up in the service so we just have to pass the event handler a value and then it will use the user ID, movie ID, and that value to update Firestore. So let's jump over to the HTML and put this all together. System. So for the average rating, we can just call async, and we're good to go. It's already been mapped down to the value that we want. To show other users that have start a movie. We can loop over the star observable with the async pipe and then just display some data about that particular star. So we'll say this user ID gave this movie x amount of stars. So now we can actually build that form where the user updates their star rating. System. A lot of this is CSS that I'm not going to cover but you can get the full code at the lesson link in the description. So we set up an NG container here which basically system allows you to loop over something without affecting the DOM. In this case, I'm just going to loop over the integers of 5 through 1. I set up this loop so we only have to create 2 input fields rather than 10. So the first input is going to represent a full star. So what we can do is pass the number down to our star handler. So if the user clicks the 5th star, it'll send a 5 star review up to Firestore. And then we need to set up an ID which is different for each one. So we'll just say star plus whatever number we're looping over. And these are just HTML radio buttons with some fancy CSS styling. Each input also has a label, and it's important that the 4 attribute that matches the ID on the input exactly. To create a half star, we can duplicate this process. We just have to subtract 0.5 from each number. So So when we call the star handler, we'll subtract 0.5. Then we also have different CSS styles on the half star, so that's why we have them separated as 2 different inputs like this. Sys the CSS to make this look good on the front end is actually based on a code pen, so I'll have a link for that in the description and you can also get the full code at angularfirebase.com. So if we check it out on the front end, you can see we have the movie here with just one average rating of 2.5. If we had another 5 star rating, that average to changes in real time to 3.75. Then if we go ahead and add another review, this time with Firestore pulled up, You can see that the values are being saved in the back end in real time as well. That's it for star ratings with Angular and Firestore. If this video helped you, please like and subscribe. And if you wanna take your app to the next level with exclusive pro content, consider becoming a pro member at angularfirebase.com. Thanks for watching, and I'll see See you next time.
The speakers discuss how to build a five star review from scratch using the new Firatera document database and Angular, emphasizing the importance of setting up a root collection and creating a method to return the observable of a query. They explain how to use the app component to create a middle relationship between users and movies, and how to set up a method to update Fir throughput. They also discuss how to use the star review component in the app and create a half star with a different input ID, and provide a link for the full code at angularQuad.com.