Implementing a Pagination Bar in Susper Angular JS Frontend

Searching on Susper most queries will have a number of search results. These results may span over several pages, and hence, a well designed pagination bar becomes essential for easy navigation. This blog deals with how we created the pagination bar in Susper. This is how the pagination bar in Susper looks:

There are a maximum of 10 pages displayed at any point of time.
At times you may have lesser results too:

where you can either directly click on a page or use the Previous and Next buttons for navigation. As you move along the pages, this is how it looks:

We will now see how we go about creating this bar.
The html source code( with a line by line explanation of the code):

<div class=“pagination-bar”>
<div class=“pagination-property” *ngIf=“noOfPages>1”>
<nav arialabel=“Page navigation” *ngIf=“(items$ | async)?.length!=0”>
  • Previous button is used to decrease the current page number, clicking on the first S in ‘Susper’ also accomplishes this task. Notice that this button is to be displayed only if you are not already on the first page of Susper.
<li class=“page-item” *ngFor=“let num of getNumber(maxPage)”><span class=“page-link” *ngIf=“presentPage>=4 && presentPage-3+num<=noOfPages” [class.active_page]=”getStyle(presentPage-3+num)”
(click)=”getPresentPage(presentPage-3+num)” href=“#”>

[class.active_page]=”getStyle(presentPage-3+num)” class=“page-text”>U

class=“page-number”>{{presentPage-3+num}}</span></span>
<span class=“page-link” *ngIf=“presentPage<4 && num<=noOfPages” [class.active_page]=”num+1 == presentPage (click)=”getPresentPage(num+1)”
href=“#”>

[class.active_page]=”num+1 == presentPage class=“page-text”>U</span>

class=“page-number”>{{num+1}}</span></span></li>

  • The above lines specify that we want to update our pagination bar only if the user has navigated more than the first three pages  
  • class.active_page gives correct css for the active page and getStyle() tells whether that particular page is active or not  
  • getPresentPage() uses the current page number to calculate which results to display  

<li class=“page-item”><span class=“page-link” (click)=”incPresentPage()”>

class=“page-text next”>SPER

</span></li>
<li class=“page-item2” *ngIf=“!getStyle(maxPage)”><span class=“spl” (click)=”incPresentPage()”>

class=“arrow”>>
class=“side-text”>Next

</span></li></ul>
</div>
</nav>
</div>

  • This part is used to increment the present page, and show the next/SPER message. Notice that it should be displayed only if you are not already on the last page

The attached typescript functions:

incPresentPage() {
this.presentPage = Math.min(this.noOfPages, this.presentPage + 1);
this.getPresentPage(this.presentPage);
}decPresentPage() {
this.presentPage = Math.max(1, this.presentPage 1);
this.getPresentPage(this.presentPage);
}
  • incPresentPage() increments the present page, if possible(that is you have not reached your maximum pages already).
  • decPresentPage() decrements the present page, if possible(that is you have not reached page 1 already).

getStyle(page) {
return ((this.presentPage) === page);
}
  • getStyle() returns true if passed page is the present page, useful to apply different css classes to the active page

getPresentPage(N) {
this.presentPage = N;
let urldata = Object.assign({}, this.searchdata);
urldata.start = (this.presentPage 1) * urldata.rows;
this.store.dispatch(new queryactions.QueryServerAction(urldata));}
  • getPresentPage () is used to change the page, and navigate to the page, with the correct rows of data

And you are done! You now have a working pagination bar to plugin at the bottom of your search results!

You can check out this elaborate tutorial on implementing Pagination bars from w3 schools : https://www.w3schools.com/css/css3_pagination.asp

Continue ReadingImplementing a Pagination Bar in Susper Angular JS Frontend

Making the footer-navigation bar stick to the bottom in Susper

In Susper, we have a navigation bar as a footer, as shown:

Previously this footer-navbar would appear immediately after the content, even if it was in the middle of the page. This is how the footer would appear:Since this could be a very common problem on a lot of websites, this blog deals with a simple hack for it.  

  1. Design your footer navbar as you please. You need not use any predefined bootstrap classes. You also need not specify any parameters regarding the position of the navbar (relative, absolute etc.).
  2. Enclose the rest of the data on your webpage in a div tag, do not forget to mention a class name or id name for the tag.  
  3. Now comes the simplest trick: Set a minimum height for your div! It is advisable to use vh (viewport-height) as your unit of measurement since it is easy to estimate how much of the viewport needs to be covered by your width.

This is how it is used in Susper:

Remember that each vh corresponds to one-hundredth of the viewport total height. So 100 vh here will mean a minimum height of the full viewport.

You can check the Susper repository for the source code or go through this link for alternate ways to create a sticky footer at the bottom.

Continue ReadingMaking the footer-navigation bar stick to the bottom in Susper

Creating a drop up in Susper

We are accustomed to creating drop-downs in our navigation bars, but sometimes we are faced with the need of creating drop ups.

In Susper, we had to create a drop up for settings for the footer.

This is how it looks:

Let us see the step by step procedure to create the drop up:

  1. Write the required Html content (in Susper it is in the footer-navbar.component.html)
<span class="dropup">

 <a class="dropdown-toggle" data-toggle="dropdown">Settings</a>

 <ul class="dropdown-menu" id="fsett">

   <li><a routerLink="/preferences">Search settings</a></li>

   <li><a routerLink="/advancedsearch">Advanced Search</a></li>

   <li><a routerLink="/crawlstartexpert" routerLinkActive="active">Crawl Job</a></li>

 </ul>

</span>

 

Make sure to link all the listed items in the drop down correctly. Notice that all of this has been put in the parent tag span, with class drop up

 

  1. We need to write the CSS part now:
.dropup{

cursor: pointer;

}

#fsett {

background: #fff;

border: 1px solid #999;

bottom: 30px;

padding: 10px 0;

position: absolute;

box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);

text-align: left;

z-index: 104;

margin-left: -85%;

margin-bottom: -9%;

}

#fsett a {

display: block;

line-height: 44px;

padding: 0 20px;

text-decoration: none;

white-space: nowrap;

}

#fsett a:hover {

text-decoration: underline;

background-color: white;

}

Here are some useful attributes, that can style your drop up:

  • Cursor: It sets your cursor to whatever you like when you move over the drop up. The pointer changes it to the hand symbol, default changes it to the standard arrow and so on…
  • Z-index: can be used to set the height of your elements, it is equal to its parent element by default, setting the z-index of something to more than that will make it have a higher stack order so it will be in the front.
  • Text-decoration: This attribute is used to add/remove decoration like underline for links.
  • Margin and position: Use margin-left and margin-right to set the position of the drop-up, combine it with position: absolute, to give absolute dimensions.
  • Box-shadow: This gives the drop up a shadow effect, which looks really nice. The first 3 parameters are for dimensions (X-offset, Y-offset, Blur). The rgba specifies colour, with parameters as (red-component, green-component, blue-component, opacity).
Continue ReadingCreating a drop up in Susper

Linking Codecov to your Angular2 Project

As a developer, the importance of testing code is well known. Testing source code helps to prevent bugs and syntax errors by cross-checking it with an expected output.
As stated on their official website: “Code coverage provides a visual measurement of what source code is being executed by a test suite”. This information indicates to the software developer where they should write new tests in the effort to achieve higher coverage, and consequently a lower chance of being buggy. Hence nearly every public repository on git uses codecov, a tool to measure the coverage of their source code.

In this blog, we shall see how to link codecov, with a public repository on Github when the code has been written in Angular2(Typescript). We shall assume that the repository uses Travis CI for integration.

STEP 1:
Go to https://codecov.io/gh/ and login to your Github account.

It will now give you the option to chose a repository to add for coverage. Select your repository.

STEP 2:
Navigate to Settings Tab, you should see something like this:

Follow the above-mentioned instructions.

STEP 3:
We now come to one of the most important parts of Codecov integration. Writing the files in our repo to enable this.
We will need three main files:
Travis.yml- which will ensure continuous integration services on your git hosted project
Codecov.yml- to personalise your settings and override the default settings in codecov.”The Codecov Yaml file is the single point of configuration, providing the developers with a transparent and version controlled file to adjust all Codecov settings.” as mentioned in the official website
Package.json- to inform npm of the new dependencies related to codecov, in addition to providing all the metadata to the user.

In .travis.yml, Add the following line:
after_success:

 - bash <(curl -s https://codecov.io/bash)

In codecov.yml, Add the following

Source: https://github.com/codecov/support/wiki/Codecov-Yaml#
 codecov:
 url: "string" # [enterprise] your self-hosted Codecov endpoint
 # ex. https://codecov.company.com
 slug: "owner/repo" # [enterprise] the project's name when using the global upload tokens
 branch: master # the branch to show by default, inherited from your git repository settings
 # ex. master, stable or release
 # default: the default branch in git/mercurial
 bot: username # the username that will consume any oauth requests
 # must have previously logged into Codecov
 ci: # [advanced] a list of custom CI domains
 - "ci.custom.com"
 notify: # [advanced] usage only
 after_n_builds: 5 # how many build to wait for before submitting notifications
 # therefore skipping status checks
 countdown: 50 # number of seconds to wait before checking CI status
 delay: 100 # number of seconds between each CI status check

coverage:
 precision: 2 # how many decimal places to display in the UI: 0 <= value <= 4 round: down # how coverage is rounded: down/up/nearest range: 50...100 # custom range of coverage colors from red -> yellow -> green

notify:
 irc:
 default: # -> see "sections" below
 server: "chat.freenode.net" #*S the domain of the irc server
 branches: null # -> see "branch patterns" below
 threshold: null # -> see "threshold" below
 message: "template string" # [advanced] -> see "customized message" below

gitter:
 default: # -> see "sections" below
 url: "https://webhooks.gitter.im/..." #*S unique Gitter notifications url
 branches: null # -> see "branch patterns" below
 threshold: null # -> see "threshold" below
 message: "template string" # [advanced] -> see "customized message" below

status:
 project: # measuring the overall project coverage
 default: # context, you can create multiple ones with custom titles
 enabled: yes # must be yes|true to enable this status
 target: auto # specify the target coverage for each commit status
 # option: "auto" (must increase from parent commit or pull request base)
 # option: "X%" a static target percentage to hit
 branches: # -> see "branch patterns" below
 threshold: null # allowed to drop X% and still result in a "success" commit status
 if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded
 # options: success, error, failure
 if_not_found: success # if parent is not found report status as success, error, or failure
 if_ci_failed: error # if ci fails report status as success, error, or failure


patch: # pull requests only: this commit status will measure the
 # entire pull requests Coverage Diff. Checking if the lines
 # adjusted are covered at least X%.
 default:
 enabled: yes # must be yes|true to enable this status
 target: 80% # specify the target "X%" coverage to hit
 branches: null # -> see "branch patterns" below
 threshold: null # allowed to drop X% and still result in a "success" commit status
 if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded
 # options: success, error, failure
 if_not_found: success
 if_ci_failed: error

changes: # if there are any unexpected changes in coverage
 default:
 enabled: yes # must be yes|true to enable this status
 branches: null # -> see "branch patterns" below
 if_no_uploads: error
 if_not_found: success
 if_ci_failed: error

ignore: # files and folders that will be removed during processing
 - "tests/*"
 - "demo/*.rb"

fixes: # [advanced] in rare cases the report tree is invalid, specify adjustments here
 - "old_path::new_path"

# comment: false # to disable comments
 comment:
 layout: "header, diff, changes, sunburst, suggestions, tree"
 branches: null # -> see "branch patterns" below
 behavior: default # option: "default" posts once then update, posts new if delete
 # option: "once" post once then updates, if deleted do not post new
 # option: "new" delete old, post new
 # option: "spammy" post new

Your package.json should look like this:

{
 "name": "example-typescript",
 "version": "1.0.0",
 "description": "Codecov Example Typescript",
 "main": "index.js",
 "devDependencies": {
 "chai": "^3.5.0",
 "codecov": "^1.0.1",
 "mocha": "^2.5.3",
 "nyc": "^6.4.4",
 "tsd": "^0.6.5",
 "typescript": "^1.8.10"
 },
 "scripts": {
 "postinstall": "tsd install",
 "pretest": "tsc test/*.ts --module commonjs --sourcemap",
 "test": "nyc mocha",
 "posttest": "nyc report --reporter=json && codecov -f coverage/*.json"
 },
 "repository": {
 "type": "git",
 "url": "git+https://github.com/Myname/example-typescript.git"
 },
 /*Optional*/
 "author": "Myname",
 "license": "Lic.name",
 "bugs": {
 "url": "https://github.com/example-typescript-path"
 },
 "homepage": "https://github.com/Myname/example-typescript#readme"
 }

Most of the code in package.json is metadata.
Two major parts of the code above are the devDependencies and the scripts.
In devDependencies, make sure to include the latest versions of all the packages your repository is using.
In scripts:

  • Postinstall – indicates the actions to be performed, once installation is complete.
  • Pretest – is for just before running ng test.
  • Test – indicates what is used while testing.
  • Posttest – is what is run just after testing is complete.

Check this repository for the sample files to generate the reports to be uploaded for Codecov: https://github.com/codecov/example-typescript

Check https://docs.codecov.io/v4.3.6/docs/codecov-yaml for detailed step by step instructions on writing codecov.yaml and https://docs.codecov.io/v4.3.6/docs for any general information

Continue ReadingLinking Codecov to your Angular2 Project

Adding a page to Susper

As a project grows, it’s complexity increases. It suddenly seems to have a higher number of components and linked files. Performing a basic task, such as adding a new page to the website, might take more intricate knowledge. This blog deals with adding a new page to any Angular 2 project, in this case to Susper project. You can also check any of the sample components in the code on the Github Repository for further reference.

STEP 1:

Use ng g component <component-name> command to generate a new component with your desired name. Make sure the component-name is relevant and describes the page you wish to add. Once the above command is run in the Angular CLI project, it will automatically generate the following files:

  • component-name.html
  • component-name.css
  • component-name.ts
  • component-name.spec.ts file

It also adds the new component name to src/app/app.module.ts in the declarations section, after importing it.

You can also do all of this manually without the ng g component command too.

STEP 2:

Write your HTML and CSS files. Ensure that your page looks how you intend it to. Using bootstrap for your CSS classes might help you. Ensure that you link your bootstrap modules in index.html and not in the individual component files.

STEP 3:

If your page uses any typescript functions, please link your functions to your HTML page, after defining them in typescript.

This is how your typescript file might look (This is how it looks in Susper):

You may want to import modules you will need first.

Notice this snippet of code:

export class DemoComponent implements OnInit {

constructor() { }

ngOnInit() {

 }

}

You can define all your variables and functions in the component class. ngOnInit() has already been listed as a demo.

Also, note that including anything in the constructor function will run as soon as the page is loaded or initialized.

STEP 4:

If you have a unit tests in place, then component-name.spec.ts is where they are listed. Make sure to update it.

This is the procedure in Susper. Initially, your component-name.spec.ts will look like this:

import { async, ComponentFixture, TestBed } from '@angular/core/testing'

import { DemoComponent } from './demo.component'



describe('DemoComponent', () => {

let component: DemoComponent;

let fixture: ComponentFixture<DemoComponent>;



beforeEach(async(() => {

   TestBed.configureTestingModule({

     declarations: [ DemoComponent ]

   })

   .compileComponents();

 }));

beforeEach(() => {

   fixture = TestBed.createComponent(DemoComponent);

   component = fixture.componentInstance;

   fixture.detectChanges();

 });

it('should create', () => {

   expect(component).toBeTruthy();

 });

});

 

Here is what you need to add:

  1. Add imports under Testbed.configureTestingModule.

TIP:  Make sure to import all the modules from their file locations first.

imports: [

 RouterTestingModule,

 BrowserModule,

 CommonModule,

 FormsModule,

 HttpModule,

 JsonpModule,

 StoreModule.provideStore(reducer),

 StoreDevtoolsModule.instrumentOnlyWithExtension()

],
  1. Add all the other components under the declarations heading.

   TIP: Make sure to import all the components first.

It should look something like this:

declarations: [

   AppComponent,

   NavbarComponent,

   IndexComponent,

   ResultsComponent,

   NotFoundComponent,

   AdvancedsearchComponent,

   SearchBarComponent,

   FooterNavbarComponent,

   AboutComponent,

   ContactComponent,

   ModalComponent,

   InfoboxComponent,

 DemoComponent,

 ],

})

 

  1. Now add service providers under the providers heading if any.
  2. Finally, add any additional test cases using the standard syntax proved by Jasmine with it and expect statements.

STEP 5:

Update all .spec.ts files with your new component name under the declarations heading as seen in point 2 of step 4. This will notify all other spec.ts files about your new component, allowing ng test to run smoothly.

Make sure to import it each time you use it, to avoid compilation errors.

STEP 6:

To be able to reach your page, you can either

  • Embed it in another page using the selector mentioned in its .ts file.
@Component({

selector: 'app-demo',

Simply include the following tag in whichever page you wish to use the demo component, in the .html file:

<app-demo></app-demo>

  • Give it a route through which the user can reach it. Do this, by adding it in the Routes in app.module.ts
const appRoutes: Routes = [

 {path: 'search', component: ResultsComponent},

{path: '', component: IndexComponent},

You are done! You have successfully added a page to Susper!

Continue ReadingAdding a page to Susper

Fixing the scroll position in the Susper Frontend

An interesting problem that I encountered in the Susper frontend repository is the problem of the scroll position in SPAs (Single Page Applications). Since most websites now use Single page applications, such a hack, might prove useful to a lot of the readers.
Single page applications (SPAs) provide a better user experience. But, they are significantly harder to design and build. One major problem they cause is that they do not remember the scroll position on a page, like traditional browsers do. In traditional browsers, if we open a new page, by clicking on a link, it opens the page at the top.
Then on clicking back, it goes to not just to the previous link, but also the last position scrolled to on it. The issue we faced in Susper, was that when we opened a link, Susper being a SPA did not realise it was on a new page, and hence did not scroll to the top again. This was observed on every page, of the appliance.
Clicking on Terms on the footer for instance,

would open the bottom of the Terms page, which was not what we wanted.

FIX: Since all the pages required the fix, I ran a script in the main app component. Whenever an event occurs, the router instance detects it. Once the event has been identified as the end of a navigation action, I scroll the window to (0,0).
Here is the code snippet:

import {Component, OnInit} from '@angular/core';

import { RouterModule, Router, NavigationEnd } from '@angular/router';

@Component({

selector: 'app-root',

templateUrl: './app.component.html',

styleUrls: ['./app.component.css']

})

export class AppComponent implements OnInit {

title = 'Susper';

constructor(private router: Router) { }

ngOnInit() {

   this.router.events.subscribe((evt) => {

     if (!(evt instanceof NavigationEnd)) {

       return;

     }

     window.scrollTo(0, 0);

   });

 }

}

“NavigationEnd” is triggered on the end of a Navigation action, in Angular2. So if the “NavigationEnd” hasn’t been triggered, our function need not do anything else and can simply return.  If a Navigation action has just finished the window is made to scroll up to (0,0) coordinates.
Now, this is how the Terms page opens:

 

Done! Now every time a link is clicked it scrolls to the top.

Continue ReadingFixing the scroll position in the Susper Frontend