In this part, we will create the menu items APIs in node.js and dynamically load them instead of the hardcoded menu (Home, Services, Our Clients, etc.). We will then update our client application to load the menu through API and create the menu links accordingly.
Introduction
In this part, let’s move forward and create the Menu APIs to add, load and update the menu items in the database. Later in future parts, we will create the admin section in the client-side to manage the menu and secure our admin section by implementing the admin user login and log out.
Let’s Start
- Let’s first create the Menu APIs in the server application but before that, create the menu model. In the server application, right-click on the models folder and select the New File option, enter the file name menuMdl.js. Add the following code in the newly created file:
JS
- The above code is well explained in the previous part, just a brief description, the mongoose is a MongoDB ORM (Object Relational Mapping). You can map MongoDB document elements as a model and specify the data validation rules. Also, it provides a useful method to query the data. You can go to the mongoose official website to learn more.
- So far, on the menu, we are saving menu name, code, order, parent menu(in case of child menu) and other helping information that is not directly used but good to save in DB.
- Next, let create the API routes for the menu to get, add, update and delete Menu. Right-click on routes folder and select Create File, enter the name menu.js. Add the following content in it:
JS
- The above code is quite self-explanatory, we are creating CRUD APIs for the menu.
- GET API: Will pull all menu from Menu collection and sort it by OrderBy field ascending. We are also saving the menu status, so if you want to get only active menus, you can add the condition here. I am skipping that condition, for now, to keep things simple.
- POST API: This is created to add a new menu. This API will get the Menu information from the client-side and map it to the Menu model object we created in mongoose. We are explicitly assigning Date Added and Updated here, you can also set the default date option in the Menu model while declaring it. Take it as an exercise and try to implement it. We will do it end while refactoring the code.
- PUT API: This is created to update the existing Menu record in the database.
- DELETE API: This is created the delete the Menu record from the database, it will first search for a record by calling a
findById
method and delete the found record. - For all APIs, we are creating the custom JSON response object with success variable as
true
orfalse
based on the response type andmsg
variable. We would use both variables on the client-side to show a message to the user.
- Next, we need to add the Menu route in main class mazharnco.js as we did for contact API earlier, edit the mazharnco.js and replace it with the following code:
JS
- Just a minor update, we added the Menu router reference and then add the route path “/api”, so now menu APIs can be accessed from the server through http://localhost:3000/api/menu
- That’s pretty much it on the server-side, for now, let’s update our client-side to load the menu dynamically from a database. We need to update DataService, AppComponent class and it’s a template.
- Let’s go to the mazharncoweb folder that is our client-side application in Angular. Edit the src -> app -> service -> data -> data.service.ts and replace it’s content with the following:
JS
- In the above code, we added one more function for GET API that will load the data from given HTTP API URL, just FYI, we will keep refactoring and updating this file in upcoming parts.
- Before updating the AppComponent, let’s create the Menu interface first to store the Menu fields or element (whatever you want to call them), right-click on src -> app -> model and select option New File, enter the file name menu.ts and add following code:
JS
- This interface is exactly matching the Menu model we created on the server-side in the Node.js application through mongoose schema function. So it will easily map fields returned by APIs from the server-side. If you are a .Net developer, I am thinking about the AutoMapper library now.
- Next, Let’s update the
AppComponent
, go to the app -> src -> app.component.ts file and replace its content with the following:
JS
- Though the code is quite self-explanatory let’s go through the updates;
- we are creating two Menu interface type list to keep the main menus that would display on Navbar and simple menus that will hold all menus records. You will understand why we are doing it once we will update the AppComponent HTML template.
- In
ngOnInit
lifecycle hook, we added our GET API call to load menus from a server-side (i.e. from MongoDB).GET_ALL_URL
is defined in the starting of class and we are subscribing to Data Service get function since it returns IObservable that cannot execute until we subscribe to them. - Next is a
getChildMenu
a function that is takingmenuCode
as an input parameter and returning the corresponding menu item from the menus list that we populated through get API (step 1).
- Next, let’s update the
AppComponent
HTML template, edit the app -> src -> app.component.html and replace its content with following code:
<div class="main_div">
<mat-grid-list cols="10" rowHeight="2:1">
<div>
<mat-grid-tile>
<div style="padding-left: 5px;">
<a [routerLink]="['home']"> <img src='assets/images/logo1.png' class="mnc-logo" /></a>
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]=8>
<div class="socail_btn_padding">
<a target="_blank" href="https://www.facebook.com/mazhar.mahmood.16"><img src="assets/images/fb_btn1.jpg" class="socail_btn image" /></a>
<a href="skype:plug-shop?mazharmahmood786"><img src="assets/images/skype_btn1.jpg" class="socail_btn image" /></a>
<img src="assets/images/twtr_btn1.jpg" class="socail_btn image" />
<img src="assets/images/lkdin_btn1.jpg" class="socail_btn image" />
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]=1>
<div style="text-align: right">
<img src="assets/images/login_btn.png" class="lgn_btn image" />
</div>
</mat-grid-tile>
</div>
<mat-grid-tile [colspan]=10 [rowspan]=3>
<ngx-carousel [inputs]="carouselBanner" [moveToSlide]="1">
<ngx-item NgxCarouselItem>
<div><img src='assets/images/banner_1.jpg' width="100%" /></div>
</ngx-item>
<ngx-item NgxCarouselItem>
<div><img src='assets/images/banner_2.jpg' width="100%" /></div>
</ngx-item>
<button NgxCarouselPrev><</button>
<button NgxCarouselNext>></button>
</ngx-carousel>
</mat-grid-tile>
</mat-grid-list>
<div>
<nav class="navbar navbar-default navbar_shadow">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a [routerLink]="['home']">Home</a></li>
<li class="dropdown" *ngFor="let menu of mainMenus" style="cursor: pointer;">
<a [routerLink]="['page', menu.MenuCode]" class="dropdown-toggle" role="button" aria-haspopup="true" aria-expanded="false">{{menu.MenuName}}<span *ngIf="getChildMenu(menu.MenuCode).length>0" class="caret"></span></a>
<ul class="dropdown-menu" *ngIf="getChildMenu(menu.MenuCode).length>0">
<li *ngFor="let chldMenu of getChildMenu(menu.MenuCode)">
<a [routerLink]="['page', chldMenu.MenuCode]">{{chldMenu.MenuName}}</a>
</li>
</ul>
</li>
<li><a [routerLink]="['contact']">Contact Us</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
</div>
<div class="clearfix" style="padding-top:10px; padding-bottom: 10px">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
<br>
</div>
- We already learned about the header code, here let’s try to understand the navbar update that was hard-coded links before but now we are dynamically creating it.
- We are looping through the
mainMenus
list and showing it on top and in the inner loop, we are calling thegetChildMenu
method by passing the current main menu, if the menu main has child/children menus, it will loop through it an create the link accordingly. [routerlink]
the directive is taking the first parameter a router and second is the input parameter (kind of query string), we need it to determine what page to load from the database since page content would also come from database eventually.- Hopefully, now
AppComponent
class methods would start making sense to you. - One thing at a bottom is, a footer component that was not there, I just created the separate component since this page was getting too big.
- An exercise for you is to create a sperate component for the header.
- We are looping through the
- let’s create the
footer
component, revise the Angular CLI commands and try to create the footer component in the src -> app folder. - Right-click on the mazharncoweb folder and select the option Open in Terminal, Integrated Terminal would appear on the right bottom side, enter the command:
ng generate component footer --flat
- Edit the app -> src -> footer.component.html page and replace its content with the following:
<div style="padding-top: 30px">
<mat-card>
<mat-card-header class="header">
<div mat-card-avatar class="vst_img"></div>
<mat-card-title class="header_title">Visit Us</mat-card-title>
</mat-card-header>
<mat-card-content class="address_card_content">
<div class="address_card_content_div">
<div class="col-md-6">
<agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom" [disableDefaultUI]="false" [zoomControl]="false">
<agm-marker *ngFor="let m of markers; let i = index" [latitude]="m.lat" [longitude]="m.lng" [label]="m.label">
<agm-info-window>
<strong>Mazhar & Co. Office # 19, First Floor, Mall Plaza, Mall Road
Rawalpindi, Pakistan </strong>
</agm-info-window>
</agm-marker>
</agm-map>
</div>
<div class="col-md-1"></div>
<div class="col-md-5 address">
Email:<a href="mailto:mazhar_rawalpindi@yahoo.com"> mazhar_rawalpindi@yahoo.com</a>
<br>Website: <a href="http://www.mazharnco.com"> www.mazharnco.com</a>
<br> Skype: <a href="skype:plug-shop?mazharmahmood786"> mazharmahmood786</a>
<br> Cell: <a href="tel:+92 333 5104584"> +92 333 5104584</a>
<br> Tel: <a href="tel:+92 51 5680138"> +92 51 5680138</a> & <a href="tel:+92 51 2291858"> +92 51 2291858</a> <br>
<hr><b> Head Office:</b>
<br> Office # 19, First Floor, Mall Plaza, Mall Road
<br> Rawalpindi, Pakistan
<br> Tel & Fax:
<a href="tel:+92 51 5562241"> +92 51 5562241 </a><br>
<hr><b> Branch office: </b>
<br> H # 24, St # 54, F-11/3
<br> Islamabad, Pakistan
<br> Tel: <a href="tel:+92 51 2291858"> +92 51 2291858</a><br>
</div>
</div>
</mat-card-content>
<mat-card-actions class="main_footer">
<div>
<div>
© 2018 Mazhar & Co. All Rights Reserved.
<button mat-button>Privacy Policy</button>
<button mat-button>Contact Us</button>
</div>
<div class="main_footer_cred">
Designed and Developed by <a style="color:#bfc4cc" href="https://fullstackhub.io" target="_blank"> Fullstack Hub</a>
</div>
</div>
</mat-card-actions>
</mat-card>
</div>
<br>
- We already learned about it in previous parts so let’s not waste time on it, edit the footer.component.ts file and replace its content with the following:
TypeScript
- So our
AppComponent
andFooterComponent
are done and we are also pretty much done with this part. Run both client and server-side application and test it. You would only see two menu items Home and Contact Us those are hardcoded in AppComponent HTML template because our database collection is not created yet, we need to create the Menu items through our Menu POST API that we will be doing in upcoming parts in Admin section. - Since we don’t have admin section yet to insert menu from the application, you can call Menu POST API to insert menu using Postman, this would be a good practice how to use Postman, so after running the application, make a POST call to http://localhost:3000/api/menu and in the body section, add following JSON object:
JSON
- In the next part, we will update
PageComponent
to dynamically load page content from the database and also we will plan our admin section. Till then keep practicing the menu APIs through Postman.