2021/12/08

Imitate Jetbrains Moving Radial Gradient Effect

ไปŠๅคฉๅœจ้€› Jetbrains ๆ–ฐ็”ขๅ“ fleet ็š„็ถฒ้ ๆ™‚,็œ‹ๅˆฐไธ€ๅ€‹ๅœ“ๅฝขๆผธๅฑคๆœƒ่ทŸ่‘—ๆป‘้ผ ็งปๅ‹•็š„็‰นๆ•ˆ,่ฆบๅพ—ๅพˆๆœ‰ๆ„ๆ€。

ๆ‰‹็™ขไพ†ๅˆปไธ€ๅ€‹้กžไผผ็š„。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Glow Cursor</title>
    <style>
        .box {
            width: 300px;
            height: 300px;
            border: 2px solid blue;
        }
    </style>
</head>
<body>
<div class="box"></div>
<script>
    var box = document.querySelector('.box');

    box.addEventListener('mousemove', function (e) {
        var rect = e.target.getBoundingClientRect();
        var x = e.clientX - rect.left;
        var y = e.clientY - rect.top;

        box.style.background = `radial-gradient(circle at ${x}px ${y}px, rgba(138, 189, 61, 1) 0%, rgba(255, 255, 255, 1) 150px`;
    });

    box.addEventListener('mouseleave', function () {
        box.style.background = null;
    });
</script>
</body>
</html>

ๆ•ˆๆžœๅคงๆฆ‚ๆ˜ฏ้€™ๆจฃ Demo

2021/12/03

Imitate Laravel Collection Sum Funtion in PHP, Python, Node.js

Laravel ๅŒ…ๅซไบ†่จฑๅคšๅฅฝ็”จ็š„ๅฅ—ไปถ,Collection ๆ‡‰่ฉฒๆ˜ฏๆœ€ๅฅฝ็”จ็š„,ๅฐ‡้™ฃๅˆ—ๅฐŽ้€ฒๅŽปไปฅๅพŒๅฏไปฅๅšๅ„็จฎ่Šฑๅผๆ“ไฝœ,่‡ชๅทฑๅ˜—่ฉฆๅœจ PHP、Python、Node.js ไธŠๆจกๆ“ฌไธ€ไธ‹ไฝœๆณ•。

PHP

<?php

class Collection
{
    private array $collections;

    public function __construct(array $collections)
    {
        $this->collections = $collections;
    }

    public function sum(Closure $callable = null): int
    {
        $callbackIn = ($callable === null) ? $this->isNotCallback() : $this->isCallback($callable);

        return array_reduce($this->collections, fn($carry, $item) => $carry + $callbackIn($item), 0);
    }

    private function isNotCallback(): Closure
    {
        return fn(int $value): int => $value;
    }

    private function isCallback(Closure $callable): Closure
    {
        return fn(int $value): int => $callable($value);
    }
}

$collection = new Collection([1, 2, 3]);
$cal = fn($item): int => $item + 5;

echo $collection->sum(), PHP_EOL;
echo $collection->sum($cal);

Python

from functools import reduce
from typing import Callable


class Collection(object):
    collections: list

    def __init__(self, collections: list):
        self.collections = collections

    def sum(self, callback=None) -> int:
        callback_in = self.is_not_callable() if callback is None else self.is_callable(callback)

        return reduce(lambda carry, item: carry + callback_in(item), self.collections, 0)

    @staticmethod
    def is_not_callable() -> Callable[[int], int]:
        return lambda x: x

    @staticmethod
    def is_callable(callback) -> Callable[[int], Callable[[int], int]]:
        return lambda x: callback(x)


def call_me() -> Callable[[int], any]:
    return lambda x: x + 5


collection = Collection([1, 2, 3])
print(collection.sum())
print(collection.sum(call_me()))

Node.js

class Collection {
    private collections: any[];

    constructor(collections: any[]) {
        this.collections = collections;
    }

    public sum(callback: Function = null) {
        const callbackIn = (callback === null) ? this.isNotCallable() : this.isCallable(callback);

        return this.collections.reduce((carry, item) => carry + callbackIn(item), 0);
    }

    private isNotCallable(): Function {
        return (value: any) => value;
    }

    private isCallable(callback): Function {
        return (value: any) => callback(value);
    }
}

const collections = new Collection([1, 2, 3]);
const cal = (value) => value + 5;

console.log(collections.sum());
console.log(collections.sum(cal));

2021/12/02

Laravel Connet To MSSQL Server On CentOS

PHP ๅฎ‰่ฃ

# Install PHP 7.4
sudo yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum -y install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum -y install yum-utils
sudo yum-config-manager --enable remi-php74
sudo yum install -y php php-pdo php-odbc php-mbstring php-zip php-xml

MSSQL Driver ๅฎ‰่ฃ,ๆ“‡ไธ€ๅฎ‰่ฃ

1. MSSQL Driver(้ฆ–้ธ,ๆ”ฏๆด็‰ˆๆœฌ SQL Server 2000 ไปฅไธŠ)

sudo yum install php-sqlsrv
curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssql-release.repo

sudo yum remove unixODBC-utf16 unixODBC-utf16-devel #to avoid conflicts
sudo ACCEPT_EULA=Y yum install -y msodbcsql17

2. FreeTDS(Sql Server 2000)

sudo yum -y install php-mssql
sudo yum -y install unixODBC-*
sudo yum -y install freetds

cd /usr/lib64
sudo mv libtdsodbc.so.0.0.0 libtdsodbc.so
sudo vi /etc/odbcinst.ini

ๅœจๆœ€ๅพŒๅขžๅŠ :

[FreeTDS]
Description = ODBC for FreeTDS
Driver64    = /usr/lib64/libtdsodbc.so
Setup64     = /usr/lib64/libtdsodbc.so
FileUsage   = 1
sudo vi /etc/freetds.conf

ๅœจๆœ€ๅพŒๅขžๅŠ :

[SqlServer2000]
    host = {IP}
    port = 1433
    tds version = 8.0
    client charset = UTF-8

ๅœจ Laravel ็š„้…็ฝฎไธ€ๅˆ‡็…ง่ˆŠ,้™คไบ† MSSQL ็š„ host ่ฆๆŒ‡ๅˆฐ SqlServer2000,่‹ฅ้‡ๅˆฐๆ™‚้–“ๆ ผๅผ็š„ๅ•้กŒๅฏไปฅๅƒ่€ƒๆญค็ฏ‡,ไฝฟ็”จ FreeTDS ๅฏไปฅ้€šๅƒ,ไฝ†ๆ€•ๆœƒๆœ‰็„กๆณ•้ ๆœŸ็š„ๅ•้กŒ,ไธๅปบ่ญฐๅŒไธ€ๅฐ Server ๅŒๆ™‚ไฝฟ็”จๅ…ฉ็จฎๆ–นๅผ。

ๅƒ่€ƒ็ถฒ็ซ™

2021/11/18

update multiple package to latest at once

npm ๆœ‰ไธ€ๅ€‹ๆŒ‡ไปคๅฏไปฅๅตๆธฌๅฅ—ไปถๆ˜ฏๅฆๆœ‰ๅ‡บๆ–ฐ็š„ๅฏไปฅๆ›ดๆ–ฐ,ๆˆ‘ๆ•…ๆ„่ฃไบ† jquery@2 ไปฅๅŠ bootstrap@4。

npm ls
test@1.0.0 D:\node_test
├── bootstrap@4.6.1
└── jquery@2.2.4

ๆˆ‘ๅ€‘ไพ†ๅŸท่กŒๆ›ดๆ–ฐๆชขๆŸฅ:

npm outdated
Package    Current  Wanted  Latest  Location                Depended by
bootstrap    4.6.1   4.6.1   5.1.3  node_modules/bootstrap  node_test
jquery       2.2.4   2.2.4   3.6.0  node_modules/jquery     node_test

bootstrap ่ทŸ jquery ้ƒฝๆœ‰ๆ–ฐ็‰ˆๆœฌๅฏไปฅๆ›ดๆ–ฐ,ๆ›ดๆ–ฐๆŒ‡ไปคๅฆ‚ไธ‹:

npm install jquery@latest bootstrap@latest

ๅฆ‚ๆžœไน‹ๅ‰ๅฐฑๆœ‰ๅŠ ๅ…ฅ package.json ็š„่ฉฑๅ‰‡ไธ็”จๆŒ‡ๅฎš --save ๆˆ–ๆ˜ฏ --save-dev,ไธ€ๅ…ฉๅ€‹ๅฅ—ไปถๅฏ่ƒฝ้‚„ๅฅฝ,ๅ‡่จญไปŠๅคฉๆœ‰ๅๅนพๅ€‹่ฆไธ€ๆฌกๆ›ดๆ–ฐๅฐฑ็ดฏไบ†,่€Œ npm ๆœฌ่บซๆฒ’ๆœ‰ๆไพ›ๅ…จ้ƒจๆ›ดๆ–ฐๅˆฐๆœ€ๆ–ฐ็‰ˆ็š„ๆŒ‡ไปค,npm update ่ท‘็š„ๆ˜ฏๅœจ package.json ็š„ๆฟ่™Ÿไธ‹็š„ๆœ€ๆ–ฐ็‰ˆ,ๆ‰€ไปฅ่ทจไบ†็‰ˆ่™ŸๅฐฑไธๅŸท่กŒไบ†。

npmjs ็•ถ็„ถๆœ‰ๅฅ—ไปถๅฏไปฅ่ผ”ๅŠฉ้€™ไปถไบ‹,ๅƒๆ˜ฏ npm-check,ไฝ†ๆˆ‘ๅ€‘ไนŸๅฏไปฅ้€้Ž shell script ่‡ชๅทฑ่™•็†,ไปฅไธ‹็คบ็ฏ„ awk ไปฅๅŠ cut ็š„็”จๆณ•。

# ๆŒ‡ไปคไธ€,ๅˆฉ็”จ awk
npm outdated | sed 1d | awk -F ' ' '{print sprintf("%s@latest", $1)}' | xargs npm install
npm outdated | sed 1d | awk -F ' ' '{print sprintf("%s@%s", $1, $4)}' | xargs npm install

# ๆŒ‡ไปคไบŒ,ๅˆฉ็”จ cut
npm outdated | sed 1d | cut -d' ' -f1 | xargs -I {} echo '{}@latest' | xargs npm install

2021/08/20

Form Validator In Angular

Angular ็š„ ReactiveFormsModule ็›ธ็•ถๅผทๅคง,ๅŒ…ๅซไบ† two way binding data ไปฅๅŠๅฅฝ็”จ็š„ validator,ไปฅไธ‹็š„็ฏ„ไพ‹็คบ็ฏ„ๆ€Ž้บผ้‡ๅฐๅ–ฎไธ€ๆฌ„ไฝไปฅๅŠๆ•ด้ซ”่กจๅ–ฎ่‡ช่ฃฝ validator,ๅฆ‚ๆžœ username ๅกซๅฏซ chan ็š„่ฉฑ่กจ็คบๅธณ่™Ÿ้‡่ค‡,ๅนด้ฝกๅคงๆ–ผ 100 ็š„่ฉฑๅ‰‡้Œฏ่ชค,group ็š„้ƒจๅˆ†ๅ…ฉ่€…้ƒฝ่ฆๅกซๅฏซ。

ts
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, ValidationErrors, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'app-form-validator',
  templateUrl: './form-validator.component.html',
  styleUrls: ['./form-validator.component.scss']
})
export class FormValidatorComponent implements OnInit {
  public form = this.fb.group({
    username: ['', [this.userNameChecker()]],
    age: ['', [this.numberChecker()]]
  }, { validators: this.bothRequired() });
  public ageLimit: number = 100;

  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit(): void {
  }

  get username(): AbstractControl {
    return this.form.controls.username;
  }

  get age(): AbstractControl {
    return this.form.controls.age;
  }

  private userNameChecker(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      if (control.value === '') {
        return null;
      }

      if (control.value === 'chan') {
        return { userNameExists: true };
      }

      return null;
    }
  }

  private numberChecker(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      if (control.value === '') {
        return null;
      }

      if (control.value > this.ageLimit) {
        return { tooBig: true };
      }

      return null;
    }
  }

  private bothRequired(): ValidatorFn {
    return (group: AbstractControl): ValidationErrors | null => {
      return (group.get('username')?.value === '' || group.get('age')?.value === '') ? { bothRequired: true } : null;
    }
  }
}
html
<h3>{{ form.value | json }}</h3>
<div [formGroup]="form">
  <div>
    username: <input type="text" formControlName="username">
    <div *ngIf="username.errors?.userNameExists">username exists</div>
  </div>
  <div>
    age: <input type="number" formControlName="age">
    <div *ngIf="age.errors?.tooBig">age should lower than {{ ageLimit }}</div>
  </div>
  <div>
    <button [disabled]="form.invalid">submit</button>
  </div>
</div>

2021/08/13

Stop The Process By Condition In Ansible

Ansible ็š„ไปปๅ‹™ๆฒ’ๆœ‰ไธ‹ไธญๆ–ท็š„่ฉฑ,ๅณไพฟๆœ‰ไฝฟ็”จ when ๅตๆธฌ,playbook ๆœƒ็นผ็บŒๅพ€ไธ‹่ตฐ,ๅฏไปฅๅˆฉ็”จ fail ้€™ๅ€‹ๅƒๆ•ธ่ฎ“ๆขไปถไธ็ฌฆๅˆๆ™‚็›ดๆŽฅๆŠŠ process ไธญๆ–ท,ไปฅ stat ๅตๆธฌ็›ฎ้Œ„ๆ˜ฏๅฆๅญ˜ๅœจไพ†ๅš็ฏ„ไพ‹。

- hosts: localhost

  vars:
    path: /tmp/sync/

  tasks:
    - name: "stat {{ path }}"
      stat:
        path: "{{ path }}"
      register: the_dir

    - name: "shutdown when {{ path }} is not exists"
      fail:
        msg: "{{ path }} not exists"
      when: the_dir.stat.exists == False

    - name: "echo yes when {{ path }} exists"
      debug:
        msg: 'yes'
      when: the_dir.stat.exists

็•ถๆˆ‘ๅ€‘่จญๅฎš็š„ path ไธๅญ˜ๅœจๆ™‚,ๆœƒ็›ดๆŽฅไธญๆ–ท,ไธๆœƒ่ตฐ้“ๆœ€ๅพŒ็š„ "echo yes when {{ path }} exists"。

2021/08/11

Input And Output In Angular

Angular ็š„ @Input ไปฅๅŠ @Output ไฟฎ้ฃพๅ™จๆ˜ฏ็”จไพ†่ฎ“็ˆถ่ˆ‡ๅญๅ…ƒไปถๅฏไปฅ่ทจๅ…ƒไปถๆบ้€š่ณ‡่จŠ,ๆ‡‰็”จ็ฏ„ๅœๅพˆๅปฃ,่ญฌๅฆ‚่ชชๆœ‰ไบ›ๅ›บๅฎš็š„่กจๅ–ฎๅŠŸ่ƒฝๆˆ‘ๅ€‘ๅฏไปฅๆŠŠไป–ๅˆ‡็ดฐ,ๆไพ›็ตฆๅ„ๅ–ฎๅ…ƒ็š„่กจๅ–ฎๅฅ—ๅ…ฅไฝฟ็”จ,ๅฏไปฅๆธ›ๅฐ‘้‡่ค‡้€ ่ผชๅญ็š„ๅ•้กŒ,ไธ‹้ข็คบ็ฏ„ๅฆ‚ไฝ•่ฎ“ๅญๅ…ƒไปถๆ”น่ฎŠ็ˆถๅ…ƒไปถ่กจๅ–ฎ็š„ๅ…งๅฎน。

็ˆถ ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
  public form = this.fb.group({
    username: 'chan',
    amount: 100
  });

  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit(): void {
  }

  public patchAmount(amount: number): void {
    this.form.patchValue({
      amount: amount
    })
  }
}
็ˆถ html
<h1>Input Output Example</h1>
<h3>{{ form.value | json }}</h3>
<form [formGroup]="form">
  <div>
    <input type="text" formControlName="username">
  </div>
  <div>
    <app-example-number [amount]="form.get('amount')?.value" (patchParentAmount)="patchAmount($event)"></app-example-number>
  </div>
</form>
ๅญ ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-example-number',
  templateUrl: './example-number.component.html',
  styleUrls: ['./example-number.component.scss']
})
export class ExampleNumberComponent implements OnInit {
  @Input() amount: number = 0;
  @Output() patchParentAmount = new EventEmitter<number>();

  constructor() { }

  ngOnInit(): void {
  }

  public updateAmount(): void {
    this.patchParentAmount.emit(this.amount);
  }
}
ๅญ html
<input type="number" [(ngModel)]="amount" (ngModelChange)="updateAmount()">

็ˆถๅ…ƒไปถๅ‚ณไบ†้ ่จญ็š„ amount ้ŽๅŽป็ตฆๅญๅ…ƒไปถ,ๅญๅ…ƒไปถ้€้Ž็›ฃ่ฝ ngModelChange ๅฐ‡ๆ”น่ฎŠ็š„ๆ•ธๅญ— emit ๅˆฐ็ˆถๅ…ƒไปถๅŸท่กŒ patch form value ็š„ๅ‹•ไฝœ,ๅฐฑๅฏไปฅๅšๅˆฐไบ’ๅ‹•ๆ›ดๆ–ฐไบ†。

2021/08/09

Dynamic Form Field In Angular

Angular ็š„ form builder ่ ปๅผทๅคง็š„,two way binding data ไปฅๅŠ validator ็›ธ็•ถๅฅฝ็”จ,ๅ…ถไธญๆœ‰ไธ€ๅ€‹็‰นๆ€ง FormArray ๅฏไปฅๅš่จฑๅคš้‹็”จ,ไพ†ๅšไธ€ๅ€‹ๅ‹•ๆ…‹็”ข็”Ÿ่กจๅ–ฎๆฌ„ไฝ็š„็ฏ„ไพ‹。

ts
import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-dy-demo',
  templateUrl: './dy-demo.component.html',
  styleUrls: ['./dy-demo.component.scss']
})
export class DyDemoComponent implements OnInit {
  public form = this.fb.group({
    username: 'chan',
    family: this.fb.array([])
  })

  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit(): void {
  }

  public newLine(): FormGroup {
    return this.fb.group({
      name: '',
      age: ''
    })
  }

  public family(): FormArray {
    return this.form.get('family') as FormArray;
  }

  public add(): void {
    return this.family().push(this.newLine());
  }

  public remove(i: number): void {
    this.family().removeAt(i);
  }
}
html
<h3>{{ form.value | json }}</h3>
<div>
  <button (click)="add()">add</button>
</div>
<form action="" [formGroup]="form">
  <div>
    <input type="text" formControlName="username">
  </div>
  <div formArrayName="family">
    <ng-container *ngFor="let f of family().controls; let i=index" [formGroupName]="i">
      <div>
        username: <input type="text" formControlName="name"> age: <input type="number" formControlName="age">
        <button (click)="remove(i)">remove</button>
      </div>
    </ng-container>
  </div>
</form>

็›ฎๅ‰่ฆบๅพ— Angular ็š„ๅ…งๅฎน็œŸๆ˜ฏๆ˜“ๅญธ้›ฃ็ฒพๅ•Š。

2021/08/05

Array Object In Angular

angular ไฝฟ็”จไบ† typescript,ๅผทๅž‹ๅˆฅๆ˜ฏ ts ๅคงๆฎบๅ™จไน‹ไธ€,้™คไบ†็ฐกๅ–ฎ็š„ string、number ้‚ฃไบ›ๅฎš็พฉไปฅๅค–,array object ไฝฟ็”จๅบฆไนŸๅพˆ้ซ˜,ๆœ€ๅธธ่ฆ‹็š„ไฝœๆณ•ๅฆ‚ไธ‹。

interface Member {
    username: string;
    password: string;
}

const members: Member[] = [
    {
        username: 'chan',
        password: '123456'
    }
];

console.log(members);

้€™ๆจฃๅšๆฒ’ไป€้บผๅคงๅ•้กŒ,ไฝ†่จญ่จˆไธŠๆˆ‘่ฆบๅพ—ๆœ‰้ปžๅก,ๅ› ็‚บ interface Member ๅฐฑๆ˜ฏไธ€ๅ€‹ๅ–ฎไธ€็‰ฉไปถ,ๅฆไธ€็จฎๅฏซๆณ•ๆฏ”่ผƒๅ›‰ๅ”†ไธ€้ปž,ไฝ†ๆˆ‘่ฆบๅพ—ๆฏ”่ผƒๅฅฝ:

interface Member {
    username: string;
    password: string;
}

interface Members extends Array<Member> {}

const members: Members = [
    {
        username: 'chan',
        password: '123456'
    }
];

console.log(members);

้€™ๆจฃ่ชฟ็”จ Members ๆ™‚ๅพˆๆ˜Ž็ขบๅฐฑๆ˜ฏ่ฆ็”จ้™ฃๅˆ—,่€Œ Member ๅ‰‡ๅฏไปฅ็ตฆ member detail ไฝฟ็”จ,่ท่ฒฌๆฏ”่ผƒๆธ…ๆฅš,detail ็†่ซ–ไธŠๆ‡‰่ฉฒๆ˜ฏๆœ€ๅฐๅ–ฎไฝ,ไฝ†ๅฆ‚ๆžœ list ้ƒจๅˆ†ๆœ‰่‡ชๅทฑ็š„้œ€ๆฑ‚ไนŸๅฏไปฅ่‡ช่กŒๆ“ดๅ……,type alias ไนŸๆœ‰็›ธๅŒ็š„ไฝœๆณ•:

type Member = {
    username: string;
    password: string;
}

type Members = Member[];

const members: Members = [
    {
        username: 'chan',
        password: '123456'
    }
];

console.log(members);

2021/07/27

Specific String On Typescript

ไปŠๅคฉๅœจๅฏซ anguar ็š„ๆ™‚ๅ€™ๆƒณๅˆฐไธ€ๅ€‹ๅ•้กŒ,ไธ€่ˆฌๆˆ‘ๅ€‘ๅœจ parameter ๅพŒ้ขๅธถ็š„ๆœƒๆ˜ฏๅž‹ๅˆฅ,ๅƒๆ˜ฏ:

const tester = (params: { name: string }): void => {
    console.log(params.name);
}

tester({name: 'a'}); // a

้‚ฃๆ˜ฏๅฆๅฏไปฅ็›ดๆŽฅ้™ๅฎšๆƒณ่ฆ็š„ๅ…งๅฎน,็ญ”ๆกˆๆ˜ฏๅฏไปฅ็š„:

const tester = (params: { name: 'a' | 'b' }): void => {
    console.log(params.name);
}
  
tester({name: 'a'}); // a

ๆญคๆ™‚ๅฆ‚ๆžœไฝ ไธ‹ tester({name: 'c'}); ๆœƒๅพ—ๅˆฐ TS2322: Type '"c"' is not assignable to type '"a" | "b"'. ็š„่ญฆๅ‘Š,ไธ้Žๅคšๅฏซไบ†ๅนพๅ€‹ๆกˆไพ‹ไปฅๅพŒ็™ผ็พไธ€ๅ€‹้›ท,ไธ่ƒฝ็”จ่ฎŠๆ•ธๅธถๅ…ฅ:

const tester = (params: { name: 'a' | 'b' }): void => {
    console.log(params.name);
}
const params = {
    name: 'a'
};

tester(params); // a

ไธŠๆ–น็š„ๅฏซๆณ•ๆœƒๅพ—ๅˆฐ่ญฆๅ‘Š TS2345: Argument of type '{ name: string; }' is not assignable to parameter of type '{ name: "a" | "b"; }'. Types of property 'name' are incompatible. Type 'string' is not assignable to type '"a" | "b"'.,ๆœ€็ฐกๅ–ฎ็š„่งฃๆณ•ๆ˜ฏ:

const tester = (params: { name: 'a' | 'b' }): void => {  
    console.log(params.name);
}
const params = {
    name: 'a' as const
};

tester(params); // a

ไธ้ŽไธŠ็คพ็พค่จŽ่ซ–ๅพŒ,ๆœ€ๅฅฝ็š„่งฃๆณ•ๆ‡‰่ฉฒๆ˜ฏ็”จ enum:

enum TesterParam {
    A = 'a',
    B = 'b'
}

const tester = (params: { name: TesterParam }): void => {
    console.log(params.name);
}
const params = {
    name: TesterParam.A
};

tester(params); // a

้€™ๆจฃ็š„ๆžถๆง‹ๅฐ็ถญ่ญทไพ†่ชชๆ˜ฏๆœ€ๅฅฝ็š„。

2021/06/16

Compare two arrays is equal in JavaScript

ๅœจ PHP ไธญ,ๅฏไปฅ็ฐกๅ–ฎไฝฟ็”จๆฏ”่ผƒ้‹็ฎ—ๅญๆฏ”ๅฐๅ…ฉๅ€‹้™ฃๅˆ—。

$a = ['a', 'b', 'c'];
$b = ['a', 'b', 'c'];

var_dump($a === $b); // true

็”š่‡ณๆŽ’ๅบไธไธ€ๆจฃ้€้Ž sort ไนŸๅฏไปฅ่งฃๆฑบ。

$a = ['a', 'b', 'c'];
$b = ['a', 'c', 'b'];

var_dump(sort($a) === sort($b)); // true

ไฝ†ๅœจ js ็š„ไธ–็•Œไธ่กŒ。

const a = [
    'a',
    'b',
    'c'
];
const b = [
    'a',
    'b',
    'c'
];

console.log(a == b); // false
console.log(a === b); // false

ๅœจ็ถฒ่ทฏไธŠ็œ‹ๅˆฐไธ€ๅ€‹่งฃๆณ•ๆŒบๅฅฝ็š„,่จ˜้Œ„ไธ€ไธ‹。

const a = [
    'a',
    'b',
    'c'
];
const b = [
    'a',
    'b',
    'c'
];
const result = a.sort().every((value, index) => {
    return value === b.sort()[index];
});

console.log(result); // true

ๅฏซๆˆ prototype ๅฐฑๆ˜ฏ。

const a = [
    'a',
    'b',
    'c'
];
const b = [
    'a',
    'b',
    'c'
];

Array.prototype.equals = function (array) {
    return this.sort().every((value, index) => {
        return value === array.sort()[index];
    });
}

console.log(a.equals(b)); // true

2021/05/07

ไฝฟ็”จ CSS ่ฎ“ๅœ–็‰‡ไธ็ ดๆ ผ

็พๅœจๆ˜ฏ responsive ็š„ๆ™‚ไปฃ,ๆ‰€ๆœ‰็š„้ ้ข้ƒฝๅฟ…้ ˆๅš้Ÿฟๆ‡‰ๅผ,่™•็†้€™็จฎ็ถฒ็ซ™็š„ๆ™‚ๅ€™,ๅœ–็‰‡ๆœ€ไปคไบบ้ ญ็—›,ๆœ‰ๆ™‚ๅ€™็„กๆณ•่ฆ็ฏ„ๅฎขๆˆถๆ˜ฏๅฆๅ‚ณๆˆ‘ๅ€‘่ฆๆฑ‚็š„ๆฏ”ไพ‹,ๆ‰€ไปฅๅฟ…้ ˆๅœจๅ‰็ซฏๅšไธ€ไบ›ๆ‰‹่…ณ่ฎ“็•ซ้ข็œ‹่ตทไพ†่ตท็ขผไธ้‚ฃ้บผ็ณŸ。

html
<div class="container">
    <div class="row row-no-gutters">
        <div class="col-md-3">
            <div class="box">
                <img src="https://via.placeholder.com/700x400.png">
            </div>
        </div>
        <div class="col-md-3">
            <div class="box">
                <img src="https://via.placeholder.com/600x400.png">
            </div>
        </div>
        <div class="col-md-3">
            <div class="box">
                <img src="https://via.placeholder.com/600x200.png">
            </div>
        </div>
        <div class="col-md-3">
            <div class="box">
                <img src="https://via.placeholder.com/300x500.png">
            </div>
        </div>
    </div>
</div>

้€™ๆ˜ฏๅคง้ƒจๅˆ†ไฝฟ็”จ bootstrap ๆ™‚ๆœƒไฝฟ็”จ็š„ๆŽ’็‰ˆ,ไธ€ๅ€‹ๅ››ๆ ผๅœ–็‰‡็š„้…็ฝฎ,ๆˆ‘ๆ”พไบ†ๅนพๅผตๅฎŒๅ…จไธๅŒๅฐบๅฏธ็š„ placeholder ๅœ–็‰‡,ไธ€่ˆฌไบบ่™•็†ๆœƒๅœจ img ไธŠไธ‹ max-width: 100%,่ฎ“ไป–ๅฏฌๅบฆไธๆœƒ็ˆ†่กจ,ไฝ†้ซ˜ๅบฆ็š„้ƒจๅˆ†ๅฐฑๆœƒ่ขซๅœ–็‰‡ๆ’้ซ˜ไบ†,ๆœ‰ไบ›ไบบๆœƒๆŠŠๅœ–็‰‡่ฎŠๆˆ background,ๅ†ๅˆฉ็”จ cover ๅฑฌๆ€งๆญ้… padding-top + overflow: hidden ๆ’้ซ˜ๅบฆ่ฎ“ๅœ–็‰‡็ฝฎไธญไธฆไธ”ไธ็ ดๆ ผ,ไฝ†้€™ๆจฃๆœƒๅฝฑ้Ÿฟ SEO,ๆˆ‘่‡ชๅทฑ่ฉฆๅ‡บไบ†ไธ€ๅ€‹้…็ฝฎ้‚„ไธ้Œฏ。

scss
.container {
  .box {
    width: 100%;
    padding-top: 66.6%;
    overflow: hidden;
    position: relative;
    border: 1px solid grey;
  }

  .box {
    img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
}

้€™ๆจฃ็š„้…็ฝฎๆณ•ๅฏไปฅๅœจไธๅฐ‡ๅœ–็‰‡่ฎŠๆˆ background ็š„ๆƒ…ๆณไธ‹่ฎ“ๅœ–็‰‡ๆปฟ็‰ˆๅฑ…ไธญ,IE 11 ไปฅไธ‹ไธๆ”ฏๆด,ไธ้Žๆˆ‘ไธๅœจไนŽ。

2021/05/05

Vagrant provision with docker and docker-compose install

vagrant ๆไพ›ไบ† provision ๅˆๅง‹ๅŒ–ๅŠŸ่ƒฝ,ๅฏไปฅ้กžไผผ Dockerfile ๅœจๅˆๅง‹ๅŒ– vagrant ็š„ๆ™‚ๅ€™ๅฐฑๆŠŠๆƒณ่ฆๅฎ‰่ฃ็š„ๅ…งๅฎน่™•็†ๅฅฝ,ไธ‹้ข็คบ็ฏ„ไธ€ไธ‹ๅฆ‚ไฝ•ๅฎ‰่ฃไธ€ๅ€‹ไนพๆทจ็š„ ubuntu ๅพŒๆŠŠ docker ไปฅๅŠ docker-compose ไธ€ไฝตๅฎ‰่ฃ。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/focal64"

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end

  config.vm.provision "shell", inline: <<-SHELL
    docker -v &> /dev/null

    if [[ $? != 0 ]]; then
      echo "start install docker"
      curl -sL https://get.docker.com/ | sudo bash
      sudo usermod -aG docker vagrant
      docker -v
    else
      echo "docker installed"
    fi

    docker-compose -v &> /dev/null

    if [[ $? != 0 ]]; then
      echo "start install docker-compose"
      sudo curl -sL "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
      sudo chmod +x /usr/local/bin/docker-compose
      sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
      docker-compose -v
    else
      echo "docker-compose installed"
    fi
  SHELL
end

็”ฑๆ–ผ provision ๆœ‰ๅฏ่ƒฝ้‡่ค‡ไฝฟ็”จ,ๆ‰€ไปฅๆœ€ไธ€ไบ›ๅŸท่กŒๆชขๆŸฅๆ˜ฏๅฟ…่ฆ็š„,้€™้‚Šๆˆ‘ๆชขๆŸฅๅทฒๅฎ‰่ฃ็š„ๆŠ€ๅทงๆ˜ฏไฝฟ็”จ -v ้€™ๅ€‹ๅƒๆ•ธ,ๅฆ‚ๆžœๆฒ’ๆœ‰ๅฎ‰่ฃ,็ณป็ตฑๆœƒๅ ฑ้Œฏ,ๅ› ๆญค $? ๆœƒ้ž 0,่—‰ๆญคๅˆคๆ–ทๆœ‰ๆฒ’ๆœ‰ๅฎ‰่ฃ้Ž。

2021/04/09

FormArray And Checkbox

ๆœ€่ฟ‘ๅœจๅญธ Angular,ไป–็š„ Form ็›ธ้—œๆจก็ต„ๆŒบๆœ‰่ถฃ็š„,ไธ้Ž็‚บไบ†ๅฏฆ็พๆŸไบ›ๅŠŸ่ƒฝ้‚„ๆ˜ฏๅฟ…้ ˆ้€้Ž JS ๅŠ ๅทฅ,้€™้‚Š่จ˜้Œ„ไธ€ไธ‹ๆ€Ž้บผ็ถ checkbox ็š„ cheked ็ตๆžœๅˆฐ FormBuilder ไธŠ。

html
<h3>{{ form.value | json }}</h3>  
<div [formGroup]="form">  
 <input type="text" formControlName="name">  
 <ng-container *ngFor="let hobby of hobbies">  
 <label> <input type="checkbox" [value]="hobby.value" (click)="updateHobbies($event)">  
  {{ hobby.name }}  
  </label>  
 </ng-container></div>
ts
import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormControl } from '@angular/forms';

@Component({
  selector: 'app-checkbox',
  templateUrl: './checkbox.component.html',
  styleUrls: ['./checkbox.component.scss']
})
export class CheckboxComponent implements OnInit {
  public form = this.fb.group({
    name: [''],
    hobbies: this.fb.array([])
  });
  public hobbies: object[] = [
    { name: 'basketball', value: 'basketball' },
    { name: 'baseball', value: 'baseball' },
    { name: 'tennis', value: 'tennis' },
  ];

  constructor(
    private fb: FormBuilder
  ) { }

  ngOnInit(): void {
  }

  public updateHobbies(e): void {
    const hobbies: FormArray = this.form.get('hobbies') as FormArray;

    if (e.target.checked) {
      hobbies.push(this.fb.control(e.target.value));
    } else {
      let i = 0;

      hobbies.controls.forEach((control: FormControl) => {
        if (control.value === e.target.value) {
          hobbies.removeAt(i);

          return;
        }

        ++i;
      });
    }
  }
}