Part of this process includes moving the data back down the MVC flow, from the views into the controllers. The example in chapter 4 deals with this task in the Loc8r homepage, but it needs to be done for the other pages too. Start with the Details page.
The Details page is the largest and most complex of the pages, with the most data requirements. The first step is setting up the controller.
The controller for this page is called locationInfo and is in the locations.js file in app_server/controllers. When you’ve analyzed the data in the view and collated it into a JavaScript object, your controller will look something like the following listing.
const locationInfo = function(req, res){ res.render('location-info', { title: 'Starcups', pageHeader: {title: 'Starcups'}, sidebar: { context: 'is on Loc8r because it has accessible wifi and space to sit down with your laptop and get some work done.', callToAction: 'If you've been and you like it - or if you don't - please leave a review to help other people just like you.' }, location: { name: 'Starcups', address: '125 High Street, Reading, RG6 1PS', rating: 3, facilities: ['Hot drinks', 'Food', 'Premium wifi'], coords: {lat: 51.455041, lng: -0.9690884}, 1 openingTimes: [{ 2 days: 'Monday - Friday', opening: '7:00am', closing: '7:00pm', closed: false },{ days: 'Saturday', opening: '8:00am', closing: '5:00pm', closed: false },{ days: 'Sunday', closed: true }], reviews: [{ 3 author: 'Simon Holmes', rating: 5, timestamp: '16 July 2013', reviewText: 'What a great place. I can't say enough good things about it.' },{ author: 'Charlie Chaplin', rating: 3, timestamp: '16 June 2013', reviewText: 'It was okay. Coffee wasn't great, but the wifi was fast.' }] } }); };
Note the latitude and longitude being sent through. You can get your current latitude and longitude from https://www.where-am-i.net. You can geocode an address—that is, get the latitude and longitude of it—from https://www.latlong.net/convert-address-to-lat-long.html. Your views will be using the lat and lng to display a Google Map image of the correct location, so it’s worthwhile doing this for the prototype stage.
As this page is the most complex, data-rich page, it stands to reason that it will have the largest view template. You’ve already seen most of the technicalities in the homepage layout, such as looping through arrays, bringing in includes, and defining and calling mixins. You have a couple of extra things to look out for in this template, though, both of which are annotated and highlighted in bold.
First, this template uses an if-else conditional statement. This statement looks like JavaScript without the braces. Second, the template uses a JavaScript replace function to replace all line breaks in the text of reviews with <br/> tags. You do this by using a simple regular expression, looking for all occurrences of the characters in the text. The following listing shows the location-info.pug view template in full.
extends layout include _includes/sharedHTMLfunctions 1 block content .row.banner .col-12 h1= pageHeader.title .row .col-12.col-lg-9 .row .col-12.col-md-6 p.rating +outputRating(location.rating) 2 p 125 High Street, Reading, RG6 1PS .card.card-primary .card-block h2.card-title Opening hours each time in location.openingTimes 3 p.card-text 3 | #{time.days} : 3 if time.closed 3 | closed 3 else 3 | #{time.opening} - #{time.closing} .card.card-primary .card-block h2.card-title Facilities each facility in location.facilities span.badge.badge-warning i.fa.fa-check | #{facility} | .col-12.col-md-6.location-map .card.card-primary .card-block h2.card-title Location map img.img-fluid.rounded(src=`http://maps.googleapis.com/ maps/api/staticmap?center=${location.coords.lat}, ${location.coords.lng}&zoom=17&size=400x350&sensor= false&markers=${location.coords.lat},${location.coords. lng}&key={googleAPIKey}&scale=2`) 4 .row .col-12 .card.card-primary.review-card .card-block a.btn.btn-primary.float-right(href='/location/review/new') Add review h2.card-title Customer reviews each review in location.reviews 5 .row.review .col-12.no-gutters.review-header span.rating +outputRating(review.rating) 5 span.reviewAuthor #{review.author} small.reviewTimestamp #{review.timestamp} .col-12 p !{(review.reviewText).replace(/ /g, '<br/>')} 6 .col-12.col-lg-3 p.lead #{location.name} #{sidebar.context} p= sidebar.callToAction
A question that may arise is, why replace line breaks with <br/> tags every time? Why don’t you save the data with <br/> tags in? That way, you have to run the replace function only once, when the data is saved. The answer is that HTML is only one method of rendering text; it happens to be the one you’re using here. Down the line, you may want to pull this information into a native mobile application. You don’t want the source data tainted with HTML markup that you don’t use in that environment. The way to handle that is to keep the data clean.
The Add Review page is simple at the moment, with only one piece of data in it: the title in the page header. Updating the controller shouldn’t pose much of a problem. See the following listing for the full code of the addReview controller, in locations.js in the app_server/controllers folder.
const addReview = function(req, res){ res.render('location-review-form', { title: 'Review Starcups on Loc8r', pageHeader: { title: 'Review Starcups' } }); };
There’s not much to talk about here; you’ve updated the text inside the titles. The following listing shows the corresponding view, location-review-form.pug, in app_server/views.
extends layout block content .row.banner .col-12 h1= pageHeader.title .row .col-12.col-md-8 form(action="/location", method="get", role="form") .form-group.row label.col-10.col-sm-2.col-form-label(for="name") Name .col-12.col-sm-10 input#name.form-control(name="name") .form-group.row label.col-10.col-sm-2.col-form-label(for="rating") Rating .col-12.col-sm-2 select#rating.form-control.input-sm(name="rating") option 5 option 4 option 3 option 2 option 1 .form-group.row label.col-sm-2.col-form-label(for="review") Review .col-sm-10 textarea#review.form-control(name="review", rows="5") button.btn.btn-primary.float-right Add my review .col-12.col-md-4
Again, there’s nothing complicated or new here, so you can move on to the About page.
The About page doesn’t contain a huge amount of data, either, only a title and some content. Pull it out of the view and into the controller. Note that the content in the view currently has some <br/> tags in it, so replace each <br/> tag with when you put it into the controller. These tags are highlighted in bold in the following listing. The about controller is in app_server/controllers/others.js.
const about = function(req, res){ res.render('generic-text', { title: 'About Loc8r', content: 'Loc8r was created to help people find places to sit down and get a bit of work done.<br/><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sed lorem ac nisi digni ssim accumsan. Nullam sit amet interdum magna. Morbi quis faucibus nisi. Vestibulum mollis purus quis eros adipiscing tristique. Proin posuere semper tellus, id placerat augue dapibus ornare. Aenean leo metus, tempus in nisl eget, accumsan interdum dui. Pellentesque sollicitudin volutpat ullamcorper.' }); };
Aside from removing the HTML from the content, not much is going on here. Take a quick look at the view, and you’ll be done. The following listing shows the final generic-text view used for the About page in app_server/views. The view has to use the same piece of code as the reviews section to replace the line breaks with HTML <br/> tags.
extends layout .row.banner .col-12 h1= title .row .col-12.col-lg-8 p !{(content).replace(/ /g, '<br/>')} 1
This template is a simple, small, reusable one to use whenever you want to output some text on a page.
18.221.85.33