Transcripts
1. Introduction: Welcome to my full
stack course where we're building rail
project from scratch. It's so great to have
you here in this course, we will implement a real project starting from the
empty folder and to the finished fully functional
production application which will be deployed
on the rail server, we will implement a feature
that typical project needs, like for example,
authentication, working with the Pi, managing and creating
reactive state, working with forms
and much, much more. In this course, we
will use lots of different tools on our client. We will use Angular, which is the best solution for big scalable production
applications. On backend we will
use now chess was expressed to create our
API to store our data. We will use MongoDB, which is a really
popular solution to store data in your project. And also people use
Socket Layer to create real-time updates
inside our application. Obviously, we will write the best possible code
which will be dry, scalable, and easy
to understand. By the end of this course, you will be able to create
your own projects of any complexity
using these tools. Who am I? My name is
Alexander cohesion and I'm a web developer with more than ten years
of experience, as well as the professional
instructor with various courses regarding
web technologies. I did my best to put
all my knowledge inside this course and they
want to say it with you. So welcome on board, and let's get started.
2. What technologies do we use?: In this video, I
want to talk about technologies that we will
use inside this course. And just from the
beginning I want to set realistic expectations. We will use quite a lot
of technologies here. It will be Angular, Node.js Express, MongoDB,
TypeScript and socket IO. And this is a lot
of stuff to learn. And then this course, I won't teach you all the
stuff from start to the end. It is simply not possible. The amount of knowledge in every single tool
here is enormous. This is why this
course is focused. Building real project
from start to the end. Yes, sure you will
understand and learn all these
technologies on some level. But here, in some technologies
you might require basic understanding what
we're talking about at all. With that being said, let's jump into the list. And the first one in
our list is Angular. If you don't know,
Angular is one of three most popular
front-end frameworks, and we will use it to build front and side of
our application. Angular is a really
strict framework which fits huge companies. And we have that
TypeScript out of the box, which means by using Angular, we can build bigger
applications, make them safe because of typings and scale
them if we need to. If you don't know Angular tall, I highly recommend you to look at list on the
basics of Angular, how components are working, how to create modules, how to add routing, and so on. After understanding this stuff, it will be much easier for
you to jump in the course. The next in the
list we have known chess and actually NodeJS we will use on the backend with Framework which
is called Express. And if it does not express it, the most popular
framework for Node.js, it is super small, it is not strict, it is really flexible
and we will use it as a really popular
solution to build back-end. And in this project,
we must build not only a but also managed
socket connections. The back-end, this is where expresses a real
nice choice here, but it is not all inside expressed by default,
we have JavaScript. Of course it is not
the best approach. We really want to use
TypeScript because we will use TypeScript on the
client with angular. This is when the
back-end with Express. We also want to use TypeScript. Why is that? Because
actually TypeScript brings to JavaScript
static tightens. This makes our code much safer and we see
all our problems, not in runtime, but in
the transpiler and time. Obviously now a project we
must store our data somewhere, which actually means for our
back-end we need a database. And the most popular
solution here is MongoDB. This is really the most
popular database in the world where we can
store data and it fits really nice in our
stack with Angular on the client and NodeJS was
expressed on the backend. And last but not least in
the list is Socket IO. We want in our application to implement WebSockets to notify other users when
we create task or credit column when
we change part, all users inside this
parts must be notified. And the most popular
solution for WebSockets inside known just word
is called socket IO. It is the library which
helps us to manage WebSockets on the backend and simultaneously
on the client. This is why it is
really a good choice. Once again, if you are not
familiar with some tools here, it is totally fine. We will start from scratch, but we will focus learning of all these tools just
on everyday needs. Which actually means we will learn basic stuff,
correct stuff, and stuff which is needed to
implement this rail project.
3. Downloadable resources: In this video, I want to
remind you about source code, because actually
inside this course, in every single lecture
where we code something, you can find attached code
of this specific lecture, which actually means
every single lecture has a source code inside. And if you just want to grab the source code of the lecture, you can certainly do it. Just grow under the video and check the archive that is
attached to the video. And diversely as always, if you have some
problems with your code, something is not compiling
or you just have a question regarding the video
or the course in general, you can just write your
comments on that video, and I will certainly
answer them.
4. Installing Node & Angular: In this video, we will install NodeJS Angular on your machine. As you can see here, I am
on the official website, Node js.org, where you can download now chess
on your machine. If you don't have it installed, you can check if it
installed on your machine. If you just write node minus
version inside your console, if you are getting
the version like 16, it is totally fine if you
have something older, I highly recommend you to
update your Node version. But here's the important point, is you can see we
have two versions. First of all, 16
LTS and 17 current. And you might think, okay, I need to install current. And they can't recommend you
to install current because long-term support version is
typically much more stable. This is why for all my projects, especially if they're
shipping on production, I'm using LTS node version, which actually means if you're jumping to this website and you see like LTS version
may be 17 or 18. I still recommend you to
download of Ts and not current and it doesn't matter on what operational system
you are working. No, this work in everywhere. You simply download it here, you install it on your machine, and then you check inside
console with node minus version that is successfully installed Node on your machine. You can also type here
and Pm minus version to check that npm is
also available for you. Our next step is to install angular and actually to
work with then globe, we're using tool which
is called Angular CLI, which actually means
this is the tool which helps us first of all to
create an Angular project, then generate
different modules or components and do a lot
of stuff with Angular. So how we can install Angular CLI on your machine,
as you can see here, I'm on the official website, Angular slash CLI, and here is the first step
installing angular CLI. And we can simply copy
this command which will install globally Angular
CLI on your machine. And as you can see for
this, we're using npm, which will have after
installation of known chairs. This is where here I
can simply throw in payments tau minus
g Angular CLI. And I'm installing globally
Angular CLI on my machine. Now we must check if our Angular CLI was
successfully installed. This we can just
write in G minus version and here is some
important information. First of all, we can see that
Angular CLI is version 13, which actually
means we installed angular 13 here on our machine. Secondly, this is
our node version which is activated
on your machine. This is npm and this
is operational system. And some versions of the
Angular that you can see here. If you see such output, it means that you
successfully installed Angular CLI on your machine. The next step is to generate front and part of
our application. As you can see here, I
am inside him be folded. And they didn't create
our project here, which actually means we rarely create everything
from scratch. So what I want to do here, I can just try it MK deal and then the name of our
project, L Trello. And actually in
this project we're implementing the
clone of trailer. This is y. Here is just a name with
a nice prefix and WSSE, you can create a folder
not from the terminal bud, just in your file manager. Now, I want to jump inside
our ultra law folder. And here we must create
two different folders because we want to separate our backend from our front-end. And you might ask, why is that? And actually for
different reasons. First of all, it
is easier to split your front-end and
back-end if you want for some reasons to
put them later in different repositories or
maybe even different teams. Secondly, if we separate
them to different folders, then it is not a monolith, which actually means we
will for sure not inject some stuff from the backend
inside front-end folder, and vice a versa. They are really
isolated and separate it as they need to be in
the real application. This is why here I want
to create two folders. First of all, it will be client, and secondly it will be
server and insight client. We will have our
angular application and then says server. We will have our known
gs with Express, which will be our
back-end application. Now we can generate our
angular application, as you can see here
in the documentation, we simply run in gene hue
and the name of the folder. Then we just jumping the folder
and we start our server. But here we have a problem
because we're directly created our client folder and we can jump now insert
client folder, which actually means we can't create our angular application with engine you because
folder already exists. For this, we can use a real
nice command in gene you. And then here we must provide
application name directory, and here is dot slash, which actually
means this command will create for you in you application of angular in
your existing directory. As you can see here, I
am inside client where exactly we want to generate
our angular application. And they can paste
this command here in gene Hue app name,
directory dot slash. And obviously we don't want to name our application app name. This is where here we
can provide ELL Trello, just like we named our project after the
same hidden Enter. And we started our process of generating new Angular project. And here we get some
questions from Angular CLI. Do we need Angular routing? Of course, yes, this is
why I'm hitting yes, here, what style
sheet we must use. And here I will choose a CSS. After this, our project
will be generated. As you can see
here, all packages were successful installed, but afterwards we're getting message which might
be confusing for you. Here we are getting the
message that now was created the branch master as
our default git branch. And they might change
the name to name later, but it is not there yet. And actually doesn't matter what branch was
generated for you, master or main, it
will work in any case. So you can just
ignore this warning. And the last step that
we need to do is we need to start our
client application. And as you can see here
in the documentation, it is in G serve. So we simply insert
client must try it and just serve without
specifying anything. As you can see here, we're
getting green output. The message that now angular
live development server is listening on
local host for 1200. So now we can simply
open localhost 4,200. And here we see the default angular page
and now insert console. Our web server must
be running all time while we're developing
the application. Now, let's have a look on the files that were
generated fast. As you can see here,
I opened my editor, I'm in folder L Trello, and we have here two folders, server and client
and insight client. All these files were
generated with Angular CLI. And the most interesting part for us is this source folder. And inside AB, we have
our app component. This is exactly what we
see here on the screen. This is default
component of angular. This is where here first of all, I want to remove app component, SCSS, app components spec. And here we have just
these four files. Now we can jump inside
App component HTML, and we really want
to remove everything except of this last
line, router outlet. This is extremely
important fast. So I will remove everything from this file and just live
here drought the outlet. And on the top we
can just try it. Hello l Trello. So we know that it is working. After this, we must jump inside our app component, change it. Because actually
here we're using style URLs from the file, which will alter the removed. This is where we
can simply remove this line and we just
use here a template. And we don't even
need this title L trailer because
we're not using it. We simply have our app component and it is an empty component. Now when I will jump to
the page inside browser, I can see that
everything is removed. Our page is completely
cleaned and now we have just hello
little trailer message, which actually means
we successfully prepare the client part of our application that
goodwill implement later.
5. Configuring server: In this video, I want to prepare our back-end part of our project
to start implementation. As you can see here, it
is completely empty. So what we will create here, actually what we must do, we must initialize package
JSON file with known. So here we can write npm in need and it will create for
us a package JSON file. Here, we must answer
some questions. I typically just hit
Enter on all of them. It doesn't matter for us. And at the end we simply
hit Yes, and we're ready. Now we can jump and
said editor and go outside of our
client to our server. Now here we have
just a simple file, Beckett Jason with name,
version description, main script, where we don't have anything in the empty
author and the license. Now, we must install
two packages which will help us to create
our back-end project. So here we can write
impairments style. And after this non demon
minus d, What does it mean? First of all, what is no demon? That this is a special package which will help us to reload every single time when we change some file, our
back-end application. And this is exactly what Angular is doing fast in the client. But inside our back-end
project with don't have some web server
which is doing this fast. This is why when
Stalin and a demon, and as you can see here,
I used minus d option, which actually means
in our package Jason, no demon was installed
inside the dev dependencies. And dev dependencies are
dependencies which we are using when we're
developing a project. We don't need no
demon on production. This is just for development. And our next package
is called Ts node, and it is also only
for development. This is where here
we're writing npm install Ts node minus t, which means this is
a dev dependency. So what is node is doing? This is just a process
which will transpire our TypeScript code inside
JavaScript code on the fly, executed just like normal
JavaScript code inside node. This is extremely efficient because we want to
write TypeScript code. This is why we must transform it first from TypeScript
to JavaScript. And this package helps to
do it in a really easy way. Now let's jump and said Beckett json and
check what we have. We have here to
dev dependencies. And here I want to
create a new script, but don't need the script test. We want here to start
our web server. This is y, here is
a script style, and here we can call a
demon the path to our file. And here I want to write
source slash server.js. And as you can see here,
we have the server.js, which we will
create in a second. So this is the TypeScript file and the most interesting
part here that we didn't specify that
we're using here Ts node inside no daemon. Daemon does it automatically out of the box if we installed tier snowed and specified here not JavaScript file but
TypeScript file. And the last thing
that we must do is create ts config file, which is a configuration
file for TypeScript. This is way here inside
of a service folder. I want to create a new file, ts config dot json. Here I will paste such
conflict and this is super typical conflict for Node.js application
with TypeScript. So here we're setting
compiler options. This is exactly how
TypeScript will transpire our code from
TypeScript to JavaScript. So module CommonJS
means that we're writing our TypeScript on the
backend inside noun chest. And our target is
atmospheric optics, because node can easily
read at my script six code. So our mode resolution is node because we're writing
inside noun chairs, move on source maps. This is where
source map through. Our deal is directory. Output will be generated. Here we have a dist folder, it will be created
automatically. We also have here strict
true NDS module inter pop, and we need this option yes
module inter Pope to convert correctly are TypeScript inputs to require inside known jazz. So we successfully created
our TypeScript config, and now we can create our source folder and
inside this file, because as you can see here, our server will live
inside source server Ts. So I'm jumping in
such source and I'm creating a file server dot ts. And don't forget Ts NodeJS. Now we can jump inside
and simply write console log server just
to test if it is working. Now, I will jump inside console and here
inside server folder, I can write npm start, as you can see here, we've
got an output from no demon, which is totally fine. It is washing TypeScript
files here and here. No demon started our Ts
node source server test, which actually means it
simply uses Ts node inside, but it restart our server. And this is the output server that we wrote inside this file. And now Demon Bull
restart our server every single time when
we make some changes.
6. Installing database: In this video, we
will talk about in styling database
on your machine. And there's another dimension
we will use MongoDB here. So what did the idea? We must install on our local machine
database so we can save there and read from there some data that we will
use in our application. This data will be available
for us on live locally. At the moment when we want to deploy our project
to production, we must set up the database on production and we will do it
at the end of this course. So now the question is how
we can install MongoDB on your specific machine and you can work in different
operational systems. So we want something that will work everywhere without hassle. Actually installing the database can always have
some difficulties. Here as you can see,
I already opened the official MongoDB website with install MongoDB section. This is the URL so you can check it if you
are interested, and it looks really easy. We simply choose here
an operational system. So here I'm clicking on the
link MongoDB Download Center. And I am now here
on this webpage. And at this point it
is already not really clear what MongoDB
we must install. There are different
versions of it. There are free versions
that we want to use and the rub paid solutions
that we don't need. And actually here we
must crawl a little bit and see here MongoDB
community server. Actually this MongoDB
community is the name of the free version of
MongoDB that we want to use. So here on the
right we can choose the version current
is totally fine. Now here is your
platform, maybe Windows, and now you simply
download it and install. So we're just double-click
in here and installing it. But after this, there are
some additional steps. We must set up the
data directory. Here is you can see
they're doing it inside command line like this. And then like this, this is why you can
install MongoDB like this. This is totally fine. This is the official way, but actually my students
had a lot of problems while using databases with
official installation. Why is that? Because actually in every single operational system, you can have some problems. Additionally, you
must create a folder, then you must specify
the path and so on. It is more difficult. This is why I highly
recommend you to look on the
second possibility, how you can install
database on your machine. And then talking here about DACA and about Docker Desktop. So what is Docker? This is a specific
additional tool which you should not learn, but you can simply use it. The main idea is that Docker packs everything
inside the container. It is completely isolated and it doesn't have anything to do
with your operational system. Docker works on all operational
systems and it is free, which actually means the idea here that we're
installing Docker, first of all on your machine. And secondly, we use Mongo
official image from Docker. As you can see here,
the link for Mongo, which is supported
by MongoDB itself. So this is not some
third-party tool. This is super official
and the main idea is that this mongo is
packed inside container, so it is completely isolated on your machine and you simply
get from the container, the port where you can connect and write data
to this container. And this is how it will look
like when you start in it. As you can see here on the left, it must be green. And at this point,
you know, okay, my Docker is running and for you probably the list
here will be empty. This is totally fine. The main idea is
that first of all, you start Docker
on your machine. After this, we simply
jump to the console and we must run
such command as you can see here I wrote docker run and after you install Docker
desktop on your machine, you can write Docker
inside the console. This is the known name, and here we're writing
docker run then minus d, which means this must
be a detached process, which actually means
we don't want to run it here inside
this terminal. We want just to detach
it from the terminal. Here we specify ports and after this we are
specifying name mongodb. And here, Mongo, this name, mongodb is the name of
container on my machine. And here what we
want to download, this is exactly the
image that we will use. Here we're using Mongo 404. You can also use later
version here you can check it on the official website of the Mongo or here inside Docker. As you can see,
you can click here tags, scroll a little bit. For example, you can use latest or just
check the version, as you can see here, the
latest version is 507. At this moment, I installed
on my machine for 0 for, but that doesn't make
that huge difference. Things that we
will try it inside MongoDB are the same
on all these versions. Now I'm hitting here and, and Docker will download
this image on my machine. As you can see, I didn't get any output at all
except of one hash. And this is because
MongoDB is already downloaded by Docker
on my machine. So this is not a problem, and this line simply started Mongo DB
again on my machine. And actually if I'm opening
now the Docker tool, you can see here contain
a MongoDB and it is green because it is running at the moment and we
can speak to it. This is why, as you can see, you don't need to create
additional files. You don't need to care about
some permissions or folders. It simply works. And this is a single line which are used for
a lot of projects. Now the question is obviously
how we can jump inside the console of MongoDB when
it is inside the container. And for this we have
a special command, docker exec, which
means execute IT. And here is the name
of our container. In our case it was MongoDB. And after this we're writing our command and we
want to come and Mongo here because this is what is installed
inside container. Inside container can
be installed whatever. In our case, this Mongo image
simply has Mongo inside. This is why we're hitting Enter. We're getting here lots
of messages from MongoDB. And as you can see after this, I am now here in the terminal
of Mongo inside container, and they can write here
some Mongo commands. For example, show
databases, semicolon, I'm hitting Enter,
and you can see what databases I
have here inside. Now, you might say, okay, but I didn't use Docker, installed MongoDB just with the official way,
what should they do? And actually you must
do just a single thing. If you installed it
the official way, then you have two different
commands in your console. First of all, it will
be one God command, and this will start exactly
the same like we did with docker run MongoDB
process on your machine, which actually
means the database will be as a process
running on your machine. And the second command that
you want to use is Mongo. This command will jump
directly insert console, just like we did with torque. It will be exactly the same. The most important
in this course that your database Mongo must be running directly on your machine during the
development of the project. Which actually means you
have really three things. First of all, you have
a standard database. Secondly, have started web
server for the back-end. And the last one is the static web server
for the frontend.
7. Do you use a good editor?: We are almost finished with
subtyping all our tools. And the last question
that I want to ask you, do you really use
the correct editor? Because actually we
will write a lot of TypeScript
inside your editor. And the editor doesn't
support TypeScript that well, then you might think about
using another editor. As you can see here,
I'm inside my editor, this is Vim, but I'm not
suggesting it to you. I just want to show
what I'm talking about. As you can see here where
insert component inside Angular and here we have
input of our component. Typically when we're
writing the code with don't write
inverts with just write something
like add component or maybe just add than comp. And then we have
an auto-complete. And this you can see here we
can choose a lot of stuff. We want actually to input
component from Angular. He didn't just tend to here. And first of all, it was
autocomplete it to the end. And secondly, I got this outer input component
from Angular com. And it is extremely important that your editor can do this. You really need support of this outer inputs when
you're writing code. Because every single time
you must write an input, if you will really
do it by hands like input component from, and you don't really
remember from what package you must input it. This is not efficient. You simply spend time that you can write code on Ron things. We really need this
feature of outer input. And second feature is
obviously you want to have a support of TypeScript
inside your editor, which actually means when we're writing something not correct. For example, not select here, but just select where
getting directly here, a message argument of type select does not
exist on type component, which actually means but don't spend time debugging some magic. We simply see our typo there
directly inside editor. You can use here any editor that has support of TypeScript. But if you don't know
what editor to use, I highly recommend you
to look on VS Code. This is completely free editor, which works on every
single operational system. You simply install it. You are getting such editor with built-in support of
TypeScript out of the box, which actually means
you are getting all this features
inside your editor. And in this case, it is much smoother for you to write code.
8. Setting up socket with Express: In this video, we're starting with
developing our project. And actually the goal
of this video is to setup our back-end
server with express, MongoDB and socket IO. So let's start. Here. We must install
several new packages. First of all, we want to
express this is a framework. This is why I want to
jump inside console. And as you can see where inside root folder
this is drawn, we don't want to install here packages because we first must jump inside our server folder here boop want to
install packages. This is where I am right
in npm install express. In this case, we're installing this framework as a dependency. Here you can see now we
have a new dependency, express this as our framework. The next thing that we want
to install is mongoose. And if you don't know
what is Mongoose, this is the most
popular package to work with MongoDB inside
known chairs. Why it is most popular, because you can simply set up your connection to
the MongoDB with it, you can create your
models and work with MongoDB in more correct way
by working with models. So you typically have
something like a RAM and we will learn Mongoose steeper
in the next lectures. And the last thing
that we want to install here is Socket IO. This is why npm install
socket dot ion. And this is exactly
our library to work with WebSockets
inside known chairs. So as you can see, all
our dependencies are installed and now we
can jump in set source, server Ts, and we don't
have anything here. And we can start with
configuring our syrup. But actually here
is the question. It is really easy to just write your code for Stratton express. Her entity is really easy to create a web server
for Socket IO, but actually it is not
that easy to configure together MongoDB
Express and socket IO. Because what we want
from expressed, we want normal rounds, just like in normal application, but we also want to
bind to its socket IO so we can work
with WebSockets. And additionally to all this, we need somehow to
start our MongoDB. So as I said, it is not easy, but this is the
production away because actually nobody needs
just a single package. We really want the full-blown production
ready application. So first of all here I
will input our Express. Express. And actually at this
moment you probably think, okay, what he's doing. He's tried to an inverse
inside noun chairs. And actually this is totally fine because we're
not right in here. Javascript, where right
in here, TypeScript. And it is transpired
with our config. And just to remind you here
we have our ts config. This transpired to
ECMO scriptSig, which can be readable
by now Jess. But most importantly, more resolution is
CommonJS and node. This is where it will be
converted to require and it will work like a
charm inside now jazz. But for us it is extremely
comfortable because using inputs is much better
than right-hand require. And here we have a next problem. As you can see here,
I have an error. Cannot find the declaration
file for module expressed. What does it have a mean? Typescript tries to
help us with inverse. He can't do it out
of the box because TypeScript doesn't know
anything about express package. And here is a solution
we must install with npm install save-dev,
types express. So we can additional
install typings for express package and then type
script can help us small. And this is exactly
what we want to do. We need to jump and set console and right
and payments tau. And here is at types
slash Express, and here is important minus the option to just install
it as a dev dependency. We don't need this library
on the production. And as you can see
after installation, this error is gone. And now TypeScript can
help us with Express. So here we successfully imported our express package and now we can create our application. So I can simply write
here const ab equals, and here we're calling Express. And as you can see now, if I'm halite and express, you can see all types of expressed from this
specific package. For example, we can read
here that it creates an express application
and this is extremely helpful for
development purposes. The second thing
that we want to do, we must create an HTTP server and you will see
why in a second. So here I want to destructure, create server from,
and here will be http. As you can see, we're getting
ATP out of the box and also create server because this is the default
node package. But it might happen that you are missing typings
for known chairs. This is where we can
jump directly in the console and write
npm install types node minus t. And
in this case we're installing for sure all typings which are needed
for noun chairs. So here we imported
create server from http. Now we must use it. So after app, we can
create our HTTP server. And here we want to call our create server and
provide inside our app. And just to remind you, app, this is an instance
of our Express. This is our express server. We're creating here
are HTTP server. Additionally, the
next thing that we want to input here is socket. This is y here, where important Server with
big S from socket IO package. And now after this we can directly create
our socket server. So here const ion, and here we're
calling us server. And inside we're providing
our HTTP server. This is exactly why we created this HTTP server first
and before are expressed. So now in this file we have
three different things. First of all, we have our app, so we can work
directly with Express. Secondly, we have
our HTTP server, which we can start
with some port. The third is our IR, so we can make some
WebSockets requests. And just to check that
everything is working, I want to use here our app and
just try it apt-get slash. So we want to create
our new route on slash. And here we don't need anything, but I just want to
respond with some string. This is why here we can
write request and response. And here inside rest
dot send API is up. If you're not familiar
with Express, this is how we
create just playing routes inside our
backend server. So here we're saying, okay, we're creating now
get route for slash, which means for homepage. And here is our Kohlberg. Inside of our Kohlberg were
getting request and response. And here we can use dot send to send the string to
this specific round. The last thing that we must
do here is start our server. So here we can
write HTTP server. Don't listen and hear. We're providing some port, for example, for 1001. After this, we have a callback. The tower web server is
successful as talented. So we can write inside some
console log, for example, that our API is
listening on port. And here will be our part. And actually it would
have been isolated to put the sport in additional
config file, but for now it will also go. So let's check if it's working. I have here a tab
with open API server. And as you can see here, no demon restarted this web
server again and again. And at some point
now we're getting our console log API is
listening on port 4,001, which actually
means even browser, we will open local
host for 1001. You can see here our
message API is up, but it is not tolerable. Also want to check if our socket layer
connection is working. This is why here after EB, we can write ir dot
and here we have on. And as you can
see, first of all, we're getting all typings out of the box
with Socket Layer. We don't need to install
any additional package. Secondly, you can see that on the listener function
to our socket IO, which actually means
we can write here on, and we're providing here as the first parameter connection. And actually this is
the default action that can happen
inside socket IO. And here we have our callback. We don't want anything
for now here, but we can simply write
console.log connected. And we can't really see that
socket layer is working for us because we just set
it a tab on the back-end, but not on the client. But this is a way
how we will write our Socket Layer code on the backend and we are
fully prepared for it. So here we zap, we're working
with Express with a yard, we're working with
Socket Layer and HTTP server we're using
to start the server. And the last thing
that we must do here is setup our Mongoose. And just to remind
you, mongooses the package to
work with MongoDB. So here on the top
I want to input one goes from mongoose. And now here is a
really important point. You never want to
start your web servers before you started
connection to the database. The main idea is that
inside you've observer, you will do some requests
to the database, and database is not ready yet, then we can do this request, which actually means
every single time we want to be sure
that MongoDB is, their connection is established. And only after this we're
starting our web server. This is y here what I want to
write after our socket IO, we can write here Mongoose dot and we have here
a method connect. And actually inside we want
to provide our mongodb URL. So what I want to paste
here is this year, it is MongoDB colon two slashes, local host 27017
port slash l Trello. And actually this
part on the left is the default path of MongoDB. And it doesn't really matter
how you installed Mongo DB with the official way or
with Docker container, it will work exactly the same. Either you have a
running MongoDB process on your machine, on the sport, or you have a running MongoDB inside the container
with the sport, which is available outside
on our local machine. Here after slash, this is just the name of how a
database you can write here, any name and it will be created. I just wrote here l trailer, like the name of our project. So this line is
actually a promise. This is why here
we can write dot. Then after we connected successfully to our
MongoDB database, then will be triggered. And now inside we can
first of all write that we successfully
connected to our database. So I can write here
connected to MongoDB. And also I want to move
this HTTP listener inside. In this case, first of all, our connection to
MongoDB is established. And after this, we're
starting our server. Now I want to jump inside our web server and
check if it's working. As you can see now, I'm
getting message connected to MongoDB and after the semester that our API was successful, the standard, which actually means this is exactly our goal. First of all, MongoDB. Secondly, our web server. Actually, at this moment, you might get some Azure
regarding connection here. And the most popular problem
that you might have, you didn't start your MongoDB
process on your machine. If it is not started, then we can't connect
to a MongoDB database. Then here you will
probably get some error, like cannot connect to Mongo
DB or connection failed. But if you see on
your screen connected to MongoDB and the
pay is started, this means that you configured
everything successfully. And we started our web server on the backend with
MongoDB and socket IO.
9. Creating Mongoose user model: In this video, we
will talk about creating our user model. Actually now application
boot will registered users. So current user, we can
login with the user, which means we need somehow to handle our users
on the front-end, on the backend and
inside database. So in this video, we will focus
on mongoose and database. Once again, what
is mongoose were already installed this
package in R package JSON. You can see Mongoose here, but what it essentially does, this is the official
website of Mongoose. You can see here the example. So we inject human goods and we make Mongoose
dot connect. And here is a MongoDB database. After this we can write
mongoose module cat, and we define that our
cat has named string, which actually means
cat is our entity. And now we can create this
entity just inside JavaScript. This is where here
we're right in UK ed, and we provide inside the name. So here we created a kitty, which is just an object. But now inside Katie
we have a save method. And this method
returns as a promise, which actually means
that this is how we will save the record inside MongoDB. Now you might ask, okay, why do we need mongoose? Why we can't simply use
official MongoDB driver like mongodb dot save and throw inside some object
that we want to save. This is not comfortable to
work in the huge project, even in the medium project, it is not that comfortable because you don't
have any abstraction. You are writing super
low-level code, how you need to save
data to database, or how you will read them. Actually, this is
why we prefer to use ramps inside our
backend projects. What are Ram Dass fast, we define something like models, which are our entities
inside our project. For example, we have a user that we have maybe tasks or you have boards if
we're talking about trail application and so on. And then we can define which relations
between these models. And then all these relations
are done in much easier way. We can do it ourselves
with MongoDB, which actually means we
simply write less code. This is why in this video, we want to focus
on our user model. And the first thing that I
want to do inside source, I want to create types folder. And actually we're right
in here TypeScript, which means we must leverage it. And this is super-important
to invest more time in TypeScript then in
writing your code. In this case, it
will be easier for you to develop your application. This is where here in
source types I want to create user interface dot ts. And as you can see here, I have this notation where we have a postfix of what exactly it is. The inside, I want to create
our new interface user. And if you don't know
TypeScript really well, this is just a definition of the object which we
can use everywhere. In this case here we're
using Word interface, which is a reserved
word inside TypeScript. And here we define an
interface user and we can define what fields we
have inside our user. First of all, we must
create an email, because email is
what we're using in the application
to register user, validate this user, then
send e-mail to the user. Also, we need here a
username and it will be streamed and password
will be also needed. And obviously we must
hash of a password and never stop passwords
just as a plane strain. And last but not least
here will be created at, we don't need to use this field, but it is really
nice to have it, first of all for
debugging purposes. And secondly, we can
get it from Mongoose just out of the box
and it will be date. So this is how a user
interface is looking like. Now in our whole application, in our back-end side, we can use this user interface. Now on the right, I want to
create a model for this user. This is why in
source we can create new folder which
is called models. And inside we can
register user dot ts, and this is our model. This is exactly
something for Mongoose. So what I want to write
here is our user schema. And what it means here we
define a schema of our model. And for this we're
using new schema. And as you can see, I don't
have any auto-complete. So let's try to
import here schema. And as you can see, I don't
have a correct input here. I have another input
from inspector, and this is not correct. So I will type here input
schema from mongoose. Let's check if we have an error. We don't have an
error, which means this was just a
problem of my editor. Here I can inspect the schema, which means it is really available with
TypeScript inside. Here we're right in your schema. And now inside round brackets, we can define our object, but schema is not what we will use inside
the application. It should be a model. This is why here we can write that then export default model. And this model also
comes from Mongoose. So let's put here comma model. And here model is a function
where we provide our string. It will be user with big hue. And there's a second argument. We provide here a user schema. This is how we define a
model inside Mongoose. So here we can
export default model we provide here and
name and our schema. And here we must
define our schema. But we're right in TypeScript. And actually it is not the
best way to create new schema. Why is that? Here we
highlight our schema and you can see a lot of n is
here and this is bad. Why is that? Because we didn't define
any type of our schema. This is why what we want to do. We want create a definition
of the schema for a user. So I want to jump here on the left and create
here expert interface. So it is a new interface
for user document. Here is a huge different, we have here an
interface for the user. This is just user with the fields and this
is use a document. This is what we're using
just for mongoose. And here I want to write x tans. And if you don't know
what is extends with, simply take all fields
from our user here. So I want to write here
extends User comma document. And actually this document
must be loaded from Mongoose. So here on the top, I need to write input
document from mongoose. And actually for now we don't need to provide anything inside. And you might ask now, okay, but doesn't make any sense
What did was out here. We created an interface
user document. Here we are simply extended
user and document. And yes, it makes sense
because first of all, we have our interface user. We can use it everywhere. This is our part, but we're also mix it
with the document. And document is
coming from Mongoose. This is the definition
of the document and fast important
part is, for example, this AD, because
every single document inside mongodb has NAD. And in this case here,
we don't need to specify the user has AD, it is coming from the
document of mongoose. And now we can jump here on the right and often use schema. We can provide in tanks a user document that
we just created. And now I can now to
input it here on the top. So it is from our
types user interface. In this case here
we're saying that our user document
is what we need to provide inside our users schema. And exactly the
same we can do with our model here we can
provide our user document. And if you don't know what
this part is meaning, this is actually a generic, which actually means here
we're providing generic type. It can be whatever by
default it was any. But if we now
inspect our schema, you can see that it
is not any anymore. Here we have our user document, and this is extremely
important for us and for TypeScript
validation. Because actually here, now
inside what I want to do, I want to throw something which does not
exist inside user. For example, let's
say that we have inside user property form. And here inside we want to
provide the type is string. And I'm saving this and
then get in here and narrow that argument of type foo is not assignable to
parameter of type. And here we can see
our e-mail username, password created
add underscore ID, which is coming
from document and here to underscore version. Which actually means if we
don't try this user document here with don't get any
validation of TypeScript. This is extremely important
that we are getting it. Now inside the mask
provide all fields. Chairman d'etre for our user. And let's start with the e-mail. So here our field is
email and then said, We must provide the type, it is a string. Secondly, here we can say required property
and as you can see, TypeScript helps us and shows what properties we can
provide insight Mongoose. So actually inside required, but can provide an array with some valid
data and message. Because actually we want to
show on the front-end and nice message when our
e-mail is not valid. So it is not just a string. This is invalid email. And for this we can input
here on the top validator. So here I want to input valid
data for John validator. And as you can see, we're
getting an error module. Validator is not installed, so we must jump
inside our server. Here I'm stopping web
server and they will write here impairments tau valid data, but this is not tolerable. Also want to get types
for this package. This is y here at types
slash valid data, but it must be installed
on in development mode. So here, don't forget, minus t. We can now open on the right, are packaged JSON and as you can see here in
dev dependencies I have types validator and
independencies validated. Now here we don't have any
error for the valid data, so we can specify
it inside required. And actually here, I'm
a little bit wrong. This is not a field
required because instead required with simply provide
if it is required or not. In our case, e-mail is required. Here as the second parameter, we can provide an error
message if it is empty. And here we can provide
e-mail is required. And after this we
have our validation. This is where here we
have a property validate, and this is exactly where we
want to use our validator. So here I can write
validator dot and we're getting a nice auto-complete
because of the types. And here I will write is e-mail. So here we have lots
of validations. And where I get an e-mail
validation out of the box. And there's a second
argument we can provide here, the invalid email. Last but not least, I
want to create indexes. And actually if you don't
know what is indexes, These are things which can make your database
requests faster. And secondly, it can make, for example, email field unique. In this case, I want to
write create indexes. And here as an object, I am providing insight, unique, true what it does, it read our email is
unique index in this case, but can't save two people
with the same email. So once again, inside
mongoose schema, we're providing all fields
which we need for our user. And the first field
was an email here, but must provide a type inside. And this is exactly
the onload line that we need by default. But actually we can provide here required which we
can set in true or false here we can provide some validators and we
can create indexes. And actually we can
do all this stuff just like this inside
plane MongoDB driver. This is why I prefer to use
Mongoose because it is in a realm where we're getting all this things out of the box. Our next field is our username. So here we can provide our username and
it will be easier. First of all, the
type will be string, and secondly, required
must be true. And here I also want to
provide the validation error. This is why we will use the
same notation with the array. Here is true and
username is required. Now we can copy paste this user because it
will be the same. And the last one,
what we have here is our password and our
password is type string, and it is also required, but here password is required. And the last thing that we
want to do is select false. And actually what
select false is doing, it will never select this field when we're
doing some request. For example, we want to get a user by ID from the database. We won't get this field back. And this is extremely
important because it makes our application save. It doesn't matter what
queries we're writing. We're always know we won't
get a password back, which means we are by
default on the safe side. Last but not least,
what they want to provide here is timestamps. So we can provide
here inside an object and we can write
here timestamps, and here we're
setting it to true. In this case, our
createdAt property will be directly
generated by mongoose. But as you can see
here, there's a typo. We don't need to provide
the subject here. It is a second argument
after our object, which actually means
in the new schema as a function we're
providing first of all, this object with all our fields, and secondly, the object
with timestamps true. So this is the second
document of how new schema. So we're ready with the
first part of our model. But we have a really
huge problem here if we will just try to use
our user like this. For example, here we can
write new user and were provided inside an object with
e-mail username password. And after this we
will call safe, then our user will directly save this password
as plain string. This is forbidden, this is wrong and we should
never do this. This is why we must
fix this issue. What we can do about it, we must hash our password before we will store
it in the database. And for this, we can use
a real nice library, which is called the crypt. And this is the most
popular solution. Hash the password. This is where we must jump inside console and
install this package. So npm installed be crypt jazz and we also
want to install typings. So here at types
slash be crypt jazz, but obviously it must
be width minus t. So let's check the sound. I'm jumping to package JSON. And here I see Types
be Christians in dev dependencies and be
crypt independencies. This is completely correct. Now what we can do here, we can define user
schema dot prayer. And this is the
possibility to run some function before something. Here we're interested
to provide safe, which actually means we will run our function directly
before safe. So here I want to write a
sinc function and they will say Why I'm using here function and not arrow
function in a second. It is really important
to write it like this. And here we're getting Next. And now here we have a brackets and we can do something inside. The main idea is that
we can do whatever we want here with our object. And after this, when we change
the subject, for example, we must call next and then mongoose will proceed
with saving of our data. And pre-sales means that this
function will be called, first of all after Create
and secondly after update. And this is exactly
what we want. For example, we want not just create a password
for the user, but also updated
later in update form. And the first
condition that I want to write here is like this. If not, this dot is modified. And as you can see,
we have a function is modified and we can
provide insight word, password, and then
we want just to not do anything and return next. So what we're doing here, we're checking if our
password field was modified. It is not the case was created because there we will
have a password, but it is the case
with update if suggestions user and we
didn't change the password, then it doesn't make any
sense to apply this function. This is way here
where chicken, okay, if password field
is not modified, then we simply say for
Mongoose, go ahead. Now as you can see here, we wrote a sinc function
and not an error function. And it is important
here because we want to use here this property. And to have a correct
this reference, we must write it as a function and not an arrow function
because another case, this will be run and
also we use here a sinc function because the crypt operation
will be a synchronous. So after this, I want
to write try-catch. And actually if we
will get some error, will be crypt, then we're
coming inside cage. Here, we're getting
our error and we want to return this
Sarah inside next. Here we're throwing
inside Azure as error. And you might ask, okay, but why is this strange
notation error as era? Actually, if you will try
to just write like this, we will get an error. Argument of type unknown
is not assignable to parameter of type callback
error or undefined, which actually means
inside a cage, every Azure is unknown, which is obvious because this is sketch and we don't
know whatever happened. This is why we're writing catch, but actually we can't
use arrow like this and we can provide
it inside next, this is why we must
convert a type of our era to
something meaningful. In this case, I'm
using S arrow and we can provide errors
inside next function. This is exactly what
we're doing here. Now we must try it our
logic to hash the password. And for this I
want on the top to input our big ripped js module. So I'm inputting be crypt
jazz from Big Rip jazz. And now inside our try, we can first of all get a salt. And if you don't
know For be crypt where generate and first salt and then we provided in decrypt function to
hash the password. This is way here, we
need to get a salt. And for this we're Colin be
crimped js dot gen salt. As you can see, this is
a function which are synchronously
generates fast salt. And here we can provide
ten, for example. And this is an
asynchronous function. This is where here
we must try to wait, so it will get fast salt. And now here we must
update our password. And actually this function is called before we
saved this record, which actually means with this, we have a reference to all fields that we're
trying to save. And here I will write this dot password to change our field that
we're trying to save. Here we want to assign a weight B creep chess dot
hash and actually hash, as you can see here, we'll
have our password and inside we first of all must provide
our password, this password. And secondly salt, in this case, our password will be
hashed and we will just store a hash
inside our database. In this case, after we will call next and we must do it for sure, this password will be updated and we will save
the clean record. This is why inside
we want to write return next and we
simply call it, this will trigger the saving
for a record to database. And most important part that
redraw this logic inside modal sold this
logic with saving is completely isolated
inside model. And when we will write some code which is
related to the user, we won't even know
about this logic. It is all inside the model. It doesn't have
anything to do with finding a user or
saving the user. It is what is happening
inside model. The last thing that
we need for future is the function
validate password. Why do we need it? Because actually when we
will try to login the user, we want to compare
not only e-mail, but also provided password. And we have a really
nice thing which is called methods
inside Mongoose. So here we can write
user schema methods, and here we want to
create a new method, for example, validate password. And actually it is working
in exactly the same way, like for example,
methods inside classes. So we can call on our
instance of our user. This method validate password. And here we must
provide the function. And again, I'm not writing
here arrow function but just a function here insight by getting password
as a parameter. Because actually when we
want to compare a password, we will provide something
that we want to compare. This is y here
password is a string, and here inside we
want to compare the provided password with our password inside
our instance. So here we can simply return
be grip js dot compare. And this is a function to
compare, first of all, a plane password which is
provided from outside. And secondly, our string, and this is this
dot password that we're storing as a hash
inside our record. So our latest usage
will look like this, where right in
here, for example, that we have a user and
we want to save it. So here we have a new user. We must provide
inside our email. Then we must provide
here a username, and then we will provide
here a password. After this, we will
try to save a user. So here we will call
use. It does save. And actually after
Colin this thin, our press Save will be called and we will
store correct password. But after this line, we can also write a user
dot validate password. And here we can provide any password that
we want to compare. This function will return for us true or false depending of the correctness
of the password. And this is fully correct to do all this stuff inside User model and not inside some controllers where we simply work with users. Because in this
case our logic is completely isolated inside user. And now we're missing
just a single line here inside our types user interface with defined user document. But actually we
must specify inside this user document that we wrote here a new method,
validate password. This is why what I
want to do here, I want to create a new method
inside validate password. And we know that inside we're
getting some parameter, we can just name it param1, and this is a string, and we know that back we're
getting a string. In this case, I will
use a document is completely correctly
typed and we can use it later inside
TypeScript to call this validate password
method and get autocomplete.
10. Adding registration: In previous video, we successfully created
our user model. And they can understand that previous video was really
dry because we just created a model and
you didn't see how we use this model in a
real application. This is why the goal
of this video is to create our register method, which means we will
register user, and this is exactly how we
will use our user model. So let's look in our code. For now we have just a
model inside source models. And what we want to build
is MVC architecture. What does it have
a mean actually inside expressed as a framework. Don't have any architecture
in expressed with simply defined some routes start web server and
we're good to go. We don't have a
lot of rules which are defined inside expressed. This is why we must do
something on our own. And the really popular
architecture which use nicely our back-end
project is MVC, which actually means model
view and controller. And actually 90 per
cent of the cases, we will just use models
and controllers. We won't use any views just
because we're working and creating an EPI and we don't
need to render views there. We just respond with the
chasten and this is it. This is why my idea is to create a new folder which is
called controllers. The main idea is
that here instead sorority S will register
all our routes. For example, here we have
a route for the homepage. The main idea is that we're
not writing the logic of this route here
directly as a callback, we will write it in
site-specific controller, which actually means
all our requests we want to split in
different controllers. For example, we have a user
controller and there were riots in all our actions
regarding registration, login in, getting user
login out, and so on. Then we have a board
controller where we will write inside everything
which is related to board. The most important part is that inside model would define how we're working
with the database. So we create our
entity like user, but inside controller
way using this entity. And we're building some
responses of our API, which actually means we're
separating our logic. Everything was database
is going to models, but we're using models
in such controllers. So this was the theory. Now let's create our
first controller. And for this I want to
register here in your route, and it will be a route
for registration. This is why here we
have app post and the URL will be slushy
pie slash users. Here will be our users
controller dot register. So our first rule here
is that all our URLs, we'll start with slash api, because actually it
is really nice to have a namespace for our API. Secondly, as you can see
where not important, something like register
where inputs in here the whole user's controlling and we need to have
some good naming. The typical name and
for controllers is always with asset
than for example, a user's controller and
not user controller. Now let's import star
as users control it. And if you don't know
what this is doing, the main idea is that inside we will have a
bunch of functions. And this time as groups, all these functions
inside this object. And then we can
write something like users controller dot register. So here we want to
import it from, and here we have our
controllers slash, and here we will
create file users. And as you can see, it
doesn't make any sense to name this file
users controllers, because this file is situated directly
inside controllers. This is way here, Let's
jump in so it controllers. And here is users.js. And this is our file
which is a controller. And then say it was right, all actions which are
related to the user entity. Now on the right, I want
to open our server Ts. And As you can see, actually
this part that you can see here is what we're
writing inside controller. So this is our callback. And as you can see, this
is just a plain function with the request response. And the third parameter
here can be next. This is why what I
want to do here, I want to create a function
which is called register. And this is an
asynchronous function. Why we need here in a
synchronous function, because we will work
with database and those requests for our
database or as synchronous. And here we're getting as an argument is first
of all request, secondly response, and
the last one is next. And this is just a function. So this is exactly the same what we're paste in here directly, but we just moved it outside,
inside our controller. But this code is bad. Why is that? Because actually here we didn't type our request response. And next, so here
I can write colon, and here we have our request. And this is the most
important part. Here we have request which
is coming from Fetch API. We don't need it, but we need is request which is
coming from expressed. This is y here, import
request from express. And as you can see now we have completely
different definition. We have here inside dress,
body, request body. And this is exactly
what we need. Also, we need here not
only request but response, and I'm in Britain
here also response. Type in here response
as a response. And the last one is not
next, but next function. Here, our two input is correct. It is from expressed. So this is how we typically will create a new action
of the controller. Doesn't matter if it is user controller or it is
some article controller, it will be always the same. Now, directly inside they want to write try-catch. Why is that? Because actually we will write a synchronous code with async
await inside this function. And we want to
handle all errors. And the easiest way
was expressed to handle an error
is by using next, and we're already used it
previously inside our model. Here it is working
exactly the same. So we can write here, try and we have cage and
we're getting some error. What we want to do,
the one to call here next and throw inside the error. This is it actually, this is a single line which will propagate our error to express, and then express will show
the Sarah on the screen. Now inside we want to create a user because
actually this is the registration
and registration means simply creating a user. This is why here I want to
import the user model from. And here we have our models slash user model that
we created previously. And as you can see
here, I didn't name it user with capital U, but use the model. This is just to be
crystal clear inside of our code that we're
working with model. And now we can use
this model inside. So we can write here const, and here is new user, because here we want
to register a new user and we're right in here
in hue user model. And now inside we
must pass some data. In our case, we
must pass here in male than username and password. So here we want to write that. We want to set an email, and this is request
dot body dot email. Then we want to
set here username, and this is request
dot, dot username. And the last one is password, and it is request dot
body dot password. But here we have a huge problem. By default, express
can't work with boiling and by default express
won't pass it a tau. This is why what we must do, we must install additional
package for this. And this package is
called bodyParser. This is why I will jump inside the console and be aware
here I'm inside server. I will write npm install. And here we want to
install bodyParser. I'm hitting Enter and the
package is installed. Now we can start our server
again and jump back. So the main idea is that
here instead sorority S, I will import my body parser. So here let's name
it bodyParser with camelCase and we're importing
it from package bodyParser. Now somewhere here before
we're doing our routes, we can write ab use and inside we want to try it
bodyParser dot json. And actually here you
can directly check what bodyParser Jason is doing
and it returns a middleware, the tone lip parses JSON and this is
exactly what we want. We want to pass our JSON, but it is not only this, but also want one more
IPOs body parser. And here will be
dot URL encoded. And inside we're
providing extended through what these
two lines are doing. First-line will just look for content type
application json and then parents of a
body in the JSON. So we can work with our body as an object and this
is extremely easy. The next line we'll
do exactly the same but for URL encoded strings. And then we will
also get our body. So with this configuration
in every project, you can work normally with the API where you have
body as adjacent. And this is exactly
what we're doing here. We're reading body from request. Now I want to console log here our new user so we can
check how it looks like. And after this, I want to
try and save the user. So here we can try
it saved user, and here we can call a weight
in your user dot save. This single line will create France new user inside database. This is why here I
want to console log saved user, coma, saved user. Now I want to use
such tool which is called Postman to make request. And if you don't have
postman on your machine, you can simply jump to Postman. Don't load it here. It is completely free. It has paid tiers, but we don't need
them for our course. Here, it looks like. And actually what we want to do, we want to make a post
request to our URL. And here we have
our local host for 1001 slash slash users. Now here we must jump to
body and select here wrong. And on the right we can
say that this is adjacent. Now what we want
to pass inside is an object with three fields. First of all, we
have here email, for example, foo at gmail.com. Then we have our username field, for example foo, and we have our password field,
for example, 123. Now let's send a request
and check if it's working. As you can see,
the request hangs. And this is completely
normal because actually here but didn't call rest
Jason for example. This is why it is handgun. But now we can jump inside console and this is our output. First of all, here we
can see our new user. This is before saving. So this is what we have
after Colin, new user model, which actually means
we're throwing inside these three fields and we're
getting user from one Goose. And as you can see,
the main difference of our object is that
we have here NAD, which is a MongoDB AD, and it was automatically
generated fast. And actually after this, we can use this new user
and save it, for example, to database with
dot save method, which is extremely easy. And the most important
part is here saved user. This is our saved user, which comes from the database. How we can tell that
it is already saved. First of all, here we see our password and actually
our password here. We gave like 123, but here we didn't
save it like 123. It is a hashed password. Why it is happening?
Because actually we define the model and here we
have a method press Save. And just to remind you, here we generated a
hash from our password and we save this hash with the
crypt instead of password. And this is extremely
important pattern. We don't want to write here any logic regarding changing
the password to the hash. It doesn't make
any sense because we want to define
model with some logic. And then it happens magically
because it is defined. In this case here we save, we just changed our
password and hash and inside of our controller with don't know anything
regarding it. The same goals about this fields created at and updated at. These two fields were
added for us because here we defined timestamps true. So our user was successfully
saved to MongoDB. But actually we can't just throw the saved
user as a response. Is that first of all, we don't need all
fields and for sure, we should never give
this password outside. And actually just
to remind you here, inside our models user, we said that password
is not selected, it is selected false. But actually after
we saved user here, this password is
given fast back. And actually if we're
doing some fine, we won't get the password field. But after saving the user
will obviously is getting it. This is why we must create a nice response which
fits our needs. This is why here
I want to create additional function,
normalize user. And here we're
getting user and we know that this is user document. And as you can see
our user document, we can input from
types user interface. Just to remind you, use a document is just
how a user object with ID and validate
password method. And as you can see here, we can inspect new user and we can see that this is use a
document and the property, this is exactly
what we're passing here and here inside
this function, we want to return the
normalized for API user. So first of all, here
we will have an e-mail, this is user dot email. Then we want our username. It will be used username, and the last one is 80, it is user dot ID. And just to remind you, in MongoDB or ladies are
stored with underscore ID. But actually here
inside Mongoose, we can use them in both ways, like underscore ID
and like dot AD. This method already
exists and it's simply as references
underscore id. So I'll normalize use a function is completely ready and now we can call here eat when we
respond with this saved user. So I can simply write here
as sand and inside we're passing normalized user and
here is our saved user. Let's check if it's working. We don't have any errors
here in web server. I will open here Postman
and hit Send again. And as you can see
with successful in normalized our user and
we didn't get here, for example, password back, which is extremely important. But here is something that I don't like about our responses. Actually, we did some
validation inside of a model. But if I remove here
username and hit Send, we're getting here 500. And this is actually HTML page
with some validation here. This is not what we want,
what we can do here, we can use the sketch and
read messages from our era. But the main problem is that our error is not always
validation error. It can be, for example, 500, but we can also get
validation errors. And the most correct
way to check it inside TypeScript is like this, where right in here that
are Azure is instance of Azure DOD validation error. But here it is important
to input error correctly because we want to import
this error from Mongoose. So here I am inputting
our era from Mongoose. In this case, it will be treated correctly
because actually here, era that validation error
is a class of mongoose. And if we got any validation
errors of Mongoose, we can work with them here. Now let's just try it inside console log and check
what we're getting here. I will hit Send again. And as you can see
inside the console, we're getting here errors. And this is an object. Which actually means
we can read messages from the subject and
show them on the screen. Here I want to create
a property messages. And here we can write
object dot values, which will read off
values from our object. And inside we're
throwing error errors. This is exactly what
we're getting from mongoose and we want to go
through every single field. And here we're
getting error and we just need error message. So this will be an
array of strings. Now we can simply write
return risk status, for example, four to two, which means
unprocessable entity. And here dot JSON messages, which actually means
here when we're getting any validation
messages were answering with this status and we're showing
this error messages, we don't have any
errors in the console. So let's try again. I'm hitting here sand and we're getting a nice error messages. Username is required by this happening because here
we checked inside cage, for instance, of the class, we normalized our messages here and we responded with them. And actually it makes
a lot of sense to move this function later to some helper because we will do exactly the same stuff
again and again, where we have one
goes validation. And here is the last
step that we want to do. We actually need for our
client to provide a token, which actually means when
our user is logger tin, we generate a unique token to
make a DVT authentication. What does it mean?
We have a special string token which were
throwing on the client. And then client can attach
this to be t to the header. And later we will check
if the request is authenticated and diffuser is
allowed to do some changes. But in this video, we simply need to throw
inside our response, the GBT token that
we will generate. And for this we must
install additional package. Here I will write npm
install JSON web token. And it is not only it will
also want to get typing. So here will be at types
slash JSON web token. So we installed two packages and then restarted
my web server. Now let's jump back in. Here we want to import now our JSON Web Token, or just DVT. So let's try it here in
productivity from JSON web token. And now what we want to
do here inside normalize, and this is the best
place to do it, because here we have the
whole user and we're building something which is
not related to the database. So we want to generate
here our target. And for this we're just Colin, GBT dots sign the inside. We're must provide a payload
and some secret key. So what do we show
insight payload here? First of all, we want
to get an AD and this is user dot ID and
then an email. It is user.email. Actually it is enough
for us to provide just an ID so we can
find later use it by ID. But email is also nice to
have for some validation and understanding with Susan
and what is secret here. It is just some random
string which will help us to decode and
then call tokens. So what we want to do
actually want here inside those services
to create a new file. For example, config dot ds. Here we will store all
such needed properties like for example, secret. So here I just want
to export, const, our secret property and
they will name it secretes, obviously for
production reasons you want to have here
something more secure. Maybe some long hash
like 12 symbols are. So now we can use
the secret here just by inverting
our secret for John. And here we have
our config file. Now, instead of this
secret or private key, I will just write a secret. So what this line does, it generates a token
which is just a string. And now we need to add here
to our response, that token. Let's check if it's working, but don't have any errors. Let's jump inside. Postman, hit Send. As you can see here, I
must provide my username. So for example, for,
let's check this out. Here we get all our
fields and also talking. And as you can see, our token is just a unique string
that we will attach to all of your requests on the client and decode
back on the backend, which we will do in
our next videos. So we successfully implemented our registration method also with validation and with
normalization for our API.
11. Implementing login: In previous videos, but fully implemented our register
method in this video, we want to implement the
beginning of our user, but actually I want you to try and do it
yourself because it will be super similar to registration and we're
already prepared everything. So what do you need to do a toe? First of all,
insights server.js, you want to create a new route. And actually here we already have our route for registration. Now we must create
one for login. For example, we can create a string slash api slash
users slash login. Now here in such controllable must create new method login. The question is obviously
what we will get there and what this
method must do. And actually insert Postman, we can just try to use it. So here you will have slash
login and instead of e-mail, username, password,
we simply throw to our request
email and password. We don't have username
because this is login and email is unique. And here you have two
possible variance, how you can implement it. First variant is easier. You simply want to read
an email from the body. You want to try and
find the user inside database and return
this user back. And actually don't forget to use normalized user because
we need that talk. And also, if you want
more difficult approach, then you can also try and
validate not only e-mail but also password for this validate
method inside our model. But even if you try to do
the first step on your own, this is totally fine. If you want to
implement one of them, just pause the video now, and now, let's do it together. So our first step will be to
jump inside or sororities. Here we want to
create a new route. So we have here a post because this is a post
request following Guinean. And here we have the CPI
slash users slash login. And here is our users
controller dot login method, which we will
create in a second. Now I will jump in save controllers user
and they won't copy paste anything because we want to try and write
it from scratch. So here we have our login
method and we know that this is an asynchronous method
where we get our request, which is request exactly like on the top inside registration. Secondly, we have
here our response, which is type response. The last one is next, this is next function. And now here inside
of our function, we want to write, try and catch. So inside cage here we
will get our era and they just want to propagate it
to next era. Why is that? Because actually here we won't
have any validation rules, but we'll just check, oh, validation inside our try
and not inside catch. What do we need to do inside? Here we're getting
our request body with email and password inside. And our first step is trying to get this user inside
the database. Here we can write a K. We need our user and retry with a weight user model
dot find one. And as you can see here
we have find by ID, find and find one. So actually using
model.fit find tries to find all documents as an
array by some predicate. For example, here we can find
a list by is active field. Here, find one. We'll do the same but fine, just a single record. This is what we want
to use and sometimes we will need to find
an element by ID. Here we have a nice
fined by the method. And as you can see here, we also have lots of other methods, like for example, find one and delete and update and so on. But for now we will
use find one and then said We must give a
predicate as an object. And here we have
our email and this is request body e-mail. So actually this single
liner will try to find our record inside user's
collection by this email. Here as you can see where
getting user document. But actually this is not
true because we can get here now and not a document because maybe the
female does not exist. This is way here. I want to check if
we don't get a user, then we want to throw an error. And for this we
can simply return our response dot status. Here we have photo to status
just like previously. And here we want to
throw some JSON back. And actually here with don't have a different validations. In any case, we simply throw
invalid login or password. This is way here on the top. I can create and save to
the object our errors. So here I want to create errors. This is an object with field, email or password, for example. And here the value is
incorrect email or password. So the main point
is that we have exactly the same
structure of our errors, just like we have in
all other places. Here, we're just returning our errors back inside of JSON. If we don't have a user. After this, we can
respond with our users. So here is sand and
here we can call our normalized user and were provided insert
user that we found. And actually, if you wrote this code even
without this error, check your golden, because you tried to do
something by yourself. Now let's check if
this code is working. So we don't have any errors
here inside web server. Let's open our
postman and hit Send. And as you can see here,
actually it is already working. Here is our slash api
slash users slash login. This is post request
with our two fields. Here we're getting bank the
correct user with the Tonkin. And actually if here, our email does not exist, hidden send and we're getting an object with
email and password, incorrect email or password, which actually means
we successfully implemented our lung
Kenyan of the user. But here we're missing
just one small thing, and this is validated
of the password, but this is extremely
easy to use Just because we already prepared
everything inside our model. And just to remind you inside our user model here
on the bottom, we have this validate
password method and we're using here
be crypt compare, where we're comparing
the password of the user with some string. This is exactly what
we want to do here. We can just create a
variable is same password. Here we want to call user
dot validate password. And as you can see, we have here our autocomplete
of the TypeScript. And actually we are
getting this autocomplete just because inside our
user interface document, we wrote this line. If you didn't write
this line here, then you won't get
these auto-complete. So here we have our validate
password and inside we want to provide a string
to check if it is correct. And this is request
body password. And actually if this
passwords are equal, then we will get here Boolean. But as you can see,
we're getting a string, which means something is wrong. Let's check our interface. Validate password
returns a string, it is wrong, it should
be Boolean here. Now is same password
returns false boolean. And here we can write
some condition. For example, if naught
is same password, then we want to throw
exactly the same error. So I will copy paste here. Since status for the two JSON errors,
Let's check this out. I'm sitting here sand and
we're getting this nice user. But what will happen if we will throw here incorrect password? I'm hitting here send and
we're getting an error. So let's look inside console. Here we're getting quite
a strange message, illegal argument,
string undefined it. And as you can see here
in our stack trace, it is coming from our
controller and hear from our source Model Ts
line for default. So let's jump inside
our model Ts line 44. As you can see, this is
our big crypt compare. The question is,
what is the problem? This is why what we can write
here is validate password. And here we have first of all, our password and this password. And actually I just
want to see here this, to know that we are
on the safe side. Our server is restarted. So let's check the sound. I'm hitting send endless
look inside console. And as you can see
here, the finer we see, first of all, validate password. This is a string and
here is our object. But as you can see
in this subject, with don't have password, this is why we can't compare object with the password because we don't
have a password. Why we don't have it? Because actually
here on the top, we said for the password, select false, which is
completely correct. In 99 per cent of the cases, we don't want to select a
password because this is safe. But actually in
this specific case, in this login method, we can't work without
password because we need to compare our
password of the user. This is why what we can do, we must assume this find one. So it also gets a password. And for this we can
write dot select. Here is a string inside
we're writing plus password. And actually this is a
really nice notation because we can use
here plus password, minus biography and so on. If we want to remove or add specific fields,
in this case, just for this specific request, we're getting not
only the whole user, but also password field. And now if we will try
again, I'm hitting sand. We can check and said console. And now we're getting
our user with password, which is hash, and then
we don't have any error, but it is not working
correctly because here I have a wrong password and
we still are getting user, why it is happening. And actually, if
here we will look on our validate password
method we're using here big crypt compare. The question is what we're
getting back in here, we can see in the Taipings
were getting promise Boolean. And this is extremely important. This is an asynchronous
operation. It is not synchronous. This is way here
we must jump back inside our types user interface. And here we can say that we
are getting the Boolean. It is actually Rami's
of the Boolean. And now it is completely
correct because now if we will jump inside our controller here we have used the
validate password. We see that we're
getting back promise Boolean Now is same password
is promised Boolean, which is not what we want. This is where here we can write a weight and this will
resolve our promise. And here we will
get our Boolean. As you can see, TypeScript
really helps us a lot in understanding
correct typings. And we can always check
what type we have. Here. We're getting the same password
and now it should work. Let's check this out
inside Boltzmann. I'm hitting sand and
we're getting a message, email a password or not correct. Here with the correct
password, 123, we're getting back
our user and this is exactly are implemented
login request.
12. Creating auth middleware: In this video, I want to
talk about middlewares. So what is Middleware? Typically when we're making a request from the
client to the backend, we simply throw this
request inside our route. And then inside controller, this is exactly what we did here inside our source server. So here we have two post
request, register and login, and we simply jump
inside our controller, which actually means
here insert controller, we're getting request
and response. Middleware is
something which can be called before we're
getting here, which actually means middleware is being applied
on the back-end, but before our request is colon inside our controller
or inside our route, Kohlberg, by doing it
middlewares at all. If you need to do something with request before this request
is get into the controller, this is exactly when
you need a middleware. Middleware, do we
need in our project? This is the authentication
middleware. Why do we need it? Just
imagine that every single time when we need to do something with
Strategist at user, we need to check his token, which actually means in
every single method, like for example here register, we're getting the
token of the user. We must pass this token. We must validate this token, and we must find current
user with this token. And it doesn't make
any sense to write this code in every single
controller action. This is why we must
create a middleware which we will reuse everywhere. This is why I want
to jump here inside source folder and create
here a new folder, which is called middle. Whereas here we can store
all our middlewares. And the first middleware
that we're must create is owls dot ts. And actually, what
is Middleware? This is just a function. This is why here
I want to export default and the
synchronous function. And you might ask, okay, But why it is a synchronous? We simply get here our request and within
do something with it. Because actually here we want to also work with the database. If we have a token, this token is valid. We want to read an idea
of the user from this token and get this user
from the database. So we can use this
user that will be prepared inside our
controller later. This is why it is an
asynchronous function. Here we're getting
a request response. And next, exactly like
we did previously, here, Let's type our request, then we have our response. The last one here will
be our next function, which will be next
function from R expressed. And here I will
invert on the top our request and response. And this function does
not return anything. This is why it is
a void function. And inside this function we want to read the token
from our request, but I won't directly to wrap
all our code with try catch. Why is that? Because
we will try to make some asynchronous request to our database and it might fail. This is why here it is a
good approach to write try-catch where we're getting our arrow and then side catch. We just want to make risks and status and here will be 401. You might ask, okay, but why we didn't
show an error here? Because actually we don't care. This is a middleware to
check our authentication. If for some reason we can't parse the talk and talk
and listen, valid. We couldn't find this
user in any case, it means that our user
is not logged in. This is where here we're
directly our 401 status. Now here we must
read our status. So here I want to create
our header and we can get our header from request dot
headers, dot authorization. Actually this means
that we will store our token inside our
authorization header. And actually a typical approach, hope we implement our
duty authorization is we have here
authorization key, this is our header
and the value here will be Bearer space token. So here we will have
some unique stream. This is why we must split
our token accordingly. But first of all, here we read our header and it might
be that it is not set. In this case, we simply can
say for 01, this is y here. If we don't have
our, our header, we simply can copy paste this line with rests
and status for 01. After this, we really
need to parse our token. So here we will get our talking and this is our header split, and this is just a string
which was split by space. So we're getting array
with two elements. As you can see here. In the first position, we will have better. And on the second position
it will be our string. What we want to get, this is where here I will take the second element of the array
and it will be our token. But as you can see here, we're getting the message
from the TypeScript. That object is
possibly undefined. And this is why I like
TypeScript so much. It helps a lot
during development. What does the problem? Actually here we have our
IV and dress and status, which actually means
we won't come here. But TypeScript
understands that we will come here because actually we
didn't try it here, return. And this is why this code is invalid because in
this case here, our header can be
strain or undefined it. But after this correct
check with return, it can be only a valid string. So the next step that we need
to do is verify our token. By using GBT, this is way here, I want to import DVT
from JSON Web Token. And just to remind you, this is a library which we used to generate a
token on the backend, and now we need to validate it. This is why here
we can just try it that we are getting some
data from our token. And here will be GBT
verifying the inside. Well Python, first
of all, a token. Secondly, our secret key. And just to remind you
here inside our config, we have our secret. This is why here I
will just try it secret and it will be out to
inputted from our config. And we don't need to provide
hear any additional options. But if we will check here our
data strain or DVT PE lot, but actually we know that
this is not correct, this is not what we're
starting inside. If we will look here, inside our controller users, here we generated our token and inside we have ID and email, which actually means
it is valid here to say after derivative verify as. And here we can say that we are getting back an object with a D string and also our e-mail, which is a string, in this case here now in data we're getting correct database. This is an object
with a D and D mail. So we're getting here
the idea of the user and now we can try and fetch
it from the database. But for this we need
to use our model. This is way here on the top. We can import our user
model from our models. So here we can jump back
inside our models slash, and here we have our user. Now after our data, we can make a request
to get a user back. And actually here
we're using a weight. Here we will have
used a model dot. And here we want to
find user by AD, and we have this function
by default inside mongoose. And here instead of a div, we can write data dot AD, and this user will be there, or it might be now, this is where we'll
also need to check it. If we don't have a user back, then we want to also say
that user is not logged in. But if everything is fine, then we want to set
inside request our user. So actually the
main idea is that this request will be
updated by us here. And then later when
we're calling next, this request will get
to our controller and then we will have
direct access to this user. This is way here,
I want to write request user equals user. And this is this user that
we got from the database. And after this romance,
just call next. And this line is saying
that we're ready with our middleware and our request can proceed to our controller. But here we have a problem,
as you can see here, where I getting an error
property user does not exist on type request and actually
it is completely valid. This request is
coming from express and inside expressed
there is no field user. So what we can do here, the Ron approach
will be to write here any and I highly
recommend you to avoid using any in your projects because then TypeScript can't
really help you. You simply have your code with
holes of plain JavaScript. This is y here, Ras as any, will be a super bad approach. Why is that? Because
here we simply say, we don't care what
is about requests. We simply say it is any dot
user and it is working. This is the beginner approach. We're not writing
code like this. Here. Request user is totally fine, but this request should not
be requests from express. We must extend it. And actually here inside types, we can create a new
type and let's name it express request dot
interface, dot ds. Now inside I can create
this new interface. And let's name it express request interface and
actually it must extend. So here is extend request. And actually this request will come directly from expressed. So here on the top I can write import request from expressed. So what we're doing here, actually we simply created
an interface and we extended everything that we had inside
request to our interface. And now here we can simply say that we have a
field, the user, which might be undefined it, and this is our user document. Here you might task or k, But why user can be undefined it here we
don't have a case where inside request user is undefined it and
you are totally right, but we're not using middleware
with every single request. Sometimes we won't have
user inside our request because not every single
request must be authorized. And now we can just copy paste this express
request interface and jumping set our middleware and put it here
instead of request. So now I need to import
express request middleware. We're not using requests
from express anymore. We're using the regular
are extended version. And now we don't
have any errors. And what we're getting here is complete request from
express plus our user field. And this is exactly
the correct approach to use TypeScript. So we successfully
created our middleware. Now we need to use it. And for this I want to
create a new route. Well, we will get
current user by talking. So let's jump back and
say source server. And here we have two
posterior rails here. Now I want to create apt-get and we have here slash
API slash user. This is the route to
fetch current user. Now after this with comma, I want to write out middleware. And actually in
this file we didn't declare what is our middleware. So we need to input here our middleware from
our middlewares. So here we have
middlewares hours. And this is exactly
what we will do. If you are writing like this, then you will apply
this middleware before we're colon
here controller. And here we will get our
users controller dot, for example, current
user action, which actually
means first of all, in this route, this
owls middleware will be executed if we will get a user request is go
into the controller, then where champion here. And as you can see with express, it, quite easy to
read and understand. Now I want to jump inside or users controller and
create this new method. So let's on the bottom, create our new function, which will be a current user. And here we know
that we are getting request and response,
but important, but here we're not using
requests from express, we're using our
extended version. So here I will write
that way I get an express request interface. And the next parameter here
will be raised response. It is staying the
same like previously. And here inside of our function
we must apply some logic. What we want to do here, actually inside current user, we can directly get this
user from the request. Here we can say dress
dots and normalize user, just like we did on the top. And here we'll
request that user. And actually it will work mostly because what
we're doing here, we're using our user from the request with throw it
inside normalized user. And just to remind you, we have normalized
user here on the top. And this is just a
normal user document. And we generate here talking our response and
we send it back. But actually here we have
TypeScript. What does it mean? Here we're getting
an error argument of type user document
or undefined it. He is not assignable
to use a document. And this is completely
valid because we said that inside our request with
don't always have a user. And actually the point
is that this logic will never happen because
inside our server here, we'll roll this middleware, which actually means
if we don't have user, then this house middleware
will return for 01. But TypeScript
doesn't care about our middleware because it
simply reads our function. And if I'm just looking on our function without
our middleware, then our code is
invalid by that, because here we're
trying to throw undefined it inside
normalized user. And to handle this
for TypeScript, we must try it here. With don't have a user, then we want to throw 401. So here we can
write return, res, send status, and
here inside for 01. In this case, it is
completely valid for TypeScript because
here in request user, it can't be undefined it
with did this check here? And actually this code is much better because in this
case here we have this single function and we can completely tested in isolation, but don't care in this function, what we did outside with
additional functions, middleware over terror
with simpler note that this function will work
correctly in every single case, because we covered
all cases here, our function must be
correctly implemented. Let's check if it's working. I'm jumping to the server
and we have some error. Let's check what we have. And actually here you can
see that it was the era of TypeScript and the last
compiling was successfully. We started the
observer connected to MongoDB and here is our API. So we can jump directly to Postman and try to
make a GET request. But inside our request, we must provide a token. This is way here. I will copy paste to talk
in because we must use it. And for this I will
create here GET request. And this is slash
api slash user. I'm just hitting here sent. And as you can see, we're
getting here unauthorized. Why is that? Because inside
authorization with didn't provide a valid token. But if instead of this string, I will paste our talking
so Birra than space. And then we have our string, I'm hitting here send, and it magically worked. And here we're getting our
normal user with the Tonkin. But most importantly is
that we didn't try it. All this logic with getting current user here
inside this method, it is written inside middleware. And now we can use
our middleware in every single place
where we want to check for current user or if we need the current user information
inside our controller.
13. Creating auth module: In previous videos will always
be prepared some requests of Pi for our registered user
and gating current user. So now it wouldn't be nice to start implementing
something on the front-end. This is why here I want to
jump inside our client. And here I want to start
with our user module. And what we will have
in our user module is two pages logging
in and registering. But it is not enough to
just have two pages for registering and logging
in inside of our module, we'll also need a service
to work with current user. For example, we need
to register user, login user, get current user, and so on and up this loop, we need an interface for
our current user also, this is where in this video, let's focus on creating basics of our
authentication module. For this, I want to jump
inside our source app folder. And here I want to
create a new folder. Here we want to
isolate everything, which is speaking about authentication,
registration are beginning. Our first step here is
to create a module. And if you're not that
familiar with Angular, just two words about
modules inside Angular, inside the other frameworks
like for example, react, we simply use
imports and exports, Bot Insight angular,
we have much more, we have dependency injections, which actually means
the whole application is splitted in
different modules. For example, in our case, we're defining here
and now module. Now we can create different
things inside this module and they will be isolated
inside this module. And we can define what we want
to expose to use outside. And if we didn't expose
anything for using outside, then we can't just use the
stuff from this module. And this is really a nice
approach for huge applications. Let's create first of
all, our, our module. For this, we need to export
our class, our module. Now on the top of
this class we want to provide an NG module decorator. And inside we will
pass different things, but for now we don't need to
register anything here yet. What I want to do now I want to jump back inside our app module, because actually
we must register this module inside
our app module. In another case,
this module is not bind it to our application
because we're just loading a module and we must lot all children modules
also inside it. This is where here
inside in birds, we can simply write our module. And with this line where get an auto input here on the top. And now we're sure
our module is loaded. Our next step here is to create
a current user interface. And from my point of view, it is completely unrelated
to the owls module. So here we can create a new folder types
and register here, current user interface dot ts. And inside our Express project, we didn't have any
rules about file naming because there we had just expressed and everything
that we're writing, we simply write with our own guidelines
in say the angular, it is highly recommended
to name all our filenames, Start and then postfix
of the entity. For example, here we wrote out dot module because it
is announced module. In this case we're
right in here, dot interface because it will be an interface and exactly the
same goes about classes. Here we're not writing
class owls, but our module. And here inside currentUser,
we're right in here, expert interface,
and here we have our current user interface. Now the question is what
we will get inside? And it is easy to answer that we just need to look
inside our Postman. So here we're getting back
for our current user, e-mail, username, ID, and token. So we can simply write here
that we get a DStream. We have our token,
which is string. We have our user's name. It is also string, and the last one is our email. And with this we're
successful with defined our current user
entity on our client. And now in every single place where we're talking
about current user, we can use this interface. Our next step here is
to create a class, because actually before
we will start with creating components for
registering and leukemia, but must create a service which will communicate
with our API. And the service for sure
belongs inside our module. This is why here I will create
a new folder, services, and I want to create here
out dot services.js. Actually it is
really a nice naming if you don't know how
to name your service. If you just want to pack some
methods inside your service inside module and you don't really know what this
Methods II about. You can simply name the
service like a module. In our case, we have here
our module and our service. But if you are service at
some point will be too big. You can always split. It may be in login
service, register, service, Current User
Service, whatever you prefer. For now, our service
is completely fine. Here I want to export
new class hours service. Now, it is super
important to not forget to write on
the top injectable. Because if you want,
try this single line, it will be super difficult
to debug a problem. Your inputs will work, but you will get some magic
occurs in the console. This is why never forget injectable if we're
talking about services, now we must register the
service inside of a module. And this is exactly, it's going in the direction of dependency injections and
modules inside Angular. So what we want to do here, we want to create a new field
which is called providers. And here it is an
array and we're writing inside our, our service. So this is exactly the
correct way to register all services inside our module. Now we want to create
our first method here and it will be getting
of the current user. Well, here we can simply
write that we want to create, get current user
function and it will return fast observable
of current user. And at this point, you might have questions if you don't know
Angular that deep, and if you don't know what
our observables at all and this thin with generic here might be confusing for you. So what is observable? This is just a representation
of the stream. So what is stream? This is something which
has changed over time, which actually means we can
subscribe to the stream. And when the change is
happening in the stream, then we will get new value. Then say the angular,
everything is working on love with streams
when not using promises. That which actually means
streams and observables, is a specific pattern, how we will write our code. So what we're saying here that we're getting back
an observable. And here we're providing what datatype we're
getting back. And in our case, we are saying here that this function must return an observable of type
current user interface. Current user interface is exactly our current user object. Now inside we want
to fetch some data, and for this inside
Angular we have HTTP. This is why here I will write constructor and inside private, http equals HTTP client. So what this line
is doing the top, this notation with constructor, then private some variable
equals some class is how we're injecting some
dependencies inside a service. And it is totally
fine if it is a little bit scary for
you to see such code, we will write exactly
the same code again and again in
every single video. For now, you just need
to understand that we must use HTTP here
inside our service. This is where here we must
inject this HTTP client. And now here in our methods, we can use this dot HTTP and dynamic is here we want to use GET method to get our user. So what I want to do now
I want to create a URL. And actually here we
can just paste http localhost 4,001 slash
api slash user, and we simply throw
here our URL. And now we need to return
this HTTP GET URL. But here we're getting an error. Object type observable
is not assignable to type observable
current user interface, why it is happening? Because they actually HTTP
GET returning by default observable off object because our HTTP GET Cantril and know what data we're
getting back when no, it only ourselves
in our application, but we can do here, we must specify what
we're getting back. And in this case here
we're saying, okay, this specific HTTP GET
by this URL will return false current user interface and not just some random object. This is why in this case
when not getting any error, because this single
line is returning fast observable of current
user interface. And you just must remember
that all this HTTP will return for us always
observable of something. Now, the next thing
which is really bad is this line. Why is that? First of all, here we
directly road base here. This is super bad
approach. Why is that? Because this line will
break on production. It is suitable only
for development. And we need to write
exactly the same code again and again in every
single method, this is bad. For this, we have environment
variables inside Angular. We can jump back inside source environments and
here environment, yes, this is exactly
where we must define all our constants based
on specific environment. So what we must do here, we can create new
property API URL, and we can just paste
our stream here. So local host for
1001 slash API. And this is totally fine because here in our development
environment where set in API URL will also have here environment
for production. And we can define
different API URL there. This is the most
correct way to do it. Now we can just use here
environment, and as you can see, we're getting an outer
input dot API URL plus. And here we simply need to use slash user and nothing more. In this case, we're reusing
this environment URL and it is completely isolated inside
the environment variable, our services fully ready. But what we want to do, we
want to get this user every single time when we load
our angular application. Why is that? Because they
actually were stolen. Our current user normally in memory and after logging
in or registration, we're simply talking
inside local storage. This is why every
single time when we're jumping inside
our application, we need to get current user. For this, I want to jump back
inside our app component. And this is exactly
the component which will be
loaded on any page. Here we can write
implement on init. And if you don't know
what isn't in it, this is a special
method which will be called on initialising
of our component. Here I'm right in
engineering in it. And then said we can
use our service, but in order to use it, we must inject it here. And here we're writing
exactly the same stuff like we wrote inside or service. Private, not HTTP like
we did in the service, but ours service that
we just created. And here we must input
are our service, which is inside our module. And now we can use it here directly inside
the engine in it. So this our service and we have here get
current user method. But what is most importantly these returns for
us and observable. And typically we
want to do something to listen to the changes
of the observable. This is why here we
will try dot subscribe. And now inside subscribe here we will get
some information. So here I will just
write rads and console log Ras so we can
check if it's working. I will jump to the browser
and reload the page. And just to remind you, your client web server
must also be started. Here we're getting an error. Http client service, no
provider for HTTP client. What does it mean?
It means that we use the HTTP client
in our application, but we didn't inject a module of HTTP client inside
our application. This is way to fix it. We must jump inside of a module, and here we must import
HTTP client module. In this case, our
error will be fixed. Let's reload the page. As you can see now,
it is working, but here we're now
getting an error about cross origin request. And actually this is totally correct because we
didn't configure properly our Express back-end application to work with
cross-origin requests. This is why we can simply
jump back inside our server. And here open source
server tiers, our next step will be to
install additional package. This is why I will jump
inside our server. And right here, npm install
course and course is the most popular
package to solve course problem with
Express application. I will just jump back
inside or sorority S. And here I can write on the
top input course, drum corps. And now the only
thing that we must do here is before our body
parser, for example, we can write a pews and we're providing insight
course as a function. As you can see, we don't
have any errors now inside backend and we can reload
our front-end page. As you can see now
with don't have discourse era and we're
just getting unauthorized. Let's check what we
have inside network. We have this request for user, and if I will make it
a little bit smaller, we can see what we're getting
inside response headers and we're getting here access
control allow origin star. And actually this is
why it is working. Our back-end, set it correctly. These allow access origin. This is why we're not getting
an error from the browser. But as you can see here, we're getting 401 unauthorized. And actually this is
completely normal. We're not logged in,
in our application. We simply fetched current
user and we got an error. This is totally fine. What is not fine
with didn't react in any way for this
authorized request. So what we can do, we
can jump back inside our app component where
we wrote this code. And we can also handle an error. And for this we can
write inside Subscribe, not a function but an object. Here inside the object
we can have two fields. First of all, next, this is exactly our success. And as you can see
here, I'm leaving this function as it is. But after next we
can create an error. And in this case, this is what will happen if we have an error. So here we can simply write console log error and maybe
we want to see the Sarah, let's check what we're getting. I'm reloading the page
and we're getting our error, HTTP error response. And here we have
unauthorized and we can react on this era. So what I want to do now, I want to create set
current user function. And actually it
doesn't make a lot of sense because we're
not lucky tin, but we still need
to save information inside our application that
the user is not locked-in. In this case, the
whole application, every single
component can check, are we locked in now and then our component knows
how to react to this. This is why we must jump back
inside our, our service. And here I want to create
new method set current user. Here inside we're
getting current user, which is our current
user interface. And it will return a void
because actually we will just change data inside
and not return anything. And actually it is not
completely correct. Here is current user interface or null if we don't
have a current user. Because actually, if
we're not locked in Poupon to set
current user to now. Now the question is what
were getting inside? Typically what you will see
in lots of applications. People simply define
a local property inside hours, like user. And then here you can simply
write this user equals user. This is not the best
approach with Angular. And inside the Angular, it is super comfortable
and efficient to work with streams because it
is much easier to react on streams
and combine them. This is why we must use
more difficult approach, but it is most suitable
for big applications. It is correct. This is where I want
to write that we're getting here,
current user stream. Here. I want to create
new behavior a subject, and it said behavior a subject. I'm saying that we
are getting here current user interface
or now, or undefined it. And after this round brackets, and here we're saying
undefined it by default. So what I wrote here at all
and how we will use it. Actually, what is
behavior subject? This is just a
representation of streams. This is just a stream
which has a default value. In this case, our default
value is undefined it, and we can also set
inside the stream such types as current user
interface now or undefined. And now inside set current user, we can change the stream. We can write here this
dot current user. And to change the
stream we're using dot next and we're
providing you value. In this case here we're
providing current user and it is completely valid because
current user interface is a valid type. The main point is here that
a lot of components inside our application can subscribe to this current user stream, and they will be
automatic looters ended at this moment here, when we're changing the value
inside current user stream. And don't worry, if you're
not getting it completely, you will see how we're
using it in later lectures. So we successfully created
our set current user. Now here inside
our app component, at least in error, we can do something. We can set our current user two. Now, here I will write this dot our service dot
set current user. And here inside I
will right now. So what is happening here on Initialize of our application, we are getting
here current user. If we didn't get current user, then we're setting it to null. And now you want for sure
to ask me why I wrote here now and define
it and current user, it makes some sense to set
here current user and now, but not undefined it. Additionally, they did it
with the purpose because actually we must handle
three different states. First of all, by default, we have undefined it, which actually means for us, we didn't fetch
current user yet. It is not ready if
we're sitting here now, it means that we
fetched current user, but it was unsuccessful. We're not locked in. So now here means
we're not locked in. And current user interface obviously means that
we are locked in. So we successfully created
our module, our service, current user interface, and some basic functions which we
will need in next lectures.
14. Register page - Markup + Form: In previous video, we prepared lots of staff regarding
current user. In this video, I want
to focus on creating our register page
because the pie for registration will
already implemented. And for this, we first of all must create a new component. This is why here I am
inside App hours in here we must create a new folder which is
called components. The first component that we
can implement is registering. And here is one important word. Actually later we will implement two components,
registration and login. The differences between
these two components are not that significant. We have in registration,
additional field, username and login with,
don't have this field. Then we have different The
tire rubber. This is it. All. Other stuff is staying the same. This is why we have
two possibilities. We could create a
single component and just manage it between
login and registration. Or we can create two
different components. I prefer to create two
different components just because it is easier
to maintain later, even when we duplicate
code a little bit. So here let's create a new
folder and call it register. Now inside this folder, we must create ts
file, an HTML file. And here we will have registered dot component dot ts and also registered dot
component dot HTML. Now inside our component, we must expert our new
class register component and then say this
component we must first of all provide a selector. Here you have an
important decision. You can name all your selectors starting with the name
of your application. For example, you
make some prefix which is valid inside
the application. For example, in our case, we can name it L
Trello or just L. And then you have a
difference between all libraries that you use and components from our project. Because actually if we're
jumping inside the HTML, here we have L register, then we know that this is
component of our project. If you have here, for example, prefix empty and then a
button, you understand the k. This is a material library
and it is not our project. This is the first approach
which is possible. I prefer another
approach where I prefix every single component
inside module, which actually means now
we're inside our module. This is where here
the selector will be out there registered. It is much easier because
we won't use libraries. And in this case we can see from what module we're
getting this component. The next thing that we
must provide inside our component is
our template URL. And this is the URL for our
register component HTML. Now, we should not
forget to register our components inside
of our module. Here we're jumping
inside our module Ts, and we create here a
field declarations. And inside we can now
write register component and delta E imported from components
registered, registered. So our component is there. Now we can write some markup for our component inside
our register component, yes, and actually here I missed letter S in the word register. Now let's run it a marker
for our register page. And the first-class
that we have here is div with class login page. And it might be
confusing for you, but this is just because of
our styles were written for login page and it is exactly
the same like Register page. So here we have our
div login page, and now inside we will
have a link on the top. Here we have a router
link and here is slash. So this route is going
to our homepage. Inside of our router link, we want an image. So here will be image
source slash acids, slash Trello, logo dot SVG. And after this class, thread law, main logo. And at this moment you
want For sure to say, okay, we don't have any images. This is by especially
for you prepared all the images and put them inside this folder,
inside assets. And you can take all this
images from the archive of this specific lesson in
the description box below, as you can see here, inside
source, inside assets, I have quite a lot of images, so don't forget to take
them for our project. Now let's jump back
inside our app, our components register,
register HTML, and after our a tag
here on the top, we want to create div with
class firm container, and it will be our
container for our form. Now, inside the container, we want to first of all write
def class login header. And again, we have the
class from the login, but it is not a big deal. And here we're writing
register to Trello. After this, we
will write a class for errors of validations. And actually for now, we won't
put any errors inside it, but we will use it later. This is y here, just div
class errors were closing it. It is completely empty and
nothing is rendered there yet. Now after this we have our form. So let's open and
close the form tag. And inside from we
have specific fields. So first of all, here
we have an email, so input type e-mail. And here we want to
place holder email. And the last one will
be class login input. Now I want to copy
paste this line two times because we need
not only e-mail, but we also need here
our user's name. So here we don't need Type. Placeholder will be
username and class. We'll be looking input. And after this type password
and place holder password. After this, we can create
our button to register user. Here we'll be button
type, submit, class, login, submit button. And inside this button, let's try it for a register. After our form, we want
to render our links. So here we will have div
class bottom form links. Let's close this
div and inside just create a single link
to our sign-in page. So here will be a router link. Again. Here we will have slash login page and class
Register link. Here inside our a tag, we will simply run the sign in. Let's check if
anything is rendered. Actually, we want
to jump to slash register page and see our page, but it doesn't work because
we didn't register our route. Registered. For this, we must jump back inside our module, our module. And here we must
register all routes which we want to create
inside this specific module. This is where here on
the top we can create routes array and we can
say that this is rounds, and in this case it
won't be just array. We will have inside the
validation of every round. So here we need to add one
object with our path which will be registered and our component which we just created. And it is register component. In this case, it
will work after we will add this routes
to our inputs. This is where here we must
straight in birds and use hear route module dot for child. And it is important
to use here for child and not furrowed because we want to create the Strauss
inside or a child module, not inside the app module. This is where here
for child routes and routes for this
specific module. Now let's check this out. I will reload the page and
jump to slash register. And as you can see in this
case, it was rendered. We can see our phone
without any CSS, and here is our SVG file that
we provide it on the top. So now your question
is for sure, whereas is our CSS and
this is the point. I also prepared the whole
CSS for our project. So we will be fully
focused only on Angular and write
in business logic. This is why you must
take the source code of our project under the video. And then inside source folder, inside styles.css, you
must copy this lines. And as you can see
here, these are all in parts of New Folder styles, which you also must copy. So you need to copy two things, source styles folder
and source styles, CSS, you must
override this file. So inside our source styles, we have lots of
different styles. As you can see here. For
example, create task. We have everything
regarding creating task. And all these styles are
global and we will use oldest styles just to be fully focused on the
Angular application. Also, I want to remind you that our global file,
source styles, CSS, is automatically
used by Angular, which means if you wrote
this symbols here, it will work out of the box. And now if we will
reload the page, we have here, our markup. So we have here trello icon, and this is actually a
link to the homepage. And we have our register form with e-mail, username
and password. The only problem is here, hello l Trello that we
can see in the corner. This is just some
leftovers inside our source app, app
component HTML. Here we can remove this line and just live here
router outlet. So here we successfully created our markup for register page. Now move on to bind somehow this form together with Angular. And actually inside the Angular, we have reactive forms. And this is an additional
Angular module to work with forms in
this week's chess way. This is y. Let's jump back
inside are our components, register, register component ts. And here first of all, I want to put inside our form, new attribute form group. And here I will try
it equals form. And here also we need NG Submit
for submitting the form. And here we will
create on Submit. Now the question is what
is this form group? And actually inside
reactive forms, we can create a form
group inside a component. And it will be a representation
of our HTML form. And it will be fully binded to our HTML elements and it
will work out of the box. What we want to do
here inside our class, we want to create our form. And this is actually
this dot if b dot group. And we don't have B inside
of our component at all. This is why we must insert
constructor injected. So here I will
write private if b, and this is form builder. And as you can see, it was
imported from angular forms. Now here on the top
we have access to this big group and
here is our form. So what fields do we have? First of all, we have an
e-mail and actually here we can say as an array
our validators, so initial value here
is empty and here we can write validators
dot required, in this case reactive forms. We'll check by default
this field for emptiness. Now we can copy paste this line because we have
exactly the same. We have here our username, also empty field and
validation required, and here is our password. It is also empty field
by default and it is required so our firm
is successful already. And this line here for Firm Group form is binding this specific
form to our HTML, but it is not all. We also must bind
every single field. Here everywhere we
have this input. And actually what
I want to do here, I want to put a form
control name attribute. Here we will bind specific
field for every input. So the first one here
will be our e-mail. The second one is our username, and the last one is password. In this case, when we're
changing this inputs, they will be updated inside
our form group here. And the last thing that we must create is on Submit method. So we're already created
it inside our HTML. Here we can simply add on
submit and it returns void. And now inside I want just
console log on Submit comma, this form dot values. Let's check if it's working. I will reload the patient we're getting an error cannot bind to form group since it is not
known element of the form. Why it is happening? Because we didn't inject reactive forms module
inside our module. So let's jump back
inside or out module. And here inside
the inputs we must write reactive forums module. And after this input, we should not get the Sarah. Let's reload the page. As you can see, we
don't have any errors. Now I can provide something
inside and just hit Register. And as you can see here inside console where getting onsubmit, e-mail, username and password, which actually means
all our fields were successfully binded to
our angular component.
15. Register page - service + validation: In previous videos,
we created market for register page
and also the firm, but we're still
missing our API call, which we must implement
in this video. This is why First of all, I want to jump back inside our, our services, our service, because actually here we
will write all our API calls and we have already
here get current user, which we will polish later. But for now I want to
create register API coal. This is why here we can
simply write register. And the question is, what we're getting here? We're getting actually our form, but we didn't type
it yet at all. This is why it is not
comfortable to use. And we don't want to write here that we are getting any because it doesn't make a lot of
sense what this method wants, it wants to know what
were provided inside. This is why I wanted
to jump back inside our types and create
here a new type. And we can name this type
register request interface. And you might ask, okay, But why such strange name? And actually it is not strange. The main idea is the time post fixing everything
that we're writing regarding request and response with specific postfix in
this case here I know. Okay. This is a register request. So this is the body
of the request. If we're talking about response of our register
if when needed, then it would be registered
response interface test. In this case, it is easier to understand what you are
using this interface for. Now Let's create this interface. So here I want to export our
new interface and it will be register request interface. What we will have inside exactly all our fields that
we wrote inside of a form. So here we will have our e-mail, it a string, our username, it will be also string. And the last one
is our password. And the password is also string. And actually it is not
only about the form. If we will look inside our
server source controllers, users were already created
here a register method. And inside our register method, this is what we're looking
for inside the body. So request body
e-mail, username, password, and this is exactly what we're
sending from the client. So our registered
requested that we can jump back inside of our services,
our service tiers. Here inside register, what
we're getting as a parameter, it is our register request and our type is registered
request interface that we just created. Now the question
is, what do I get in back after registration, we're getting back
our current user. This is way here we can write exactly like can get current user observable of
current user interface. We already have it. In this case, everything inside our method is correctly typed. Now we need to create
a URL property. So here URL and we're
using again environment dot a payer realm because
it is the same slash users, because they were
registered request is just to post for slash users. And here now I want to return this HTTP and we have access here to HTTP post and inside we're providing
festival URL. And secondly body, and our
body is our register request. But again, we're getting here an error because our type is observable of the object and not observable of current
user interface. Because obviously by default, HTTP post doesn't know what
we want to provide insight. This is where inside
our HTTP post, we must provide what
we are getting back. And it is current
user interface, but it is not all, will also must create an additional method to set the token inside local storage. What does it mean? Actually when we're registering a user or getting the user, we are getting also at token field that we
prepared on the backend. And we should not do anything with this field on the client, but we must just
save it and said local storage and attach
to every single request. In this case, our backend can
understand when the request is authorized and that we
provided the correct token. This is where here I want to add an additional method set token. And actually inside
we will provide the current user because
either we will call this method after getting a current user or after
registration or Guinean. So here current user is current user interface
and now it will return void because inside we simply want to use
local storage. And here I will write local
storage dot set item. And inside I want to
provide field talking and we're writing inside
current user dot token. So this method
simplest stores inside localStorage our token
of current user, and now we're fully ready
to adjust our component. So let's jump back inside our components
register component. And here we have on submit, and obviously this firm
value is drawn here. We want to use our service. This is why here I want
to inject our service. So we're writing again private
and we have our service, which is our auth service class. And here now inside on submit, we can write this out
service dot register. And as you can see,
we are provided inside a register request, but inside of our
component we just have this dot form, dot value. And actually this firm
value, as you can see here, the type of it is any, this is why it will work for us and we can't really
type it in any way. So what registered returns fast? It is an observable. This is why here
we can again write Subscribe with so object inside into fields which will be next if it is correct
or it will be an error. So inside next, but we'll
get current user bag. And here we will do some logic. And if we will have
an error here, then we must specify
an airfield. And here is a function with Era. And inside we can simply
console log and narrow. So first of all here I
want to write error, error here inside
next console log our current user,
coma, current user. Now let's check if it's working. I will provide some email
which does not exist inside username and
password and hit Register. And as you can see
here is our network. We're getting here to request. First of all, we are
getting options request. And this is completely normal
because we used course. And we have a request
between two different hosts because we're hosting our applications on
different parts. So you will see these
options every single time. It is completely normal. But we're interested
in our post request, which is slushy
pathless users post. And here is our payload. The payload is
completely correct and here is our response. As you can see,
we're getting back our e-mail ID token
and username, which actually means
everything is working fine. And here we're getting
our current user. But as you can see before, we're getting error,
HTTP error response. And it should not bother you
because this is the error of this unauthorized request and we will fix it in
the later video. It doesn't do anything to
our register component. So as you can see
here in the console, we're getting our current user, which means our code here
is completely correct. So what we can do now, we can write this dot and here
we have our service and we can call here a method set token and provide
insert current user. Then this line, because
we have here current user will save our token of
currentUser to local storage, but it is not hold. We also want to save a user
for the whole application, and we're already
prepared before for the same method dot
set currentUser. Inside. We can simply provide our currentUser and
listen this method. It will just set this current user in
current users stream. You will see how using current user stream
in later videos. Most importantly, that
now after registration, we set it at Target
and we sat at a currentUser inside
our application. And the last thing
that we want to implement is our validation. Because actually if
we will get an error, we want to show it.
This is why here. First of all, I want to type our error because
we know what is it. It is an HTTP error response. And here we're getting
our error and we can write here dot
Azure at least. And yes, this arrow will be any, but still it is
better than nothing. But we want to do now we
want to save our error here. Now the question is, in what format we will
get our errors. And for this second open
again, our controllers, users. And as you can see
here is our catch. And what we're
doing in this line, we're mapping through
our errors and we're getting messages as
an array of strings, which actually means we are sure that if we're
getting an error, it is always an
array of strings. This is why what
we can do in our client inside
register component, we can generate an error
message and show it here. This is y. What I want to do here, I want to create
just a single error and it will be of
type string on now. And by default it will be now because we don't have any error. Now here inside our error, we can write this dot era. We know that here we're getting our error inside as an array. This is why here we can
simply create Azure dot dot join in here
will become a space, which actually means
we want to join all our errors with
comma and space, and it will be just
a single field. Now, I want to jump inside
a register component HTML here where we
have div class errors. I want to write and GE
and show this field only when we have an error and inside they can simply
render our error. Now let's check if it's working. But as you can see here, we're getting an error cannot bind to G if it is not
known property of div. And it is happening
when we didn't inject a common module
inside our module. This is where here
inside the imports we must input common
module from Angular. Now as you can see, we're
not getting any errors. And what I want to do, I want to provide invalid data. For example, inside email, I will provide data and
not incorrect format. Now let's clean everything
and hit Register. And as you can see here, we're getting our error. And this is an array
with invalid email. And we're rendering this
invalid email here on the top, which actually means we
correctly is reacted azure and Don success of
registering of our user. Now, let's check if we really safe talking after registration. This is where it
Let's reload the page and provide correct e-mail, username, password,
and hit here register. As you can see,
we've got our user, which actually means
we started in memory. But here when I'm champion inside application
local storage, you can see our token
and here is a value, which actually means
we successfully implemented registration of the user and we stored the
token inside localStorage.
16. Login page: In previous video, we successfully finished
our register page. In this video, we must
implement login page. And I think this
is an awesome idea that you try to implement
it on your own. So what do we need to
implement on this page? Actually login page is simply
URL slash login and we see exactly the same form like
register, but login form. So actually we have
just an email and password and we
don't have username. Obviously all texts
are different, but essentially this is it. Also we will use the another
request follow gaming on slushy pie slash
users slash login. And here I have three levels
of complexity for you. First of all, you can
pause this video right now and try to
implement it yourself. Second level is you're getting some guidance from me before
you start to implement. So what do we need to do at all? First of all, as
you can see here, inside out components, we
have a register component. And as I said earlier, with don't want to
share component between registration and login, which actually means
we can implement the new component login with
exactly the same markup, but without our username and our ts file will
be super similar. We need a form, we need
onsubmit and so on. But the main difference
there will be in a service, we won't use our
service that register, but we must create
a method login, which actually means we
must in our service here, create a login request, which will make an API call. And actually it will be super
similar to our register. But here we're must create not registered
request interface, but login request interface. And then inside we must provide a correct URL if you
want to try it yourself. Now, just pause the video here. And if you don't want
to try it on your own, Let's do it together. And our first step will be to create the interface
for our service. So here we have our
register request interface. And actually as you can see, we have three fields here, so we can't reuse
it inside login. But I want to do here, I want to create
a new interface, login request interface. Let's jump inside this
file and we can copy paste completely our register
request interface, just because it will be super similar and they don't
want to type a lot. So here we must create our interface, login
request interface. And we have inside
email and password, and we don't have our username. So we successfully
created our interface. Now we can jump back inside our service and
we can copy paste register method fully because I will login method will
be super similar. Let's name our method login. And here we don't get
registered request, but our login request, here we need another interface
that we just created. It is login request interface. And back we're getting our user, which means it is correct, observable current
user interface. Now here is URL. We have here API URL
slash users slash login. And this API we're already implemented inside our backend. Login is for sure
a post request, which means here we must provide a post with body login request that we set as a parameter and our login in service
is fully implemented. Our next step will be to create a component here
and actually has this set register component is super similar to our
login component. It doesn't make any sense to retype everything on your own. This is why I want to
copy the whole folder and paste it here and rename
the folder login. Now we have login component. We must rename here pages. So it will be login dot
component dot HTML. And here login dot
component dot ts. Nova must jump inside
our HTML and change it a little bit and it
won't be that different. First of all, here, instead
of registered to Trello, we can write log in to trailer. Now we're leaving here error, just like we had them. There were also need form submit and here we have
e-mail, username. We don't need the tall,
we can remove it. We have our password. And now here is submit button, not with register,
but we sign in, for example, last but not least is router link
here on the bottom, it should go to a
registration page. This is where here
is slash register. And instead of this text, we can write inside
sign up for an account. So we successfully
changed our HTML. Now let's jump to
our TypeScript file. First of all, we must
change our selector. It is not registered, but our login and template
is login component HTML. Now className also is different. It is login component
Live era as it is. And here is our form. We need e-mail and password, but not a username. So let's just remove
username here. Our constructor stays the same. Our onsubmit stays
almost the same. But here we won't
use register method. We created a login method where inside where providing
the whole form. And here we have subscribe and
if we're successful login, then here we console log
and current user where set in talking and we're
set in current user, which actually means it is 99% exactly the same code
like inside registration. And our last step here will be to register our components. So we must jump inside
our module tears. Here inside declarations,
we must say that we have a new component and
it is login component. And we're also must
create here a new route. So I will copy paste
the register route and that path login and component
will be login component. Let's check if it's working. We don't have any arrows
here inside of observer. I will reload the page and try to jump
here on the bottom, for example, in sign-in page. And as you can see
where on slash login. And here is our form
and we can reload the page where stain on this
page, everything is fine. Now let's try if we
can login at all. So here, first of all, I want to write
something incorrect. Here is some email
which does not exist. And then some password. I'm hitting here saying in, and we're getting an error, as you can see here before, we're getting photo to
error with validations. And inside our error where getting filled
email or password, incorrect email a password. And we're doing this in this case because
we don't want to notify user what
exactly is not correct. We should not say something like this email is already taken. We simply say it is invalid. This is why this logic inside login component of bone work. And just to remind you
here we copy pasted on submit and here inside an
error with joined our errors, because in the case on Register page we had here
an array of strings. Here we don't have
it with directly see an arrow, email, a password, which actually means here
instead of the join, we can write era Dodd
email or password. In this case, we will apply it correctly area inside.
Let's check the sound. I'm reloading the page. Let's type here some email
which does not exist, some password sign-in and we're getting incorrect
email a password, which means our validation
is working correctly. Now let's try to login
with correct credentials. So here I have full at gmail.com and here
is our password 123. I'm hitting here, say mean, and we're getting current user. But the main problem is
we're staying on this page. And secondly, we didn't
remove this area at all. And actually we can do both
things simultaneously. First of all, what I want
to do here on submit, we can remove the Sarah. So here, this dollar error
we can write inside now. And actually I can
say now that era name is not the best one
because actually error is super generic and they
would like to change this name in here from era
to era message for example, in this case, we need
to change it here inside next and here inside era. And after this, we must jump to the template and
change it there also. So here we have Angie if error, it should be error message. Here we're rendering our era. And I think that this
approach is much cleaner because it gives
us understanding what we're rendering here. It is not some generic error. This is really an error message. Now we must apply exactly the
same inside our register. So let's jump back
inside our register. And here first of all, I
want to write error message. Here is also error message
and then say ts file, I want to change arrow
two error message here, right error message inside era, and we want to set it
in now in our success. So here this error
message equals now, the last thing that
we want to do, we want to redirect the
user to another page. It doesn't make any
sense that we're staying in this
page and it lists, we want to jump to the homepage
after login in a user. In order to do that, we must
inject here our router. This is my insert constructor. I can write private
router equals router. And as you can see, this router is coming from
angular-ui-router. And now here inside
success, on the last line, we can write this dot, dot navigate by URL. And we're providing here URL, for example, just slash. And now we must do exactly the same inside our login page. So I'm jumping inside
login component in here. First of all, I want to
inject private Zhao router. And after this, I
can paste this line, this route navigate
by URL slash, which actually means
in both cases, with registration and login, we want to reject our
user to the homepage. Let's check if it's working. I'm here on login
page I'm writing here f2 at gmail.com, here 123. I'm hitting sign-in and then
champion to the homepage, which actually means
we successfully implemented our login page.
17. Home page: In previous videos, we
finished our login page. In this video, we will implement our homepage for the project. And the main point is
that this page will be just markup and no logic at all. Why is that? Because actually this
page is only for anonymous user if
we're locked in but never see this
page because we're directly redirected
insight boards page. So first of all, let's
implement in your module. And for this inside the app, I want to create a new
module which is called Home, which actually means this is
completely separate module. It doesn't have anything
to do with hours, and there is just a
homepage component inside which is isolated
inside this module. So let's jump inside
this folder and create new file Home module Ts. And a lot of students are
asking me really often why they don't use generators
together with Angular. And actually there
is a reason for it. I find that it takes more
time to use a generator then to just copy paste the
module if we need to do so, and also for the
educational process, so you remember it better. I am writing it from scratch. This is y. Let's create a home module
once again from scratch. And every next module we
will just copy paste. This is what we want to do here. We want to create
a new class and let's name it home module. After this, we must inject
here a decorator in GI module. And inside we must
provide our dependencies, at least here inside the inputs, we must provide common module because we need it inside
every single module. For example, for
loops like in G4, offering G If now, let's create our home
component for this biomass, create a new
directory components. And inside we want to
create a new folder home. And yes, I understand
that we have just a single
component here and it doesn't make a lot
of sense to create slash components than
Home folder inside. But nevertheless, it
is a good structure. And if you didn't need to split this homepage in
different components, you can for sure do it. So let's jump inside home and
create here home component dot HTML and home
component dot ds. Now let's jump inside of HTML page and just
ride home here, but don't need anything, just something for testing. Now let's jump
inside our ts file. And here we want to create our class and it
is home component. After this, we must
register our components. So here we want to
inject our component and inside we must first
of all provide a selector. So here I will simply use a home selector because
it doesn't make a lot of sense to prefix
our home component with module name
like home, home, it's simply not needed
after our selective we must provide here at template URL
and then say template URL, we will have home
component HTML. And our last step here is to add our component to
our declaration. So inside home module here, we want to create declarations and put inside our
home component. Everything is looking fine here, but we didn't create a route. This is why here I want to
create a property routes. And we can say that
this is type routes and this is an array
with just a single key. And here will be our path. This is empty string,
it means homepage, and here is our component, and our component will
be home component. After this, we can register
this route inside inputs. So here will be route module
dot for child routes. And now we should not
forget to register our home module inside
our app module. This is why I want to jump
back inside our app module. Here, insider in birds, we can add our home module. Everything is looking fine. I don't see any errors
inside of observer. So let's open a browser
and here is our homepage. As you can see, the word
home is rendered here, which actually means we
successfully created our whole module and
empty component. And now we simply must try to
markup for the whole page. This is my, let's jump back
inside the home components, home, home component HTML. Here, start to write our markup. Nothing special here, just
lots of DOM elements. So here we have header
with glass home header. And now let's close this header. Then side we want to create
a link to our homepage. So here will be a
router link slash. And here we have class
home had a home link. Let's close the sale and inside the double bond
to show an image. So here we will have
image source slash, etc, slash Trello, the logo, the white dot SVG. And let's close this image. As you can see in
browser, it is applied. And here we see our header
and link to our homepage. Now we must provide links
to login and register page. This is why after a, we can write div. And inside div we
can write two links. First of all, here, inside div
will have a router link to our slash login that
we just created with class WHO had a login, I will close the sale. And right inside login, now we can copy
paste this link and here we have slash register. Here is class home
header register, and the text inside will
be also registered. Let's check if it's working, as you can see in browser
here on the right, we have login link
and register link. Now, let's say the first
block of our page. So here will be div,
class home hero. Let's close this div and
inside we want to add a div class home container. Let's close this div. And inside home
container we have a div. And inside this div
will be H1 tag. And here we have class
home Desh title. Let's close this H1 and inside
each one I want to paste. Although the prepared text
helps teams to work more collaboratively and get more done after each
one we have p tag. This is our description. Here should be glass
home description. And inside this p I will paste the description about Trello
boards, lists, and current. After the first div, we will have a second div. Let's close it. Inside. It should be an image.
So here will be image source slash acids
slash hero dot SVG. And let's close this image
and check if it's working. We're jumping inside browser. And as you can see here, we have a first section
of our homepage. Here on the left we have text and nice image
on the right. I think that you get an idea how work rate and blogs
for the homepage. As you can see, we simply have such blog title,
description and image, which actually means
that you want to speed up the process of creating homepage because this is just
an HTML without any logic. So here I want to paste
the second blog home team. If you want, you
can simply pause the video, retype everything, or you can simply
take the HTML of the homepage from the source
code under the video. So what we have here, we have home team, home container team
container inside. We have worked with any team, some description and an image. Let's check how it looks like. Here. After our first element, we have the second with
text and now the image. Let's create now one
more block here. I'm pasting home information. Same stuff here we have classes
in image than H1 and p. Let's check this out. I'm reloading the page
and we have here on the bottom one more block
with an image and texts. And now let's paste
our last block. As you can see, it is
a little bit bigger. We have here something about
workflow and automation. Here we have H1 description
and also the bullet list. And on the right we have
an image as always, as you can see here
on the right we have an image and on the left we have first of all a
title description and then a bullet list, which actually means
we successfully created our homepage. It was not something special, just a marker for
not logged in user.
18. Auth interceptor: In previous video, we successfully
created our homepage, but now we have just
a single problem. We're actually implemented
getting of the user after page a lot in
just to remind you, inside our client insight app, app component ts, we're calling our service
get current user. And actually now
we're always getting an error here and unauthorized. And actually if I will
jump to register page and just create some account that
didn't exist previously. As you can see
after registration where there's reacted
to the homepage. And inside of local storage, we have this token. This is what we set it from the back-end to
authenticate our client. But afterwards reload the page. This token is not used and we're getting
here unauthorized. And the idea is basically the tower client must on
every single request, apply this token from the local storage if we
have it, in this case, our backend knows
when were authorized, but just imagine if
we rarely need to go inside every single method, like for example,
get current user and that he has some
hair from local storage. This is not deficient,
time-consuming, and we really want to just add this token on every
single request. For this inside of the
Angular, we have middlewares. This is why now we will
create a now middleware. So what is Middleware? This is something in
the middle between start off our request and, and, and actually here we
have, for example, the HTTP GET and we will
create a middleware. It means that after this
request is started, but it was not sent here. We want to apply middleware, so we want to do something. For example, it takes
a header there. This is why I want
to jump inside our house services and create
here in your service hours, interceptor, DOD service dot ts. And it will be just a class, just like normal
injectable service. Here we have experts,
class hours, interceptor, and here we must say
implements http interceptor. So the main idea is that this
middleware in Angular is called interceptive because
it intercepts our request. And here we're writing implements
http interceptor to get a narrow class house interceptor incorrectly
implements interface and property
intercept is missing, and this is exactly what we must create in order to
implement this middleware. This is why here we will create a new method which
is called intercept. And as you can see
here, my autocomplete already got request and next, and it returns observable
office HTTP event, which actually means we
have a full access to or request here inside
request property. And we will call next
when we're ready and we finished everything
that we need to do here. This is why first of all, what we want to do inside is get a token from
our local storage. So let's write here that
we have a target and this is local storage get item. And inside we're
providing token. After this here I want to write request equals request clone. And here inside we're providing an object with
field set headers. The insights that headers it
is an object we're providing authorization equals
talking or empty string. And after this we're
calling return next door handle and were
provided inside our request. So what this code
is doing at all, first of all, here we got a
token from Google Search. So it is either an undefined
it or it is a valid token. Now here we're doing request
clone. Why we're doing it? Because request is immutable, we can't really change it. This is where we must cologne it in order to set something. Here we're using properties
set header to set a header. The header is called authorization and inside
we're providing our token. So if we don't have a token, we're saying here
an empty string. And after this we're
calling next handle, which essentially
means we must continue our request and we're providing
updated request inside. So this is how we're
creating middlewares or interceptors inside Angular. But now we must inject this out interceptor inside
our application. But the main thing
but we want to do for one to inject out interceptor, not inside out module, but inside app module
because we want to do it on a global level
to inject interceptor, we're using providers here. So what we want to do
inside this provides an object with field provide, and here we're using
HTTP interceptors. It is coming from Angular also were right in
here use class. And this is our class, our interceptor, which
we just created. And the last one
is moved to true. And this is exactly
how we must provide http interceptors
inside Angular. So here we're
saying that we must register and you
provided and it is an http interceptor and we must use our class
hours interceptor. Now let's check if it's working. I'm reloading the
page and let's check inside network,
our request user. Actually here I want to
scroll to the bottom. Here we can see
authorization and our token, which actually means our
intercept, is working correctly. And we directly applied this authorization header
to every single request, for example, to get
a current user. But we're still getting back our 401 error and they actually
know what is the problem. If we will look
inside local storage, we can see that token
simply a target, this is just a value. But if we will jump back
inside our backend here, inside source,
middlewares, owls. And this is where we're
checking a token here as you can see where making
split by a space. Because as I said
previously were right in beer and then space talking. If we're using a
DVT authentication, this is not our case here. We don't have here a word, beer, and this is
exactly our problem. We didn't implement it, and we must do it when we're returning a token to the client. So here inside servants
source controllers, users, when we generate torque in here, inside normalized users,
we must write here space. Here we have justice train, where inside, where
right, and beer. And here we're injecting our
token that we generated. Let's check if it's working. So our token is invalid, they will simply
remove it and it will jump to register page. Now I want just to put some credentials here
and hit register. And now when we're checking
our local storage, you can see that we have beer
than space and are talking. This is why when
I reload the page with don't get for
01 error anymore, we're getting here our response. And actually here
you can see that our request user is 200 and our response is exactly what we expected, how it is working. Once again, we're
registering, for example, or login in and we're setting a token
inside local storage. So Birra space and the
token then every single time when we're making a request out interceptor inside Angular, it attaches inside header,
this authorization token. And here we have bear
and then our token and our back-end checks this user and gives us a response here. This is why every single time
when we reload the page, we're getting back
our current user. And this is exactly what
we wanted to achieve. Now here inside our clients
source AB, AB component, yes, I want to make a small improvement because actually here we have
announced service, but we don't use this
Ras and actually now we're getting a user so
we can write here not trans, but this is current user. And instead of console log, we want to set it with the method that we're
already prepared. So here we have this, our service dot
set current user, and inside we're providing
our current user. So now our user is successfully logged inside our application.
19. Auth guard: In this video, I want to
talk about Gvd in our URLs, which actually
means, for example, we should not allow user
to jump to the homepage. If he's already locked in, he must jump directly
to the board. For example, if
we're not locked in and we're trying to jump
to the board's page, then when not allowed inside and we must be redirected
to the homepage. So there are different
approaches to this problem. But for any use case, I highly recommend you
to start by creating an observable of Islamic
tin. Let's do this now. So actually I want to
jump inside our app, our services, our service here will be
created current users stream. And as they said, this
is trimmed that we can use from any place
of our application. And just to remind you, we're using here set current
user to change the stream. So essentially what we
can do now for example, inside our app component somewhere here after
in June in it, we can write this dot, our service dot current user here we're right and
subscribe board, and we're getting
here our response. And now I just want to
write here console log. So you see what
I'm talking about? So here we subscribed to the
stream from our service. And now every single time
when we change our stream, we're getting this data
back inside that component. Let's check in browser now. And we're getting here
two console logs. First of all race and define it, and then Ras and the
information about our user, why it is happening like
this at the beginning of the stream is undefined it because we didn't
fishy user yet. But after some time when our currentUser call is
finished and it is successful, we're setting this currentUser
inside this stream. This is why every
single place where we subscribe to this current user
can get this information. And this is exactly
this information. But we can make it
even better because essentially we want to
check if locked in or not. This is why typically
you want to take this response and you want to convert it to Boolean
and check for true. In this case, we're locked in, but we don't want to write this code in every single place. This is where we can inside our, our service create
additional stream based on our first stream. So here is our
Stream current user. But now I want to
create a new stream, which is called is logger tin. And now inside our application
at any place we can use, new stream is log team. So what did they there? Here we're using
this current user and this is already a stream. We simply want to transform
it to another value. This is where I am
right in DOD pipe, then Site Map function. And if you don't know
this is Rix JS code. So essentially we're
using looks chess and say to Angular to
transform our streams. And we're using it in this way. We're always writing dot pipe and then the list of
our transformations. This is where you will see
that pipe everywhere and here inside where using
map to map our data. So we know that inside our map we can get three
different states, undefined it, false and true. But the main point is that our application does not
care about undefined. It were relevant
to check is login. If we have this information, if we don't have this
information yet, then we just want to wait
for this information. This is way before I want to skip this undefined as property. This is where here we can
use filter before our map. And filter is also a function. So essentially, first of all, we're using filter inside
pipe and then a map. So what we're getting
insight filter inside filter by getting
our current user, and actually this
current user can be undefined it now or
our current user. And here we don't
want to come to the map if it is undefined it, this is why here we can write current user does not
equal undefined it. Then in this case, we will come here
and here is our map. So what we want to do inset map, where I get in
here, current user. Here we want simply to
convert it to booleans so we can write here boolean
and then current user. In this case, this logic will bring us back true or false, and we will skip this, undefined it that we don't need and won't use inside
of our application. But we can simplify
this code even more. Instead of flight and
this logic can set map, we can simply write
here Boolean, and it will do exactly the same. So R is look at tin is a new stream based
on the current user, which will return for
us true or false. Let's check if it's working. I will jump back in
setup component. And here I want to write this dot or service
dot is locked in. Here we can also
write Subscribe and our heads and our rest
will be is locked in. So let's check this out. We can even name it to be more understandable, is logged in. And here let's console
log is log of tin, coma is login property. Let's save this and
check and browser. I'm reloading the page and here we don't see Islam
getting undefined. It. We simply see here is low
getting through because this is the first state
where we have true or false. And this is exactly
the way how we can use in any place of
our application, this stream to know if the
user is logged in or not. So now I want to remove this code because we
don't need it here. It was just for testing. And now I want to show
you the easiest solution, how you can redirect
user to another page. And we can simply start
with our home component, because essentially here we
want to redirect the user to the board's page if he's already locked-in,
how we can do it. First of all, here,
we must inject inside constructor our, our service. So here private hours
service is announced, service that we already have. Now here we want to write
implements on needed, so we have initialized. Now here we will use
engineering in it. And inside now we can write exactly this logic Lake Road
inside our app component. So here will be this
dot, our service dot. Here we have is locked in. So after this we can write Subscribe and we can
get here is log it in property here inside
this subscribe either get true or we get false. And if it gets in here true, we want to redirect the
user to the board's page. This is where we
can just try it. Eve is like a tin. Then we want to
redirect the user. And to try to redirect, we can use as we used
previously, router. So here let's create a new
private property router. And this router, now
inside our if condition, we can write this dot, dot navigate by URL. And here we can just
provide slash boards. And actually we didn't implement
this slash boards yet, but it doesn't matter. We simply make it as an example, how you can restrict
access into the URLs. Let's check this out. And actually here we
already get an error, cannot match a new routes. And here is boards. And essentially we can check it, for example, with
slash register. To understand that
it is working, I will reload the
page and as you can see where on slash register, why it is happening where
jumping to the homepage, I'm hitting Enter and
we didn't even see our homepage where they
directly inside register. So this is the easiest way
how you can implement in say, the angular redirect
into another route. But here is a problem
we used here, subscribe from an exchange. You must be really
cautious with it. Because if we're
using subscribe, we must also write unsubscribe. If we didn't try unsubscribe, then it means that we have a handgun subscription
inside our application. Actually, our home
component pathology destroyed because we're
inside register page, but this subscribe is still there because we didn't
unsubscribe from it. This is where we
must always remember to unsubscribe from
our subscriptions. To do this, we
simply must create a subscription here on
the top, for example, is log it in subscription, and the type is subscription. Now here inside
our engine in it, we can assign this is
logged in subscription. The result of Subscribe
will be our subscription. Now here on the top we
can add on destroy. So when this component
will be destroyed, but wanted to create in
G on destroy method. And inside we can simply
write this is like getting subscription,
DOD, unsubscribe. I'm saving this but
we're getting an error. We don't have
initializer here because there's low get in by
default is not set, it is undefined it,
and it is true. This is why here we can
write or undefined it, because it is not set by default and we simply set
it in engineering in it. But this is my code here, is invalid because this
object can be undefined. It, this is why here we
must put a question mark. So this line won't do anything if this is
an undefined it. But if we have a subscription, we will be successful the unsubscribed when the
component is destroyed. But now I want to show you the second possible
variant inside and color to make redirects
or to guard your routes. And this is exactly
the feature which is called quadrants inside Angular. And the idea is exactly the
same like with interceptors, were doing something before
work set the component. So essentially we want
to do some check, then return true or false. This is way here inside
our module hours. I want to create insights
service and you file, and it will be our
word dot service. So here I want to export our new class hours
word service. And here we're writing
implements can activate. And this is exactly
the same thing like we did with
our interceptors. As you can see here,
we must define, can activate to make
this R-squared working. This is why here we can write, can activate, and here we're getting lots
of stuff inside. We actually don't
need all this stuff. We can remove it and this
return is too verbose. We want to return here. We want to return back an
observable of Boolean, which actually means we're
returning true or false. But as the stream, now inside here we want
to use our, our service. This is why here we must define the constructor and inject here inside our service
of our hours service. And the idea is that
inside this method, we must return an
observable of Boolean. This is way here directly. I can write this. Our service. Dot is log tin. Here I want to write pipe because we need to do
some stuff inside. This is my insight. I also will add MAB
and we're getting here as an argument
is login property. Now here inside
they want simply to check if we have is login, so it is true, then they directly
want to return true. But if we have false, I want to redirect the
user to another page. This is where here we're
also need a router. So we can inject
here private router, and this is our router, and we can use it here. So we're writing this dot
router dot navigate by URL. For example, with the detector, use the to the homepage. If they are not locked in. After this, we
must return false. It is mandatory because
essentially this is an observable of Boolean and we must not only
do something here, but also return false. And here I want to
navigate by URL, not empty string,
but just a slash. And you might say, but
why we didn't return, simply hear this, because this locked-in is a stream
with true or false. Because here we want to also use route and navigate by URL, and we can do it if we
simply return true or false. So now our gouache is ready, but we must
registered correctly. This is my inside our module. Here inside providers,
we must put our owls wire service
that we just created. Now we must check on some route, our route quadrant,
this is where here we can, it can activate. And inside we're
providing an array with our auth service, which actually means when
we are jumping to login, our outward service
will check if we're locked in a node with
usage of our stream. And if it returns false, then we will be redirected
to the homepage. Let's check this
out. I'm reloading the page and we're
getting an error. The class, our square
service cannot be created because it doesn't
have angular decorator. And actually here
inside-out squared, I forgot to write injectable. Don't forget it.
This is way here. Let's put injectable
and round brackets. Let's reload the page,
but don't have any error. Now I want to try and
jump to our same mean. I'm hitting same mean. And as you can see,
I can access login page because here we wrote
can activate our squad. Which actually means if here
we are getting through, then we can access this page. Which actually means if here
inside our application, I will remove our token and they will reload
the page login. I will be redirected to the
homepage because they don't have access to
this specific page because of can activate, which actually means we
successfully implemented are accessing of the pages
in two different ways. First of all, by
using component and secondly by using Angular
feature which can activate. But actually we created
this owl squared, not for login or register, but for future boards and
the port pages because there were making a lot of requests only for
logged in user. This is where here I will
remove, can activate. And they also want to
change our code back inside home components home, because here we should not
redirect to slash register, but instead slash boards, which we will implement
in our next video.
20. Gettings boards: In previous videos, we
finished implementing our homepage and now we're
starting a new section. And this is a page boards. So what this page is about, this is a page where we can get the list of
boards of the user, ran them on the screen
and create a new board. Here you for sure want to ask, okay, but we have socket IO. Will we use sockets that
you're on exactly this page? And my answer is no because
we don't need to use socket or your
everywhere we have for this is to TP yesterday, a lot of cases where
we need socket IO, but for sure not for this page. Why not? Because actually this
is the page where we, for current user simpler
random list of words. We don't have any
other users who need access to this
specific page. This is where it doesn't
make any sense to use here socket IO,
but don't worry, we will use socket ion a lot later on the single board page. And in this video we will
focus on creating our board on the backend and getting the
list of boards from the API. This is why Let's jump back out of our client to our server. Here inside our source
folder, inside types, we want to create a new interface and
let's name it board, dot interface and dot ts. So board is our new entity. Inside we will register our board interface which we
will use in different pages. For example, in the
page with the list of boards and on the
single board page. So here we want to export our new interface
and this board. And the question is, what
we will have inside, exactly like we did
inside our user. We will have here interface
for the board like this. And we will have
here board document, which will extend the document
to get at least an ID. So we don't need NAD here, but we need at least a title. So each port must have a title. Secondly, we will
have here created it, it is date, and it
will also be date. And actually here on
the right-hand side, user, as you can see, we didn't create updated head, but it is there
because of Mongoose, so we can write it here also. The last field that we need
here inside our board. Our board must
belong to some user. This is where here
we must save MAD of the user who
created this board. And for this we can
write here user ID, and we're not
writing here string, but Schema dot types,
dot object ID. Now we must import here on
the top schema from Mongoose. As you can see here, we
have this special notation, schema types, dot object ID. And this is exactly how we're creating a d's inside Mongoose. So inside this is
not just an idea, this is an object ID, but at the moment when
we will build our API, this user ID will be
simply a string fast, so we can understand what user-created this specific part. Our next step here is to create our document
for the board. This is where here
I want to expert interface and here we
have Word document, exactly like we
had on the right. Here we want to use
extents document and this document is coming
to us from Mongoose, so we should not forget
to add it here on the top because another
case it won't work. Here, we simply put
brackets and nothing more. So we don't have here validate password or something
similar because we simply extend the document and
with daunted anything new. So we successfully created
our board interface. Now it is time to
create our model. So inside models we're creating
new file board dot ts. And again, just like
we did in the user, we will create our board schema. So here on the right, I will open our user
so we can have a look. First of all, here on the top, I will import schema and model. And now we want to create our board schema just
like we did for our user. And here we're seeing new schema and this
is mongoose schema. And inside we're providing our board document
that we just created. After this, we have a round
brackets and inside we must provide all fields
of our board document. The first field here
will be titled. So what is title? It is a string. So let's say here that
our type is string. And secondly, it is
required because we can't create our
board without a title. This is why we put
here required through. As you can see here,
we're getting an error. So argument of type title is
not assignable to parameter, which actually means we did something wrong with
our board document. And see our problem because here were extended
from the document, but we didn't extend
from our Board, which actually means
all these properties were not available fast, as you can see now we
don't have any errors. Title is that now we just
need to provide user ID. And here inside we
must set our type and it will be exactly
the same schema, dot types, dot object ID. And the next one is required through in this
case when no, Okay. Use ray D is also mandatory. After this, we must
export our model. So here will be expert default model and we are
providing insight as a generic our board document
here now we can open our brackets and does the first perimeter we're
providing here named board. And there's a second
parameter, our board schema. So as you can see, we
implemented our board in exactly the same way
like we did with the user. But board is much
simpler because we don't have here additional methods and the additional validations. Now we must create a
new board's controller. And the method to
get Albert's inside. This is why what I want to do, I want to jump in such
service source server tiers. Here. First of all, I want to register a new route and it
will be app.get. And we have here slash
api slash boards. And this is the list to get all boards of the current user. This is where here we want
to use our OS middleware, because if we're not logged in, we can get boards. We must have a user. And the last one here will
be our board's controller. We don't have it here and here, dot, for example, get boards. So we must create our board's controller and
here get board method. This is why on the top. First of all, I want to input our star as board's controller, Rome, and here will be path
controllers slash boards. And we don't have this file yet, so let's create it now. Here inside controllers, I
can create boards dot ts, and here we must
create a new action. Here we will do it in exactly the same way like we did inside our users Controller. So first of all, on the
top we need to input our request response and
next function from express. After this, we can
create our new function, get boards, which will be
an asynchronous function. And we're getting
here first of all, our request and this
is type request, then response, it
is type response. And the last one is next
of type, next function. And this previously
we want to write here try-catch so we can handle
everything correctly. Here we're getting our
error and we can simply throw it inside our next
here will be next era. Now, inside our tribal must
try to allow logic for this. We must inject here our board model that
we've already created. So here will be important
Board model for Rome. And here to dance, we're jumping inside
models slash board. And now we can try to find all boards by specific user ID. So here we want to get
our property boards. And here we're using a
weight Bohr model dot find. And if you don't know,
find will find for us old records by
specific predicate. So inside we can provide an
object with fueled usury t equals request
dot user dot ID. And as you can see here, we
directly get an arrow from TypeScript that user does
not exist inside request. And this is totally right. Just to remind you, we created here our
own custom requests with use a field inside. Here we can write express request interface,
and in this case, it will work correctly, but our request user
can possibly be undefined it and then
this code won't work. This is where here
we must try it. If we don't have
request dot user, then we want to
throw here for 01. So return address, send status, and here will be 401, exactly like we did previously in now a
user's controller. So here on the bottom, we did exactly the same. And now we must simply
respond with our boards. So here we can write
rest dot sand, and we're sending here boards. And the central, this board's
list is just an array. Let's check if it's working. As you can see here,
insert server, this is connected and they
don't have any errors. So let's jump inside postman. Let's make here a request
slushy pie slash boards. And as you can see
here inside headers, or you already have
an injected token because we use this
request previously. I'm hitting here sand. And as you can see
where I'm getting back an empty array because we
don't have any boards. So now the question how
we can get some boards if we still don't have any
pie to create a board. And for this we can
simply jump inside Mongo and create this
port on our own. This is way here. Inside console I will write docker exec minus
80 MongoDB Mongo. So essentially we want to call Mongo command inside
our container, MongoDB. Just to remind you if you
didn't use darker here, but you just simply installed
MongoDB on your machine. Then you just need to write MONGO insert console
and it will work. I'm hitting here, enter them here inside the
console of Mongo. Now here we need to
use our database. This is why here I am writing
use space ultra long. And after this they
put semicolon. I'm hitting Enter. And as you can see here, we switched to dB HL trailer. Now here we can write show
collections semicolon, and as you can see, these
are our collections. We have here boards
and we have our users. What we want to do
now we want to insert a new record inside our boards. For this, we can
write db dot, ports, dot insert, and here
we have a function, so round brackets and
inside an object. And here we must provide
what we want to insert. In our case, it
will be just title, for example, first board. And we must write
here some user ID, but here we should not provide the string VM must
provide an object ID. This is where here I
want to write user id colon and then object IED. I opened here round brackets and inside I'm paste
in our stream. As you can see, this is not just usury, the
end of the string. We're starting here inside user ID and object
ID with the string. And at the end we
must put a semicolon. I'm hitting Enter and we're getting the right
result inserted one. And now here we can try and get all records inside this
specific collection. So db dot boards, dot find and round brackets. I'm hitting Enter and we're getting just one object with ID. And this is the idea of
our board title first board and user AD is
a correct object ID. This is why now we can jump
back inside our postman, choose our API boards
request and hit Send. As you can see now we're getting our ASHRAE with
an object inside. Here we have our ID and
here we see it as a string, title and user ID, which actually means
we successfully created our first board from the console and we've got
the list of our boards. But here I want to
improve last small thin, as you can see
here, we're getting our IDs from the backend
with underscore. And actually typically from
the API we're getting a DES. Normally without underscore. This is just the
thing of MongoDB, which means this is not
that comfortable to get a, this was underscore on the front-end and put
don't want to do that. And inside Mongoose, there is
a possibility to tune this. This is why I want
to jump back inside our code, inside sororities. And here, for example, after our configuration
of abuse, we can write here Mongoose dot set here as the first parameter we're
providing to Jason. And as the second parameter
we're providing an object. And inside first of all, we're saying we're
Charles through. And secondly, transform. And here inside transformed
we have two arguments. First one will be underscore and second will be converted. And this is the function. And inside this function
we want to write delete converted
dot underscore ID. So what we're doing here at all, here we can change to
JSON method that is written inside one goes here we're providing
transformation, so we're saying how
we will transform it. And actually inside Mongoose, we will get back NAD
and underscore ID. And here we will remove just
from JSON underscore ID, which actually means
inside Mongoose, we still have this
underscore ID. We can safely use it, but we won't get it inside JSON, we will get a normal ID. And you might also want
to know what is virtual strong and actually incitement
goes inside models. We can create virtual
properties and by default inside Mongoose
whoop won't get them back. And actually we want
to get them back. This is where here we
write virtuals, true. So let's check if
this code is working. I'm jumping back to the
postman and I'm hitting sand. And as you can see here, we're getting exactly
the same response, but we don't have underscore ID. We have just a normally
d, which is a string. And this is just like we want
to use it inside front-end.
21. Frontend for gettings boards: In previous video,
we successfully implemented getting our
boards on the back-end. Now we must start with our
frontend part festival. For this, we must implement
our boards module. But just to remind you
here inside clients source, app, home components, home, home Ts, we have very direct on the slash boards
when we're locked in. This is where we
must first of all, implement our boards module
and inside this route, so at least our code
can work successfully. This is where here
inside that we want to create new module boards. And this is separate module for a single page where
we have a list of the boards and
actually inside we could also create a component
for a single board. But I really want to separate these two modules because
they are two different. This is my inside of
our boards module. We can create boards
dot module.js. And here I don't want to write
everything from scratch. I want to copy paste whole module completely
and just change it. So what do we have here? First of all, our class
will be bored module. We won't have here
in declarations, home component and we
need here and you round, but here we don't
need path home, but we need path boards. And this component home
does not exist here, so we need to change it to our new component,
boards component. This is where actually
what I want to do, I want inside home components
to fully copy paste this home directory
and paste it here inside the boards, inside
components folder. So actually you can use Angular generators
if you want to. I really just want to
copy paste module. It is faster for me. So here I want to rename
this components to boards, and this is our root
component of module boards. And inside we have two files. First of all, boards
component HTML. Secondly, boards component ts. Here inside the HTML we can
simply remove everything and just write boards so we
can check if it's working. Now, we can jump inside
of our boards component. And here first of all, we can change our selector
and we can write here boards and our template URL will be important
component HTML. Now here inside we
have our class, which is boards component, and here we must
remove implements. We don't need it for now. And they will remove all code
from our boards component, and we can also
remove all inputs. So we successfully created our new empty board component and we can now use it inside
of our boards module. Here we can write boards
component and do this. Auto input it here on the top. And now here we have a
route for slash boards. And here inside
declarations we can define our boards component. And the last step that we
should not forget is we must register this board's module
inside our app module. So here inside our inputs, we must add boards module. Let's check if it's working. I don't have any arrows
here inside web server. Now I want to jump to
our page and reload. And as you can see where on
slash words here is our text. If I will jump here
on the homepage, I will be directly directly to the board because I'm locked in. Now, let's check if we
will be redirected back. If we're not login. For this, for example,
we can simply remove token and
reload the page. And as you can see here,
we're getting unauthorized, but when not redirected
back to the homepage. And essential for this, we created inside our
house services r squared, and we can use this R-squared now inside of our boards module. So inside boards,
inside boards module Ts here on the route we can
attach, can activate. And here we must
provide an array with World Service that
we created previously. It is totally fine to
use it just like this. We don't need to
register it here. As you can see here,
there are no errors. So let's reload the page. As you can see now,
we can't jump to our sludge sports and were
redirected to the homepage. Which actually means r can activate, is working correctly. And now just with a
single line here, we can define what pages are allowed only for
logged in users. So we successfully created
our boards module, and now I want to create
a port interface. And we could create it
inside boards module. But actually, I want
to create here inside AB new folder and
call it shared. Because actually I want
to put all types which we're using everywhere
inside this shared folder. For example, task column board. These are all shared
entities that we can use across
different modules. The same goes with the services or services
which are shared, like board service,
single board service, column service, task service. We could put it in
a specific module, but I really want to put
all this stuff where we just fetch data to
the shared service. This is why here
we have AB shared. And here first of all, I want
to create new folder types. And now inside our
app shot types, we can define a new type and
it is board dot interface. Ts. And let's export here
our new interface. And this will be an interface of our board and we must
name it port interface. And inside we must define exactly the same fields like
we created on the backend. And first of all, here
is NAD, it is a string. Secondly it is title, it is also string. Also we have here
created that this is strained and last
one is updated. It, it is also string. And actually here additional
were getting user ID, just to remind you here inside our server source models
and here we have our board. We have UserID, which
actually means here we will also get user
ID and it is string. Our interface for single Boyd is completely ready and we can
use it in any component. Now I want to create the
Service to work with boards. And actually any requests
like create board, get bored, get boards, delete board
will go inside the service. This is where here
inside shared, I want to create a new
folder and name it services. And then said we will create
a new service which is called Boards service dot ds. So here let's now
define our new class. So here we have class
Boards service, and we must write
on the top of it injectable so we can
use it everywhere. The first method that we want to define here is get boards. This is exactly what we're prepared already
on the back-end. So here is our get boards and we don't need to
provide any argument here. And we will get back an observable with a
ray of our boards. This is why here we can
write port interface array. This is exactly what
we just defined. Now inside this get boards, we want to make
sure HTP request. This is why here we must try
to construct and inject. Here are HTTP and HTTP client. Now, inside of our boards, we want to get a URL exactly like we did previously
for the user service. So here we have our URL
and it is environment. Don't API URL to us. And here is slash board. This is exactly our child. Now here we can return
this dot http.get, and we want to get our
URL without any options. And as you can see here, we're obviously
getting an error. Observable object is not assignable to observable
board interface. This is where here,
as previously, we must specify that we're getting back in array of boards, so our services completely ready and they just
want to test it. This is why here I
want to jump back inside of our boards
boards module and we must first of all inject the service here
inside providers. Here we can write that we
have here Boards service, and it is available fast. Now we're allowed
to jump inside of our component here injected. So first of all,
I want to define our constructor and
we know that here we have Boards service and
this is our boards service. Now, additionally,
I want to write here implements on in it. And now uninitialized,
they want to fetch this list of our boards. So here we have our
energy on in it and inside they can simply
write this board service, get boards, subscribe
so we get the result. Here is some result. Actually this is
boards and they just want to console log what
we're getting back. So here we're getting
our boards, coma boards. Let's check if it's working. But don't have any arrows here inside front-end web server. And let's login now again
because we're locked out. So here I will provide
our foo at gmail.com. Now as you can see
after logging in, but I get into slash boards. And here inside console
we can see boards. And this is a single
board while we're getting it because in
the previous video were created inside the
console to test the pie this point and essentially this sub boards for our current user. Why is that? Because
they actually here is our back-end request. Here we're finding our
boards by user ID, which means we're finding
all boards of this request user ID from where we're getting this user ID from
our network request. Here is our board's request, and here is our headers. And as you can see here
in headers on the bottom, we have this authorization
and here is our token. This is exactly the information
that our backend needs in order to deliver fast correct data of
the current user. So as you can see,
are getting of the boards inside
module works correctly. And we successfully prepared our service to get a list
of boards on the client.
22. Inline form: In this video, I
want to talk about an additional module
that usually live want to create for
our application. And I'm talking here
about an inline form. Let's have a look on the
fully finished project that we're implementing. As you can see here, I'm on the slash sports page and
here I just created a user. So we don't have
any boards at all. And here we have create board. As you can see, this is just
a square with some texts. But when I'm hitting it, you can see here an input
without any placeholder, we can type here, for example, foo board, and we're
hitting enter. And after this, our board
is directly created. Here. We see this card again, and this is essentially an
example of an inline form. So we have here some
square with the text. Then we're hitting
here and activate an editing mode where just type in something and
we're hitting Enter. So the easiest solution here
would be to simply create this component and throw
it inside of our boards. But then we'll jump
into a single board. Here on the left, we have exactly the same. You can see here the
title of our board I'm hitting here and we're
getting in edit mode. Here we see the
title of our board, but now we can change
it so we can update it and hit Enter and web date, the title of our board, exactly the same we have
here with the list. This is just some
texts time hidden here and we're getting here
not only an input, but also a button, at least. And as you can see, styles are different in every single case, but it doesn't make a
lot of sense to create additional component
for every single case, like create an onboard, updating the name of the board, then creating a task, creating a column, and so on. Because also inside
our column we can add the task and this is
also an inline firm. This is why I really wise
approach would be to create a single component that
can cover all our needs. So what cases do we have? First of all, in any case, we have some markup. Then we have an editing state when we just click
on the element. But as you can see, the markup can be completely different. But what is different
is not the markup, it is just style it actually, if we will check here, as you can see, this is
an inline font that I created and we simply attach
here Create Task form. The main idea is that all
these classes are available globally and we can just
change them with CSS. Inside were provided
in different things. For example, we can
just first of all type some text that
will be shown here, like for example at the current. But we can also show here that text like the
name of our board. Now we're hidden here, Edit and we might want to propagate this text
in the edit form. But here with a task, we don't need to do that. But also here we have a
case where we have at Cart button and
not only an input. So all of these
cases must be inside this single component
to work smoothly. So let's try now to
create this component. And this component
is super shareable. This is why I want to
pack it inside AB srand. And here we want to create
the folder modules. Here you might ask, okay, but we're talking
about component. Yes, you're totally right, but you can't use
components without module. You can either
register component inside some module
or you can inject module and injection module is much better because you can define what components you want to allow outside
and what's not. This is where it
typically when we want to share a component, I highly recommend you to
create a module for this. This is where here we have
a modules and we can create a new folder which
will be inline form. And this is exactly the
form for all this cases. Now let's jump inside
and create here inline form dot module.js. And inside here we
must export our class, which is inline form module. Now on the top of one to inject our NG module just like
we did previously. And here we want inside inputs
to add our common module. The next step is to
create our components. So here we will have
components folder, and here inside we will have our inline form with two files. First of all, in live form
dot component, dot ts, and secondly, inline form
dot component dot HTML. Now let's just try it
inside some texts, for example, in line form. And let's jump to our
inland from here, expert, our class inline form component. Now here on the top we
must define our component. The inside we can
provide a selector, and those selector will
be just inline form. And here we'll also need
to provide a template TRL. This is inline form
component HTML. So our basic component
is ready when mass now registered
inside module. So here first of all,
our declarations, we will have here
inline form component. And secondly, and it is super important is experts because here we want to allow using
this component outside. This is way here we're adding inline form component
inside experts array. Now let's jump back
inside our component and define some inputs
as earlier this set. First of all, we want to provide a title for our inline form. This is why here we have an input title and
this is a string, and by default it
is an empty string. But it is important to distinguish between
the title of the form. This is what we want to change
and just the texts that we're showing because we need different things in
different cases. For example, you want to
run the some default text, but when you are
hidden on our phone, you want to render another text. And this is essentially title, but we also now need
here at default text, this is what we're
rendering when we're not activated our firm. Here, let's create default text, and this is a string. And here I want to make
a really nice twist. I want to write
here, not define it. And this is essentially what
I recommend you to do a lot if you just start to implement something or even
for production, when you are not sure
that you have some data, it is much better
to set not defined it than just undefined
it or just empty string. In this case, people
can directly see that the value is not there
and it is not broken. Also have a case when we
want to show a button, this is where here we must
create one more input, has button and it is Boolean. And by default it will be false, which means but
don't render button. If we have a button, we want to change the text on this button. This is where here
will be our input with button text and it is a string. And by default they want
to set it to submit because this is the most
popular type of the text. After this, we'll
also want sometimes to provide a place
holder for our input. This is where here input
with input place holder, and this is a string, and by default it will
be an empty string. And the last thing
that we need is an input type because we have a case where we're
mass trend and not an input but a textarea. This is where here I want to
write input with input type, and this is just a string. We could create an enum here, but I will leave
it like a string. By default, we can write that this is just a string input. And at the moment when we
want to write the text area, we will provide here
a string text area. Now here we're also
must define an output. This is the values
that we want to propagate after
submitting a form. This is by here, Let's define an output endless name
it handle submit. And this is new event emitter. And here there's an
important point. We must input it from angular
core and not from node, and then say add
new event emitter. We want to provide that type of data that we
want to get back. And it will be a string
because the noun form, we have just a single string
that we want to give back. Also here we need
the local property to handle is edited by default, we need is editing
to set to false, and then we activate editing, we set it to true. This is y here is
editing will be a Boolean and by default
it will be false. And last but not least, here we want to create
a reactive form. This is where here I
want to provide insight constructor private if b and this is form group
and we're already created reactive form previously
for our registration. This is where here we will
use exactly the same, but we'll just
create here a form. And here we can write
this dot FB dot group. We must provide
inside our fields. And in our case we
have just a title. And here we can say that
this is an empty string. And actually here
I made a mistake. If b is not firm group, it is actually form builder. So here we need another input. Now let's write some market so you understand the logic Beta. So here I want to jump inside of HTML and we don't
need this text. Now here we first of all, I want to create a div, and this div will be our
default div with some markup. This is why here we need a class which we can use them
globally and over, right, for our needs. So here we can write
inline form container. Once again, we're not writing any styles for our
inline form container. Every single use case must
create their own styles. Also here I want to
create another class also for styling if
in editing mode, this is where I will
create in G class. And here I am
providing an object, whiskey glass in line
dash, dash container. So the same name, dash editing. Here we're providing the value is editing that we just created. The main idea is
that we're given enough classes for
other developers to use this component and to style it properly for
every single state. And the last thing here
is, of course a click. We want to activate our editing. So let's create here
a new function, activate editing and with don't need to provide
anything here. Now let's define this function. So inside our file
here on the bottom, we can create
activate editing and what this activated
it and is doing it. First of all, set this
dot is editing to true, but it is not all. We have a case where we want to see the value that
we want to change. For example, we
have an edit form like fun name of the board. And for this we provided
inside this input title. But as you can see in our firm title is empty,
which is correct. But in our case, when we're
jumping to the editing, we want to set our title. This is why what we can write
here if we have a title. So if we have this.title, then we want to put
it inside our form. And for this we can write this
dot form dot pitch value. And inside we're
provided an object with title, this title. So what this line is doing, if we activate it in editing and we provide
it a title inside, then we will update our value of the form with
function pitch value. Now let's go back to our HTML. So inside of a div with
Festival one to create a div that we will show when we're not in the editing stage. And this is just a text with some class that we can style. This is y here, div class. Here we'll be inline form text. And here we want to
show this DFF only one. We're not in the
editor and state this is way here,
NADH is editing. Then we show this div
and inside one to render our default text that we're
getting in the input. And after this, we're
creating our form, just like we did
inside registration. So it is reactive form and
well-defined our form group. So here we can provide
our form group. This is our form and we'll
also have here NG Submit. And we must create
onsubmit function. But let's finish
with this form here. First of all, we want to render this form only one
bar in editing stage. This is why in GE is editing. And we'll also want to
provide a class for styling and we just
name it inline form. Let's jump back now
inside this file and create here our
onsubmit function. And here is the tricky part. We could open up a form, don't provide a value, and then just hit enter. And this is not what
we want to emit. It doesn't make any sense
to meet empty string. So here we want to check it. If we have this dot form, dot value, dot title, only, then we want to emit it. And for a meat, we have this
dot handle submit a mid, and inside we're providing
this firm value dot title, which actually means
if we're submitting this form and we
have an empty input, we want immediate
what we want to do after we want to close
our ears editing state, This is why is
editing equals false. And we'll also want to reset our firm to the initial state. This is why this dot
form dot reset and this functionality we're getting from the Angular out of the box. Now, let's jump
back to our HTML. Here we have a form. Now we must define an input. So here let's create an
input with a type text. And we're getting here
inside form control name. And when in this
flower reactive form, here we have our name title will also want to create here
a class for styling. So here class input form input. We also want to provide here our placeholder and we're
getting it from the input. So it is input place holder and we want to
render this input. Only one who provided
the correct type inside. Here will be NG IV
with input type, where our input
type equals input. And now we want to do exactly
the same width textarea. So we have here at
exterior and we want directly to
close our text area. And now inside we want
to check this NG IV. So here our input
type equals textarea, then we will randy. But we're also must provide
here a form control name, and this will be also a title. And also we must
provide here a class. And in this case, class will be the same input form input and after how it
extends over mask, create a button for submit. This is where here in button and inside we will
have our text, button text that we're
getting from the input. And we want to render this button only if we
have a proper input. So if we have an
input has button, then we will render it. Then here we want type submit, and here we want to disable
it if it is invalid, this is where he
is disabled and we can use form dot invalid. And after this, we'll also want to add a class to style it. And here will be
inline form button. And actually with these are input form is completely ready. So what we're doing here, we have a div, we have
here activate editing. We have here
different classes for every single element
here were rendered in our default text and
here is our form with input or textarea and
button if we need it. So in the next video, we will try to use
our inland firm on the use case of
creating the board.
23. Implementing creating a board: In previous video, we successfully prepared
our inland form, but it is not clear how
we will use it with just created it with all
possible values inside. In this video, we'll
want to use it, but we don't have anything
inside of our boards page. This is the way to put
inside our inland form. We must also generate
our boards page. And here we have
mostly online markup because we already have
access to our boards, because we already
created get board method and we subscribe to
it to get our boards. So let's start with our markup here we can remove
the sports world. And first of all, on the top, I want to write up top because
actually in this video, we won't implement our top bar, but we should not forget that we need to implement it later. And here we can
start our markup. So first of all, we have
here boards, page container. Now let's close this div
and inside we will have one more div with class
home left sidebar. Here we're closing our div. And actually here we're
creating our sidebar on the left and inside we will have links to our different pages. This is why here we
have a router link and we want to provide here
a link on slash boards. After this, we'll also
need to add a class here, boards side menu option. Also here we want
halite the link when it is inactive route and an Angular for this we have a special attribute which is
called router link active. And actually it is a directive. And then said, we can provide
what class put want to get. In our case, we want to get a class boards side
menu option selected. Let's close our a
here and inside. Let's try texts. It is boards also. Let's set here one more
link which will be home. This is why I will
copy paste completely this a router link
here will be slashed. Same class router link active. Here we can write
not boards but home, but also here we want to write router link active
options by that, because actually here we
have routed link slash and we have slashed in
every single router link. This is why this router
link active will always highlight this element
we want to avoid this. This is why we must provide
router link active options. And here inside we're providing an object with field exact true. In this case, we will
highlight this lean on loop and we have
full mesh on slash. And actually from
our perspective, it doesn't make a
lot of sense to create these two
different links. Because when we're locked in
but can't access our home, which means we will be directly redirected to our boards page. But this is the markup that we're getting
from the project. So we simply implement what
we have after our sidebar. We must create a main part with our container
for the boards, this is where here will be div class Boards section container. Now let's close here div and inside we want to
create one more div. Here we will have
div class my boards. Let's close this
div and inside of it we want to create one
more class with a header. This is way here, div class, my Boards section header and inside we must
create one more class. And it will be the glass
boards page header, name, and inside we will try it. My boards. After our header, we
want to render our list. This is way here. We will have a div
board tile list because we will have here
each board as a tile. Here we can close our
div and inside this div, we want to render
our inline form. I will end it in a
second because for now I want to just
finished our markup. And after we inline
form group one to render all our tiles,
which are boards. And for this we will
write our graph here, so a router link. And here we want to provide a
link to every single board. This is where here we
have a dynamic parameter. So here we can provide an
array with slash boards. And the second parameter
we're put in here, comma board dot ID. In this case, we will generate correct router link to
every single board. But in order to get
access to the board here we must write
and G for loop. This is way here in G4 and we're looping
through our boards. So here let board of boards, and here we'll also
missing our class. So here we should have board
tile and let's close this a. Now inside our a link, we want to provide a div with class board tile details name. Let's close this div and
just render inside the name. It will be bored tile and
they'll markup is fully ready. Let's check if it's working. I actually have here an arrow because they used
property not correctly. Here, we should put
double brackets and not single brackets.
Let's check again. We have a narrative that we
don't have property boards, and this is completely valid
inside our type script. We're getting our boards, but we simply have here console
log with Dan, save them. And actually we must
do it this way here on the top we can
create boards property. It will be our board interface
that we already have. This is an array and by default we will have an empty
list of our boards. Now here instead of
our console log, we can simply assign
to this board's, our boards that we're
getting from our service. Let's check again. As you can see now we
don't have any error, but we have an error board. The tile does not exist. And this is why I like
TypeScript so much. It was simply a typo, but we don't debug
it in runtime. We simply have a
validation of TypeScript. So here should be title, and now we don't have any
error. You shouldn't be valid. Let's check this out. I'm reloading the page and actually it is looking somehow. We have here our app to bar. We didn't implement it yet. Here on the left we have a
Boards link and home link. So links are there. And here is our
main content with my boards in landforms that we will create an a second and our first board with
the correct link. And as you can see
here on the bottom, it is slash port, slash id, which means
our URL is also working. But I have a small
typo in markup. As you can see, our sidebar
is not looking that great because here inside of a div
class home left sidebar, I missed word container, so it should be home left side, but container, as
you can see now, our sidebar is much bigger
and it is looking better. So as you can see, we
successfully fetched our boards with this code inside our
component with our service. We save this information
to our boards property and was rendered
our list of boards. And actually here
inside our network, we can see that we
have a request on local host for 1001
slushy pathless boards. And we're getting our
single board that we have. And now it is time to
talk about inland firm. So how we will use it
here inside our HTML. And for this, I want to open
our inland form here on the right so we can understand what we have
inside our component. So here we have lots of inputs. So what we should provide
for this specific usage, actually here we're
right in that we have our inline dash form. Let's close it here. And now first of all, inside, I want to give a class. And the main idea
is that this is the parent class that we can style because we want to apply unique styles to our component. And just to remind
you here we have classes like inland
from container, inland from text and song. The idea is that this class plus this class will
override any CSS. And this is exactly the idea. This is where here we're
providing some class, for example, create board form. Now all these clusters
inside will be simpler nested inside our CSS. The second thing that we
need here is default text. And this is the
texts that we will see on the card beforehand. And here we have our default
texts, create new board. And the last thing
that we must provide here is handled submit. This is what we're getting
from our form. Here. We can write, for example, create board and we're
getting here event. So what did this event, just to remind you here, we have a handle submit
and it is a string. This is our title and we don't need here has button title, button text,
placeholder, input type, just because this is the
main default inland firm. Now let's check if it's
working where jumping here inside console and die
directly have an error. We don't have a
method create board, so let's create it now here. This is a method where
we're getting a string. So we can say that here
we're getting a title, it is a string and back we will return void,
which means nothing. And it lists here I want
to consult our title, which were created
inside our inland form. Now we're getting here
and narrow argument of type event is not assignable
to permit a string. And actually it might
be super tricky for you to debug because
you might think, okay, I'm providing here
something differently. But the problem is not in this. The problem is that
inside this module, we didn't inject inside
our inland form module. This is where here
inside boards module, we must import our shared module and it will be in
line for module. And the main idea is that we're inverted here on a module, but inside we have an
expert id component. This is why now we can use this exploited
component inland form here inside our component. Let's check this out. As you can see, we don't
have that error anymore. We have something different and we have here cannot bind to form group since it isn't a
known property of the form, this is why what we must do. We must put reactive forms
inside our inline form module. This is why I want to jump
inside inline form module. And here inside the inputs, I want to write
reactive forms module. Now we should not
get such error. Let's check this
out. As you can see, everything is green
and we're ready to go. Let's check them
in a browser where reloading the page and voila, this is our element. And it might be super difficult for you now to
understand what is happening here because
we didn't write any CSS and CSS was
already prepared fast. So let's inspect this element. What do we have here? As you can see, this is
a inland form and this is our class create board form. Let's check this
out. What we have inside our project
and our styles. We haven't said Source
styles and here lots of styles and we're interested
now in create board form. Let's open it, and
this is our CSS. This is exactly
what we see here. This is create board form. This is our parent. Most importantly that
inside inland form, we have different styles. For example, here is div class inline form container
and how we're styling it, we're using here our parent
class that we have just here. This is create board form and
then inline form container. And this is what
you can see here. Always prefix here, all inland from classes with
create board form, which is our parent form, just for this specific usage. So this is how we're making our inland from super flexible. It contains 0 CSS
inside and we simply define all our CSS outside
for every single case. This is why here we defined, okay, we have inland
from container, you should have display flex, align items and justify content. Then if we need to style
inline from texts, for example, in this
case, we didn't stylish, but we could also
just right here, create board form, inline form, text, and style this element. So this is how we created
our awesome inland form, which is super shareable
for every single case. Now let's check if
it's really working. So we have here our default
text create new board. We can click on it, and now we have our input and we can write something here. And they already
see here a mistake. As you can see here,
we have input with a class input form input, but this is what
we're style and where styling inline form input, which actually means
we made a typo there. Let's jump back inside our component, inland
form component. And here we have our input
and here we need to change our input form input
to inline form input. And as you can
see, we're getting exactly the same problem
here in textarea. It should be inline form input. Let's check again. I'm reloading the page
where hidden on the button, and now we have a
correctly styled input. Now I can pay something
like foo and then hitting Enter and our form is
successfully closed. We see create new boards again. And inside console,
we see our title for this is exactly our output
inside board component. But what we're missing
here completely is creating a board on the
client and on the server. And now I want to do vice-versa. I want to start with the front-end and
finished with the Pi. And then you can
see how we're doing it in reversed order. So Festival here we have our Create board
inside component. So the idea is that here we
will use our board service. This is my inside
our board service, we must create a new method. For example, let's
name it create point. So what we're getting here, we simply get here at
title and it is string and back we will get an observable
with created Board, which means it will
be bored interface. And now what we will have
inside, first of all, we can copy paste the URL because it will be
exactly the same. It is slash boards and we're
making a post request. This is where here we can
say that we're returning this HTTP and not get, but post. And inside. First of all, we
must provide a URL. And secondly, we
will provide here an object with field title. And actually this is our body. And for our board with
just provide the title. Because on the backend
with set just two things. This is title and user ID. And user ID, our backend must understand by
itself because we're providing a token with our request and we
are authenticated. The main problem is here that we're getting around typing. We must provide
insight or a post. We're getting back
our board interface. And in this case,
we successfully created Create Board method. And as you can see
all our API methods, I'll look in exactly the same. This is simply URL and then HTTP post get overthrow we need. Now let's use the service
method inside our component. So let's jump back. And here is our Create board. And here we want to call
it this word service dot. And here Create Point. And inside we're providing exactly the title that we got
here from our inland form. And after this we're
writing dot subscribe and here we're getting
created board back. What do we want to
do with this point? We simply want to change
our, these boards rate. This is way here I will write
this dot boards equals. And here we're spreading
our thes dot boards and then they want to
put our created board. So essentially we're
creating here in your array, where inside we put
all our fields from the old array and then
are created board. So our front and
this fully prepared. Now, we must jump
to the backend and create everything
there. On the backend. It is not that difficult
because we're all created a model for the
board and we can use it. We only need to jump
and said sorority S. And here creating
your API request. Here we can copy, paste
up, get completely, and it will be up
post on slash boards. Here we must use OS middleware because this
is an authenticated request. And here we're using
board's controller dot, for example, create board. And now we must create this
method inside our controller. So let's jump back inside
our board's controller. And here we want to
create exactly the same, but it will be a post
request to create a record. This is why I will
copy paste this completely and just rename it. So here we have our Create board and we're getting here
our request response. And next, and we have
inside try catch here. First of all, inside
where chicken for request use that this
is totally fine. And after this, we want
to create a new board. This is where I will
remove this line. So here I will write
that we're creating our new board and for this we're using you bored model and then said We must
provide a valid object. Here. First of all, we're
providing a title from request board,
the dot title. And second is the user
AD and actually use relative to mass take from our request, from
our middleware. So here we will have request
dot USA, dot underscore ID. And actually we can use both underscore ID or a
D, doesn't matter. We have both of them available. So this line won't save
anything to the database. We simply create here an
instance of our model. And after this, we want to save this record
to the database. This is why here we
can try it saved board and we're using
here a weight here, and you bought dot save. In this case, we're
saving this record and we're getting our
saved board back. And as you can see, this
is a Word document. So after this we can respond
with our saved board. So once again, we
have a post request, we have a Create
Board action here. We're checking that
we're locked in. After this, we're using our board model to
create a new board with the title that we provided and use 3D that we didn't provide. But God, from our locked-in user and we saved this new
point inside database. We've got our saved
board back and we're responding with this API. Let's check if it's working. I don't have any errors
here inside back-end. So let's jump inside
client here I want to hit create new board and I'm typing something and
I'm hitting Enter. And as you can see
directly here, we've got our new board. Let's check our network. And actually inside network, we have our board's request. And this is a post on
slushy pie slash points. And what is more importantly
here inside preview, this is our response. This is a title User ID. This is exactly what was
saved inside the database, which actually means if
I reload the page here, we are getting two
boards now because this information is all
the saved inside database. And here inside our network, we're getting our boards. And as you can see
in the preview, we're getting already
two boards and not one, which means we are fully
finished with our boards page. First of all, be rendered
here the sidebar, but we didn't do
anything with it. We're also used here
our inland form, which is super shareable. And we created our service, which is speaking to our API, to create a new board
from this page.
24. Adding Top bar and logout: In previous video, we
finished our boards page, but we forgot to
implement our top bar. And actually, this is the TBA on every single page for
a logged in user, which actually means not
only my boards page, but a single board page also, this is why we must create
it as a shareable module. So I want to jump to our
client source app shared. And here we have our
modules and inland firm. And actually here I want to
copy paste this inline form completely and rename
it to our tongue. But now inside the mass
change everything. First of all, let's
start from our module. So here we have a
top bar module and inside with don't have
any reactive form module, we simply will have some
markup and the logout button. Here we can remove
this input and we must change our
component also. But first of all, let's
change the name of the class. It should be our top bar module. Now let's jump inside
our components. And here we don't
have inland firm, we have our top bar and
we have two files inside. First of all, it will be top
bar dot component dot HTML. And here is our top bar
dot component dot ts. Now let's change the markup. I want to remove everything
in simpler right, top bar. Now let's jump to our ts file and remove everything
from our component. Now I want to rename
our selector to the top bar just to make
it clear where it belongs. Actually, we have
just a single Toba inside our application. We could simply
write here Tampa, but as it is shareable for us, I will write here up top, but just in this single place, here we have our template URL. This is top bar and our component name is
top bunk component. Now I can remove the symbols and just live here our
input of component. Now let's jump back
inside of a module. Now we don't have this
inline form component, but we want to declare here
our top bar component and expert TBA component because
we want to use it outside. So our module now
is fully ready. Yes, we didn't implement
any logic inside, but we still cannot
really bind it. And we want to jump and
said boards module and inside our inputs we can
add here top bar module. Now here inside our
components boards, boards, HTML on the top. Instead of this text, we can simply write AP top
bar and we can close it here. Let's check if it's working. We don't have any errors here. I will reload the page. And here on the top you
can see texts top bar, which actually means
we successfully binded our module and component
to our boards page. Now let's try to market
for our toolbar. This is where I'm jumping back inside our Tombow
component HTML. Here, let's write it. So first of all,
we have here div with class navbar boards. Let's close this div inside. We will have one more div, div, navbar, left side. Let's close this div and
inside first of all, we will have a link to our home. This is why a router link. Here will be our slash, and here will be
class navbar icon. Let's close our a and inside
we must provide a new image. So here will be imaged source slash assets
slash home dot SVG. After this, we will have
one more router link. This is why I will copy paste it completely and it is
going on slash boards. And it will be Naropa
eigenspace, navbar boards icon. And inside we have
another SVG file. It is slash assets slash boards, as you can see now, when
I am reloading the page, we have two nice icons, one to the homepage link
and one to our boards. But as you can see this
here, rails are invalid. We don't have them
at all because actually we must jump
inside our top bar module. And here, import route module. Let's save this and
reload the page. And as you can see now, this really links, first of all, homepage and here slash boards. Now let's continue
with our markup. After our left side, we have our right side. Here. We want to
provide our image with source slash acids, slash Trello logo,
white dot SVG. And here is our class navbar. Longer after an image, we have a div with class navbar right side.
Let's call this t. And inside we want to create a link follow gown, this is y. Here will be div class, navbar icon, nav
bar, logout icon. And here inside I want to
simply write log gout text. Let's check if it's working. Here we have our
links on the left, we have trailer
image on the center, and we have a button as the
logout link on the right. But we didn't attach
any logic here. So our top bar is
essentially fully ready. We implemented
everything, but we need now to implement
our logout. Logout implementation
is really simple. We just need to remove our
token from local storage, clean currentUser
inside our memory and redirect or use
it to the homepage. This is what we want to do here. We want to attach a click event. So here let's right-click. And what we're doing, we're calling our logout method. And number must create this
logout inside our component. So what do we want to do inside? Actually, I want to store our logos logic
inside our service because this is related
to authentication and they don't want to write
this logic directly here. This is y. What we must do, we must
inject our hours service, which is our service provider
inside this component. And now here we can use our
this dot out dot logout. And actually we don't
have such method. We must create it, but it is not all, but also after this one
to redirect the user to the homepage because we're
not logged in anymore. And for this we must
inject here router. So I can adhere router and
it will be our drought. After colon are our service. We can write here this
router dot navigate by URL, and we simply want to redirect
the user to our homepage. So we're almost ready. We just need to implement our logout method here
inside of our service class. So here on the bottom, I will write log out with
don't need here anything. We simply want to
remove our token. So here we can write
local storage dot remove item and were
provided inside the target. This is enough, but we
didn't clean our memory. And actually this current users still has a valid current user. This is not good. We
must set it to now. This is why this dot
current user dot. Next, we're provided inside now and with this single liner, all our components will be notified that we're
not locked in anymore. Let's check if it's working. I don't have any errors here. We don't need to implement
anything on the backend. Now let's simply
hit here logout. And as you can see, boom, I'm directly on the homepage and we can check that
we're not locked in. Actually been named jump in here inside application
local storage. I don't have my token. This is why when I'm
reloading the page, I'm getting 401 Unauthorized, which actually means we
successfully implemented our top bar and
logout functionality.
25. Creating board module: In previous video,
we successfully finished implementing
our boards page. In this video, we're starting to implement our single board page. And we're focused here just on creating our basic
module and component. This is why I want to
jump inside source set. And here I want to create a
new folder with new module. And actually you
might say, Okay, but why we don't pack our component board inside
boards module and yes, you can do that, but I
want to split it because the logic inside this two
modules is quite different. We still need to pay quite a lot inside our board
module. This is y. Let's create here a new
folder which is called board. And inside we want to create
our board dot module.js. Also, let's directly create here our components folder and mu folder inside which
is called board. And here we need two new files. First of all, board
dot component dot ts, and secondly board dot
component dot HTML. Now let's trend inside of HTML, word boiled, and let's
create our component. So here we can export
our new class, and it is called
board component. And on the top remastered
a component decorator. The inside we want to provide our selector and we can
simply say here that we have selected board and we're
also must provide here a template URL which is
poured component HTML, our board component
is fully prepared. We must know create our module. So here we want to
export new class, which is called board molecule. And we want to add on
the top of decorator and GI module and
provide insight, at least the list of inputs. And here we will
have first of all, common module also now we can create our declarations
and provide insight, our board components
that we just created. So we want to register
here in your route. This is where here
on the top we can create a new property
which is called routes. And it is our shouts, which is an array. And inside we must provide a
new object with field path. And here we will have
the URL boards slash, and here we have colon board ID. What does this board ID means? It means that we have
a dynamic parameter, which means here
we have a board ID and it is different for
every single board. After this, we must
provide here a component, and in our case it will
be our board component. Also here I want to
use the word service. This is why here we can
write, can activate. This is an array, and here
is our required service. Why we're doing that? Because this URL is only
for logged in users. If we're not logged in, we don't want to access
this URL at all. And now we must register this
routes inside our inputs. So here we can put route
module dot for child, and here we're providing
insight our routes. And last but not least, we must register this module
inside our app module. So here inside our inputs we can simply write board module, and this is our new module, which is inputted
here on the top. Let's check if it's working. We can jump now
inside our browser, open our dev tools. Here we can click
on our first board. And as you can see now
we don't have any error. We see here on the
top word board and then the URL we
see slash boards, slash and they D of
this specific board. What we need to do now is to get a single board for
this specific page. Why is that? Because actually we
don't jump always from the boards page
to our single board. And you might say, Okay, well, on boards page we
have all our boards. But then again, when we're on this single page and
reload the page, we don't have any
important information, which means we must
get this AD from our URL and get this
specific board with the CD. Also additional
information later, like columns or tasks. This is why I want
to open our app, shared services,
boards, service tiers, and the sale will be set
previously here we're writing all our
requests to the dye, which are related to our boards. This is why here we can create a new method which
is called get bored. And then side to get a board, we must provide a board ID, which is a string, and back we will get an observable with our
board interface. Now, inside here we
must prepare our URL. So here we will use environment
Doherty Pi URL plus, and here we want to concatenate
our boards plus AD. This is why it makes
a lot of sense to use ECMO scriptSig strings here and inject inside
this environment DPI URL. Then we have here slash, boards, slash, and here is an
AD as an argument. And actually it is not an AD, it is born and AD. So this is the URL that we want to fetch from the backend. And now we must return our request from
this dot http.get. And we're providing insider get our urine realm
that we want to fetch. But again here
we're getting back observable of
objects and we must specify that we're getting
back our board interface. Now let's try to fetch this URL. This is why we must
go back inside our board module,
components, board component. And here inside I want to
create a new method fetch data, and this method
will return void. And inside we want to
use this method that we just created to fetch
our board by AD. This is where here
we're must provide a constructor and here will be private boards service that we have ended this
Boards service. And inside our fetch
data we can write this dot board service
dot, get bored. We're providing
insight board ID, but we don't have yet but
a D in this component. And actually we must store this board ID from
the URL in this, because we will use it a
lot inside this component. But if we will write here
board ID equals string, then we will get here a
narrow board they dig has no initializer and is not
defined it in the constructor. And actually, we don't
want to get here undefined it because it won't be
comfortable to work later. Board AD is mandatory
for our component. And to get the
perimeter from our URL, we must inject here route. So here will be private shroud. Here we want to use
activated route. Now here inside we want to try and read this activated drought. So here I want to
get board ID as a parameter and we're using
here this dot shroud, dots, snapshot, dot params map
get here will be board Id. And this is how
you typically get parameters from the
URL inside Angular. But as you can see, this is
what we are getting back. We are getting strain on now, this is totally reasonable
because Angular can be sure that this
parameter is always there. This is why what
we can write here, because we really want
to save just a string. We can check and throw an
error if it is not there. This is why here we can write. If we don't have our board AD, then we will throw an arrow. Here will be throw new
error, for example, can't get bored AD from URL. And after this, we simply write this board ID equals board Id. And as you can see in this case where sign-in inside the string, because we checked here for TypeScript that it is not now. And if it is now, then
we're throwing an error. This is actually the
best wherein that we can do in order to get a
string in this component. Because it won't be
comfortable to check every single place that we
are getting this parameter. And now here inside good board, we can provide insight. This dot board AD. After this we can write dot
subscribe and we're getting back our board and we can
assign it and save somewhere. But at least for
now, I just want to console log our
board Comma board, but we didn't call this
fetch data somewhere at all. This is why here we must
write implements on in it. And now after our constructor, we can in June in it and inside we can call
this fetch data. And actually every
single time in every component where I
need to fetch some data, I prefer to create an
additional method for this, which is called fetch data. And not just throw
this logic inside and join in it just
to make it cleaner. As you can see when we're
jumping to the browser, we're making now request
on our slash api slash, forward slash and this specific
ID from momma parameter, which actually means
we successfully implemented on the
client or when you bought module and even fetched our board
from the backend. Obviously we didn't
implement a back-end part, and we will do it
in the next video.
26. Getting a single board: In previous video, we
successfully implemented the front-end part of our board component
and port module. But now on the backend, we want to implement
getting a single board. This is exactly the request
which is broken here. And I think that we
implemented a lot of stuff together and you already didn't know
how to do that. This is why I highly
recommend for you to implement it on your own. As always say, have three different levels
for EU level number one, you just pause the video
here and do it yourself. At the end, you must get here
our board from the backend, level number two,
let's talk about it. So what do we need to implement? First of all, I
want to open here our server source server. And here we must provide in your request and it
will beget on slash, api slash boards
and then Boyd AD, and it must be a
dynamic parameter. It must be protected drought. Inside of our boards controller, we must create a new action. Now we're jumping here
inside controllers boiled, and we simply do exactly the same like we did forget boards. But here Groupon to find a single board with the d
of this board from the URL. So if you want to do a level to just pause the video here, and if you just want to do it together, Let's get started. First of all, we must
jump back inside our server tiers here and
create a new API URL. So here we will have apt-get slushy pie slash boards and here risk colon board ID. Now we want to
create a new method. This is why I will
jump inside or controllers ports and
here we have get bored. They want to copy
them completely because they will
be super similar. So here I copy
pasted, get bored, and they want to name
this method, get bored. And here we are getting
request response and next, and we're checking
if we're locked in. Now here instead of
find, all boards, will want to use find one to
find just a single board. And actually even better we
can use here find by ID. It is also a method
which is possible and inside vermis just provide
an ID of this sport. This is why we can remove an object and
simpler right here, request dot params dot board Id. And back here we are
getting our board and we simply send this board
bag inside our API. Now let's check if we use
our method correctly here, and here is board's
controller dot get boards. This is not correct. It should be get bored. We don't have any errors
here inside our API and we successfully implemented
getting off the board. Let's check this out. I'm reloading the page, but don't have an error. Here. We're getting our board by d, and here is the information
from the database. We have a D title and user ID.
27. Adding board stream: In previous video, we
successfully implemented gauging of our single
board from the API. In this video, I want
to share my idea how we will use this
board and save it. And actually it might
be a little bit tricky. Why is that? Because actually
here inside client, we have our board
component and we simply can store it
in a property here. This is totally fine. We can do that. But
later we will have a model and this is where
things will be different. Just to mention, we have here a page with the
board and then we hit on one task to open
this task inside model, the main problem is when
we reload the page, we want to go back to
that specific task, which means we want to
have in our URL slash board slash board ID slash
tasks slash task ID. Then we reload the page and we fetch first of all our board, then maybe all
columns, all tasks. And we want to solve
this task inside model, but does it have a mean? We're saving, for example, our board here inside
board component, but we can't really use this information inside
our task component. Why is that? Because
later we will register our task component here as the child component
of this path. And we can't really access
in any way some data that was stored inside this board component
from the child. What is the solution
for this problem? We must use a service. How we can do that? First of all, inside of a board, I want to create a new folder
which is called services. And here we can create
board dot service, dot ds. Here. We should not mix
our board service here with our boards service, which has shared
services boards. This service is just to
fetch data from the backend. If we want to get our boards, we use good boards, get bored, create
board and so on. But what we're doing
inside the board, this is kind of state
for this specific page, which actually means
here we will store our board columns, tasks, everything that we
have in this page, and then all components
in this page can use the service to get this
information through streams. This is why here
we want to create our class and it will
be bored service. And here on the
top we should not forget to use an injectable. What I want to do now here, exactly the same what we
did with our current user. Just to remind you, we
had here, our service. Here on the top, we created this current user stream that we can use across
our application. We want to do exactly the same. Now here, we want to create
here Board was taller and this is a stream and this is in your behavior subject. And we're getting here either
board interface on now. And here inside
we're putting now just because by default
we will have no board. We didn't fetch it yet. And then after we
fetch this board, we can set it and
update the stream. This is way here we must create
a new method, said board. And inside womb must
provide our board, which is our board interface, and this is void. And here what we want to do, good one to call this
dartboard Stoller dot next because we want to update a stream and withdraw
inside our board. Now we should not
forget to register our board service
inside of a module. So here we must create
our providers and we're writing our
board service inside. And now we can use it directly inside our component board. So we don't want to create
here property board. We want actually to inject here inside our constructor
new property. And it will be bored
service from board service. And as you can see, this is
the difference we have here, both services, boards
service and board service. And this is a huge difference. First of all, this is
service to work with eBay. Second service is
more like a state. And what we can do now with
fetch data after subscribe, we can write and said this
board service, dot set board. And we're provided
inside of our board. In this case here we're storing
this inside the stream. And now we can use
this information not only inside our
board component, but later also inside
our task component. And this is extremely flexible
and the best solution that I can imagine to share
data between two rounds. And just to check
that it is working, I want to create here on the
top stream for the board. So actually here we have a
board stream and we know that this is an observable
of board interface. So we're showing here
we must get a board. There is no other possibility. And to avoid this type script
error with no initializer, we will write this code
directly in constructor. This is totally fine. So here we can write this dot
board with dollar equals, and here we want to use
the stored service dot. And here we have our string, which is board dollar. And you might say, Okay, why don't we use this directly? Because here inside
boards stream, we can get now inside of our component with
don't need null at all. We simply want to work with our component when
board is not now, this is why what
we can write here, we can write pipe and
then filter the result. So here just filter
Boolean will eliminate. Now from here, we won't get
here anything if it is now, we will just update our board. If we get a board, and now we can jump
inside our HTML file and render here our board
stream just to test, this is why board dollar. And here we're
using a single pipe to render this information. Let's check this out. I'm reloading the page
and we're getting object, object because
essentially we've got adjacent and we must
pass it with Jason pipe. Let's check this out again. We're getting here our
board, and as you can see, this is our information of
our board that prefetched. This is Title, user, user ID. So essentially the idea is that this service has
information inside it, but we don't need to
store this information somehow inside of
our board component. We simply use streams from
the service and use them inside every single component where we need to, for example, we will write exactly
the same code inside our task component if we need the access for our
bird, for example. What is more importantly, we can map data like this, which is really convenient.
28. Creating socket service: In this video, we're starting
an interesting part of our application and it is setting up our socket
layer connection. And just to remind you, we already set it up. So Quetta, you're
on the back-end. So here inside our code we can open service source server test. And here on the top we
have IR new server, and then here we have
IR on connection. And here is our
console log connect, which actually means we
successfully set it up. So get on the back-end, and now we must implement
it on the client. And for this we will use exactly the same library
we're using on the back-end. Just to remind you
here we used socket. This is y here inside console, I want to jump inside
our client folder. And here I want to install a
new package which is called socket Datta your dash client. And this is exactly the same
package from the same team, but it is for setting
up our client. And this package doesn't have anything to
do with angular. This is just a plain JavaScript. So what do we want
to do now at all? First of all, inside
our clients source app, shared Insight services, I want to create a
new service and it will be our service
to work with socket. This is way here. Let's name its socket, DOD service dot ts. And now inside we must create our class which is
called socket service. And obviously we
should not forget injectable here on the top. So what we want to create here, we want to create
a function which will generate for us
a new connection. This is why here we can write
setTab socket connection. And here we're getting
our current user. Why current use it because
they actually want to send some private data through
the socket connection. Why is that? Because actually we
will use socket. You're on the board page, which means our
user is registered. But inside soak into yogis, we don't have such
system like GET, like we used for authentication. We don't have HTTP
headers there and we can't attach our
token to our socket. But there is a way to pass
our token by using socket. This is way to make
this possible. We want to set up this
connection for our current user. And we can then throw the
token from the currentUser inside our socket connection
with every single request. So as I said here, we're getting as an
argument current user and this current user interface. And back we're getting void. Now inside here we
want to use our ion. And actually I must input
here on the top or your drum. And here we have our
socket IO client. Now here inside the ER, I can provide a URL where
we want to connect. And actually here
we can just try to HTTP and our port of the server. We don't want to
use that because for this we have
environment variables, which actually means
we want to jump inside clients
source environment, yes, and here like we
have our API Euro, we want to create socket a URL. This is where here we
can create sockets URL, and this is the http localhost. Here we have 4,001 and we
don't need to put here slash eight pi because we set up our socket layer connection
directly on our server. And you might say, okay, but we could reuse
this API just like a single property for
API URL and socket IO. Yes, We could, but I highly recommend you to split it to
two different properties. In this case, it is easier
to split it later if you want to move your socket
layer to another web server. Now inside our socket service, we can use our environment. This is way here,
environment dot, we have sockets, URL. This is exactly
what we must give inside-out or your connection. And excellent. Now additionally, I want to provide the token
from our current user because actually we set up socket layer connection
only for login user, which means we never have a case that we don't
have this token. This is way here, we can write field hours and then side
we're providing talking, which is current user dot token. And actually here as
the second argument, we're providing an object
with our field and this outfield inside socket IO is exactly to
authenticate a user. And what we're getting
back is the result of our AR is this
socket connection. This is where I want to
write this socket equals IR. And here we must
create this property, it inside the socket. And as you can see,
this is our two input. This is not correct. We want to import the socket
from Socket Layer client, or it is undefined it because by default we
don't have a socket. And then at some point we set it up by using setup
socket connection. Also additionally to
our setup function, I want to create
disconnect function, which actually means at some point our user
will be locked out and we want to disconnect him from our socket connection. This is where here let's create an additional
function disconnect. And this is really simple. What we want to call here, we want to write
this dot socket. And here we have a method
which is called disconnect. And as you can see here, I don't get an autocomplete because actually this socket can
possibly be undefined it. And there are two possible
variance how we can fix it. First of all, here I can just put question mark and I'm done. But I don't want to write
the code like this. I really want to notify a developer that if
we don't have socket, we have a problem. We can't really use disconnect before setting
up our connection. If we don't have
this dot socket, then we want to
throw and narrow. So here will be throw new error. And for example here socket connection is not established. So our basic sockets Service
is successfully created. Now we must at some point
implement the setting up of socket connection and
they really want to do it inside our app module. Why is that? Because every
single time when we are authenticated and Berlin, now that we've got currentUser, would want to set
up a connection. This is where we can jump
inside our app component. And here we have a next. And just to remind you, here we are getting current user every single time when we
start our application. And here we're setting
current user and we can, before here, set
up a connection. This is where here we can write private and we have
a socket service, and this is our socket service. Now here before
we can write that we have here these dots, socket service, dot,
setup socket connection. And actually here we're getting our current user and
we can give it inside. Let's save this and
check if it's working, as you can see here
with don't have any errors and then
jump into the browser. And here it is red, we're getting an error. No provider for socket service ended this happened
because we've created our socket service, but we didn't injected
inside of a module. And here we're using
the socket service inside our app component, which means it makes
sense to inject this module inside
our providers. This is why here we can write socket service and we're good to go. Let's
check this out. I'm reloading the page and
we're getting another error. So whatever we're getting, we're making this request
on socket a yarn. You can see it here, but we're getting an error. We have a course, which means cross-origin policy. Because here with don't
have access control allow origin header on the
requested resource. And this is actually
completely correct and we must unit inside of Serum why
it is happening at all, because our server does
not allow connections from other host and will for
sure use here another host, this is local host for 1200. This is why we must go
inside of a server source. And here we have
our server tiers, and here we must configure our IIS server a little
bit differently. So here as the second parameter, we can provide some
options and we're looking for the option
which is called cores. And inside we can set
origin star and actually start means that we're allowing connections from any host. And on the rail project, you might want to write here
just a host of your domain. But for our purposes, origin star is completely valid. Let's check if it's working. I'm delighted now the page
and we don't have any errors. Now, we can jump inside network, and here as you can see, we have WebSocket connections. So here we're getting
into hundreds. And if you can see in your
browser something like this, it means that your request of the socket connection is that. And actually now we can jump
inside our backend console. And here we can
see connect three times because every single
time when I reload the page, there is a new
connection established, which actually means
we successfully created a socket
connection on the client. Now the only question is, what is about disconnect? We create a disconnect
but could didn't use it wherever we
want to use it. Actually would want to
use it inside the logout. This is where we can
jump inside our app, Health Services and
here results service. And here we have our logout. And here we first of
all remove our talking, then when null and
our current user. And here we can use
socket to disconnect our connection because we know how we use it is not
locked-in anymore. We don't need this
socket connection. This is y here inside
the constructor, we can inject our
new socket service, and this is our socket service. Now here on the bottom
inside our logout, we can write this socket
service dot disconnect, and we will disconnect our current user and close
our socket connection.
29. Joining and leaving board: In this video, I
want to show you how we can use sockets on the example of connecting to our board and disconnect
them to the board. What does it ever mean? Actually insert socket IO. We have such thin
which is called rooms, which actually means
when not just notifying our backend with some
socket message or another client when they define all clients which are
connected to specific room. And actually when we're talking about boards inside trailer, it makes a lot of sense. We really want to notify only users who joined
this specific board. Which actually
means, for example, we have two different users. One user opened board, full. Second user open board form. Now the first user created a task and we must
notify a second user about creating this
task because he's connected to this
room or this board. This is where in this video
we will implement how we can join our board
and leave our board. And for this, we must create a new method inside
our socket service. This is why I want to open our circuit service
tiers in here, create a new method
which is called emmet. Why we need this method at all, actually insights socket itself. We have these dots, socket, dot and meet, and we can
provide just a string like foo. It will be our emit message. And here we can provide
some information, for example, Ambar string. This is how we typically can use sockets inside our application, but I want to wrap it in additional method
for two reasons. First of all, we want to isolate a library and create a wrapper. In this case, we can always
throw away this usage, for example, socket
to your client and use some another library. Secondly, here I want to validate that were
connected to socket a yarn. This is way, here is our
image and we're getting here, first of all, our event name, it is just a string. And if you don't know
inside socket IO, we're always emitting
just with unique strings, which we're listening on other clients or
inside our backend. And the second here
will be a message in here I will write any waste, any, because it's a
message we can provide a string and no object or
any data that we like. And here we're getting a void, but we want to do inside. First of all, I will copy paste this line to check the
socket connection. Because if we don't have
a socket connection, we can't use a meat. After this. We can simply use this
socket dot commit and were provided inside
our event name comma. And here is our message. So our image function is
ready and now we can use it. And actually I want
to use it directly inside our app board components, board component, component ts. And here we have in
June init method. And actually it is
a nice place to put just here
joining of the user, which actually means
every single time when this component is loaded, it means that uses jumped to this specific route
and we must join the user if he
changed the route and we want to leave this
room or this board. So with disconnect a user
from that specific room. This is why what
we can write here. First of all, we must inject
here our socket service. This is y, here will
be socket service, and this is our socket service. Now here we can use this method and write this dot
socket service dot. And here we have our image
and we must provide insight first of our event name
and secondly some data. So essentially what we
want to write here, something like boards join. And here our message will be, for example, an
object with board Id. And here we're
providing the board Id of this specific URL. This is this board ID. We already have
this information. Why I packed this information
inside the object. Because deflate, if
for some reason we want to put here more
information with the object, we can directly do it. If we have here, a
string must reflect all these places from
string to the object. And as you can see here,
we can emit any strain. We can write here for. It is totally fine, but we will have a lot of different socket events and we want to organize
them somehow. This is why here I
prefer to use boards, which is a plural
of some entity, like boards or maybe
columns or tasks. And here we have what happened? For example, we have joined, this is just a start. We can later make something like joint success if it is in
a synchronous process, which actually means for now, birds join is fine. What is not fine is that
we're writing here a string where right in TypeScript
and we must use innumerable. And actually innumerable is just some constants
inside a single property. This is why here on the right, I want to open our
shared folder, and here we have types in. Here. I want to create socket
events dot in m dot ts. Here. Now we can export our enum, which is socket UN's ENM, and this is just an object
in here we can write field boards join equals, and here we're
writing boards join. So first of all, here we
have some code style. Second Luba not drawn
here, a string. Right in here, socket events
in m dot boards chain, in this case in
every single place where using the same string, but we will never write it incorrectly because we're
using here Justin unum, which is a variable and
not a plain string. So what this line does, it emits for our back-end
in new socket IO message. Here were provided some
string and some data. Why we are providing
exactly this data, because we want on the backend
to join our current user. So our current socket connection
to the specific board. And this is enough for our
backend to react for it. Now, it is time to implement
it on the back-end side, this is where we must
open our servers source. And here we have
our server test. And here on the bottom we
have our own connection. This is totally fine. Now inside we can remove
this console log and we want to react with
our socket dot on, on some events and actually
hear what we're waiting. We're waiting Boards joint. But here we have exactly the
same problem where inside backend and we don't want to
write just plain strings. We want also to organize
them in the enum. And actually we will duplicate
exactly the whole file of our socket events in the
client and in the syrup. And you might say,
okay, this is useless. We can create just
a single file. You could, but potentially you can split your
client and server into different repositories and then you can share
anything between them. This is why I really want to isolate our client
and our back-end. So what we want to do
here inside our types, we can create exactly the same. It will be our socket
events don't enum dot ts. And now inside they
will could be placed exactly the same code like
we have on the client. What I want to do now here, instead of birds join, I can write socket events
in m dot boards churn. And as you can see
that this input is from types socket events. So this is how we subscribe to our event from the
client with socket, the tongue, but from where
we're getting our socket, this is our first
parameter here. As you can see, this is socket and here we have socket dot on, but here we must
provide two arguments. First of all, it is our event, and secondly it is our Kohlberg. So what we're getting here where typically get in some data, and this is exactly the data that we provided
from the client. We could write here some logic, but we don't want to, because actually we wrote
everything which is related to our boards
inside control it. And it makes a lot
of sense to write all our socket IO code
also inside controller. We don't really care if it is Socket Layer or justice GTP. We can isolate all this code
inside our controllers. This is where here we can use
our board's controller dot, for example, join board. This is what we
want to implement. And now inside first of all, I want to give higher than
socket and then data. And we will in every
single case when we're working with Socket Layer and give this
information inside. This is super similar to what we're doing with our Express. Here. We're using birth
control or create board. And actually this
is how we directly give inside request response. And next, why we're doing that. Actually, when we're
providing everything inside, we can use whatever we want. If we need a y'all, we can
use a yarn when it's cycling. We can use this information if we need some data
from the event, we can also use it. And now we just need to
implement our joint. This is why I want to jump
inside our controller sports. And here on the bottom we
must create a new function. Here we have expert
cones, Joan board, and we're getting
here, first of all, Iowa, which is a server. Actually we have here
lots of inverse. This is why you need
to be cautious, but we need here we must input
our server from socket IO. This is important. This is where we're
getting a yarn and it is our server as a type. After this, we have our socket. And socket is also
coming from socket IO. And last but not least
here is our data. And actually we know that inside we're providing board ID, which is a string, and this is enough
information for us. So this is how a
typical function inside controller will look like when we're working with Socket Layer. And now what we
want to do inside, we want to just write
socket dot join. Here we're providing
data dot board Id. And this is it, this
is just a single line. So what this code does, so Wed shown in this
current socket connection. So our current user
to this board ID, and this is just a
string, nothing more. But actually when we're
writing join and some foo, we simply tell socket to join current socket to this
room with this name, but don't have rooms
in our application, we have boards, but the
idea is exactly the same, where joining our users
to the board and then they will receive our messages
to this specific board. Now there's some who want to test that our code is working. This is why here I
just want to write console log server, socket join. And here I will write comma data dot board
AD in this case. But now, when we see
this console log, it means that the
successfully emitted our message from the
client or the backend. And our backend reacted to the message and joined
our user to this room. This is y here let's jump inside our backend and reload the page. And as you can see, we
don't have any errors. And actually our code with
this board component and this image will be triggered because our
component was rendered. And now we can jump
inside console and you can see service
circuit or your join. And here is the d of our board. This is where we're joining
our current socket. And as you can see
inside the browser, this is exactly the
d of our board. And now we want to do exactly the same stuff
with living and board. And actually what
they wanted to do, I want to create here
additional method, this initialize listeners and actually all my listeners
like subscriptions, for example, I always pack
in this additional method. This is the same name in like
for example, fetch data. In this case, they
don't pack a lot of information insight
in June in it. This is where I want to write, initialize listeners
and subscribe here for live in a page. And actually we can
do this if we're using a drought app and
we have here a drought, but not a router. This is way here we must use
router and it is our job. And now here we can write
subscription to live in a page. And here we can write this
dot router dot events. And here we have subscribed. And actually we subscribe, we will get here access to every single event which
is fired by a router. And what we want to
write here, if event, instance of here we
have navigation start. And navigation start is the starting of
change in our route. And actually if
this is happening, then here I want to console
log leaving a page. So once again, here we're using drought events to subscribe
to all drought events. And we're getting with
Subscribe access to every single event
now here to check for the specific event
where using instance of and we're providing
here navigation stack. And here is our
console log where actually we want to emit a socket message and this message will
be live in a board. Then we need to disconnect a user from this specific board. Let's check this out. I'm reloading the
page and we have a narrow I forgot to put a
comma here on the top. So let's put it now
and reload the page. And as you can see, we
don't have any errors, but we can't really jump to another page where they
can try to do here. I can just hit back
and we're leaving the page because here we're
now on slash boards page. And here is this console log, which actually means that
this console log will fire every single time
when we're living a page. But actually here I don't want to use sockets service summit. Instead I want to write here this board service
dot leaf board. And actually you
might ask, but why, why not just write in here
socket amid the answer here, it is not on LinkedIn
that we want to do inside of a board service
when we're leaving the page. And actually here inside
overboard service, we have the stream of the
board and we're also must set it to now when we left the
board, this is way here. Let's create our leaf
board method and we just have here
a void now inside. First of all, whoop, want to
set this board next to now, which actually means we
don't have a board anymore. And after this, we want to meet this message that
will left the board. This is why we must
open our shared types. And here is socket events. Here we have Boards churn. Now we can copy
paste this message and name it boards live. And this is just bored. Leave. Now I will copy paste this line. And now here we can use
our socket service. But for this VM must inject
it here inside constructor. So here we have our
socket service, and this is a socket
service and we're using it exactly like
we did previously. What I want to write here is this dots socket service dot m it and were provided
inside our event name. And in this case it will be our socket events E
and M dot boards live. And actually here we must provide what the
board we must live. This is y here we're
missing a board ID, which must be a string, and we must provided
from our component. Here I want to put board ID. Now we must update
our component. So here we have our
board component, and here inside live board, I will write this dot port ID. So now we just need to
implement exactly the same on the server that
we did with joining, but we need to use live. This is where it Let's
jump back in silos, servers, source, server it, yes. And here we have our
connection and ports giant. What I need to do now, I want to update the socket events in
them on the backend, and it will also be ports leave. And here I'm boards live. Now, here we can copy
paste this lines completely and register
one more socket on, not for boards churned, But for dot boards live
guessing he has some data. And here we want to
use ports controller, not join board, but leave board. And we must create now this live pod inside
our controller. And actually here it will
be exactly the same. This is why I will
copy paste this method completely and just name it live born here we're
getting our IR as previously socket as
previously end inside data, we have our board ID. Now here inside console log, we can write our yard to leave. And here is data board AD
and instead of socket joint, where right in here,
socket dark leaf, and with this single
line where we're moving our current socket from
this specific room, which means we won't get
any events of this board. Let's check if it's working. I'm reloading the page
and we're inside. Now here I am hidden bag and we must dispatch the section
for our back-end. Let's check this out
as you can see here. First of all, we
had here socket I adjoin and with join
this specific room. And then when I hit back, we have a leaf from this room. And this is exactly how
a client and begging are working together when
we're using socket IO.
30. Authentication in socket.io: In this video, we
must finish working on authentication now a
user insights socket. But first of all, we want
to fix one big problem. As you can see here, I'm logged in and I'm on the board page. But what will happen
if I will log out? So I simply want to jump inside application and remove our token and reload the page. I'm now directly
on the homepage. This is completely valid. But then at some point I
want to authenticate again. I'm hitting here login and then paste in here f2 at gmail.com. And our password, I'm
hitting here same mean. And as you can see, we
don't have any error, but after the same jump into firstborn and we're
getting an error, socket connection is not
established why it is happening. Actually, we have our
initialize socket connection inside our source app, app component ts, this code. So after page Lord, we're fetching current user and here we're setting up
socket connection, but we're not locked
in at the beginning. This is why we came here and
we set our current user now, then we successfully locked in. And just to remind you, this is what is happening, where inside our
components login. Here is our onsubmit here
where subscribing to the login and we're sitting here talking and current user, this is totally fine, but we didn't update our socket. So our socket layer connection
does not exist after registration or after login and don't live when we
will reload the page, this socket connection
will be established. Again, this is obviously
not correct and we must establish this connection after login or after registration. And this is extremely
easy to do. What we must do here, we can simply inject inside
our constructor new property, and it will be our
socket service, which is our socket service. Now here we can use this method. So for example,
after certain token, we can write these dots
socket service dot. And here we have setTab
socket connection here well that they have access to a current user and we just throw it inside our
setup socket connection. And we must do exactly the
same inside our registration. So here first of all, we're injecting our
socket service. So private socket service
is socket service. And now after this, we can use this method
inside our success. So after set token, it is these dots socket service, dot setup socket connection
with current user. In this case, it
will work properly. I want to remove are
talking once again, jumped or login page
and now try to login. So here is full at
gmail.com and our password, I'm hittin say mean, and then jump into first part. And we don't have at this
moment any error because we've successfully set up
our socket connection right after we locked in. But we don't do anything with our authentication
inside socket IO, and this is exactly
what we want to do. So what is happening inside
our setup socket connection? Here we're thrown inside
our current user token. This is totally fine. From the front and side, we did everything
that we need to. But now we must jump inside of a server and parse
it accordingly. For this, boom must jump
inside our server tiers. And here is your own connection. And actually before
our own connection, move on to write here, dot use. Here we will have our function and it will be our middleware. And after this, we have
our own connection, which actually means it
is exactly the same, like how our
middleware here move on to authenticate
our request and know that we are locked in because our socket connection is only available to
logged in user. This is where here we're writing IOUs and inside we must
provide the function. And here it will be an
asynchronous function just because we want to fetch our currentUser
from the database. And here we're first of
all getting our socket, which is socket,
and we're getting, and next function, because it is a synchronous
function we want to write here, try and catch. So first of all, we must
straight try and where, kitchen and error and what
we will happen inside catch. We simply want to write here next and throw
insight new era with, for example, our
authentication error. And here is a super
important thing that you must remember. We don't have any
errors inside socket. They are not there at all. We don't propagate any
errors back to the client, which actually means if we're getting some error, for example, on the backend,
we simply want to send a message to
the specific client. We can send a narrow we
just throw inside an object may be an error message with some error code or
whatever you prefer. And it is important to remember. So Socket Layer is not working
in exactly the same way, like how should it be errors? And now the first thing
that we want to do inside our try is to get our
talking from the request. And here we can
write socket dot, handshake, dot,
house, dot token. And it will be exactly
what we're past. And actually here we're
getting the token is any, and they don't want this, because actually here I
want to get a string. This is why here I can write round brackets and
just write S strain. And if we don't get this token, then I want to use
here an empty string. Why I'm doing this? Because here we want
to use Jason verify, to verify our token. And in this case we don't
want it to be undefined. It. We can verify it
when it is a string. This is way here. We can now try to parse are talking by using JSON web token. So here on the top we can import GPS from our JSON
Web Token package. Now we can go back to our
code in here, use it. So here we will have JWT dot, and here we have verified. And now inside we must pass
first of all, our target. But just to remind you, inside of a token, we have Bearer space
and then our token, this is where I
want to split it by space and take the
second argument. And after this, we must
provide here a secret. And just to remind
you of what the secret it is coming
from our config. And this is what we're using
to parse our JSON token. And this is what
we're getting back. We're getting here a
string or DVT PE lot. But here we really want to tell what we're getting back. Here. We're getting as always, our object, we say
D, which is string. And secondly, we're getting an e-mail which is also string. In this case, we're getting
correct data here and we can now get a user just like we did inside our Auth middleware. And for this we must input
a user here on the top. So we're using now a user model exactly
like we did previously. So we just use slash
models slash user. And now here we can
make a request. So here we want to
get back our user, and here we want to
make a request to the database with user dot. And here is our fine Bye. Here we can make find
by ID because we have NAD insight data dot ID. And if we don't have our usable, want to throw an error. So here, if we
didn't find a user, it means that we're
not locked in here. We simply run return
next and we're thrown inside new era
authentication error, just like we did
inside our catch. And after this, we want to write down this user
inside our socket. Actually socket is our instance, and we can write inside the additional
information just like we did previously with request. In this case, later in
every single controller, we can access this current user. This is the way here,
socket dot user equals user that we found. And as you can see here, we directly get an error
property user does not exist on type socket and we
must do exactly the same what we did
with SAML request. Just to remind you, we
created express request where we extended our request
to put user inside. And now we want
to do exactly the same but with our socket. This is way here. Let's create socket dot interface dot ds. And here inside we want to
create a new interface, and it is called socket. And here we want to use
extends from socket, socket. Actually here I have
quite a strange name because this is not a valid
input from the socket. This is way here.
I want to import socket as socket or your socket. And I'm doing this from socket or just because we can't
have exactly the same name. So here Gabon to
export our socket and here were important
socket, this is invalid. This is where we want to
rename our input from socket IO and after this way extended from our
socket, socket. And we want to add
here our user. And it is not always done this way here there's
a question mark, and this is a user document
which we must also import here on the top
from our user interface, Our socket interfaces there. Now we can jump back
inside our server and use it instead
of the simple. So just to remind
you, here we have an input socket from socket IO. This is not valid anymore. Here Woburn to invert
our socket from, and here is our
types slash socket. Let's check if it's working. I'm scrolling here
to the bottom, and we don't have here
an error anymore. Socket user is completely valid and it is either user
document or undefined it. Now let's check if
our code is working. So I'm jumping here inside the console and we don't have any errors, so it is working. Now what I want to do, I want to jump inside our
controller boards. In here we're making join board. So here instead
of data board AT, I can console log here a
user from mom, a socket. And actually here socket
dot user is obviously invalid because here we
didn't use correct interface. This is y here on the top. We don't need to use
socket from socket IO, but we must import our socket
interface that we just created from our types
slash socket interface. Here we don't have an error. Let's jump inside console
and check if it's working. And for this, we
simply need to reload this page and we
don't have any error. Now let's look
inside the console. This you can see there
is no message here, which means we forgot something. And what we forgot is inside
the sayo use to write next, which actually means
after this line, our code is not
getting anywhere. This is y here, but
must write next, and in this case it must
work. Let's check again. Here I am reloading the page. We'll jump into our backend
and as you can see, here is our information. So Socket Layer to join and
we're getting here our user, and here we are getting
two console logs because every single time when
you update the page, we're getting this socket IO to join because this
abuses happening. We're checking our user, we're getting it
from the database. Now, it is available for
us inside our controller. So with this code, we successfully got our
user from the request. And now in every single action inside controller
through a circuit, a yarn, we have this
user available fast.
31. Getting columns: In this video, we start with
implementing our columns. And I think it is a nice task that you try to
implement it yourself. So what do we want
to implement? A top? We have our board and
we're already quoted on our client and we implemented it on the backend
and on the client. Now we must implement columns. Why do we need columns? This really places inside of
a board where we will store tasks and our users can create
columns inside the board, which actually means column
must belong to the users. So we must have inside
use 3D but also board AT, because our column can't exist without a
board and this is y. Here are three level of
difficulties for you. First of all, if you want
the most difficult level, just pause the video now and implement on the backend
festival a model for the column, then at type for the column, and then a get method
to get a list of the columns inside our board if you want to try it
on the second level. With my guidance, here are
some tips as the set inside our services types must create just like
we did with board. We must create here column and we need
here an interface for the column and also an interface
for the column document. So it will be super
similar to our board. After this, inside our server, we want to create a new route. And here we will have
apt-get API boards, board AT just like
here, slash columns. And it will be a
GET request to get all the columns of
this specific port. And we really need this
quantity inside our route. In other case, we don't know for what board we must
get our columns. For this we must implement
in your controller, which is columns controller, and we must implement
inside the method, get columns, just like
we have here inside. Get bored. And they did, is that we want to
get all our columns by this point AD
from the database. So if you want to
try it yourself, just pause the video now. And now the easiest variant, Let's implement it together. And we're starting here. First of all, from our types. Here we want to
create a new type, which will be column
dot interface ab.js. And actually I want to
copy paste completely our board because it
will be super similar. So here we have our
interface and datas column. And what do we need
inside our column? First of all, we need some name, which means title is
completely valid, will also have here
created at and updated at. And we have here UserID
because it's the set. We might need information who created this specific column. And after this, we want to
have a reference to our board. This is where here
we have a board AT. And it is the same
schema types object ID, just like we have here
on the top for our user. And now here instead
of our board document, we want to create
our column document and we're extended here document and our column
interface that we just created. And now it is time
to create our model. So here we have Bohr model, now we need a column model. So here we have a board tears. Now we need to create
here column Ts. And actually I will copy
paste everything from our board because it
is super similar. So what we have here, we're building here
a column schema, and here we're using
NADH board document, but instead column document
that we just created here, board document on the
top we can remove. Here, we know that
we have our title, we have our use 3D,
it is required. And the last one here
will be our board ID, which is a type object ID, and it is also required, and it is our model of type column document
and the name is column. And here we have
our columns schema. So as you can see, it is
super similar to our board. And actually lots of models
that you will create in your future applications will be really looking like this. So we successfully created
our type and our model. Now, we must generate and use out inside our service tiers. And here I want to copy paste this line with a Pi
boards board Id. And here we have
a get on exactly this route slash columns
because as I said, we want to get our columns
for this specific point. And here we must
use our middleware because this request is
only for logged in users. And here we don't need to use our board's controller would
want new controller here, columns controller, and
here we can name it, for example, get columns, but don't have columns
controller tall. So here I want to copy paste board's controller
here on the top and just name it two columns controller From slash controllers
slash columns. And our last step here will be to create a new controller, which is called columns. And this is controller to manage all our actions for the columns. So here we're creating
our columns dot ts. And here I want just to
copy paste one method from our boards because it is super similar and it is good boards. So let's jump back inside our columns stairs and
paste this method here. First of all, it is not get
bored, but get columns. And we need here are expressed request interface because we want to check for current user. And here we need
our next function. And also we must input here
on response from express. And here as always, we're checking for currentUser. We're throwing for 01
if it is not there. But now here we must
use not bored model, but instead column the model. So let's input here on the top column model that
we just created from. And here we're writing
slash models slash column. And now here we're
waiting to make a request for column model find. But instead of
finding our columns, and here we will get
an array by user ID. We want to find them
by our board AD. In this case, we will get unlucky columns for
this specific board. And we get this information
from request dot, params dot, and here
we have board ID. So back here we're getting an
array of our columns and we simply want to respond to
the client with this array. And actually we are done. Let's check if it's working. I am jumping to the back-end. There are no errors. So now I want to
make a get request, and here I have our API, but instead of slash
api slash boards, I want to have here
slash and then NAD. This is why from
our application, I want to copy the d of our board to paste
here inside postman. We're making a GET request
and let's hit Send. As you can see, where
I get an a message where unauthorized, which means first of
all, I must login. This is my user, I am login and here I
am getting a token. Let's copy paste this
stalking and try once again. Here is a GET request for
slushy baseless point. Here is AD and they missed
here slash columns. This is a get request, and here is our
header. This is beer. And then I will put my token I'm hitting now send and
we're getting our answer, which is an empty array. And actually it is completely
correct because we didn't create any columns
inside our database. But to make it safe and clean, Let's create them
from the console so we can check if it's
really working for disabled jump inside
console and try docker exec minus 18 MongoDB, Mongo, just like we did
previously for our board. Now here we must use
our L Trello database. So useful trailer
and now I want to insert a record inside
our columns collection. This is why here we can write
db dot columns, dot insert, and here inside we want to
throw first of all at title, for example, first column. So here we must
provide a user ID, and here we must write
the date is an object with some string inside that we must take
from the request. And last here will
be our board AT, and it is also an object ID and inside the string.
So bore the D. We already know, we can
just take it here from the postman and they
will put it here. But our user AT, we can check inside our backend. As you can see
here, I still have this console log with our user. And here are the D of my user
with which I am locked in. Here inside our user ID, I will paste the
hash of my user. Let's hit Enter.
And as you can see where getting inserted one. Now we can check
if it is correct. So db dot columns, dot find and just
round brackets. And as you can see
here is our response. So let's check now in
Postman, if it is working, I'm hitting here send again, and we're getting inside
the array, a single object. Here is our AD Boyd
AD user ID and title, which actually means
we successfully created our column model and the first request to get all columns for
this specific bond.
32. Create column with websockets: In previous video, we
successfully created a method to get a list of
our columns for the board. In this video, we will focus on the back-end part of
creating of our column. And we will do it not in the typical way because
we won't use here HTTP. We will create it with socket by do we need to create
columns with socket? Because actually this is the
case where Woburn to notify all other people
who are looking on our current board about
creation of the new column. In this case, all users with this single open board will see directly our nucleated part. So how should we do that? And we're starting here
inside of a source server. They actually were already
know how we're doing things. We have here are
your own connection and here are our socket on. Here we must reflect on in
your socket. Why is that? Because actually
we will trigger, create enough the column
on one of the clients, which actually means inside
or Angular application. In the next video, we will build a frontend pod with the form to
create a column. And then we will emit an
event for our socket IO. This is why here we must
subscribe to this amid. Our first step will be to
create here a new event. And actually here I want to create three events while that, because we have an
asynchronous operation, we start creating a column, then we have success
and failure. And they said we can't really get errors from the socket IO. This is why it makes a
lot of sense to organize all our events as Start
success and failure. And in case with board join, we don't need it. We just need a single event
because essentially this is not in a synchronous operation with don't have success here, but we have it with
create another column. This is way here, Let's
name it columns grade. And this is a string, and we can name
it columns colon, and then create this. You can see I'm using the same notation like here on the top, we have an entity
and it is plural, and then our verb, what we're doing now
here we can copy paste this line two times because
we must create here, columns creates success and
columns create failure. In here on the right we
will have columns create success with camelCase and
here column create failure. So our events are ready and now we can open bag
our server tiers. And here we want to write
one more socket on. So here we want to react on now socket events in them don't. And here we have columns grade, and there's a second
parameter we're getting some data With don't even
care what this data. Now here inside we want to use our columns controlling and
we're also the created it, but we didn't have any
socket information inside. This is where here
we want to create a new method which is
called Create column. And we're passing inside
exactly the same what we did here inside live
board for example, it is a your coma socket
common data, in this case, in all places where writing the code and all parameters
in the same way. This is why it is easier to
understand our application. Now, it's time to create this section inside
the controller. This is where let's jump back inside our controllers columns. Here we have get columns. And actually let's write here from scratch our new method. So here we have our constant
and it is create column. And this is an asynchronous
function and inside where getting exactly all stuff
like we got previously. So first of all, it is
a yarn and it is serum. Secondly, we have
here our socket, the socket, and the
last one is data. So the question is, what data we will get
to create a column? And we need two things. First of all and
name of the column. And secondly, our board ID. This is the information
that we already wrote inside our model. This is why here we have
body D and it is a string. And secondly, we
have here title. It is also a string. And just to remind
you at the moment when you are our two
input in here, socket, you should know how to input it from types socket interface, not from the socket
IO because we must use a user property
inside that interface. And now inside we have
try-catch, just like always, because we're making here an asynchronous operation
and inside cage, what we want to do with
don't have access to next, but we can emit hear
something you, for example, here I want to write socket
Datta meet and we're thrown inside our socket
events E and M dot, and here is failure, so columns create failure. And secondly here I want
to give a message back. But the main problem is
that this sarah is unknown and this is completely normal
because we're inside catch, we can't know what the Sarah is. This is why if we need to read
a message from the error, we must try something like this. So here we want to read our
error message and we can check that our arrow must
be an instance of an era. In this case, we can
read here error message. And another case
we simply want to stringify our error here
as the second parameter, we can provide insight
our error message, which will be a string. In this case, this is the best possible parent
and say TypeScript. You can work with unknown
error inside catch. But here is the problem. We will write this
single line in every single action when
we're getting an error and we want to read a message
and we will use it inside every single
image inside catch. This is why I want to move
this code to help her. And we don't have
our helpers yet. This is why insights source. Let's create helpers dot ds
and back this method inside. Here I can create a new method which is get error message. And we're getting here our
error and it is unknown. So this is exactly what we're
getting in catch butt back. We want to get a string. Now, I can copy paste this
line fully and paste it here. Don't need here
constant a message. We can simply return
this single line. It, as you can see, this is how it looks like. We're getting here
error unknown, where chicken
instance of error and well-written either error
message or string era. And now here inside
our column stairs, we can import this method. This is why here we
can destructure, get error message and
it is from our helpers. And now here instead of this
line we can write here, get error message and were
provided inside our era. And now this helper
is super usable fast. Now we need to write
our code inside tribe. So first of all, we must
check if burlington or not. We're doing it only
for TypeScript because they actually
were locked in here because of the IOUs and our middleware inside
that we build previously. But here we must check if put don't have a socket dot user. And just to remind you of
this socket interface must be our interface and
not from socket ion. Here inside we can emit
exactly the same stuff. So here is socket and meat and were provided
inside of a socket. Events ENM columns
create failing. In here inside we can just write the string user is
not authorized. So what this line is doing, we're emitting a
message to our socket, which actually means we have a client and this
client sends a message, please create a column. And then either
here inside cage, here inside the CIF
will emit only to a single client that send us a message, this message back. And this client can listen for this message and do
something accordingly. And after we meet, we can simply call here a return with don't
want to do anything. But if we have a socket user, then we can create our column. So here let's try that we have a new column and to create it, we must use new column model. Actually column model world with the inverted
here on the top. So we can use it here. So to create a column, the model, we must
provide insight, first of all, a title and
it is data dot title. Secondly, we want
to provide here board ID and it is
theta dot board AT. This is the whole
information from our event. And last but not
least is our user ID. It is inflammation from
socket dot user dot ID. So here we successfully
created a new column number, must save it in the database. This is y here. Let's get back our saved column because we want to
get here NAD also. And we're waiting for our new column that
were created dot save. We're waiting to save
it to the database. And after this, we want to send this information not
just to our socket. We want to notify all people, all our clients who are
subscribed to our board. Which actually means we're
sending this message, not just vector socket, we're sending this message
to a lot of people. And we can write this code
with socket here like this. So it is not socket dot two and k inside we're
providing data dot point AD. And just to remind you
here before inside boards where we wrote join
code with John Boyd, wrote here, socket
join, data, port ID. Here. Now we have a room
with a d of our board. And now here we can
meet some data to all users who are inside
this room with I2. Here is our data board AT dot m meet and were provided inside our
socket events ENM, and in this case, it will be success. This is y column
creates success. Here we want also to provide the whole information
of our saved column. And actually this is the
same like using HTTP, but this information will be
propagated to all our users simultaneously if
they're subscribed to this specific board. Now here without any
difficult client logic, I want to test that
it is working at all. This is y here, for
example, after our i2, I want to write
console log saved column so we can check
if we're coming here. Now after this, I
want to jump inside our clients source, app, board, components, board HTML in here, I just want for testing to
create a button was clicked. So we will click on the button, and here we have
a test function. Now, when we're
clicking on the button, I want to meet this event. This is way here we have our test function and
inside I want to write this socket service dot and meat and were provided
inside our event. And in our case it is
columns create and there's a second parameter
we must provide here an object
with pour the tea, which will be this board ID. And secondly,
title, for example, for this single line, must tell our backend
that we want to create this specific
column for this part. Now we can jump to the backend and check
if it is working. As you can see, we don't
have any errors in API. Now let's open a browser
and reload the page. And we're here on our
single board page. In here there's a button. And actually I forgot to write the text
inside the button. So let's write here
test so we can at least see a button and then
clicking on the button once. Now let's jump inside, beckoned. And actually here we're getting the last message saved column, which actually means our
backend got our request. And it is happening in
our service tiers with this code where we're
subscribed to column grade. And then side where Colin, our columns controller create column and inside this function, first of all, we're checking
that we're locked in. After this, we're generating a new column with this data
that we got from the event. Then we saved this
column and we send the message to all our users which are subscribed
to this point. We will test this code
deeply in the next video. But as you can see here is my constant locked saved column. And this is exactly what we're seeing here inside console log, which actually means we
successfully implemented our working between socket on the client and socket
on the backend. And regarding creating
of our columns.
33. Getting columns: In previous video, we successfully prepare
the noun backend, create an off our column. But before we will implement inline form to create a
column on the client, we must at least create our
boards page because for now it is completely empty
and also render our columns. This is why the first
thing that I want to do is create on our client the
interface for the cone, because we don't have it yet. This is why we must
champion sets, source, app shared types. And here we must create
column interface Ts. And let's expert here
our new interface, which is a column interface. And the inside, first of all, we're getting NAD,
which is a string. Secondly, at title,
which is a string. And lastly created it,
which is a string. And actually also we're
getting here updated head, which is also a string. Our next step is to
create a service with the method to get all columns
for this specific board. And actually here we
don't have such service. We have just Boards service, which is a stateless service just with method
like getting boards, get bored, create
board and so on. We must do exactly the same. But for columns, this
is why here I want to create columns, service dot ts. And as you can see where in AP shared and not in the board. Now here, first of all, biomass trait injectable and second loop one to
export our class, which will be our
columns service. Now inside we must introduce
you to the client because we will make requests to get
a list of our columns. This is way here. First of all, we must
write a constructor. And here we're get an HTTP, which is an HTTP client. This is enough. Nobu must create
get columns method, which will deliver for us
in the way of columns from the backend and on the backend we're already
created that method. This is where here,
first of all, we must get here a board ID. This is a unique
identifier to get a list of columns for
this specific board. And back we're getting
an array of our columns, obviously as an observable, because it's always from HTTP
where getting observables, this is where observable of column interface and don't
forget it is an array. Now here inside
we're doing exactly the same like we did in board. So if you don't want
to retype everything, you can simply copy paste, for example, get bored, and you are good to go. The code is super similar. So here first of all, we want to create a URL, and here we want to
merge first of all, our environment
DOD API URL slash. Here we have our boards
slash n. Here we want to inject board
ID slash columns. And after this, we just need
to make an HTTP GET request. This is why here I want
to return this http.get and we must provide
that back where getting our column
interface array. And here inside we're
providing our URL. And with this code we will get our columns for specific board. And now it's time to use the service inside
our component. But for this, we
must jump inside our app board, board molecule. And the input here
inside providers are in your service and it will
be our columns service. Now here we can jump
inside components, board, board component, and
fetch this data here. Now here inside our fetch data, we want to fetch our columns. But for this inside constructor, we must inject this service. And it was columns service, which is columns service
that we just created. Now here we can jump
inside our fetch data and use here this.com
service get columns. This is exactly the method
that we just created. And here inside we're
providing now at this point, we already have
this information. But here we want to
do exactly the same, but don't want to save this columns directly
here in the component. We're already created our
board service for this. Inside, we can save
all this information. This is why here we can write, Subscribe and we're
getting back our columns. And what we want to do with columns we need here
in your method, this board service, DOD, for example, set columns. And inside we will throw
our list of columns. And now we just need to implement this
method set columns. This is where here
Let's open inside Boards Services,
our board service. And we're doing exactly
the same like we did here. We said board. So
here is set columns. We're getting our
array of columns, which is a column
interface array. Here we're getting big void and we need here a new
stream of data. This is where here I can copy, paste our boss stream and
name it column stream. In this case, every single
place of our application, if we are using this board, can subscribe and get the list of the columns
that we have now. And actually this
will be a behavior subject of column
interface array. And we don't need to
provide here now because by default we can simply
provide here an empty array. And now inside our
set columns method, we can simply call this
dot columns dot next. And we're providing insight
are columns that prefetched. Now let's jump back to our component and
dead this stream. This is why here on the top, we already have this board, dollar, which is a stream. And now we must create columns. Dollar, which is a
string for our columns, and it is an observable for
column interface array. And now here we can
write directly after this point, this dot columns. And here we're using
this board service dot. And here we have our columns. We don't need here pipe filter Boolean because this
is simply an array. It is either empty or older, the field with our columns, the main idea is that
we have no streams and we can use them
directly inside the HTML. But here I want to show
you even better variant, because actually here we
already have two streams later. We will have one more for tasks, which actually means we have three different sources
of data and we want to render our page when all the sources of
data are fulfilled. This is why we can rewrite this code a little
bit differently. We can write here this
dot data with taller. And this is a special
notation which I am using. This is a super popular variant, how you can manage your
data for your component. Typically you have a page
with a lot of sources of data and you want to combine
them in a single stream. And then you can
run the whole page only when you fulfill
all the streams. And we will fulfill
the stream with the data when we're
getting you in your data. This is where I want to write combined latest and
were provided inside an array of streams and
festival here we can copy this line with boards stream
and just paste it here. And after this we're
taking this port service columns and we're
also paste it here. Obviously we must
straight here comma, because these are two
different streams. The main idea is that we're
combining the streams. And here we want to do
this with pipe and map, because actually we
are getting this as an array and die want an
object here, this is y. Here inside map, we can
directly the structure, first of all, a board and
here order is important. Secondly, we have here columns. And after this,
we want to return the data in another
format as an object. And here we're
returning first of all aboard and secondly,
our columns. So what this thing is
doing for us at all. So we are getting here data, which actually means we
don't need two streams here. We can now create here
and use stream data. And this is the whole data
for our component board. Now we're saying
here that this is an observable of the object. And first of all, we
have here a board, which is a board interface. And secondly, we're
getting here our columns, which is a column
interface array. And now we can remove board
dollar and columns dollar, we don't need them anymore. The main point is that our
logic was fetching data and set in this data inside word
service is staying there. This is just a combination of two streams to map this
data for our component. So now the question
is how we will use this data inside our HTML. This is where I want
to remove everything and actually also remove
here test on the bottom. We don't need it at all. Now I want to write
marker for our page. First of all, here we want
to render our app top bar. It is already there. After this, let's run the board. So here we will have
div class board, and here is a trick
we're using here in GE, but we are providing
insight data dollar, this is our combined latest
sync as data, and this is it. The main idea is that we're not working with streams
inside anymore. We're working with data
as a local property. And this will create for us a local property just
inside this div. Now here, first of all, we want to create our
board header container. This is where here div class
board header container. And let's close this div. But we will run the
late Insight is an inline form for
updating our board. This is where here I will
simply write inline form bond, so we won't forget about it. After this, we will render our
div with the class columns and inside I want to make and G for loop for every
single column. This is y here, class
column and then G4. And here we can write lead
column of data dot columns. As you can see here
with don't need any async pipes because we're resolving our data property and we're getting
this data inside, and this data will
be automatically updated if the stream of
columns is also updated. Here, let's close our column
and then side we want to render our div
class column title. Now inside we will
have one more fun. This is where here in line
form for updating column. And after this column we will
have one more in line form. So here we will have inline
form for creating a column. So this is our basic markup. Actually, we didn't
render a lot here, but at least we're
rendering here at columns, which we're getting
from our data. Let's check if it's working. I'm jumping to the console
and we're getting an error. Epitope is not available for us, so we must import it
inside our board module. So here inside the
inputs we can simply add top bar module. As you can see now I
don't have any errors, so let's reload the page. Actually it is already there. We can see our nice top bar. Here are some placeholders and actually these are
already our columns. And yes, we didn't trend at
the title just for testing, we can do it really fast. Let's jump back
inside our board, and here will be the
title for our column. So instead of inline form
for updating column, we can simply render
here column dot title. I'm reloading the page in here, we're getting first
column and form. This is exactly
these two columns that were already created. First of all, inside
console of MongoDB. And secondly in the
last lecture where we created with the
event, our new column. And they highly recommend for
you to use combined latest to combine data like this
in all your applications. This is extremely
efficient and scalable.
34. Create column form: In previous videos, we
successful event ID columns for our board and now we can continue with Create
form for our column. And just to remind
you, on the back-end, we already implemented
the whole logic to create a column by
using socket a yarn, and now we just need to
implement it on the client. And first of all, I want
to start with some HTML. And actually here we're already prepared inline form
for creating column. This is exactly
where we want to use our inland firm and
we're already have it. We just need to provide
insight correct data. Here let's close our inland
form instead of the message. And now what do we
need to give here? First of all, we
must set a class. And just to remind you, this is a parent class
with styles that we're overriding for our inline form for this specific case here, our class will be
create column form. After this, we want to
set a default text. This is what we will
see by default, and it is the text, at least also here, we must render a button
to submit this form. This is why here we can write that we want his
button property. And it is true after this, we want to provide
our button text, and this text will be at least. So here we want to change
our default placeholder. This is why here we can
write input place holder, and here we can write
add column name. And last but not least is of
course here handle submit. And this is a callback
where we will get a title of our column
that we want to create. So here I want to add new
method which is called Create column and then site where getting our title through event. And the last thing
that we need to do, we need to create this grade column function
inside our board. Here we will get a title as a strain from our inland forum. And here is void inside and they want just to console log here, create column and see a title. Let's reload the page
and check how it works. And actually here we're
getting a message. Inline form is not known
element of our module. This is where we must jump
back inside our board module. And here inside the inputs, we want to input our
inline form module. As you can see in browser
now we have a new button. At least we can click
on this button. And we see now our
input which is styled and our atleast button. And actually if you're wondering from where all the
styles are coming, Just as he told you previously, we style all inline
form elements with our parent and dynamic case here it was create column form. This is where here you
can see the styles create column form and then inline
form container editing. So this is why our
form is so shareable. And now let's check that
we can add a new column. Here I'm typing something, I'm hitting at least. And inside the
console we can see our credit column and
our title of the column, which actually means
now here we can emit an event for our socket, for our back-end, but I
really want to do it better. I want to specify our input. This is why here in
such shared types, we have board and column, but I want to create an input. What does it mean? Just like we had inside our owls types, we had here login request
and registered request, which is essentially a
body of our request. Typically we want
either to name it request or maybe you
want a word input. This is way here
in shared types, we can create our column input or you can also name
it column request. So here let's say that this
is column input interface Ts, and this is the data
that we must provide for the back-end in order
to send our event. And here we want to specify
it inside an interface. So in the whole application will know how we create your column. This is way here,
Let's name it Column input interface and
the main differences, it is not calmly
interface, it is input. This is why it is a body. And here we have our
title, which is a string. This is what we must
provide for our back-end. And secondly, a board ID, which is also string. And now we can use this input interface inside
our board component. So here instead of console log, we can create our column input and we know that
the type is here, column input and here now we're must provide
all possible values. In our case it is just title. And secondly,
board, AD and body. Do we have inside
these dot port ID? And as you can see here,
we have a correct type, and now we must throw it
inside our socket IO. This is way here, this column
service dot create column. And actually here we don't
have create column method. This is why we must create it. I want to jump inside our
shared services columns. And here we have
just get column. Here we want to
make create cone. But this method won't do
anything with this GTP. It will use our socket IO, and this is completely normal, whereas the late, all
this methods inside our services and we
simply call our services. This is way here,
Let's make create column method and we're
getting here our column input. This is really nice that were
created an interface here. This is where we can
reuse it and it will be our column input interface
and back we will get void. And just to remind
you here with HTTP, always get back an observable. But in socket layer, it won't work like this. With socket, you have
a single flow of data. This is where here the
only thing that we want to do is trigger our emit. But for this, we must inject
here our socket service, and this is our socket
service that we created and we can now use
it inside this method. So this dot, socket
service dot and meet, and we must provide here a name. So let's check first, if we have such name
inside our shared types, we have socket events, and here we don't have anything. And actually we can
open inside back-end this enum because inside types but also have socket events. And here we have column create, success and failure, and
it is exactly the same. This is why I will copy
paste it completely. And now we can use it here
instead of event name, we can write here socket
events ENM don't end here. We want to trigger column grade and then
save it as a parameter. We want to give
our column input. It gives us one more
benefit to write our code with socket
inside the service. We isolate this code
inside of a service. This is why our
component doesn't know that we're using here
socket layer for example, we simply call here column
service dot grade column, and we're given inside
our column input. Let's check if
it's working here, we can try to create
a new column. I'm hitting Add. And now inside our beckoned, we can see saved column. And after reload the page, you can see here
are created cone, which essentially
means it is working, but we don't update
our page on the fly. And obviously we want
to update this page, first of all to
our current user, secondly to all
other users also. But for this, we must
improve our socket service. Why is that? Because actually what we
need to do inside fronted, we must subscribe in a
comfortable way to our success. And just to remind you, inside our backend here
inside controller columns, we have a meet and actually these are your two
amid will emit this message to
all users who are subscribed to this
specific room. This is why what we need to do. We want to subscribe to
this specific event, which will be success
of creating column, but we want to do
it inefficient way. And then said socket layer, it is not efficient. We want to do it in Angular way. This is why we must open inside shared our service,
socket service. And here I want to
create after meet one more method and it
is called Listen here. First of all, I want
to write t inside. And if you don't know
how to use generics, The main idea here is
that we can provide whatever datatype for
bond inside listen, and it will be what we will
get back from the socket IO. So here we're providing t and here event name will be string. And back I want to
get observable of t. And this is
essentially the idea. This method will give
us back an observable. It is not comfortable to work with Socket
Layer inside angular. This is why we want to convert
it to observable here. First of all, good
one to check if we have socket IO or not. This is why I can copy
this if condition here, because if we don't have
socket and you are connected, then we should not do anything. After this. I want to return new observable. And here inside we must
provide a function. And here we have just
a single argument which is a subscriber. And now inside we can dry it. Socket on. This is just a plain
code of socket ion. Here we have our event name, which means we are
subscribing on the client to this event name that we
passed as a parameter. Here is our second
parameter here we're getting some data and
what we want to do, we want to emit our subscriber
dot next with the data. So what this code
is doing at all? First of all, here we
must try this and this, you can see we're
getting an error. Object is possibly undefined it. And actually this is a TypeScript problem
because here he can't understand that this code
with Eve covers undefined it. What we can do instead, we can write here con
socket equals the socket. And after this we're
covering this socket here. And then instead of this
circuit where using just socket and in this case when not get it in
TypeScript theorem. So what is the idea
of this function? We will use it like this. So here is listen. Then we're providing
some datatype, for example, a string. And after this here withdraw
and event, for example, column create or column
creates success. In our case, this single line, it will give us
back an observable. This is why after this we
can write, for example, subscribe and here, and we
will get here our data. You will see how we will use
this function in a second. The main idea is that this function will create
fast and observable. We will subscribe with Socket Layer to this
specific event name. And when this event
will be triggered, we will simply update
our observable. This is really
comfortable angular way to do things with Socket Layer. Now let's try to
use this function. I want to jump back and say
that our board component, and here we have our
initialized listeners. And here is a really nice
place to listen to the events. This is why here I want to write this dot socket
service dot lesson. And here we must provide the type because
it is mandatory. And here we won't provide that. We're getting back
our column interface. This is what we are getting
after creating of the column. And here is our
event name and we want to subscribe here
to Socrative ends in a dot column creates success because we're
waiting for the success. And here, after
this we can write, just subscribe and we're
getting inside here a column. And here we can just
console log this column. So once again, what
this line is doing, it creates for us a
new observable and we can simply use it
as an observable. Most importantly, inside
where right and socket on. So we're waiting for our emit. Let's check now if
it's working here, I want to create a new column
and I'm hitting at least. And Viola, as you can
see inside the console, we're getting our column. And actually this is this
code with the success event. And here we're getting exactly our column which has saved to database because as
you can see here is an 80 of our saved column. And now we can do
something on the client to update the loop is
outpatient a lot our board, but it is not tall
as you can see here. I opened the same tab
for the same user. But the difference is
that socket here is different because
every single time when we're opening a new tab, but establishing
a new connection with another socket ion. This is why when here we
will try to add a list. And I'm just hitting here, at least we are getting here
this column console log. But actually here we also get this column console log because these are
two separate tabs, are two separate users, which actually means if two different clients will be connected and open this board, they will receive simultaneously our event regarding
creation of new column. And this is essentially the core functionality that we want to achieve
inside socket ion. Now we must somehow change
a list of our columns. And actually this is way here. I want to write this
dot boards service and be careful here, not poor, it's not our stateless
service with functions, but our board service
for the client. In here we must create a new method which is
called Add Column. And we can simply throw
inside our column, which essentially
means our component doesn't know anything
about business logic, how to work with columns on
our board service knows it. This is why here we must jump inside our board service and we can create here a new function which
is called ABC column. And here we're getting
on your column of type, column interface and
back we're getting void because we don't
care about return. We simply need to
update our stream. And just to remind you
here on the top we have a stream of our columns. And now here we need to
update this somehow. This is why what we can
write here we want, first of all, to
update our columns. This is y. Let's create here
property updated columns. Here we need first of all, to spread what we have now. And this is this column stream. And here we have a
function getValue, and it will just
read the value from our stream that we
currently have. And at the end we must
write on your column. So this will be our new
updated columns array. As you can see, the
type is correct. This is array of
column and interface, and now we can just update
it with these columns next. And here we're throwing
in site updated columns. So we simply create
in your array and we'll write it
inside our stream. Let's check if it's working. I'm reloading the page. Here at the end, we have at
least button and actually it looks kind of broken
because it is zoomed out. Now here let's
create a new column. I'm hitting edit list. And as you can see
directly here, we see our new column, which actually means this code updates our stream and
then our component is subscribed to the stream here on the top with this
combined latest, these columns is being updated. And then we'll resend this
component automatically. Once again, how
this all working? First of all, we are getting
here our inland firm. And this inland firm produces
for us just the title. After this, we'll create
this object with title and Boyd AD and throw it
inside our column service. Here is our credit column. Here we're meeting our event and we're throwing
inside our data after this back-end receives
this event creates for us the column and the means
to all users in this room, in this board, the message that we successfully
created our column. And after this, every
single client is subscribed with this code just from the
beginning to this column. Great success. And when it happens, we're getting here,
our credit column, and we're colon here, but service to add a column. And this essentially will update a stream
with the columns. And our component will be
rear-ended on all our clients.
35. Creating a basic task: In previous video,
we successfully finished with
creating our columns. In this video, we're
starting to work on tasks. And actually we must implement
the back-end part of creating the tasks and getting a list of
tasks for our board. And actually think that
it is a nice possibility for you to try and
implement it yourself. Because actually you already did small fissures
inside this project. And now you are ready
to do something bigger. And actually getting a
list of tasks and creating a task is really
similar to our columns, which means you can use the
whole logic from there. So what do you need to do if you want to implement
it yourself? First of all, here we're
talking about beckoned. So we must create a new
interface for our task, then the model controller
method to get this tasks for the boys and method insert control
it to create a task. The question is on, what fields do we need inside our task? And actually here, I just want to write what fields we need. First of all, it is a title of our task description
which is not mandatory. It can be empty, then use 3D so we know who
created this task column ID. So we know in which
column of our board we must trend this task and
the additional aboard AT. In this case, it will
be easier for us to get all our tasks by
specific board ID. The back-end, you need
an interface and model, then register in your API to get this tasks
inside the board. And the method to get this
list of tasks by fallen board. Also, you must create a new create method also
inside our controller. And it will be a method
with socket yacht because we want to
notify all of our users who are subscribed to this specific board
regarding this new task. And if you want to
proceed by yourself, you can just pause the
video now and try it. But if you just want
to follow along, Let's do it together. And there's all this said. It will be super
similar to our columns. This is why I will
copy paste a lot. First of all, here, I
want to jump inside our server source types. And here we'll have our
column interface test. And actually we can
copy paste it fully and just create our
Task interface Ts. Here inside the muscular it
exactly fields like the plan. First of all, we have our
title created at updated it. We have always, here is our user ID and board AT
this is totally fine. But also we need here to
create a column lady. In this case when no, wherever mastering that
the specific task. And also we need
here a description, but it is not mandatory. This is why I put
here a question mark. So our interface
is looking fine. Now let's change the document we need to create
here task document, which is extending document
and our task that we created. And as you can see
here, I forgot to rename our column to the task. Now let's jump and
create our model. Here inside models were
already have column. I will also copy, paste everything and just put
it in our new file, which is task dot ds. Here we must change everything. Festival Here we have another column schema,
but task schema. And here not column document, but our task document that we just created here on the top, I can safely remove
column document. Here we have our title. It is required. This is totally fine. After this, I will paste title again and rename
it to description, but it is optional. This is where required
through is not needed here. We also have here our user ID, board Id, and here I want
to put new column ID. And it is also
required for sure. And type is the object ID. It is looking totally fine. Now on the bottom, we must provide here our task document. And here it is a task, which is a task schema. Our model is completely ready. Now we must create a new
API inside our server. This is y. Let's jump back inside
Service source server tiers. Here we have our API calls. And actually our
new API call will be super similar to this code. Here we're getting columns
by specific board Id. Actually we want to get all
our tasks for specific port. This is why I will copy, paste this line, paste it
here, and just rename. It will be slushy
pie slash boards. Board is dynamic parameter, and here we have our tasks and here we'll also
need our middleware. And here we need
a new controller, which will be tasks controller, because inside we want
to manage our tasks. And here the method
will get tasks, and this method must return all tasks for this
specific board. Now it is time to
create a controller. This is why I will jump
and said controllers. And I want to copy paste
the whole file columns just because it is easier to change and it will
be super similar. So I'm pasting it here and
renaming two tasks, dot ds. Here inside we have two methods, create column and get column. And actually here we
can simply rename get columns to get tasks. And here we're
leaving everything as it is, request response. Next, this is totally fine. Chicken for the
user is fine here. Reschedule, everything
is awesome. Now here we don't need
to use column model, but instead we need to
use here task model. This is where here on the
top I will input task model. From models task. Now here we can change our
column model to task model. Here we're doing find by border different request
parameters, board Id, and this is exactly what
we need and this is why we have body inside our schema. In this case, with just
a single line of work finding all of our tasks
for this specific board. So here back we're getting
our list of tasks, and here we can simply
send them back to our API. And with that, our action to get the list of tasks
is fully ready. Now let's change the
directly this create column. And as you can see here, we're talking about socket IO, and it will be also
super similar. First of all, we have
here a method create task here where getting
your socket and data. So what do we need
to get inside data? First of all, we are
getting bored or ED, because we must notify all users inside
this specific board. This is totally fine. Now we have here title string, and the last one
here is column id. And column id is also string
and duties mandatory. And you can say
here that we don't provide a description inside. Your total is right
in create, recount, change description, description we can provide
only inside Update. This is why this
is totally fine. So what we're doing inside here, we first of all want to have a failure for our tasks create. But for this, we must first
update our socket events. This is why I will jump
in such source types. And here we have
our socket events, and here we already
have columns grade and they want to copy
paste all three actions. Here we can rename it from
columns to tasks create, and here it is tasks
create, then Tasks, great success and tasks create, failing our socket
events is ready. We can jump back
inside our tasks and change here socket
events in them to dot tasks create paler and they won't directly
to change here our catch, and here we can just provide
dot tasks create failure. Now let's update an inner part here we have not column model, but task model, we're
already inputted. Here, we're getting
titled from the data. This is fine. Board Id. And here user from
socket is ready. This is all totally fine, but we must also provide here alkalinity from data
dot column may be. And after this here we're saving not new column, but new task. Here. Let's rename
it to new task. And here we can
simply call new task dot save and we're
getting back saved task. Now with this line,
we want to notify all our users that we
created a new task. This is way here we're
emitting this event and do these tasks
creates success. And here we want to provide
back the save task. And here we can remove
this console log. We don't need it
anymore and we're fully created our Create Task method. And the last thing
that we must do, we must subscribe to start of creating the
task from the client. This is why we must jump
back inside our sorority S. And here on the bottom
we have the socket on. Actually we can
copy paste columns Create and just change
it to our socket, even CNN dot tasks create. And here we're getting
our data and we simply need to call our tasks
controller create column. And here you will not be created
column, but create task. And as you can see
here with didn't import our tasks controlling. This is why on the top, I want to copy paste
columns controller and rename it to our new file, which is tasks controller. From controllers tasks, it looks like we
implemented everything. Now let's jump inside console. As you can see, we
don't have any errors were getting
connected to MongoDB. Api is listening. This is why actually I just
want to copy paste the board Id open Postman here and try
to get our list of tasks. So it is slash
sports than a paste this ID slash tasks and
them hidden here send. And as you can see back, I am getting an empty array, which is totally fine because we didn't create any tasks yet, which actually means our
backend is fully prepared. And now we can start with
implementing client side.
36. Getting tasks: In previous video, we
successfully finished preparing our tasks models and getting
tasks on the backend. In this video, we must do
exactly the same on the client. And I highly recommend
you to also do it by yourself because you already
have so much knowledge. What do you need to do if you
want to do it by yourself? First of all, you
need to jump and said clients source, app shared. And here we have our types, we have here Boyd and column, and now we must create a
new interface for our task. After this, we must create a new service to
work with tasks, whether they have
boards, columns. Now, we need a new server
which is called task service. Here boop will get tasks
in exactly the same way, like we're getting here
columns for our board, but it is not all. You also must update insider board molecule in such services, board
service tests. Because here we're talking about stream for board and
stream for columns. And we must additionally
create here stream for our tasks and
additionally function which will update the stream. And after this, you must
simply fetch data here inside this combined
latest. And this is it. If you will implement this spot on your own, you are awesome. But if you want to
implement it together, Let's do this now. So first of all, I will jump in such clients source,
app, shared types. And here I want to
create a new type for our Task interface Ts. And we must have exactly
the same properties like we had on our back-end. This is why here we can
export our new interface and it is Task
interface, the inside. First of all, we have our ID, which is a string. After this we have our
title, which is a string. We'll also have
here description, but it is not mandatory. This is why Here
is question mark. It is also a string. We also need here
our column they D, which is the reference for the specific column, our parent. And we need here our board ID, and it is also a string. And actually we're
getting here usury t, which is also a string. So our task interface
is completely ready. Now we can jump
back inside shared services and create
here in your service. And actually what I want to do, I want to copy paste
columns service just because it will
be super similar. Let's rename columns
service to Tasks, service Ts, and As you can see here is MenuService
task service. What we want to do here, we want to change the name. So here we have tasks, service where living
constructor as it is, we must use here http and
latest. So good service. And here is a method not
get columns, but get tasks. And here we want to get
our tasks by board AT, because we're fetching just single time all our
tasks for specific board. And here we're getting Becker observable of Task
interface array. And here is our URL. It is slash slash
board ID slash tasks. Here is the HTTP GET not
column interface surgery but Task interface array. For now I want to remove
credit column completely because we won't implement
create task in this video. This is why I can remove these
three inputs on the top. We don't need them for now. Now it is time to update
our board service. This is why I'm going to add Board Services, Board service. And here we must
create a new string. I will copy paste
here column stream, and change it to Task Stream. And this is a behavior a subject
of Task interface array. And by default here we
have an empty array, which is totally fine fast. Now here we have a method, send boards at columns. We must create a new method
which is called set tasks. And here's an argument. We're getting our tasks
and it is an array, so it is Task interface array. And now here inside our
stream, tasks with dollar, we want to set our new tasks
so we're almost finished. Now biomass jump inside of our component, board,
board component. And here is our combined latest. And just to remind you, here in say data, we're combining all our streams to fetch data for the board. This is what we must do here. Now, we must add here
this board service, and here is in use stream tasks. And now inside map as
the third argument, we must try to tasks and we
want to return them here. In this case here
inside our data dollar, we will have not only board
and columns, but also tasks. And if you did everything
here by yourself, you are really awesome and
you're making good progress. And now the only thing
that we must do here, we must render them
inside the HTML. So let's open our
board components HTML. And what we have here
is our column for loop. And inside we have a title and we don't have anything else. And after this title, we want to render one more deep. And here we need. And, and G for loop for
our tasks inside column. And actually we can't
do it because we have just this stream for
our tasks inside data, but we don't have tasks for this specific column and we must somehow filter this data. This is why here I
want to write in G4 and it will be led task of, and here I want to
create a new function. For example, get
tasks by column. And then side we must
provide first of all column id and secondly, all our tasks, it
is data dot tasks. And just to remind you
here inside data stream, where we get in our
board columns and tasks. This is way here inside this
function as an argument, we can simply throw
data dot tasks, often G4, we must also
right here a class, it will be task. And here inside we want
to render task dot title. And now we just need to create this kid tasks by
column function. This is y here inside OTS file. I will create this
function in here. We, first of all, I
get in column id, which is our string. And secondly, tasks, and it
is a Task interface array. And here back we're getting our Task interface
array. Why is that? Because essentially we
simply want to get our array with all tasks and filter by
this specific columnated. This is way here, Task interface
array is totally fine. And what we want to do inside, we want to filter our
tasks that were God, this is why tasks filter will get an access to
every single task. And here we can
check task dot colon equals our column name that
we provided as an argument, which actually means
we're colin for every single column
that we're rendering, this function gets
tasks by column and we will render here in energy
for loop our tasks. Let's check the sound.
Do we have any errors? Yes, we have property tasks does not exist on
type board columns, which essentially
means we didn't update our data property. And as you can see
here on the top, we're defining that
inside our observable, we have just bought and columns. But here we also have our tasks, which is a Task interface array. Now here we don't have any
errors inside our web server and we can jump to page and
we see all our columns, but we don't see any tasks
inside why it is happening, because actually we
didn't call our fetch. And actually here
inside our constructor, we must inject our task
service that we just created. And it is our tasks
service which is shared. And now we must jump
inside our fetch data, just like we did here. We can copy, paste
our columns service and rename it to
our tasks service. And here won't be get columns, but get tasks and the inside
worth throwing our board ID, which is this part AD and back, we're getting not
columns, but tasks. And here we want to
call this board service not set columns, but set tasks. And inside we're throwing
at tasks that we fetched. This is why with this code, we updated our stream and the stream will get new
data inside our HTML. Let's check this
out. I will reload the page and we're
getting an error. As you can see now
we're on slash boards. And when I'm jumping
to first part, we're getting an error
that we don't have provided for tasks service
because actually yes, we must inject it inside
our board module. This is where here
inside providers, we must add additionally
our tasks service. Let's reload the page
and as you can see now, we don't have any error. And here inside network, we must have a request
for our tasks. And here is a request slushy pie slash boards
slash ID slash tasks. And here is preview with
don't have any tasks, which is totally fine. We didn't create them yet, but obviously we want to check
that our code is working. This is why what I want
to do inside console. I want to open our Mongo, just like we did previously. Now, I need to select our
database so we use L trailer. And here we can
check what we have. For example here
now we can write db dot boards, dot find. And with this we will see
all boards that we have. We just have a single board. And in the same way
we can write db dot, columns, dot find, and
where I get in our columns. And what we want to do now, we want to insert in your task inside one
of these columns. This is why here we can write db dot and we have tasks dot, insert, and inside we're
throwing an object. So the first question, what do we need to
provide insight? And for this, we can
check our Task interface. So first of all, id will
be outer generated, we must provide a title. So here is title. For example, my first task after this promise
provide a description. So let's say description
is my description. And after this, we must
provide the column needy. And actually here on the top, I already see columns. This is where I can
just take an AD or FirstColumn and they
will throw it here. So it will be column id. And here is this AD. And after this, I also must
provide a board AD Here. We'll also have a board AD here on the top and we can take it. And last but not least is though a user AT and actually uses 3D. I don't see here on the top, but I can take it
from the back-end. And as you can see here
inside our backend, we still have a console log with email of our user and object ID. This is why I will
just copy paste the idea of how a user
and write it here. So use re d equals
this object ID. I'm hitting Enter, and
here is our first record. Let's create one
more, for example, my second task, and
here is my description. I'm hitting Enter and we're
getting one more task. Let's now reload the
page and browser. As you can see here, voila, we have our two tasks. So how it is working
here is our network and we have a request with
PS tasks to our backend. And here we're getting all tasks filtered by
this specific board Id. And then inside our
code, inside our board, we have this code that we
wrote to filter our data. So here we have a function
get tasks by column where inside where filtering
all our tasks by this specific column AD. This is where here
we're rendering only tasks which are
related to this column, which actually means
we successfully implemented on the front-end, getting our tasks and rendering them inside
our component.
37. Create task form: In this video, we can at last implementer or inland
form to create a new task. And actually we're
already prepared everything that we
need on the backend. We just need to implement
it on the client. So let's start with our forum. This is by what they want to do. I want to jump in, set our board component HTML. Here, is there a diff
to render at task? And after this, at the end, we want to render
our inline form. Let's trend here
inline form component. And let's close it here. And now inside when must
provide some values. First of all is class. This is our pattern
class that overrides the whole styles and it
is Create Task Form. After this, we're also
must provide default text. It will be add a card, but also have a button here. So here's button must be true
and we must provide a text. This is where here will be button text and
it is at current. Also we want to provide here our input place holder
and it is enter a title for this current
and last but not least is our handle submit
method that we must create. Let's name it, create task, just like we did
with our column and the insight we want to first
of all give any event. This will be the title
of creative task, but we'll also need
here our columnated, because our backend must know in what column we're
creating this task. And if you want to ask
why we're given here just columnated and not Boyd AD. We have a board 3D inside
this inside component. This is why we don't
need to do that. Now, let's create this method. I will jump to our board and actually we must
do exactly the same. What we did was create column. But here is a question. We have here column
input interface. This is how it looks
like and we must now create our task
input interface. So all our stuff is written
in the same way and this is avoided that we must send
in order to create a task. This is where here we
can name it also task input interface Ts and what
do we want to write inside? This is our interface, which is a task input interface. And the inside, first of all, boom must provide a title. It is mandatory and
it is a string. Second loop we're providing
here our column ID, which is also a string. And last but not least
is our boiled AD, and it is also a string. And now we have a nice input and we can jump
back in our board. And now I want to copy
paste this create column because it will be exactly
the same but for task. So here we need to rename
it to create task. Here we're getting
not just title, but also our column ID because we provided
it from our HTML. And now here we can
change the name from Column input to task input. And our interface here will be Task input interface
that we just created. And as you can see where I'm
missing here, our column ID. This is where here we must
add it from the argument. And here we don't need
to call a cone service. We must cohere our
tasks service. And just to remind
you that this is our stateless service where we're writing our HTTP
request and our socket IO. And this method must
be named create task, and we're throwing
inside our task input. But this method
does not exist yet. We must create it. This is why let's jump
back and say it's source app shared services, and here is our task service. But what they want to do, I want to copy paste the
method create column, because actually this
will be exactly the same. I copy pasted create
column and they want to paste it here
inside task service. But this method will
be called create task. And we're getting as an
argument our task input and well-written know that type it will be Task input interface. Here we also want
to call a socket IO because it is working
in the same way. We have an
asynchronous operation and we have started
success and failure. So we start to create a task, will notify with the
meet our back-end. Then Bacon creates facet task, and then backend notifies
all people who are subscribed to our board
regarding new task. This is where actually we must update our socket
events in them. I want to open it here on
the right, and additionally, open the enum inside of a server because we're already
wrote inside our events. These three new events, tasks create success
and failure. And now we can paste them
here to use on the client. After this, we can
jump back inside our task service and
change the name here. And it will be DOD tasks create. And there's a parameter here. We're given a task input, which actually
means after filling our inland form here we
will send our socket, your image to our backend
and our bacon is already prepared to create our task and the meat success back
to all our users. Let's check this out. Is it really like so we can jump in our server
source controllers, and here is our
tasks controller. And as you can see, we have
here create task method, and here we're creating
a new task and then notifying with this
line all our clients, which actually
means we just must subscribe to these changes
inside our client. This is where here
I am jumping back inside our board component ts. And just to remind you, here we have our initialize
listeners and here we have a listener for our
column creates success. Now, we do exactly the same
for our task, great success. So here we will have
our Task interface, and here the type will be DOD. Tasks creates success. And what this task interface
is doing in this way here we're getting back Task
interface because it is generic. And now we specified or k, This lesson is given us
back our Task interface. This is where here
we must create a new method at task
inside the word service. And we're providing here our task that we got
from the backend. And the last thing
that we want to do is add this method
inside our service. Let's open our board service. And here we have at column. And as you all have
done the stent, I want to copy paste it and
just do exactly the same. Here we have a task
method and we're getting here when
you create a task, and it is a Task interface. Here we need to make an
array of updated tasks. So here we are getting this
than not stream of columns, but stream of tasks,
then get value. And here at the end, we want to write our new task. And after this, we must
update our strain again. Here is tasks stream and
here is updated tasks. And with this code, we will successfully update our stream and all
our components who are subscribed to this stream will be automatic letter ended. Let's check if it's working. We don't have any errors here. And now I want to
open two tabs here. And they want with
both clients to jump inside the same board. And as you can see here
is our console log. And they want to
try the cut here. So here is our inline
form and let's name it, created a task feature. Now I'm hitting Enter, and as you can see
directly here in this tab, this element appears. So how it is working, actually we filled
our inland firm. We send it our image
to the back-end. Back-end, got it, created a new task and
notified all people, all our clients who
are subscribed to this board regarding
this change. And with this listen, we updated on declined our array of tasks and this
component was rear-ended. The most interesting
part is here is another tab and another user, this is another client
with another socket AD. And as you can see
here, we've also got our Create Task feature because now all our clients are in sync. And you might say, okay, but your destination, you
need just single browser. It doesn't really matter. It is just different socket
connection to your server. If you have just
two computers and your project is deployed
to the production, it will work in
exactly the same way. For example, here I can
just create a new task. For example, foo, I am hitting here at card and then getting exactly the same
information without Pedro lot in another tab, which actually means
our feature was creating tasks is
completely finished.
38. Update board name: In this video, we will implement one more missing
feature and it is an update form of
our board name. And as you can see here, we have a placeholder
inline form board, and here we must use
our inland form. And actually this
is our first video where together we will implement the whole feature backend and client in just a single video. And actually the whole
logic will be exactly the same like we
already did previously, because we built a
nice architecture and now we can create
easily new features. So what this feature is about, we will have here
and then mine firm and we're getting
from it a title. Most importantly, first of all, we must throw our
title of the board in sign this form on our
success of update, but don't want to
use the GTP here. Whoop, want to use
Socket IO by that, because all our clients
must be notified if the ER, in this point, we
changed the board name. This is where socket or
your meat as always. And then on our backend, we must catch this event
and update our name. And actually we can create a method to update
the whole board, but we will update for
now on layer title. So let's try to do this now. For this, I want to jump
inside our code and start. First of all was our server. And for this boom
mass register and use socket event here inside
our Socrative and xenon. And we already have here boards, leaf and ports chain
and actually put want exactly the same like
we did with columns. And here we have columns
create for example, and they want to copy
paste all three of them. Because actually here for
example, after boards live, we want to write boards
and here will be update. This is where here we can
directly change it to board update boards updates success and boards
update failure. And now here on the right, we must do exactly the same. Here will be boards and update. After this board's
update success and boards update failure. So our socket event is
successfully created and now we must register
it inside our socket IO. This is by let's jump back
inside or a service, yes. And here on the bottom
we have our socket on. Here we must register
and use socket ton. For this, we can simply
copy paste for example, task create and write here, not does create, but react on dot boards update
that we just created. So we're waiting for the event boards update
from our client. And here we don't want
to use tasks controller, but board's controller here not create task, but update board. And actually inside we're
passing or your socket data, just like we did previously. Now, let's create this
update board method for these payments jump inside
our controllers boards. And here we already have
this method create board, but this is not what
we want because this method is with a shoe TP. This is why actually we want something like live
board for example. This is why I will copy paste this method because all options will be similar and we can
name this method update board. And we know that
here we're getting our IR socket and then our data. And the question is, what data do we need
to update a board for sure we must know at title of the board that
we wanted to provide. And secondly, the idea of the board that we
want to update. This is where our
body is totally fine. And actually here I don't
want to write a title, I want to write fields. And here we will
pass title string. Why do we have such notation? In this case here,
we can easily add new field that we want to
update inside our board. For example, if later we
will have a description, we can just throw it here, but for now we
have just a title. Now here we can remove
this console log, we don't need it and
also socket leaf. So the question is what we
will write here at all? First of all, we must write here try-catch just because
we can get an error. This is way here, Let's
create, try and catch. And here we're getting
inside, catch an error. And if we have an error, we just want to write
socket and meat. And here is our socket
events in a dot. And we're already created
our boards update failure. And what we want
to provide insight is our get error message. And just to remind you, this is our helper which
will transform an arrow. This is my inside. We can simply provide
our unknown error. Now let's create our trie. So Festival here
we want to check if a valid user, this is y. Let's check if we don't
have a socket dot user, then move on to emit
exactly the same error. So here let's try socket, the socket events, ENM
boards update failure. And here instead of
get error message, we can simply write
user is not authorized. Also, we should not forget
here to write a return. In this case, we will
just stop doing anything. Because after this, if we
want to write our logic, this is updating of the board. And actually it is
extremely easy, but we must make this method as synchronous because we want
to use this in Kuwait inside. Now here inside
what I want to do, I want to get back
our updated board. And to update a board, we must use here a weight and we have here accessed
or work model. And we can use find
by ID and update. And actually this is an
amazing method from Mongoose. In this case here we're
just providing NAT and in our case it is
data dot board Id. And here is our update. And actually update
is exactly what we want to update
inside our entity. This is where here
we can just try the board dot fields
and this is an object. And the last one is options. In here, I want to provide
an option you through. This option is really important
because in this case, we will get back
our updated board. Also, as you can see here, I made a mistake. It is not a board dot fields, it is data dot fields. Now we don't have any errors and we can respond with this port. And for this we can simply
use socket and meet. Here is our socket
even seen them. And we have here success. So boards update success. We want to provide as the second parameter
are updated board. In this case, all
our clients will be notified about this event after this coupon to notify all
our clients which are subscribed to this
board about our change. This is where here we
can write ir dot t2, and here we want to notify
all clients inside our room. This is where here
we will have data, dot board Id dot, and meet just like
we wrote previously. And here inside we can use
our socket events in m dot. And here we have a
board update success. And there's a second
parameter we want to write here, updated port. In this case, Oliver users
will be notified TO board was updated and they will know what fields
they need to change. Our back-end part is
completely ready. Now we must implement
a client part. This is where let's
jump back inside our client source app. And here I want to go
first of all inside Shared Services, Board service. Why is that? Because actually here we're throwing our events
for the back-end. And actually before
we wrote here just as GTP cat and the choosy proposed. But in our case now, we must emit a socket event. This is why what
we want to create here is the method update board. We want to provide here. First of all, our board AD, this is a string and secondly, fields that we want to update. As you can see, we
have parameters exactly like on the backend. Here fields is titled,
which is a string, and back with don't need to get anything because we simply
use here socket IO, but to use it here, but must injected
inside constructor. This is y here we're
getting our socket service, which is our socket service. Now inside our update board, we can use this dot socket
service, dot and meat. And here we must provide
our socket event. But here's the problem. We didn't register it
yet inside our client. Here, we don't
have such strings. This is where here
on the bottom, I want to open our server
socket events in them. And here I want to
copy paste this pores, update success and failure. And now we just
want to paste them here inside our client. So now we haven't
said our client the strings and
we can emit them. This is why here we can
simply write socket events in m dot and here we
have a board update. This is the start of
our update in protest. And does the second parameter
here move on to provide our board ID comma, our fields. So we have exactly
the same data lake we prepared on our backend. And now from any part
of our application, we can use this
shared board service. Use this update board
method to send out a meet. Now it is time to add our form, which we didn't create yet. This is why let's jump
back inside our app. Board components, board,
board components HTML. And as you can see
here on the top inside board header container, we have this placeholder
inline form board. And exactly here,
we want to write an inline form to
update our board title. This is way here, Let's write our inline dash form
we want to use, and let's close it here. Now, first of all,
inside womb must provide the class to
get correct styling. And in this case here, it will be added board forum. After this, we'll also want
to provide our default text. And here we will have
our data dot board, dot title. Why is that? Because actually, just to
remind you here on the top, we have our data, has a local property data, and inside we have
all our columns, all our tasks, and a board. This is why here we
have a direct access to data board title and we want to render it
as a default text. After this, we'll also
want to provide a title, and this is the
value for our firm. Here we also will write data board title and last but not least,
is handled submit. This is our event and we want to create a new
method, for example, update board name and inside dollar event is a
string to update a board. Now let's create this method. I will jump in RTS file and somewhere on the bottom we
can add update board name. And we know that here we're
getting our board name, which is a string. And back we want to get
a void because here we want simply to
use our service. And actually here we
already have access to our thes dot boards service. And this is important,
not bored but boards dot. And here we have our
update board here inside. First of all, we must
provide our board Id, and this is this dot board AT. And secondly, our fields. In our case here we
have just a title, which is our board name that
we got from our inland form. So we successfully
created a single flow. So we created our inland from here were updated
our board name, and were emitted an
event for our back-end. Let's check if this
part is working. I don't have any errors on
the client or on the backend. And now let's open a browser. As you can see here we have now first part and I can hit
here to open a form. And here we see our first board. They can write something
here and hit enter. And as you can see,
it was not updated because we didn't react on
our changes from the backend. And actually, as you can
see here inside beckoned, we don't have any errors and probably our title
was already updated, but we didn't notify our client. And actually we can
easily check it. We can just reload the page. And as you can see now we're
getting our updated title, which actually means the
whole logic is already working and our backend
successful updated our board. But the main problem
is we didn't notify all our
clients about change. Actually, yes, we did
it on the back-end, but we didn't create
a subscription for this event on the
client. This is y. Let's do it now. So first of all, we must jump back inside our board component. And just to remind
you here on the top, we have subscriptions
for different events. This is why here I want to copy paste one subscription
and change it. So here we have our this
socket service and we're listening for the event
of updating our board, which actually means back
we're getting a new board. This is where here we have a board interface and here we have socket
event C num dot. And here we're listening
for the success. Here we have Boards,
updates, success. And inside our
Subscribe Now we're getting our updated board. So what do we want
to do with it? Actually would just want
to call a method inside of our boards service and it will
update our stream of data. This is why here we
can use board service, dot update board and worth row. And inside our updated board, super successful is
subscribed for our event. And we just need to create this update board function
inside our board service. So let's jump inside
our board service. And here on the bottom, we can create it. So here is update point, and we know that we're
getting here updated board, which is actually a
full board interface. And here we will get wide because we simply want
to update our stream. But here we have a problem. By default, our board is
now, this is way here. Typescript will scream
that we can't work with. Now, this is where
here I want to check if we have now
inside board or not. So here I want to get
a board, and this is, are these boards stream
toward get value. And after this, I want
to check if it is now. So if we don't have our board, then I want to throw an error. So here throw new error because actually we can't update the board if we
don't have a board. Here, let's say board is
not initialized after this if we can work with part and actually we just want
to update our stream. So this board with
dollar dot next, and here I want to merge the
whole board that we have with the title because I
want just update at title. So title is our updated
board dot title. And you might say, okay, we could just take
the whole object. Yes, we're good, but
I want to stay on the safe side and just implement a feature
that we need to solve. This code should
successful update the stream and our component, as it is subscribed
to the stream, will be notified and rear-ended. Let's check if it's working. We don't have any errors here. Let's reload the page and
here we have first board. Let's just change your
title and hit Enter. And as you can see here
is directly my changes. Now let's duplicate
this tab and try again. So here on the second tab, I will just try to
tune and hit Enter. And as you can see
here on the first tab, it was also updated, which actually means
we successfully implemented a feature of updating a board title
from start to the end.
39. Delete board: In this video, we will implement
deleting of the point. And actually this feature
will be super similar to our previous feature with the update enough our
board, this is y. Let's implement it
really fast as always. Put one to start
with our server. This is y. Let's jump
inside server source. And here we're interested
in our socket events. And actually exactly
like this update, we need three new
events for deletion. This is y here, let's
create three new events. It is port delete boards to
its success and failure. And let's change on the right, our string two boards delete. Then boards delete success
and boards delete failure. Now let's subscribe to our event for this bonus
jump inside our server. And here we can copy, paste our board success and
change it to our boards. Delete. Here we want to
use a new method, not update board
but delete port. Now let's jump to
our controller and create this method inside
our board's controller. And actually for this, I want to fully copy paste our update board method because of a deletion
will be super similar. So first of all, let's
change the name. It is delete board and it
is an asynchronous method. Now, inside of our data, the only thing that we need
isn't a d of our board. This is where I bought
the D is enough here, but don't need to
provide anything else because we simply
delete our board by d. And for back-end, it
is enough information. Now here inside we have
try-catch as always, and we're checking
for our socket user. But here, in case of an error, we want to throw another error and it will be boards
delete failure. Here. Let's also
change our cage. It will also be DOD
boards delete failure. And now we need to change
our logic whispered model, because here we did an update. Now we have Azure move and actually we don't want
to get anything back, but we simply want to
remove this bond by d. And for this, we can
use different methods. For example, we can use
find one and delete. This is totally fine. This is exactly like
we did inside update. But actually you can also use directly without
sugar, delete one. And this is method
just for you to know inside delete one, we must provide an object with fields by which we want
to find the sensitive. And actually here we can
just provide underscore id equals and d Now in case
it is data dot board AD, and actually doesn't
matter if you are using delete one or find
by the end delete, it is working exactly the same. So this line successfully
removes for us our board. And after this, we want to
emit our success of removing. This is where here to all
our users inside this port, we want to emit boards,
delete success. And actually here we
don't need to provide an ID of our board because
all these users are inside this specific
board and they know what board was
removed and they must be redirected to the
homepage because they can't stay on the board that one
of the clients removed. So our server logic
is completely ready. Now let's return to our client and the
exactly like previously. First of all, we
want to start with shared and with
our socket events. And I will copy paste
from our socket events, three events, board delete
success and failure, and paste it here on the bottom. Now we must jump inside our
services, board service, because here we want to create a new method to remove a board. This is where here
let's copy paste our update board and
change it to delete point. And the only thing that
we need is our board AT, we don't need here any fields. And here we want to emit
our board delete event. So let's try it here. Bought delete and inside we will provide an object
with just one field. Indeed is our board ID. Now inside our
application we can call this delete board and it will throw insight Socket
Layer corrective end. Now let's update our component. So I want to go back
inside our board. Components bought HTML. Here we have our inline form. And after this inline form, we must create an icon
which will delete board. This is where here let's
say div class Delete Board. And here we will
have our click event and unclick Poupon
to delete our point. And actually we don't need to provide anything
inside because we have NAD of our board
inside our test file. So here I want to
close out our div and just try it inside
Delete Board code. Now we must jump
inside our ts file and create this method
here on the bottom. And what we want to do
inside our delete board, we simply need to
call our service. Well, here we know that it is a void and we want
to call a service. But actually additionally, I want to write here
a confirmation because we must be sure that users really want
to remove a board. So let's try it here. If confirm and if you don't
know what is confirmed, this is a standard Javascript
popup inside the browser, which will show you a yes or no. So here we want to
write something like, are you sure you want
to delete the board? And if the answer is yes, then we're going
inside this sieve and we will call this board service. And here we have delete
point where inside we can provide our board AD. And now you might think, okay, we must update now
our board service. But we don't need
to do this at all because actually it
is much simpler. What we want to do Poupon to subscribe here to
our socket event, which will be deleted
success. Let's do this now. For this we want to copy
paste our socket service, listen and hear boop
want to listen to void because back whoop
won't get a board. We know this is just success
of deleting of our board. This is where here the
event will be bought, delete success, and back
when not getting anything. And the question is, what we'll want to do inside
and actually we simply want to redirect the
user to our homepage. Why is that? Because actually we
have a listener here on the top of navigation Start, which actually means
at the moment when our user is going
to another page, for example, to the homepage, we will handle live
poured through our board service and we will remove all streams correctly. This is way here what we want
to do inside our subscribe, you simply write this
router dot navigate by URL. And here we have, for example, slash boards, which actually means the
board does not exist. We must show for every single client list of
possible available boards. Let's check if this
code is working. As you can see, we don't have
any errors on the client. We don't have errors
on the backend. Let's open it now in browser. And as you can see, our
page is still working. And now we have
this delete board. But actually, I
don't want to remove this part because we built
quite a lot of tests in here. But I want to do, I want
to jump back inside of our boards and
create a new board, for example, to remove. In this case, we can test this functionality on
this new empty bar. But we want to do now, we want to duplicate
this tab just to check if it will work
for the second user. So what I want to do now, I want to click Delete point, and as you can see, I
see a confirmation. I assure you want to delete
a board where hidden, okay? And where there is regularity
reacted to our boards. And as you can see
here, we don't have our board because it was
successful or removed. And as you can see
in the second tab, where all the digits
directed to our slash part because all our clients
inside this room were notified about successful
in removing of the board and they must be
rejected to our boards page. And as you can see,
our architecture is so amazing that we can create new features just
in a matter of minutes.
40. Delete column: In this video, we must implement
deleting of our column. And actually here we have a
column inside our board and the mass near the title show
the icon to delete a column. And again, it must be
implemented with the socket IO. So we will notify another
user's about our deletion. And I think that it is a really nice feature that
you can implement on your own because it will be super
similar to delete in a bird. And here is some
help from my side. First of all, as always, we must on the backend, implement new events and
registering use socket event. After this, we must create a new controller action
which is columns. For example, delete column. We must also make sense
just on the client. First of all, registry
and new event. Then we mustered
new method inside the column service which is
shareable to remove a column. After this, we must update
our component to add a button on which we will remove this specific column
from this point. And actually this
is just a meeting of the event for the back-end. And we're also must subscribe to the success to remove this
column in all our clients. And here is the
markup of the button. So you know what you
need to implement. As you can see here, I'm
inside board component HTML. Here we have our column titles and directly after the title, we want to write here
an image with source. And here we will
have slash assets, slash close underscore,
icon dot SVG. And here we can close our image, but we're also must
provide here a class which will be
column delete icon. Let's check this out.
I'm reloading the page and here now we
have a nice cross. And this is exactly where you will bind the event of click. But if you just want
to follow along, Let's implement this together. And actually as I said, it will be super similar
to deleting of the board. This is y. Let's do it fast and furious. So first of all, I want to
start with our backend and biomass jump here inside
our server source types. And here we have
our socket events. And again we have
here boards delete, I can copy paste it and just
change it to columns delete. This is y here on
the left we will have columns delete and on the right instead of boards will write everywhere columns. And also we must change it
in success and failure. So our events are there. Now we must jump inside
our server tiers that here and use socket on here
instead of boards delete, I want to write columns delete, which we just created. Here. Instead of board controller, we must use our
columns controller. Here we won't delete a board, but we will delete our column. And now we're mustard
delete column method inside our columns controller. But I don't want to
retype anything because the code is exactly the same
leg deleting of the board. This is where we can copy, paste completely this delete
board and paste it inside our columns controller because
it will be 99% the same. So first of all,
the question is, what we must get
here as a parameter? Here we're already,
I get in board AD and they want also to
get here column they D, because we need to know
what we're deleting. And you might say here, okay, But why do we need poor lady? We just need a columnated and actually not because we're must notify everybody who has subscribed to this
specific board AD. This is why we must
provide both values here. After this, we're
checking here for our user and here we must
meet a failure event. In our case it will be
columns delete failure. Here this user is
not authorized, and here we have our
catch and we can use here the same columns delete failure with our
get error message. Here. Instead of board model, we'll use our column model. Here we're deleting one
record by data dot, and here will be column
ID because we must delete our column not are born and after this way
meeting our success. But here we will try to
success for our column delete, and here will be success. And actually we're already
ready with our backend. What I want to do now
is copy paste this socket events because we
will use it on the client. This is the way here I
will copy paste columns, delete success and failure, and that the disabled Champion
set clients source app. And here we have shared
types and here are our socket events where inside we can paste
columns, delete. Our next up will be tub Data, Service, Insight shared
services columns. Because we want to implement
delete column exactly here. We can write new
method delete column, and we know that we are
getting here our board ID, which is a string, and
also our column ID, which is also a string. And we're getting
big void because we just want to emit
our socket event. This is why here we can write this socket service and meet, and we're using here
our socket events in m dot columns delete. And we must provide as a
second parameter and an object with our board AD
and also column ID, which we're getting
from the parameters. After this, we must
update our board service. And just to remind you, bore services exactly where we're storing our
stream of columns. Which actually
means here we must implement delete of the column. So here on the bottom, I can create delete
column method. And here we just
need our columnated. We don't need here Board idea
at all because we will call this method just from the
component to update our stream. So here we know the
column ID that we want to delete and back
we're getting void, and now we want to update
the list of our columns. This is way here. We can create a property
updated columns. And here we can access our stream with these
columns stream. Here we're getting our value. And after this we want
to filter our array. And actually here we're
getting access to every single column and we want to reject this
specific column id. This is why here
we can check that column ID does not
equal our column names, which were passed
as a parameter. In this case here we're getting exactly the same
list of our columns, but without this one column. And after this, we must
just update our stream. For this, we can
write these columns next and we're throwing
inside our updated columns. So actually this
method will update our stream and remove a
column from the stream. Next step is to
update our component. And actually we're
already changed our HTML. Here we now have an image, but we must attach, hear a click event. And here what I want to do, I want to delete a column. This is the way here we can provide a method delete column, and we must pass
inside com dot id. And here on the top,
as you can see, we have access to our column. Now we can jump inside our component and
create this method. So Dalit column is getting
just a single argument, which is our column ID that we want to remove and bank
where getting void. And now inside we simply want
to call our shared service. So here will be this column
service world or the Herod. Now here we have a method
delete column inside. We must provide a board AD, and actually it is these
board AD and then column ID. So this line of code
will emit an event to our backend to remove this column and notify
all our clients. And now we just need to subscribe
inside our subscription here on the top that we want to update our
list of columns. This is where here actually
we can copy paste something similar and welded have here
columns creates success. I will copy, paste it, and paste it here on the bottom. And here we have delete and actually delete with
didn't do it correctly. I want to jump back inside
our controller columns. As you can see here we have
i2 data board they emit, and here I just changed the
name columns delete success, but it is not enough. We must provide here some
data because actually all our clients must know
what column we must remove. This is where here as
the second parameter, I want to provide data
dot column they deep. And in this case we are
given enough information to all our clients regarding
our column delete. Now we can jump back inside the board component and
here is our listen. And actually here
we're listening to our columns, delete success. And here what we're getting
back is not a column, but just the string
that we want to remove. This is why here we can write
that this is column ID and actually this is
enough information for the client to remove a column. This is why here we can now
call board service dot. And here we have
our delete column. And the inside, as you can see, we're providing column they D, and this is exactly what we
have here from our back-end. So actually we implemented
everything for our feature. Let's check if it's working. First of all, let's
check the backend we have here a problem. Delete column does not exist on type and here is columns.
Let's check this out. I will jump back inside
our controllers, and here we have
our delete board. So this is the problem. I copy pasted Delete
Board method, but they didn't rename it. It should be delete column. Let check that again. I'm saving it and jump
into the console. And as you can see now, we don't have any errors. Now let's check our client. Everything is looking fine. So let's jump to our board. As you can see,
our pages working. And now here I can
click across to remove, for example, this full column. I'm clicking it and
as you can see, it is completely gone. And what is more interesting, we can duplicate the tab and try to remove this next column, F, F, d, d, and so on. I'm hitting here cross and
it is removed not only here, but also in this tab. And everything is
automatically updated, which actually means we
successfully implemented removal of the columns
inside our application.
41. Update column: In this video, we
must implement one of the last features
regarding our board, and this is updating
of the column. And as you already
can understand, it will be super similar
to delete the column. The only difference
is that we will have an inland from the updated, but a lot of stuff will
stay exactly the same. Where a meeting, I'm a socket
event where getting it on the backend and when we define all our
clients, this is y. Let's implement it together
now, first of all, I want to jump back inside
our server source types. And here we must create
inside socket events in new types and hairball to
the head columns grade, I can copy paste it and
rename two columns update. So here we will have
columns Update, columns updates, success and
failure. Here on the right. Then we can change it to update. Then a birthdate success
and update failure after this lithium back inside our sorority S and here
and you own event. So here I copy pasted
column delete, and let's change it to our columns update
that we just created. And here we're using
our columns controller, but here we're getting not delete column
but update column. Now we must create this action, but actually we can copy paste it from our boards because here we're already
implemented update board and it will be super similar. This is why I want to copy
pasted completely and paste on the bottom inside
our columns controller. Here with first of all, must change the naming. So here we have our update
column and actually I want to keep it exactly the same with poor lady and fields. So we have exactly
the same structure. This is where here
the only thing that we need is column ID, which is a string. And the only field that we're
updating here is our title. Now let's change our content. Here we will have
socket events in m and here were created
column update failure. And let's change
also our catch here will be also Column
update failure. Now we must change our logic. Instead of Bohr's model, we will have here
column model and we're making fined
by the end update. This is totally fine. But here we want to
get not a board, but column by column id. And here we have data fields. This is completely normal
and backward getting updated column and
not updated board. Now here we want to emit to
all our clients our event, which will be column
update success. And here we must provide our updated column so they
can change it on the client. Our backend is
successful, it changed. Now we must update our client. And first of all, I want to copy paste this three new events, which are columns update,
success and failure. Now let's jump back inside
our clients source app. And here we haven't said shed
our types socket events. And here on the
bottom I will add columns update
success and failure. Now we want to change
our shirts service, which is responsible to
make requests to our API. Here are the services
columns and here we're already implemented
delete column, but we need to do now is our
new method update column. Here we already know
what we're getting. We're getting here our board
ID, which is a string. After this we're
getting our column ID, which is also a string. And last but not
least is our fields, which is an object with a
title, which is a string. And back we're
getting void because here we simply
emitting an event. This is where hear sockets
service Datta meet and we want to use here
socket even see them dot. And here we have our columns update action as the
second parameter. We must provide everything. First of all board AT
secondly column needy. And last is films. And with that, our service
is completely ready. Now we must update our board service so we
can change our component. This is why I will
jump inside our app, board Services, Board service. And here we're already created
a method update board. This is where here let's create
our update column method. And we know that
group want to update one single record
inside of our array. And here the only
thing that we want to get is a bit dated column. This is what we got
from our back-end. Here. We're getting the
whole column interface and back we want to get void. Now, here is our idea. We need to map through every single column and
update this column by dy. This is way here we can get
back our updated columns. And here we want to
get first of all, the value from our stream. So here we will have
our columns get value and we're mapping
through this array. So here is map and
we're getting access to every single column in here inside we must write our logic. So if our column ID
that we're looping through equals to our
updated column dot id, then we must do our magic. And if not, then
we simply want to return our column
without an update. Now, what do we
want to do inside the loop one to update
this specific column. This is why I want to merge
our column with title. And here we're providing
updated column dot title. In this case here we're updating just a single jacket
when it matches. In other case, where simply
returning our column. After this, we just
need to update our stream with
these columns next. We're provided inside
our updated columns, so our method is
completely ready. Now we just need to
update our component. So let's jump back inside
our board component, and here we have
our column title, but instead of just a title, we must trend here are
inline form, this is y here. Let's try it inline form, and let's close
it here directly. Now, first of all,
here we must set our class which will
be added column form. After this, we must
provide default text. And here we want to write
the name of our column, which will be column, the title, but also must
provide here a value. This is y, here will be title, which is our column title. And the last one
is our callback, which is handled submit. And actually this
method we can name, update, column name, and inside we're
getting Festival, our event, which is
actually our column name. And here we must
also additionally provide column ID
because it not the case Groupon know for what column we want to
update this specific screen. Now, let's create this
method inside our component. So here on the bottom I want
to add update column name. And here we are
getting first of all, our column name, which is the
string from our inland fun. And secondly, we're getting
here our column ID. In this case, we
know what we need to update and what we
want to do here. We just want to call our
method from shred columns. This is y, here,
this column service, and here is our
method update column that we just created. First of all, we must
provide here our board ID, column ID, and also our fields. In our case here we just
have a single field, which will be our title, which is our column name. From the client side,
everything is ready, but we must subscribe to the success of a
birthdate in the column. This is why we must
go here on the top, on now a socket services. And actually here we already have our update of the board, which actually means
I want to copy paste this lines and just change them. Because here back, we will get our column interface after
we updated our column. And actually here we
want to subscribe to call them update success. And here we're getting
not updated board, but we're getting
updated column number must call another method
from our board service. And this method will
be update column. And inside we're providing
our updated column, which actually means
with this code, all our clients will be notified and we'll update this
particular column. Let's check if it's working
here on the backend, everything is looking fine. Here on the client, we don't have any errors. Let's jump to the browser. And as you can see here, I have this nice name and
now I can click on it because this is our inland firm and
not just the title. Now let's try it here, updated and I'm hitting Enter. And as you can see, it was updated and deferred
loading the page, then this code is also stain, which actually means
we successfully made this change is on the backend and they
did all our clients. And here, if I'm jumping
to the second tab, it looks exactly the
same because it was also updated and notified
through socket IO. With that being said, we successfully
implemented our feature of update in the column.
42. Unsubscribe: In this video, I
want to talk about unsubscribe because
actually we have a problem. Let's look in our
board component test. Here we have our initialize
listeners and we're using dot subscribe
quite a lot of times. And actually you must
know that inside Angular every single time when you are writing the word subscribe, you are in danger because you are creating a
subscription there. And this subscription will Hank there until the
end of the world. Which actually
means, for example, we're leaving our board and we're jumping
to another board. And all these subscriptions are already there and
they will never be destroyed because all
the subscriptions doesn't have anything to do with our component
and the angular does not do anything about them. But here it is important
point to mention, if we're talking inside
Angular about HTTP client, then we must not
unsubscribe from it. This is not mandatory because angular will unsubscribe
automatically, which actually means
here when we're using this router
events subscribe, This is totally fine. Here we can also look on
our fetch data and here we're also use subscribe
to our get bored. And for this we must open our get bored and check
what we have here. And actually this is
a pretty big Gad, which is an HTTP client, which actually means
this code is totally fine and we should
not unsubscribe. But actually, I prefer to
unsubscribe everywhere, in every single application
where a C subscription, because you never know what exactly is this
get board method? Is it really HTTP client or is it just some
wrapper around? And you really need
to unsubscribe. And the question is how
you can unsubscribe in your application to
make it comfortable. And there are a lot
of ways to do that. And actually we always want to unsubscribe when we're
destroying a component. And typically where
this turner component, when we're changing around, which actually means here
we can write something like this board subscription equals. And here we're
getting the result of our subscribe and the result of the subscribe easy subscription, which actually means
per mass create this property and then
right inside our engine, destroy Boards subscription
dot unsubscribe. This is totally valid, but then we will create lots of properties and this is
not that comfortable. This is why there are
better ways to do this. I wanted to show you
one simple approach that you can use. What I want to do inside
my board component, I want to create one
more property and they want to name this
property unsubscribe. And actually I want to put here dollar because it
will be a stream. And here I want to
assign a new subject and actually also the
youth behavioral subject inside our
board service. But here we have a new
subject with void, and I'm just calling it, the main difference between subject and
behavioral subject is the behavioral subject always has an initial
value and then say, subject, we don't have
any initial value. Now here we want to add
implements on destroy, which actually means we must
create in G on destroy. And now here we're talking
about in Germany in it. And here we can write in June destroy and what we
want to do inside, we want to write this
unsubscribe dot next, so we're column the next value. And after this, this
unsubscribe dot complete, which actually means after
we destroyed this component, we don't want to get new values
inside this unsubscribe. And now we can use
this unsubscribe everywhere to ignore
subscriptions. How does it look like? Actually here, we
don't need to use it, but we must use it inside the socket service because
it is a custom subscription. And in order to do that
before our subscribed, we can just try it dot pipe and inside we want to
use method take until and inside
they want to put this unsubscribe that
we just created, which actually means
here we're taking, okay, We must take new values and have this subscription until
this unsubscribe is valid. At the moment when we're
Colin complete with bond, come here to our subscribe. Which actually means
in every single case, when we have a subscribe, we can just try it before by uptake until
this unsubscribe. And we're good to go after our components
will be destroyed. Here we will have complete and this logic
will never happen. This is really a comfortable
and easy variant to implement, unsubscribe. This is why I want to copy
paste this code and put it in every single lesson that we're writing here just
before subscribe. In this case where
on the safe side, and we won't call this code
after destroying a component. Now you for sure want to know if it is really
working like this. This is why in order to test it, we can simply comment out
taken till for example, in columns, updates access. But actually it won't work like this because when we
were leaving the board, Bacon stops sending
events to ask, this is why what
we can do just for testing is championed
inside controllers columns. And here is our
method update column, and here we have our
function are your two. And here we need to find only clients where they're
inside this specific part. Just for the sake of testing, I will remove here to notify all clients
at all because they want to show you that
this subscription is still there when
we're leaving the board. So now here I want to
just try to console log. Got updated column. Here is our updated column. Let's check this out. I'm reloading the page, I'm jumping to this point and I'm trying to
update the column. As you can see, this
is our console log, and here we also have
the console log. It is totally fine. But now we're jumping
back to our boards and we don't have anything and our
component was destroyed. Now here on the second tab, we will update this column. And as you can see here,
insert first step. We have this got updated column and we're getting it
because this subscription is still hanging with didn't unsubscribe from our code
and denoted to do that, we can just uncomment
take until, Let's try it. Now, here we're jumping
inside our application. We're getting, got
updated column where champion back
to our boards. And now here we can try
and update our column. But as you can see now
inside the first step, we didn't get a message because here it didn't go
through this take until, this is why it is
so important to unsubscribe from all
our subscriptions. Now let's change this code back. First of all, we don't
need this console log. And secondly, I will jump inside our controller
columns and change code back
to i2 database ID. So in the real application, I highly recommend you to unsubscribe from all
your subscriptions.
43. Task module and basic component: In this video, we start new and interesting
feature and then talking here about
feature task model. What does it have a mean? Actually, as you can see here, where inside board
and when we click on specific task inside
this point, actually, we must open here a model, but it is not as simple as that, because what we want to do, we want to change our routing. So now we have slash
board slash board Id. And actually when we open
a task after page reload, we want to see the same task. And the easiest way to implement this is obviously routing. So we want, at the moment when
we're open a task URL like slash board slash board ID
slash tasks slash task KD, which actually means we have
a Nested Route inside board. And actually Angular
allows us really simple to run the
children droughts. Why do we need here
a child drought? Because they actually here we
want first of all to render the whole board and additionally
to render this model, which actually means our model, will be here on the top
and beneath our model, we will see the whole board. And this is extremely important
because actually we will, even inside the model, fetch the whole board. And actually this whole
component will be rendered Just as it is
without any changes. And what is also interesting
inside our task model, we will use this
information from the board, and this is exactly what
we implemented here. Here inside our
client source, app, board services, Bot Service, we have three streams. Our tasks column and, but, and this is amazing because
now inside our task model, we can read all our tasks, find needed task by this ID inside URL that we will
implement in a second and then just render
this information of the task without
additional requests. And in this video,
I want to start with the basics of
this task model. Here we want to create a basic component and
bind correctly our route. And for this, I want to jump back inside of our board module. And here we have our route with slash boards, slash board Id. And here we want to
have a child drought. This is where here we can write children and this is an array. Here we also have an
object with path, which will be Tasks slash. And here is task ID, which actually means
we're taking here parent path and our child path. And together we will have
our task ID path here, but also must provide a component that we will
create in a second. And it is our task
model component. But also we didn't implement
changing of our route. And for this, I want to jump
inside our components board. And here is bought
component HTML. And as you can see here, we have a link to our task. This is this div class task, and here is energy for loop. And actually here we want
to write a click event. And here what we want to do, we want to open task. And for this we just
need our task dot id. And what we want to do inside this method is just
change a route. This is way here. Let's create this open task
inside board component. And here we're getting our
task ID, which is a string. The back we're getting void. And now here we can simply use our router to change route. So here we can write this
router dot navigate, and we're using here
navigate and not navigate by URL because we want to
pass inside an array. Here. First of all,
we have boards, then we're concatenate
this string with this board ID, then comma. And we have here our tasks, and here we will
have our task ID. As you can see, it is much simpler to write
navigate with array, then to navigate by URL, where we must concatenate
this string ourselves. So our click event
is completely ready, and now we just need to
create this new component. This is why inside our
board and we're still talking about board
module insert components. We want to create
our new component and it will be our task model. Now inside with first of all, need to create a
task Model Ts and also task model dot
component dot HTML. And now here inside the HTML, I want directly to peg the whole markup of our
model without any logic. In this case, we can
directly see the term model is working and router
is also working. This is where here let's
write the whole market. So here first of all, we have our class task model container, and here we won't
bind any data yet. Now inside this div, we must try it one more deep, and it will be our
task modal header. Let's close this div and
inside it here we will render later our task
title in line form. And after our inland form, we need here to render an image. And actually here will
be source slash assets slash close icon dot SVG. And here we also need to provide a class
which will be task. Model close and actually
I wrote it not correctly. This image must go after
our task title inline firm. So we're talking here
about task modal header. And after dusk modal header, we want to create the next div, which will be Task modal body. Let's close here our div
and inside we want to create one motif because we
need to group our elements. So inside this div, we want to create
our form later. You might ask, okay, but why here we're using
Justin Lin firm and here we want to create a real firm
because actually here later, we'll also need a select
to change our column. This is why it is much easier to use for this forum.
This is y here. Let's create a form for now without any firm group,
just whisk glass. And here we will have our
column select container. Let's close this form. And inside our form later, we will render here a select, but for now we don't have it. This is where here,
just select column. And after this firm will want
to create one motif where we will have our task
model description. And here we will
have our div with the class task model
description label. And then side we will simply
write a word description. After this div, we will have an inline form for
our description. So here is inline
form description that we will create late. And now after this div that we created would need
to create one motif. And here we will have our task model actions
inside with first of all, need one additional
class and it will be task model actions labeled
and inside this div, we will simply write actions. And after this, we need
to create one more div. And inside this div, we will pack one
motif with class, task, model, actions, action. Here, Let's close this div
and then inside we can render an image with source slash
acids slash trash SVG. And here is our
class which will be tasked model actions icon. And after the second, we will
just render a word delete. And after the whole
mark-up at the end, we need to render a backdrop, so our whole board will
be a little bit hidden. This is where here we
must create div with class task model backdrop here Let's close this div so our markup is
completely ready. Now, we just need to
create this component. Let's first of all
create here a class and it will be our task
modal component. On the top were
mastered our component, and let's register inside. First of all, a selector, it will be tasked model. And after this, a
template URL and welded, the heavier it is task
modal component HTML, but it is not all. We must provide
one more class for the whole wrapper
of this component. Only in this case, our market will be correct. And here we must
straight host binding. And here I'm providing
insight class. Here we can write classes
equals task model. And if you never wrote
something like this, we're using such notation
when we don't need to apply this class to some
deep inside this component, but we want the exactly to apply a class on our
component element. So we did some basic components. Let's check now if it
is working at all. So Festival here we
have a lot of errors. For example, here
in board module can not find task
modal component. So first of all, here we must jump inside our board module. And here we need to input
our task model component. And we're also must provide
it here inside declaration, this is task model component. Let's check again with
don't have any errors here. I will reload the page and they don't see any errors
inside console now. But what we must do now, we must click on
one of the tasks. For example, here is my first
task and I'm just clicking. And as you can see,
nothing happened. But inside drought, we
can see now slash boards, slash our board ID, slash tasks and slash the d of our task
that we just opened. And now you might ask, okay, but why we don't see our
component and actually we didn't trend the outlet
for our children, which actually means
inside our board, inside board component HTML, somewhere inside our markup, for example, on the bottom, we must render Zhao outlet just like we did inside
our app component. So here let's just close router outlet and now it must work. I will reload the page. And as you can see,
we see our model by that because actually
this is the route. And now every single time
when I'm reloading the page, we see our parent
in the background. And here we see our model and actually already have some
basic markup for this model, which means it is fully working. But now I want to
fix a huge pitfall which will be super difficult to debug what I'm talking about. As you can see here inside
our board component, here we have a route check
and actually here on the top, we subscribed to this
event navigation stars. And here we're
triggering live born. What does live board doing? Actually here we're meeting a socket event for our back-end, which actually means we are unsubscribe in this
current socket, our current user from
you went on this point, which actually means here
we were on this point, slash board slash AD. And now here we're
getting events. Everything is fine. But at the moment when we open our model, we changed around. And actually it means that
we are here in this IV and here we see live what
we can easily check it. For example, here I
will just try it live, but I'm reloading the page. It looks fine. But when I'm jumping to our boards and then just
click on my first task. As you can see here, we're
getting leave board. This is completely redrawn
because in this case we won't get any notification
in our board, but already left the board. This is Sean behavior because we don't want
to live a board. If we just open the model, we still want to
be on this page, but we can do to fix it, we actually just need to check for our slash boards route. Because actually if here inside drought we have
slash boards slash, it means that we're still
staying on this page. Because if we're jumping
back on our boards page, then here we have just slash boards and we don't
have a slash. And then this is where
here I can write end not event dot URL. And this is the whole URL
where we're going includes, and we're checking
that this specific URL slash, boards slash. So actually here we will trigger our lives board when our
navigation start happened. And this URL we're going does not include
slash port slash. This slash board slash is either our single bonds or
our single board was model. Let's check if it's working. I am clicking here
my first task and now we're not getting left part, which means everything
will work fine. And now let's
implement together. Go to the board because this is just a single line I want to do. I want to jump in
our task model. And here on the top we
have this close image. And actually here we can
additionally just create a click event and let's
name it, go to board. Now we can jump directly inside our task model and create
this method, go to bond. And what do we need
to know inside this component in order
to jump to our board, we must read from this year. Well, first of all, our task ID, we will
need it in any case. And here most
importantly, board AD, which actually means we will
write the same code like we did inside our
constructor inside board. So here let's register
our constructor. And inside we need to get
first of all, our board AT. For this, we need to use route, which actually means here
we must inject our route, which is an activated route. And now here we can write
this route dot snapshot, dot, params, map, dot, get. And here we're trying
to get our board Id. And as you can see here, we're getting strain on now. This is why it makes
a lot of sense to check if we really
got our board ID. In this case, we can
save board Id and task AD directly as a property
inside our component. This is way here I first of all want to create our board AD. It is a string. And secondly, task Katie, it is also a string. Now here we can check, okay, duplicate a board. If not, then we need
to throw an error. This is where hear
thrown you error. And here we can say can't
get board ID from URL. And now we need to
do exactly the same for our task ID.
This is way here. Let's try to task AD and we're trying to get task
a different route. And now here we want
to write one more Eve and check here for task AD. So here we can write, can't get task ID from URL. But actually here I
made a huge mistake is you can see we have
a board Id and task ID, but with Taskade,
It is totally fine. It is this route
snapshot params map. But with Boyd AD, it is not fine because we want to read all this information
from our parent. This is why here we must
try route dot parent, and here we must put a question mark and then
snapshot programs map. In this case, we will read
it directly from the parent. And now after these two checks, we can just assign this task AD, and this is a string now, and here we also have this board ID and it
is also a string. Now here we can implement
go to board method, but for this womb
must inject here. Additionally, shout
to change your route. So here we have our router. And now inside this method, we can simply write
this route and navigate just like
we did previously. And here we want to go to board. And as a second parameter
it is this board. In this case here,
the URL will be slashed points, slash board ID. Let's check this out. I'm reloading page here. I am inside task and
here I'm clicking Cross. And after clicking
where they directly jumping insight slash
forward slash id. And most importantly, we
didn't have a page reload. We don't need to fetch data for this part because the
whole data is there. And now we can simply open
second task, close it, and it is really smooth
and fast because we don't need to fetch
additional information here.
44. Get task and columns: In this video coupon
to talk about data streams inside
our task model. This is where it will
be really interesting. Why is that? Because they actually were
already have the whole data for every single model
inside our board, we just need to use this data and map it
correctly inside task model. This is where the first
thing that I want to do is inject here our board service
that we already have and not Boards service
not to work with the pie, but our board
service with state. Now here, first of all, I want to find the task. And actually here for example, after this board ID
inside constructor, we can just write this
task was taller and it will be our stream of the
current task that we opened. And here we can actually just
tried these sports service. And here we have our stream
with all tasks of our board. And as you can see, it
is already available, but we don't need the
whole array of our tasks. This is way here boop
want to use map. This is by here we can
write by map and then cite where getting
access to all our tasks. And now we just need to find one specific task that we
need for this task model. So here I want to just
return tasks find, and inside here, we will get
access to every single task. And here we just get this
task by AD and we compare it with this task AD that here on the top
inside constructor. So actually what this does, it transforms our stream
board service tasks. And here we're just mapping the single task and here
we're getting it back. But it is not all
actually after map. I want to write filter and here inside I want to
provide boolean. Why we're doing that? Because actually it might happen that at the
beginning with don't have a task because we didn't fetch all
these tasks here, which actually means
where jump into constructor inside our model, this is our stream. Tasks is simply an empty array. Here we tried to find it and we can't and we get
an undefined it, but undefined it is not
interested in fast. This is where here I wrote
filter Boolean and it will just get rid of
every undefined it on. Now, in this case here, this stream won't be fulfilled
until we can find a task. So now let's try to use
this trim inside our HTML. Here on the top, we must
create an inline form, the updated title of our task. And we can simply write here our inline form that put
already used hundreds of times. And then side first of all, we must set a class, and here we will have task
model added title forum. After this, we want to
set our default text. And actually here we need to provide the title of the task. But again, I want to
write it even better. We could use this
stream inside our HTML. But as I already
talked previously, we can combine
different streams of data inside a single property, and this is exactly what we
can do now here we can write this data was taller and we
can use here combined latest, just like we used previously
and actually later, we won't have here just a
task at least will also must get here a list of
our columns by that, because actually we
have a select to change the column
where this task is. This is way here. First of all, we will have our this task and later
additionally pupil right here, stream for the columns. And here we need to pipe map all this data and we want to convert our array to the object. Here. First of all, we
will get our task. Here. We need to return just a task. Now here we must create this
data inside this component, this way here on the top. First of all, we must change
task dollar to observable. And here we're getting
our Task interface and never now because we
checked it with filter. And now here we also
need data we stolen. And here we will also have
an observable of the object. And here we have our task, which is a Task interface. Now, data is ready, actually inside the HTML, we can use this data directly. This is where here on our
top div, I will just try it. And G, Here we have our
stream with data as, and here we're getting data, which actually means
now in the whole file, we can just use data. This is y here
inside default text, we can provide data
dot task, dot title. And after this, we need to
provide a title inside form, it will also be data
task dot title. And as you can see, we
don't have streams here, but actually here inside data, we created a task Stream, which is a string based on our stream where we
have an array of tasks. And the last thing is an output here and here we have
our handle submit, for example, update task name. And here inside where
getting an event. And they just want to create this function inside
our component. I won't implement anything here. So here we're getting
our task name. Which is a string, back is void. And now I just want to
write here console log. This is update task name, and here we're getting
our task name. Let's check if it's
working at all. We don't have any
errors here and they will just reload the page. And as you can see
directly here, we're getting my first task. How does it work? Here we
build our stream task, which is based on our tasks and actually after pages a lot
when not getting an error. And we could potentially get it. Actually not because we
have here in GE with Chegg, but it is always
nice to write filter Boolean just to know that
we're on the safe side. And here we're getting our first task inside
this data stream. Here we can directly
change this, my first task and updated. And as you can see, this
is our console log. Obviously we didn't
implement update yet, but we're on the right way. Now. We can write
exactly the same code with our inline
form description. This is why I want to jump
back inside our HTML. Here is our inland
from description. And actually here
we can just try it inline form like we
did for the title. And here we already have the
data inside the data stream. This is way here. First of all, our class task model, edit, description, form. And after this, first of all, we have here default text. Here we will try it
data task description. And here I want to write, or because actually
a description inside the task
is not mandatory, which means at some point
it will be an empty string. This is way here, add a
more detailed description. After this, we also
want to set our title. This is why data dot
task description. Also we want to provide
here at exterior, and this is our first
usage of textarea. This is way here. Input type is text area also has Button property
must be set to true. And after this button
text can be set to save. And we also need to provide
here I handle submit. Here, let's name it,
update task description. Here we're getting
our event actually. Here we must create
the function, but it will be exactly the same. This is why I will copy
paste, update task name, and just change the name
and the console log. But obviously it is not a task
name but task description. Let's change it and console log and check if it's working. As you can see
here, we're getting an error type undefined. It is not assignable
to type string. Actually it is a valid point
from TypeScript because our inland From want to get just a string and not
an undefined it here. This is why we can just
try it or empty string. This is totally fine. As you can see now we
don't have an error. Here we're getting
now my description, which is a real
description from our task. We can change it and hit Enter. And actually here
this is a textarea, So Endo doesn't help. We must click on Save button. And here we're getting
inside console update, task description,
my description, which actually means our
inland firm is working and our stream is also
providing data fast. And now let's talk
about columns. Actually it is even easier. We must just jump
in, say task model. And here we need this stream for the columns from our board. And actually we don't
need even to create additional property here
inside combined latest, I can simply write this
board service dog. And here we have a stream
columns and we can just pack array of columns here
inside this component, and we're good to go. So here we are getting our task. And secondly, columns
in here inside the object boop want to
return this array of columns. And now here we can
use these columns to build a select
inside our Markov. So actually here
we have a form and inside it we want to write a
select. Let's do this now. First of all, here will be S Select and we
want to write here form control name
because actually we need to bake
it in from Group. Why we're doing it? Because
actually it is much easier to work with
reactive forms if you have, for example, a Select. This is way here. Let's create a form group. And here we want to create, for example, column form. And here inside select, we can just pack
form control name, and it will be our column ID. And here we also must provide
the class column select. Now inside select,
we want to render all our options and it will
be our array of columns. This is way here,
option within G4, we're rendering it in here with just loop through our
columns because we have them in data dot columns and we don't need to bother
ourselves with streams here. Also here we want to use in G value and provide the
value inside R option. This will be column dot AD because we're looping
through them. Let's close this
option and just ran the inside every single
option at title. So here we will just run
the column dot title. Let's check if it's working. I'm jumping here
and we're getting an error and G value is unknown. And first of all, we
must create our form. This is why let's jump back
and set our component. And here on the top we must create a form for just
a single property. So here I will try that
we have our column form. And it is this FB. And actually we didn't
inject here if p. So let's do this now. If B is as always,
just form builder, and here we will write this F B group and inside
we will pass our controls. And here we have
just a column ID, but don't need anything else. And by default it will be. Now here we still
get this Sarah. And actually, because
we didn't inject reactive forms inside
our board module. So we must jump back and
set our board module. And right here inside the
input reactive forms module, as you can see now we're getting another error inside our HTML. And here we're getting
columns does not exist on type Task interface because actually we didn't
extend our interface. Here on the top we
have data stream and here we say that we just have a task and this is not true. We have here columns, and this is just an array
of our column interface, as you can see now inside our console with
don't have any error. So let's check this. Now I'm reloading the
patient here we have now our awesome select
and we don't see any value because we didn't
provide a default value. Now here we can open this select and we can choose between
different columns. And as you can see
this a directly our columns that we
have inside board. Now the only last thing
that I want to implement, setting default value
inside the select, because actually by
default we have a column and this task is
situated in some column. And we can easily do this
because we have streams. So here we can just use this task was stolen
because we have the stream. And here we know that we're
getting our task and we can just write Subscribe here and we're getting
access to our task. And now we need to
update our form. So here we have access to this
column form, which value, and if you don't know what
pitch value is doing, it update properties
inside form. So here we must provide
an object with fields. And in our case this is just
a single field column ID. The inside we want to provide an ideal for column
from that task. It is task dot column ID, which actually means
inside constructor on initialize where subscribing
to the Task Stream. And when we're getting
our task here, we're patching the value. As you can see now
instead browser, we're getting our first column. But what I don't like here, we're using subscribe
again without unsubscribe. This is why I want
to do exactly the same what we did
inside our board. First of all, here we must
create our unsubscribe. And this is in use subject where inside we're
providing void. And now here we want to
create in G on destroying. This is way here,
implements on this Troy. And somewhere after constructor
we can create in June, the strike and insight
with first of all, want to call this unsubscribe
next and then complete. So these unsubscribe complete. And after this we
should not forget before our subscribe Write pipe. And here take until here we're providing them with
these unsubscribe that we just created. In this case, we are on the safe side onDestroy
of this component. Our subscription will
be also destroyed.
45. Update task: In this video, we must
implement updating of our task from the
beginning to the end. And actually inside of a task we have three
different things. First of all, here we have an
inline form for our title, for all of the implemented it. And we're getting
here console log. Actually this is a partial
update of our task. We have exactly the same
logic with description. Here we can just change
it and hit Save. But also here we have a
changing of our column and actually we didn't handle it at all and we must do it. And this is also an
updating of the task. Most importantly, these are all partial update which
we must meet for our back-end because
we want to notify all our clients about
this change inside task, which actually means
that our logic will be exactly the same like
previously inside part, we're emitting something on
the client, on the backend, we do something to our database, for example, web data task. And after this, we're notifying all our clients who are subscribed to this
specific board. And the first thing
that we need to do is create a new socket events. Let's start this
time on the client. And inside our source
app shred types, we have a socket events. And actually here I
see tasks Create, and now we need exactly the
same with PS tasks update. And here we have success
and failure as always. And here we can change
the name to tasks, update, then update success,
and update failure. So our event is ready, Nobu must implement a new method inside our shirts service tasks, because as previously,
we want to hide our socket inside this
method inside share tasks. This is way here. Let's create our method
which will update task. Here. First of all, we're getting
our board ID because we must know what
users, we must notify. This is where hear birthday, this string will
also need a task ID because we must know what
task we must update. And last but not least, here is a list of fields. And here we will
have an object where all our fields
won't be mandatory. This is why I'm
writing them with question mark so we
can update our title. It is a string. We can also update
our description. It is also optional and string. And here we also have
our columnated and our column lady is where
our task is situated. And back here we're
getting void. The only thing that we must
do here is socket meet. This is why socket
service summit. And here we want to
use our new method, socket events in m dot. And we have here our tasks updates start here as
the second parameter, we must provide an object
with all these fields. First of all, it is
a border radius, then column ID, and then
our object with fields. And as you can see
here, I made a typo. It is not continuity
but task ID and because of TypeScript with
directly see an error. So now we can jump back and set our component and do
necessary changes. And here we have our
update task name and the date description. Here in both cases we want to call this update task method. This is way here on the
top with first of all, must inject these tasks service
that were just updated. And it is tasks service. After this, we can simply
write here this task service, and here is our new method
update task, and then say it. First of all, we
must pick a board AT this board ID here is of a task or do we
have here task ID? Yes, we have, we set at it
here inside the constructor. So here we can also
write this dot does KD, and here we have our fields. In this case we just need
to update the title. This is way here
we can say title, Task, Name, and as
you can see here, we're getting a strange
property column ID is missing in type, which actually means I
did something wrong. And they already can
see the problem here. Column lady is also optional. It must not be there always. This is where here is. You can see now we're
not getting an error because all our fields
can be undefined it. And here we just need
to provide a title. So here we updated
our task name. Now I can copy paste this code and update
here our description. Here I will just
provide a description, and this is our task
description from the parameter and now is
the most interesting part. We must handle this change
of this single select. And actually this
is a reactive form, which is actually
good because we can react on the changes in
completely reacted way. What I want to write here is
this dot column form Gad. And here we can
get our column ID, but actually here
we want to react to value changes and the
actual value changes will give us back
and observable, which actually means we
can write here subscribe. But as you can see here,
we're getting a warning from TypeScript that possibly
this field can be now, but not in our case, because in our case, it is always said this is
where actually here we can suppress this warning
just by using bank here. In this case, we're
saying TypeScript, don't bother with the check where sure that this
property is there. And as you can see now here, value changes is an observable. This is where here
I can now just try it subscribe and then getting here what is inside this column lady
after the change, which actually means eat this column lady
when it was updated. And now I just need to
write insert console log. Changed column id. And here we can check if it's really working. Let's
reload the page. As you can see here
inside console, I'm getting changed
column they D, and here is our column ID. And actually this is
a problem because what we want to do now
inside this subscription, we want to trigger a
change for the back-end, like we did here
with update task. But it doesn't make any
sense to trigger it at the beginning when we
have the same column id, we really want just too
tricky when we change it. This is why what
we can write here. We can check if
the column needy, what was changed is not
the same like valid half. But here too, the problem, we have this information
only inside our task Stream, which actually means we must combine these two
streams together. This is why here we can use combined lasers just
like we did previously. And here we can provide, first of all, this
task was taller. And here after this, this stream that we wrote
here on the bottom, I will just paste it
as a second parameter. And this combined latest
is given us back an array, which actually means here we
have access to our task and after this to our column
id from the form. And here now I can
console log both of them. Here I want to see first of all, changed column lady and secondly what we
have inside task. And actually here we want to
check task lot column id. Let's check this out. I'm reloading the page and
we're getting both eighties. And as you can see,
they are similar, which actually means
this is exactly the case when we don't
need to do anything. So what we want to write here, if our task column they D
does not equal our column id, then we need to do an update. Here we can simply
write this task service just like we did on the
bottom update task. And here inside were provided. And first of all, this
board ID, then task, do we have inside task dot id, and here is our fields. Inside fields, we just have
our column id property. So as you can see, Eric's JS really helps us to work together with
reactive forms, streams, our own streams, or a path which is
extremely efficient. Now we can remove
this commented code. We don't need it anymore. But what they want to do here, I want to write taken to deal with unsubscribed
in this case here, we won't have a
handgun subscription. This is where here we
must straight pipe taken till just like
we did here already. And inside these unsubscribe and actually our frontend part of the code is completely ready. Now we must try the standard
stuff on the back-end, which actually means we first of all must copy paste events. Then we need to create new subscription to
this event and then implement a new method inside controller to update a task. Let's do this now. First of all, I want to jump inside our socket events and
copy paste tasks update. After this, we can jump inside
of a server source types. Here we have our
socket events and on the bottom I can paste
these three new events. Now we can jump back
inside or sorority S. And here on the bottom we
can add new socket ton. Here we want to listen to
our socket events in m dot. And here we have our
tasks update here with don't want to call a
columns controller, but tasks controller. And here we have a new function, update task, and we must provide insight or
your socket and data. Now it's time to create
this update task. And actually I need to
copy paste from columns update because it will be super similar as you can see here
we have update column. I will copy, paste
the whole method and jump and say tasks, and paste it here on the bottom. Now let's change it here. We first of all have our update task and we're
getting here the data. So what did we get inside data? It was a board ID. Then we have here at task ID, and inside our fields, we have three fields. First of all, title,
it is optional. Then we might get
here description. It is also an optional string. And last is our column they D, which is also optional. Now let's update our
try and catch festival here we're checking for
columns update this is drawn, we need to change
it to our tasks. Update failure. Here we're sending user
is not authorized, were also must change
failure inside cage. So here we will have also
tasks update failure. And now here we must
change our model. The task model find by ID
and update is totally fine. And here we have our data dot, and here we have our task ID. In here we simply throw all
data fields that we have inside and it will be just updated with
what we've provided. Here back, we're getting
updated task and we want to send this updated
task to our client. And here we're providing
data board ID. This is totally fine and demand here is tasks update success. And here inside we're
providing our updated task. So we're fully finished
with our back-end part, but we must on the client, also write a listen to notify all our clients
about changes. So we must handle the
Summit on the client. And for this one must go back inside our clients source app, board Services, Board
service by here, because actually we simply
need to write a lesson inside our board
components with don't need to write listen inside
board model because actually bought model simply use
our streams from here, which actually means with just subscribe inside our
board component. In here we will call a method to update at task inside
our task stream. This is why here I
just want to look on our update column
because it will be super similar to
our update task. And actually we can
copy paste it fully. So let's change it now. Here we have our update task. We're getting here
are updated task, which is actually
at dusk interface. Here we need to do exactly
the same stuff with just need to update a single task
inside of our array. So here we're getting updated
tasks and here we're using our stream tasks get value and we're getting access
to every single task. Now here we're checking, okay, our task ID should equal
our updated task AD. In this case here
we must return, are spread that task and we want to update here just a title. And actually this is
not valid because here we need to
return more here. First of all, updated
task, the title, but also we must update here our description because it might happen that we updated it. This is where hear updated
task dot description and the last here will
be our column needy, and it is also updated
task dot column ID. It is looking now totally fine. And in other case, we just return our task. And after this,
we need to update our task stream with
some updated tasks so our method is fully ready
now we just need to go back and set our components
board, board component. And here we must create
one more lesson, lecture. I want to find here our cone update success because
it will be super similar. Here it is. I will just copy paste it. And here we need to change it. First of all, back,
we're getting our Task interface and here is Socrative and C num dot
tasks update success. Now here we have
our unsubscribe and back we're getting
our updated task. And now here we can just
call this method that we just created it,
this update task. And inside here we must provide our updated task that we're
getting from the backend. In this case here
with this code, we will update the
Task Stream for every single client who is
subscribed to our page. Let's check now if follow
a code is working here. We don't have any errors
inside front-end, no errors inside back-end. Now let's open our website
and actually I want to duplicate the tab so we can
check it with another client. So here we're opened
my first task and they just want to
update a title here. Let's just write
full and hit Enter. And as you can see, it was
directly updated here, here on the board. And also in the next step, it was also updated, which actually means
we successfully updated it on the backend, will notify the law clients with this listen
subscription where updated the stream and now
all places which are subscribed to the streams
off dramatically rear-ended. This is working amazing. Now let's check that we
can change a column. So here I want to select
the second column, updated and some number. And as you can see directly, this task is gone from
the first column, and now it is here
inside second column. It was updated inside our form. And here on the next step
We see my first task, which is also updated
in the next column. Which actually means
we successfully implemented updating of our task and also moving
it between our columns.
46. Delete task: In this video, we will implement the last feature
inside our project, and this is deleting
of the tasks. Let's do this now. First of all, I want to
start from our server here, but must jump inside our
source types socket events, and create three
new socket events. Because actually
we want to notify all other clients that
were removed a task. This is why we will do it
through socket events. This is where here I will
copy paste tasks update, just change it to tasks, delete. And here on the right we can
change it to tasks, delete, tasks, delete success,
and tasks delete failure. After that, we can jump inside those celebrities
and new socket ton. So here we're must name it dot, and here we have our tasks
delete, which is a standard. And here is our tasks controller where we're calling
our delete task. And now actually I want to copy paste our deleting
of the column. It will be super similar. As you can see here on the top, we have delete column
method and they will just copy it and put inside our
tasks here on the bottom. And now let's
change this method. First of all, does not delete
column but delete task. And what we're getting here, we're getting as always bored, they need to notify
all our clients. And here we must get
just the task Katie, to know what we need to remove. And here we have our
error and we just need to call here tasks delete failure. And here inside our cage, we can also call
tasks delete failure. Now here instead
of column model, we can just use a task
model dot delete one, and we simply delete our task by task ID
that we provided. And after this, we're not define all our clients with
this socket tasks. Delete success in here Beckwith, just need to give a task a D because we don't have
more information and this information
is sufficient for our client to understand
what task must be removed. Now we must continue with client path and
for this one must copy paste these
three socket events that we just created. I will go back inside
our clients source AB, shared types, socket events. And here on the bottom
we can add them. And now we must update or
service to work with tasks. And what we want to do here, we want to create a new method
which will remove at task. This is way here. Let's create delete task. And we know that we simply
provide here a board ID, which is a string. And also we need
here our task ID, and it is also a string. And back we're
getting here void. And now inside we can simply call our circuits
service dot image. Here is showing you event socket event
cinema dot tasks delete here as the
second parameter. We're providing our options, which is first of all board
AT and secondly, our task ID. And with that, our API
method is completely ready. Now we must jump inside of board module and board service. And actually here we need a new method just
like delete column, but it will be deleted task and actually the code
will be super similar because here we
just want to filter one specific task
from our array. This is where here let's
change it to delete task, and here is an argument
we're getting our task AD. This is where here we want
to loop through our stream, which is Task Stream get value here where I get an
access to every single task. And we're comparing our task k, d with the task id that
we need to remove. And here backward
getting updated tasks. And now here we want
to update our stream, which is our Task Stream Reserve and you updated tasks array. So we successfully created our delete task for
the board state. And now here I also want
to remove this comment. We don't need it anymore. Now, we must create some
HTML to remove our task. This is where we must
jump back and said our components task model. And here on the bottom of our
task model component HTML, we have actions, and here we
have a delete with an image. And actually now on this div, we can simply add the
click event and call here a new method which
will be deleted task. Now let's create this delete
task inside our component. Here we don't need anything, we just need this task
ID and this board ID, and we already have them. This is why here we
can simply write this task service
Dot Delete Task and we're providing them inside. So here this point
AT this task KD, so our clients successfully emitted this event
to the back-end. Back-end updated our task and
notified all our clients. But actually we must subscribe with the listen into
different places. First of all, we want to do it here inside our task model. And secondly in our board, and actually inside the board, we simply need to call this
method that we created Delete Task just so
we updated a stream. But what I want to do here, I don't want directly inside delete task to go
to our board page. Actually, it makes sense, but we're not sure that we
removed successful at task. This is why I don't want
to write code here. I really want to write listen, for example, inside
our constructor. For this, we must inject
here our socket service. This is where hear
private circuit service and we're getting here
our socket service. And now here in
South constructor for example, on the bottom, we can just write this
socket service lesson. Here, must provide that
by getting back strain. And this is the task k
d that we're removing. Now here we can use
socket events in m dot and where subscribing to
our tasks delete success. And here I want to ride
by taken till we're on the safe side and then providing insight
these unsubscribe. And after the pipe, we
can use our subscribe and actually don't care
about Tuskegee at all. We simply want to go too broad and already
created such method. This is, this goes to what? We have, this method directly
here inside this file, which actually means
when we are getting success and our model is opened, this code will
directly jump back to the board because we can show
this deleted task anymore. And now I want to copy
paste this code fully because we will
write exactly the same inside of our board. And here as you remember, we're writing all these
calls to our board service. This is where it makes a lot of sense to put this code here. And instead of this go to board, just try it here, this word service dot. And here we have our delete task where we're providing task ID. And in this case here we need this task AD that we're
getting from the backend, and they actually were fully
implemented this feature. Now let's check if it's working, but don't have any errors here and no errors
in the backend. Now, let's jump to the browser. Here I have my first task, so let's remove it now, as you can see
inside the actions, we have this delete and then
hitting here delete and this task is completely gone with don't see it here
inside the board, and we don't see it
inside second board. And actually on success
where successfully redirected to slash
slash board ID, which actually means
we successfully implemented this feature, Fool Live from start to the end.
47. Deployment: We successfully
finished our project with creating Trello clone, and now we must talk
about deployment. And typically deployment is not an easy task and a lot of
people have questions. How should the player project, how we will manage it, what service we must use for it. And this is actually a problem. There are hundreds of
different companies where you can pay money and they will
deploy for your project. But first of all,
you must compare all these companies understand how to deploy their project, learn their online
tools, and so on. It doesn't make a lot of sense. Actually, all these tools
at doing exactly the same. They are setting
up your project on production on a real server. And actually the best
variant to learn production and deployment is by deploying our project by
ourselves on our own server. This is the bare
bones of deployment. This is where it makes a lot of sense to learn how to do it. Also, it will be probably
the most cheapest variant, how you can host your project. Yes, It won't be free because you must
pay for the server, but you don't pay the company
to manage a server and you don't pay for some
graphical tools to applaud for you your project. This is where in this video, we must rent a server together, configuring it, and then
deploy that our project. And actually, if you don't
want to pay for the server, this is totally fine. You can simply leave
your project as it is. And checking said the video, how I'm doing it and
does a server company, I myself use hertz and this is quite cheap and
reliable server provider. And actually you can choose
any provider that you like. You just need access to
virtual private server. But I like hatsune because it is reliable and not that expensive. As you can see here,
we can click on the Cloud and check the
prices here on the bottom. As you can see, there are lots
of different packages and the minimum is here
for years and $0.15, which is quite cheap for
the month of 20 terabytes, tragic two gigabytes of
RAM and CPU Processor. And actually, I am using this smallest server for
two projects in production, but quite a lot of
people are coming. And if you optimize
your projects thriller, good, then you are totally
fine with a small syrup. This is why in this video we
will rent exactly the syrup. So now the first step
is to register just inside her son a comb or any provider that
you would like to. I already have here
an account and after registration and
confirmation of your email, you will see such page. Actually here, this service in your profile will be empty, but this is my own web server. Here I want to click
at server because they just want for the
sake of this course, create a new server. So the first one here is
location, doesn't really matter. We can choose here Helsinki
or whatever you prefer. Image wound is totally fine. We don't need to
choose anything. Here is type standard,
totally fine. And here, the smallest tier, which is for yours, $0.15. We don't need to
change anything. We don't need volumes,
Networks, firewalls, additional features, SSH key, and here just a name. We can name it here, l trailer, just like our project so we know what we're
talking about. And as you can see here with
didn't select our SSH key, which means we
will get an e-mail with root user and the password, which is totally fine fast. I'm hitting here
create and by now, and our server will be created. As you can see, my server is already the green and running. And actually in say, the email, I got my credentials
where I have a root user and the
password for our server. Now we must jump inside
console and write SSH. And here will be first of all, our root user add. Then the IP address
that we have here, we can just click on
it and it will be copied and they need
to paste it here. So we have here SSH, root at, and here the AP artist. I'm hitting Enter and
we're getting the message. I assure you that you
want to continue. Where right in here, yes. And hitting Enter,
as you can see here, we're getting, first of all, the question regarding
our password. So we need to take a password from the e-mail and paste here. And after this, we're
getting lots of information regarding
our server. And we start the
process of changing the root password
and actually to change the password
with first of all, must provide a current password. This is why I'm pasting it
again and hitting Enter. And now we must provide a new
password for our root user. And we're doing it just so Kasner doesn't
know our password. Here, I will provide just 123. And once again, 123. And as you can see,
we're getting a message. You must choose a
longer password. Let's make it 12345678. And once again, and actually you must understand that for the
real production project, you should make some secure
password and not like this, this is just for the testing. So if you have such
output with root at L, Trello and hash here, it means that you've
successfully, we look inside the observer. Here we can do something. Here I want to mention
something important. We're not talking inside
this course how to manage servers
efficiently and securely. Which actually means I
will show everything, the whole deployment
with our root user. Typically in a real
production project, you don't want to do
everything with the root user. You want to create
another user with limited permissions that
can just deploy a project. So again, using route
on production is bad, but if you do it as
your pet project, this is totally fine. Our next step here is to bring our project inside the syrup. And actually there are
lots of possibilities for this and I want to
use the easiest here. What we can do, we could just push our
project to GitHub or GitLab, whatever you prefer, and then clone this project
inside our syrup. This is really
efficient because you can do some changes
to a project. You will for sure one
to store and update your project inside
Git repository, which actually means
every single time when you want to
update your project, you simply jump here inside
your console and said Sarah, you just try to get pool to pool your project and
then you restart it. This is it. I hope you
already didn't know how to deploy your project
to get lab or GitHub. But if you don't
hear a short steps, I prefer for my own
projects to use MATLAB, but it is just my
personal preference. Here I already locked in inside GitHub and they
clicked Create New Project. Now here I just need to
click Create blank project. And here we can write some name. For example, we can name
our project L trailer, and we don't need to
change anything here. And actually by
default in GitHub, we're getting private for free, and this is awesome
for our needs. Now here inside our project URL, we must choose our namespace, and they will choose the
namespace of my user. Now I just hit Create Project here and our project is created. And here we're getting
some steps how we must bring this
project to get lab. Now we must jump
inside the console of our project and
write good in need. This will initialize
good for our project. I'm hitting here and then getting a message
that my folder is already a Git
repository because they already have my
project inside good, typically you won't
get such message, but just the message that we
successfully initialized. Good inside this repository, Our second step
here is to deploy all our files inside MATLAB. But the most important
point here is that we must add to get ignore all node modules inside client node modules and
insights Server Node modules. We don't need to upload all these libraries
inside Git repository. This is why we must create dot gitignore file
inside server. They have here node modules in this and also inside client. Here, as you can see, I
also have dot gitignore, and here we have lots of stuff. It was created automatically by angular with don't need
to change anything here. Here node modules signaled
and this is ignored, which means actually we
must change just server. And here is our get ignored. After this, we must jump in
set console here, right? Git add dot and it will add
all our files to the good. Now we need to create our
first commit for this, we can try to git commit am. And here, for example, finished project, as
you can see here, I'm getting a message, nothing to commit, but it
should not be your case. In your case, you will get
like hundreds of files here, which were already
created in this project. Our last step here
is you can see is add this line, git remote, add origin HTTPS, and
then the full path, I will paste it now here. And with that, we successfully binded get lip to
this repository. And our last step here will be this git push minus
uf origin main. And actually as
you can see here, it is written in main. But for me by default I
have a branch master, which is totally fine. This is why I want to
rename main to master. It is good. Push
minus uf origin, master them hidden here, Enter, as you can see here, I
must provide first of all, a username of my GitHub user. And secondly, the password is, you can see here I'm
getting a message is to deeply basic access denied, which actually means
it didn't happen. But here we're must create a
personal access token with redeposited right
repository inside GitLab. If you're using
GitHub for example, maybe it worked for you. If not here we can jump to this link slash profile
personal access tokens. As you can see, I opened it
here and here we just need to create an access token
so we can push to GitHub. And here for example, it can
create trello talking name. And here we must select scopes. For us. We must create right repository
and read repository. This is the most important. I'm hitting here,
create access token, and here we're getting on the
top our new access token. And fortunately now we
must change our remote again because of this access
token for this table, right? Good remote Ashram or region, which will remove
this origin that we created here on the
top with this line, and this will be removed. Now I want to copy paste
this line and this is how we're doing it with access
token inside GitHub. As you can see here
when writing git remote add origin HTTPS. Here we must first of all
provide access token name. This is why here we created
l trailer as our token. I will write this
here, ultra law, and now here we have colon
and our access token. I will copy paste it also from our page where
we generated it. And after this we
have exactly the same what we have
here on the top. For me it is https github com slash my nickname
slash L dot. Good. This is where here I will
write exactly the same. And then my repository, I'll Trello, lets
check this out. We're trying to push
again to our repository with git push origin master, just like we did previously. We're hitting here. And as you can see, when not questioned for
our username and password, because now we're pushing
with our access token. And as you can see, we
don't have an error. Here. We're getting that everything is resolved
and now we're successful at boost to the
master branch inside origin. As you can see now
here inside of our project we have
a master branch, and here are all our changes. We have here two folders,
client and server. And here inside we can see the whole project that we build. And now we can applaud
our code to our server. This is why here I
want to jump back inside our server
that we just created. And as you can see, I
am inside the syrup. So first of all, here
I want to create a new directory which
is called projects. Now, I want to jump inside this directory project and
clone this project here. And actually here we can try and use with same access
token that we just used. We can just write
inside console, get chmod minus version
inside the local console, not inside the server. And here is our remote
with this token URL. And actually we can
just copy paste this URL and use it here inside syrup here I just want to write git clone and then this
URL I'm hitting here. And as you can see, we don't have any
permission problems. And we successfully cloned our project here
inside projects. And here now I can write Ls. And as you can see, we
have the folder L trailer, which actually
means we successful abroad our project
to our server. Now to proceed, we must update all packages inside our server. And for this, we
can simply write apt-get update and hit Enter. As you can see, all
our packages where updated and now I
want to install, first of all in jeans
and secondly MongoDB. So what is in jinx? This is our future web server which we will use
for our project. This is where here we can
write EPT in style and jinx. Here we're must hit yes, to install this package. As you can see, our package
was successfully installed. Let's check if
it's working here, we can simply write
service and jinx status. And as you can see,
I'm hitting Enter and we're getting quite a
lot of information. First of all, what is in Jinx
and here, active running, which actually means it was successfully installed and it is running now our next step
is to install our database, and it was MongoDB. This is way here, apt install Mongo DB and we're must
confirm it with yes. Now let's check the state of our MongoDB for this
weekend, right, service then Mongo DB status. And as you can see, it is
also active and running, which means everything is fine. The next tool that I
want to install is called n. And actually
we will use it to manage our node version because actually it
might happen that we want to update node and it is much easier to do it
by using this tool, especially if you have
several projects and you need to switch between
different node version. This is where I will paste
this line inside console. So it is curl minus
l and this URL, it is Gita, your slash
n in style bash. I'm hitting Enter and
we're getting an error. This is why what we must do. We must first of all
execute apt install. And here is built essential. I'm hitting Enter and
we must confirm it. Now as you can see, it is installed so we
can try and install. And again, I'm hitting
Enter and put. Don't have any errors. Here. We can simply write yes
and proceed to install it. As you can see here, by default, n installed for me, know, 16151. And actually this is
exactly the same node that they have locally. And they always highly
recommend you to have the same node version on your production server
and in local environment. In this case, you will avoid
hassles and magic box. And the last two that we want
to install is called PM2.5. This is special
manager to restart our node processes and we will use it to
start our beckoned. This is where here we can
write npm install minus g, which means globally VM2. And as you can see here, we're getting an error
command npm not found, and the same for
command node not found. If I'm right in here,
node minus version. And actually after
installation of n, both domestically and
getting Node and npm. But we must restart
our terminal. So we must disconnect from
the server and connect again in order to bring
known chance inside path. This is where we can simply
write exit and then again, use SSH root and our IP address. Keaton here Enter and we must
provide our new password. And as you can see here, I'm inside and now I can
write node minus fashion and then getting the
version of the node and the same with
NPM minus version, which actually means
now we can install TM2 again with npm
install globally VM2. Now we don't have an error and our PM two is being installed. Now, we must install all our packages for the
client and for the server. And for this I must jump
again inside projects folder. And here as you can see, I have our ultra lab project. As you can see here, I am inside the trailer and they
have client and server. First of all, I want to
jump and say server. And here we have a lot of files, so we can simply write and
payments style and it will install all the dependencies
for our server. As you can see, everything
is installed now we must jump inside our client. So I'm writing CD client and after this npm
install again, and we will install all our front-end
packages for Angular. So all our packages where successfully
installed and now we must create a configuration
for nginx for our project. And for this we
can write cd slash UTC and jinx Cohen's d. And here inside we must
create a new file. And for this we
can write Tij and then L trailer dot com.com. And actually here to
open and edit file, we must use some editor
from the console. And I will write here nano and
then the name of our file, which is the old
trailer, calm, calm. I'm hitting here and this is how this editor
is looking like. So here we can simply type
something and then later safe. And actually here I
want to already paste the conflict often jinx the
type prepared previously, and this is how it looks like. So here we have a server blog. Now inside we have listened AT, which is a default AT part. And here is where our root
of frontend is lying. Here is slash route
slash projects where already created it. Here the folder is strong. It is actually Ultra low
folder that we just created. Then slash client
slash and slash app. And actually we must build also later our client after
installing packages. So our type script will be
converted to JavaScript, and it will be here inside
this folder, inside slash app. This is why now it
is totally fine. Here we're resolving our index.HTML and here
the server name, the surname is really important because actually
this is our domain. This is L.com and
www L Trello.com. And actually we don't
have a domain at all. And this is totally fine because actually we can inside
our local machine, inside host file, just write an APR address
of the server. In this case, we
don't need to bother with strategy soon
as real domain, and it will work exactly
like with the domain. After this we have location. So we're trying always
to load our index.HTML. And here we have
location slash API. And actually this is important
because here we're saying, okay, When we're champion
in L Trello.com slash api, then we must proxy
our request to this web server and
this web server we will be using
inside our backend. This is local host
for 1001 slash API. After this, we have exactly the same but for socket IO requests. And here we have http
localhost 4,001, and here we have some headers. And actually this
is the whole config that we need for In Jinx. So now the question is how
we can save this file? And for this, first of all, we want to click Control O. And here on the bottom
we see filename to write ys l Trello com conf, where just hitting enter. And this is totally fine. It was saved. Now here I want to
click Control X, and now we're out, we're back inside our console. And now if you need to check if everything was
saved successfully, you can write cat and
then I'll Trello, I'm hitting Enter and we're
getting inside the console this nice output with
the content of our file, but it is not all
were also must change a user inside the
genes configuration. This is way here. I want to go out from this folder in here I
want to write again nano, but in our case now it
will be in jinx.com. And actually this is the default configuration,
often Jinx. And here we want to change
just a single line. Here on the top we have
a user data and we want to change it to user route with which
we're locked in here, and we need to save
this file again. So Control O Enter,
then Control X. Now we must restart In Jinx to apply the configuration
that we did. This is why we can write
service and jinx restart. I'm hitting Enter and we
don't have any errors. And now we should not
forget to build our front. And this is why we
must jump back inside slash route slash projects. And here is our L
trailer and Client. And actually we're
inside client. And to build our
Angular project, we can simply run
npm, run build. And as you can see here, we're calling in GI Bill, which is the command of Angular, and it will build for us the whole project
inside this folder, as you can see here,
after building, I'm getting quite a lot of
errors inside the console. And actually the main problem is that it is a
production build and the payroll does not exist on the type production Boolean, which actually means we didn't
update our environments, Config inside our project. This is where it is real and
nice that we have now good, so we can quickly
make adjustments. Here I want to jump back
inside our project and we're interested in our client
part source environments. Here we have two files. Environment, yes. And on the right, I will
open environment protest. Here. As you can see on the left, we provided API URL
and sockets URL, but we didn't provide
them on the right. And actually I will
just copy them from the left to the right here. Now, we have this properties
inside our configuration. After this wound must
deploy these changes to the good for disabled jump in
the local project, right? Git add dot, it will add just all files locally
and after this commit. So git commit minus m and for example, updated
environment. I'm hitting here, enter
our commuted them. After this we can simply write git push origin
master and hit Enter. And our changes are already
inside GitHub or GitLab. After this, we can
jump back inside our server, which was entered. And here I want to go
out of the client. And here I am inside our
little trailer folder. Now I just tried Git pool and we're bringing our changes
as you can see here, environment protease
inside our server. And after this, we must
try again to run build, and it is npm run build, but we should not forget, we must jump inside
client, as you can see, no such file or
directory package, Jason, because I'm
not inside client. So the client and
here npm run build. Now as you can see, we don't
have any errors and we're getting a message that
everything was completed. And actually here we must check what we have inside
our dist folder. So ls decreased. And as you can see here, we have all trailer and
inside all Trello, we have all these files. First of all, our index.HTML, and then our assets, mangers and so on. But actually it
means that we have a small problem
inside our config inside and jinx because
there was rolled our root folder, this slash app. And in our case here we
have a decreased slash, I'll Trello, which actually
means we must update it now. So we must try Nano slash, ETC. And jinx Cohen's d. And here we have our
L Trello Comic-Con. I'm hitting Enter and
again opened our config. And here inside route we must
try this slash l Trello. Now I'm hitting Control O, Enter Control leaks and
we must restart and jinx, so service and jinx restart. So now our frontend
is completely ready and we simply need to run our backend and we don't
need to start somehow our fronted because in
jinx will do it fast. But for begun, it won't because actually
inside our front-end with just have a static files like HTML, JavaScript, and CSS. But inside our backend
we have our observable. This is why I want to
jump inside our back-end. So here CD server, and here we need to start
our server with PM2.5. But actually here we also have a problem inside our server. We have TypeScript
with don't have JavaScript files and
actually womb must convert all our TypeScript to JavaScript before we will
start to run it without PM2.5. And actually for this one
must create in your command. This is why we must jump
back inside our project and go inside the
server package, Jason. And here we have a
script for start, but we don't have a
script for Build. This is why here we
can simply write build and here the
script will be TAC. So what is TAC? Actually it is a TypeScript
and it will simply transpire the whole code that
we have inside this project with
this ts config. As you can see here is output, it is dist folder. This is completely fine fast. But after we did this changes. We must jump in set console and do exactly the
same stuff again, first of all, get head, then git commit,
and then git push. So with this commands, we're bringing our changes
inside get wrapper. Now we're jumping back
instead of a survey. Here we're going out and I'm
pulling my project again. So we're good to go now, we can jump back and
said, oh, server folder. And here we can try to
build our back-end. This is where here I can
simply write npm run build, and it will transpire my type
script to my JavaScript. As you can see here, it is done. Now we can just try it a less dist and
check what we have. And as you can see here, we see all our files that were created, but with js extension, which actually means
this is totally fine and we're must start dist slash server.js and
it will start our project. We want to use PM2.5. This, this is where
we can simply run PM to space, start space. Here will be decreased
slash search. Yes, I'm hitting Enter and
we're getting lots of output. As you can see here, this is
the whole output of PM two. It doesn't matter, but
here on the bottom we see spawning PM to demon. It is successfully
demonized and we're starting out with this
surges in 4k mode, and it is done and
here is our server. And actually why we're
using here P M2, not just note process we could write here node and
then for example, the surge, yes, this
is totally fine. But pm tubule, first of all, risks start for us, this web server if it is
broken for some reason. Secondly, PM two is better suited for the
production applications. So what we did
here, first of all, we successfully built
our client and secondly, we started our backend. And the last step that we
want to do is open a browser. But we can simply open a browser because we must
point our domain, L Trello.com, which
does not exist. We didn't buy it from our
local machine to our server. And actually here I must jump
inside the console locally. And open host file, for example, inside Linux and macOS, it is line in slash,
ETC slash hosts. So here how it
looks like for me, if you're on Windows, here is your path, it is Disk C, Windows system, so the two drivers, ETC, host and you're
opening this file and this is exactly
like my file here. And as you can see here, I already tested this project. This is why I have this line. So here I have a
domain, L Trello.com. This is what we're registered, but this is the old API address. I don't need it. I must jump back inside
my panel of head SNA and copy this domain
and just paste it here, which actually means on our local machine when
we're jumping inside the ultra low-dose come when not looking in DNS lookup table, we're just pointing to our
IP address of the server. So now the moment of truth, Let's open our project. I am writing inside
browser L Trello.com. And this, you can see
this is our project. It is working. And actually I want to open here console so we can check
if we have some errors. And actually here we have
a narrow at local host for 1001 slushy ice lift
user from origin, I'll Trello and actually
as you can see, network, this is
our request to API. This is http localhost 4,001
slushy pairs plus user. And obviously this is strong. This is not what we need to use. This is why we must jump
back inside our project and go inside client
locally and change it. So here is our client's source, environment and
environment protest. Here. Obviously this local
host does not exist. What we have now here
is a CTP L Trello.com slash API and the same
here, http L Trello.com. And then we don't need
to provide here a port. And now we must commit
these changes again. This is why git add git
commit and then git push. Now we want to jump
to our server, go out and say Trello
folder and right, get ***. But after this, we must build our client again because
we changed our client. And in order to do this, we can write c, d client. And now again, npm
run build and it will simply generate fast
static files for the frontend, our project is
successfully built. I'm reloading the page here
and we don't have an error. We have here for 01, for HTTP L trailer com
slash APIs slash user. This is totally fine. We're not locked in. Now let's try to register user. So here I am writing
f2 at gmail.com. Here is our username and password 123 hidden
here register. And actually it worked. And this is important to check because it means that here we successfully configured
MongoDB because this post request is go into the API and this
is our response. And here we're
getting back the a d of saved user and
here the token. So our backend and
DPI also works. Now here we have a board and here I will create
my first board. Let's hit Enter and
we're getting our board, we can open it. And here we're inside the board. Now let's check if our socket
IO is working for this. I want to duplicate that tap here and try to create a list. So here for example, first list, I'm hitting here at least, and we're getting the list. And also on the next step, we're getting exactly
the same list. Now here on the second term, we can create a new card. For example, first
card, I'm hitting here, add and we're getting
this card on both pages. Now here we can open our model and it is
working as intended, which actually means
we successfully deployed our project
to production. It was not easy, but this is a bare bones of
any project you can deploy. It doesn't matter what
angular view react. Any client that you want, plain JavaScript or any backend, it will always be
really similar.
48. Homework: My congratulations, you've successfully
finished this course, and I really think that you are awesome and you learned
quite a lot of stuff. Now you for sure learned how to create your
full-stack project with Node.JS on the backend
and express as a service. Also, you can for sure
easily use socket IO because we used it
quite a lot on the client. You again, lots of experience regarding n cooler TypeScript, great and interfaces and
creating a good reactive state. But obviously it is not the end. As a good teacher, I
must give you homework because actually if you want to improve your skills
as a developer, you must learn and create
your own projects. And here you have
two possibilities. You can create your own
project from scratch. This is what I really recommend, or you can implement
features in this project. And actually there are benefits
in both possibilities. If you continue to
implement this project, you already have a
really good architecture and it will be much
easier for you. And actually the amount
of feature that you can implement in this project
is really tremendous. First of all, you can implement
here different roles. For example, like admin users who can create, for
example, teams, which actually
means you start to organize people inside Teams, just like in the
real Trello also, for example, on the front end, you can implement drag and drop for tasks inside the board. The easiest variant for
you would be to open the official trailer
application and just look on their set
of features and just implement something
which is interesting for you. And actually, this
is super important and it is related to
your own project. If you plan to do
your own project, I highly recommend
you to implement something that you're
really interested in. Because if your project is
not interesting for you, you won't do it long
period of time. And if you're looking for
ideas for your own projects, disrupt plenty of them. For example, you can implement
an e-commerce shock, or maybe a bookstore, a clone of netflix, or a financial application
to manage your expenses. I really hope that you liked
this course and you learned a lot and we'll see
you in my next course.