Skip to main content

33 posts tagged with "Frontend"

View All Tags

Events of Component

· One min read

Open in Notion

Outside Click

import { Directive, Input, Output, EventEmitter, ElementRef, HostListener } from '@angular/core';

@Directive({
selector: '[clickOutside]',
})
export class ClickOutsideDirective {

@Output() clickOutside = new EventEmitter<void>();

constructor(private elementRef: ElementRef) { }

@HostListener('document:click', ['$event.target'])
public onClick(target) {
const clickedInside = this.elementRef.nativeElement.contains(target);
if (!clickedInside) {
this.clickOutside.emit();
}
}
}
<div (clickOutside)="someHandler()"></div>

Keydown of Component

@HostListener('document: keydown', ['$event'])
public onEnter(event: KeyboardEvent): void {
if (this.elementRef.nativeElement.contains(event.target)) {
if (event.code === 'Enter') {
// TODO:
}
}
}

‼️Tips

If the element contains *ngIf, this.overlay.nativeElement.contains(event.target); always return false. Because of the angular directive *ngIf

<div #overlay>
<div *ngIf="show" class="item">
Click here
</div>
</div>
@ViewChild('overlay', { static: false }) overlay: ElementRef;

//...
this.overlay.nativeElement.contains(event.target); // always return false

How to solve it?

You can check the event.target

Object.keys(event.target.classList).includes('item');

Or use [hidden] instead.

<div [hidden]="!show" class="item">
Click here
</div>

Prototype in JavaScript

· One min read

Open in Notion

All JavaScript objects inherit properties and methods from a prototype.

  • Date objects inherit from Date.prototype
  • Array objects inherit from Array.prototype
  • Person objects inherit from Person.prototype

The Object.prototype is on the top of the prototype inheritance chain:

Date objects, Array objects, and Person objects inherit from Object.prototype

Example

function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}

Person.prototype.nationality = "English";

Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};

var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");

❗Please do not add a new property to an existing object constructor as below. That is a wrong way.

Person.nationality = "English";

CSS, SCSS

· One min read

Open in Notion

Media Examples

/* Small (sm) */
@media (min-width: 640px) { /* ... */ }

/* Medium (md) */
@media (min-width: 768px) { /* ... */ }

/* Large (lg) */
@media (min-width: 1024px) { /* ... */ }

/* Extra Large (xl) */
@media (min-width: 1280px) { /* ... */ }

// SCSS
// A map of breakpoints.
$breakpoints: (
xs: 576px,
sm: 768px,
md: 992px,
lg: 1200px
);
@mixin respond-above($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$breakpoint-value: map-get($breakpoints, $breakpoint);
@media (min-width: $breakpoint-value) {
@content;
}
} @else {
@warn 'Invalid breakpoint: #{$breakpoint}.';
}
}

z-index

$zindex-dropdown: 1000 !default;
$zindex-sticky: 1020 !default;
$zindex-fixed: 1030 !default;
$zindex-modal-backdrop: 1040 !default;
$zindex-modal: 1050 !default;
$zindex-popover: 1060 !default;
$zindex-tooltip: 1070 !default;

Each Examples

$colorset: (
primary: #aaa,
accent: #bbb,
);
:root {
@each $key, $val in $colorset {
--#{$key}: #{$val};
}
}
button {
color: var(--#{$key})
}

Function Definitions

$colorKeys: 'primary', 'accent', 'warn';
@mixin each-colors {
@for $i from 1 through length($colorKeys) {
@content(nth($colorKeys, $i));
}
}
@include each-colors using ($colorKey) {
@if $color!= '' {
button.#{$colorKey} {
color: var(--#{$colorKey});
}
}
}

JS Operations

getComputedStyle(document.querySelector(":root")).getPropertyValue("--dark--primary");
document.querySelector('#id').classList.value.split(' ');

Syntax Tips

· One min read

Open in Notion

?? and ||

a || b // equals: a ? a : b
a ?? b // equals: a != undefined && a != null ? a : b
!'' // output: true
0 ?? 'a' // output: 0
0 || 'a' // output: "a"
'' ?? 'a' // output: ""
'' || 'a' // output: "a"

call, apply, bind

These methods can change the this point.

function test(arg1, arg2) {};

test.call(null, a1, a2);
test.apply(null, [a1, a2]);

var t = test.bind(null);
t();

Variables in ng-container/ng-template

· One min read

Open in Notion

<div>
<ng-container *ngTemplateOutlet="viewTemplate; content: {$implicit: {name: 'Bing'}}"></ng-container>
</div>
@Component({
selector: 'sub',
})
export class SubComponent {
@Input() viewTemplate: TemplateRef<any>;
}

bookmark

How to use:

<sub [viewTemplate]="view"></sub>

<ng-template #view let-data>
Your name {{data.name}}
</ng-template>

bookmark

Jest - Issues

· One min read

Open in Notion

Configurations for DOM Support

Use document object and methods like document.querySelectorAll

_setup.ts

import "jsdom-global/register";

jest.config.js

module.exports = {
verbose: true,
transform: {
".(ts|tsx)": "<rootdir>/node_modules/ts-jest/preprocessor.js",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
},
testEnvironment: "node",
testRegex: "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
setupTestFrameworkScriptFile: "./test/_setup.ts",
moduleFileExtensions: ["ts", "tsx", "js"],
coveragePathIgnorePatterns: ["/node_modules/", "/test/", "src/index.ts"],
coverageThreshold: {
global: {
branches: 0,
functions: 0,
lines: 0,
statements: 0,
},
},
collectCoverageFrom: ["src/**/*.{js,ts,tsx}"],
};

UT Example for Promise

// ut passed requires `done` called
it("should return a promise with callback and title", done => {
confirm("Promise confirm").then(() =&gt; {
done();
});
document.querySelectorAll<htmlelement>(".btn")[1].click();
});

// reject promise
it("test reject promise", async () => {
// do not use `Promise.reject`, because returns Promise immediately
const mockP = jest.fn(() => Promise.reject("err"));
await delay(1, mockP()).catch(() => {
// nothing
});
expect(mockP.mock.calls.length).toBe(1);
});

AMD、CMD、UMD、CommonJS

· 3 min read

Open in Notion

Asynchronous Module Definition (AMD) has gained traction on the frontend, with RequireJS being the most popular implementation.

Here’s module foo with a single dependency on jquery:

// filename: foo.js
define(['jquery'], function ($) {
// methods
function myFunc(){};

// exposed public methods
return myFunc;
});

And a little more complicated example with multiple dependencies and multiple exposed methods:

// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned

// exposed public methods
return {
b: b,
c: c
}
});

CMD(Common Module Definition)

Standard locates at https://github.com/seajs/seajs/issues/242. It keeps more compatibilities with CommonJS and Node.js Modules.

  • Published by Chinese people who is developing SeaJS.
  • It is like AMD.
define((require, exports, module) => {
module.exports = {
fun1: () => {
var $ = require('jquery');
return $('#test');
}
};
});

CommonJS

CommonJS is a style you may be familiar with if you’re written anything in Node (which uses a slight variant). It’s also been gaining traction on the frontend with Browserify.

Using the same format as before, here’s what our foo module looks like in CommonJS:

// filename: foo.js

// dependencies
var $ = require('jquery');

// methods
function myFunc(){};

// exposed public method (single)
module.exports = myFunc;

And our more complicate example, with multiple dependencies and multiple exposed methods:

// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');

// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports

// exposed public methods
module.exports = {
b: b,
c: c
};

UMD(Universal Module Definition)

Since CommonJS and AMD styles have both been equally popular, it seems there’s yet no consensus. This has brought about the push for a “universal” pattern that supports both styles, which brings us to none other than the Universal Module Definition.

The pattern is admittedly ugly, but is both AMD and CommonJS compatible, as well as supporting the old-style “global” variable definition:

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// methods
function myFunc(){};

// exposed public method
return myFunc;
}));

And keeping in the same pattern as the above examples, the more complicated case with multiple dependencies and multiple exposed methods:

(function (root, factory) {
if (typeof define === 'function' &amp;&amp; define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned

// exposed public methods
return {
b: b,
c: c
}
}));

AngularJS with TypeScript

· One min read

Open in Notion

Components

class HerosComponentController implements ng.IComponentController {
public static $inject = ['$log', '$scope', '$document', '$element'];
public title: string;
public heros: IHero[];
public onItemSelect: any;

constructor(
private $log: ng.ILogService,
private $scope: ng.IScope,
private $document: ng.IDocumentService,
private $element: ng.IRootElementService,
) { }

public $onInit () { }

public $onChanges(changes: angular.IOnChangesObject): void { }

public selectItem(item: IHero, event: any) {
if (this.onItemSelect && typeof this.onItemSelect === 'function') {
this.onItemSelect({
data: item,
});
}
}
}

class HerosComponent implements ng.IComponentOptions {

public controller: ng.Injectable<ng.IControllerConstructor>;
public controllerAs: string;
public template: string;
public bindings: any;

constructor() {
this.controller = HerosComponentController;
this.controllerAs = "$ctrl";
this.template = `
<ul>
<li>{{$ctrl.title}}</li>
<li ng-click="$ctrl.selectItem(hero, $event)" ng-repeat="hero in $ctrl.heros">{{ hero.name }}</li>
</ul>
`;
this.bindins = {
title: '@',
heros: '<',
onItemSelect: '&',
};
}
}

angular
.module("mySuperAwesomeApp", [])
.component("heros", new HerosComponent());

angular.element(document).ready(function() {
angular.bootstrap(document, ["mySuperAwesomeApp"]);
});
<heros title="Title" heros="$ctrl.heros" on-item-select="$ctrl.select(data)"></heros>

Download File via AJAX

· One min read

Open in Notion

$('#GetFile').on('click', function () {
$.ajax({
url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
method: 'GET',
xhrFields: {
responseType: 'blob'
},
success: function (response) {
var blob = response.data;
var a = document.createElement('a');
var fileUrl = window.URL.createObjectURL(blob);
var filename = 'myfile.pdf';
/* you can also use below to get filename from backend */
// var contentDisposition = response.headers('Content-Disposition');
// var matches = /filename=\\"(.+?)\\"/g.exec(contentDisposition);
// var filename = matches && matches.length > 1 ? matches\[1\] : '';
/* but need add backend code like below, otherwise you cannot get Content-Disposition header */
// + response.setHeader("Content-Disposition", "attachment; filename=\\"myfile.csv\\"");
// + response.setHeader("Content-type", "application/octet-stream;charset=utf-8");
// + response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
// work in IE
if (window.navigator && window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, filename);
return;
}
a.href = fileUrl;
a.download = filename;
a.dispatchEvent(new MouseEvent('click'));
setTimeout(function() {
a.remove();
window.URL.revokeObjectURL.bind(window.URL, fileUrl);
});
}
});
});

Install Xvfb on CentOS

· One min read

Open in Notion

Xvfb, or X virtual frame buffer is needed by selenium and chromedriver or gekodriver, so it can run via cron with your PC locked, or without your script taking focus from the user section.

Installing

yum install xorg-x11-server-Xvfb

Copy below to /etc/systemd/system/Xvfb.service

[Unit]
Description=X Virtual Frame Buffer Service
After=network.target

[Service]
ExecStart=/usr/bin/Xvfb :99 -screen 0 1024x768x24

[Install]
WantedBy=multi-user.target
chmod +x /etc/systemd/system/Xvfb.service
systemctl enable Xvfb.service
systemctl start Xvfb.service