[{"data":1,"prerenderedAt":14518},["ShallowReactive",2],{"writing":3},[4,1702,2349,6236,8830,10142,10496,11909,13027,13134,13982,14187],{"id":5,"title":6,"body":7,"category":1688,"date":1689,"description":13,"excerpt":1690,"extension":1691,"image":1692,"keywords":1693,"meta":1694,"navigation":988,"path":1695,"readingTime":1696,"seo":1697,"slug":1698,"stem":1699,"tags":1700,"__hash__":1701},"blog\u002Fblog\u002Fopen-claw-secure-hardened-setup.md","Secure and hardened OpenClaw setup on a VPS",{"type":8,"value":9,"toc":1673},"minimark",[10,14,17,25,41,44,49,57,79,86,89,115,118,142,145,168,171,194,197,213,216,220,223,230,297,303,317,320,324,327,340,346,353,382,385,445,452,464,471,474,515,518,528,534,540,544,550,574,577,600,603,634,637,641,648,662,665,669,672,679,704,710,713,725,728,1119,1122,1136,1146,1153,1157,1162,1165,1177,1180,1191,1194,1203,1206,1210,1213,1229,1232,1255,1270,1273,1296,1299,1303,1306,1322,1325,1342,1349,1353,1356,1366,1369,1385,1391,1406,1409,1420,1427,1433,1602,1605,1627,1630,1634,1637,1640,1660,1663,1666,1669],[11,12,13],"p",{},"I have been experimenting with OpenClaw recently, and while the initial setup is fairly straightforward, the secure setup is where most of the interesting decisions actually are.",[11,15,16],{},"The difference between “it works” and “it is safe enough to leave running on a VPS” is not small. If you are running an agent that can read files, write code, install packages, and expose a gateway for device pairing, you really do not want to treat the box like a throwaway toy project.",[11,18,19,20,24],{},"So in this post I am going to walk through the setup I ended up with for OpenClaw on a VPS, but with an emphasis on ",[21,22,23],"strong",{},"hardening the machine first",", and only then installing the app. The main goal is simple:",[26,27,28,32,35,38],"ul",{},[29,30,31],"li",{},"lock down SSH",[29,33,34],{},"keep the OpenClaw gateway on loopback only",[29,36,37],{},"avoid exposing unnecessary ports",[29,39,40],{},"run the service in a way that survives rebuilds and reboots",[11,42,43],{},"This is not the only way to do it, but it is a pragmatic setup that I would be comfortable running.",[45,46,48],"h2",{"id":47},"first-harden-the-server-before-installing-anything","First: harden the server before installing anything",[11,50,51,52,56],{},"Start by connecting to your VPS as ",[53,54,55],"code",{},"root",".",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-bash shiki shiki-themes github-light github-dark","ssh root@YOUR_VPS_IP\n","bash","",[53,65,66],{"__ignoreMap":63},[67,68,71,75],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"sScJk","ssh",[67,76,78],{"class":77},"sZZnC"," root@YOUR_VPS_IP\n",[11,80,81,82,85],{},"If this is the first time you connect, type ",[53,83,84],{},"yes"," at the SSH fingerprint prompt.",[11,87,88],{},"Once you are in, update the machine.",[58,90,92],{"className":60,"code":91,"language":62,"meta":63,"style":63},"apt-get update && apt-get upgrade -y\n",[53,93,94],{"__ignoreMap":63},[67,95,96,99,102,106,108,111],{"class":69,"line":70},[67,97,98],{"class":73},"apt-get",[67,100,101],{"class":77}," update",[67,103,105],{"class":104},"sVt8B"," && ",[67,107,98],{"class":73},[67,109,110],{"class":77}," upgrade",[67,112,114],{"class":113},"sj4cs"," -y\n",[11,116,117],{},"Then install the basic packages we need along the way.",[58,119,121],{"className":60,"code":120,"language":62,"meta":63,"style":63},"apt-get install -y git curl ca-certificates\n",[53,122,123],{"__ignoreMap":63},[67,124,125,127,130,133,136,139],{"class":69,"line":70},[67,126,98],{"class":73},[67,128,129],{"class":77}," install",[67,131,132],{"class":113}," -y",[67,134,135],{"class":77}," git",[67,137,138],{"class":77}," curl",[67,140,141],{"class":77}," ca-certificates\n",[11,143,144],{},"Now install Docker:",[58,146,148],{"className":60,"code":147,"language":62,"meta":63,"style":63},"curl -fsSL https:\u002F\u002Fget.docker.com | sh\n",[53,149,150],{"__ignoreMap":63},[67,151,152,155,158,161,165],{"class":69,"line":70},[67,153,154],{"class":73},"curl",[67,156,157],{"class":113}," -fsSL",[67,159,160],{"class":77}," https:\u002F\u002Fget.docker.com",[67,162,164],{"class":163},"szBVR"," |",[67,166,167],{"class":73}," sh\n",[11,169,170],{},"And after that, install the basic hardening tools:",[58,172,174],{"className":60,"code":173,"language":62,"meta":63,"style":63},"apt install ufw fail2ban unattended-upgrades -y\n",[53,175,176],{"__ignoreMap":63},[67,177,178,181,183,186,189,192],{"class":69,"line":70},[67,179,180],{"class":73},"apt",[67,182,129],{"class":77},[67,184,185],{"class":77}," ufw",[67,187,188],{"class":77}," fail2ban",[67,190,191],{"class":77}," unattended-upgrades",[67,193,114],{"class":113},[11,195,196],{},"Enable automatic security updates as well:",[58,198,200],{"className":60,"code":199,"language":62,"meta":63,"style":63},"dpkg-reconfigure -plow unattended-upgrades\n",[53,201,202],{"__ignoreMap":63},[67,203,204,207,210],{"class":69,"line":70},[67,205,206],{"class":73},"dpkg-reconfigure",[67,208,209],{"class":113}," -plow",[67,211,212],{"class":77}," unattended-upgrades\n",[11,214,215],{},"There is nothing OpenClaw-specific here yet, and that is the point. Before giving an AI agent a home on your VPS, make sure the VPS itself is not casually exposed.",[45,217,219],{"id":218},"configure-the-firewall-before-openclaw-exists","Configure the firewall before OpenClaw exists",[11,221,222],{},"This is one of the most important parts of the whole setup.",[11,224,225,226,229],{},"The gateway should ",[21,227,228],{},"not"," be reachable from the public internet. It should bind to loopback and only be reachable locally on the server. So before installing OpenClaw, configure the firewall to allow only SSH.",[58,231,233],{"className":60,"code":232,"language":62,"meta":63,"style":63},"ufw default deny incoming\nufw default allow outgoing\nufw allow 22\u002Ftcp comment 'SSH'\nufw enable\nufw status verbose\n",[53,234,235,249,262,278,286],{"__ignoreMap":63},[67,236,237,240,243,246],{"class":69,"line":70},[67,238,239],{"class":73},"ufw",[67,241,242],{"class":77}," default",[67,244,245],{"class":77}," deny",[67,247,248],{"class":77}," incoming\n",[67,250,252,254,256,259],{"class":69,"line":251},2,[67,253,239],{"class":73},[67,255,242],{"class":77},[67,257,258],{"class":77}," allow",[67,260,261],{"class":77}," outgoing\n",[67,263,265,267,269,272,275],{"class":69,"line":264},3,[67,266,239],{"class":73},[67,268,258],{"class":77},[67,270,271],{"class":77}," 22\u002Ftcp",[67,273,274],{"class":77}," comment",[67,276,277],{"class":77}," 'SSH'\n",[67,279,281,283],{"class":69,"line":280},4,[67,282,239],{"class":73},[67,284,285],{"class":77}," enable\n",[67,287,289,291,294],{"class":69,"line":288},5,[67,290,239],{"class":73},[67,292,293],{"class":77}," status",[67,295,296],{"class":77}," verbose\n",[11,298,299,300,302],{},"What you should ",[21,301,228],{}," do is this:",[58,304,306],{"className":60,"code":305,"language":62,"meta":63,"style":63},"ufw allow 18789\u002Ftcp\n",[53,307,308],{"__ignoreMap":63},[67,309,310,312,314],{"class":69,"line":70},[67,311,239],{"class":73},[67,313,258],{"class":77},[67,315,316],{"class":77}," 18789\u002Ftcp\n",[11,318,319],{},"That would expose the OpenClaw gateway directly to the internet, which defeats the point of keeping it local in the first place.",[45,321,323],{"id":322},"create-a-separate-user-and-stop-using-root","Create a separate user and stop using root",[11,325,326],{},"Next, create a dedicated user for running and managing OpenClaw.",[58,328,330],{"className":60,"code":329,"language":62,"meta":63,"style":63},"adduser openclaw\n",[53,331,332],{"__ignoreMap":63},[67,333,334,337],{"class":69,"line":70},[67,335,336],{"class":73},"adduser",[67,338,339],{"class":77}," openclaw\n",[11,341,342,343,56],{},"Set a password, skip the optional name and phone fields, and confirm with ",[53,344,345],{},"Y",[11,347,348,349,352],{},"Then give that user ",[53,350,351],{},"sudo"," and Docker access:",[58,354,356],{"className":60,"code":355,"language":62,"meta":63,"style":63},"usermod -aG sudo openclaw\nusermod -aG docker openclaw\n",[53,357,358,371],{"__ignoreMap":63},[67,359,360,363,366,369],{"class":69,"line":70},[67,361,362],{"class":73},"usermod",[67,364,365],{"class":113}," -aG",[67,367,368],{"class":77}," sudo",[67,370,339],{"class":77},[67,372,373,375,377,380],{"class":69,"line":251},[67,374,362],{"class":73},[67,376,365],{"class":113},[67,378,379],{"class":77}," docker",[67,381,339],{"class":77},[11,383,384],{},"Copy over your SSH key so you can log in as the new user:",[58,386,388],{"className":60,"code":387,"language":62,"meta":63,"style":63},"mkdir -p \u002Fhome\u002Fopenclaw\u002F.ssh\ncp \u002Froot\u002F.ssh\u002Fauthorized_keys \u002Fhome\u002Fopenclaw\u002F.ssh\u002F\nchown -R openclaw:openclaw \u002Fhome\u002Fopenclaw\u002F.ssh\nchmod 700 \u002Fhome\u002Fopenclaw\u002F.ssh\nchmod 600 \u002Fhome\u002Fopenclaw\u002F.ssh\u002Fauthorized_keys\n",[53,389,390,401,412,425,435],{"__ignoreMap":63},[67,391,392,395,398],{"class":69,"line":70},[67,393,394],{"class":73},"mkdir",[67,396,397],{"class":113}," -p",[67,399,400],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.ssh\n",[67,402,403,406,409],{"class":69,"line":251},[67,404,405],{"class":73},"cp",[67,407,408],{"class":77}," \u002Froot\u002F.ssh\u002Fauthorized_keys",[67,410,411],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.ssh\u002F\n",[67,413,414,417,420,423],{"class":69,"line":264},[67,415,416],{"class":73},"chown",[67,418,419],{"class":113}," -R",[67,421,422],{"class":77}," openclaw:openclaw",[67,424,400],{"class":77},[67,426,427,430,433],{"class":69,"line":280},[67,428,429],{"class":73},"chmod",[67,431,432],{"class":113}," 700",[67,434,400],{"class":77},[67,436,437,439,442],{"class":69,"line":288},[67,438,429],{"class":73},[67,440,441],{"class":113}," 600",[67,443,444],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.ssh\u002Fauthorized_keys\n",[11,446,447,448,451],{},"Now test it in a ",[21,449,450],{},"new terminal tab",", while keeping your root session open:",[58,453,455],{"className":60,"code":454,"language":62,"meta":63,"style":63},"ssh openclaw@YOUR_VPS_IP\n",[53,456,457],{"__ignoreMap":63},[67,458,459,461],{"class":69,"line":70},[67,460,74],{"class":73},[67,462,463],{"class":77}," openclaw@YOUR_VPS_IP\n",[11,465,466,467,470],{},"If you see a shell prompt for ",[53,468,469],{},"openclaw",", you are good.",[11,472,473],{},"Only after that should you lock down root SSH access:",[58,475,477],{"className":60,"code":476,"language":62,"meta":63,"style":63},"echo \"PermitRootLogin no\" >> \u002Fetc\u002Fssh\u002Fsshd_config\necho \"PasswordAuthentication no\" >> \u002Fetc\u002Fssh\u002Fsshd_config\nsystemctl restart ssh\n",[53,478,479,493,504],{"__ignoreMap":63},[67,480,481,484,487,490],{"class":69,"line":70},[67,482,483],{"class":113},"echo",[67,485,486],{"class":77}," \"PermitRootLogin no\"",[67,488,489],{"class":163}," >>",[67,491,492],{"class":77}," \u002Fetc\u002Fssh\u002Fsshd_config\n",[67,494,495,497,500,502],{"class":69,"line":251},[67,496,483],{"class":113},[67,498,499],{"class":77}," \"PasswordAuthentication no\"",[67,501,489],{"class":163},[67,503,492],{"class":77},[67,505,506,509,512],{"class":69,"line":264},[67,507,508],{"class":73},"systemctl",[67,510,511],{"class":77}," restart",[67,513,514],{"class":77}," ssh\n",[11,516,517],{},"And verify:",[58,519,520],{"className":60,"code":61,"language":62,"meta":63,"style":63},[53,521,522],{"__ignoreMap":63},[67,523,524,526],{"class":69,"line":70},[67,525,74],{"class":73},[67,527,78],{"class":77},[11,529,530,531,56],{},"This should now fail with ",[53,532,533],{},"Permission denied",[11,535,536,537,539],{},"From this point on, use the ",[53,538,469],{}," user only.",[45,541,543],{"id":542},"clone-the-repo-and-create-persistent-directories","Clone the repo and create persistent directories",[11,545,546,547,549],{},"Log in as the ",[53,548,469],{}," user and update packages once more:",[58,551,553],{"className":60,"code":552,"language":62,"meta":63,"style":63},"sudo apt-get update && sudo apt-get upgrade -y\n",[53,554,555],{"__ignoreMap":63},[67,556,557,559,562,564,566,568,570,572],{"class":69,"line":70},[67,558,351],{"class":73},[67,560,561],{"class":77}," apt-get",[67,563,101],{"class":77},[67,565,105],{"class":104},[67,567,351],{"class":73},[67,569,561],{"class":77},[67,571,110],{"class":77},[67,573,114],{"class":113},[11,575,576],{},"Then clone the repository:",[58,578,580],{"className":60,"code":579,"language":62,"meta":63,"style":63},"git clone https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw.git\ncd openclaw\n",[53,581,582,593],{"__ignoreMap":63},[67,583,584,587,590],{"class":69,"line":70},[67,585,586],{"class":73},"git",[67,588,589],{"class":77}," clone",[67,591,592],{"class":77}," https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw.git\n",[67,594,595,598],{"class":69,"line":251},[67,596,597],{"class":113},"cd",[67,599,339],{"class":77},[11,601,602],{},"Create the directories that should survive container rebuilds:",[58,604,606],{"className":60,"code":605,"language":62,"meta":63,"style":63},"sudo mkdir -p \u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fworkspace\nsudo chown -R openclaw:openclaw \u002Fhome\u002Fopenclaw\u002F.openclaw\n",[53,607,608,620],{"__ignoreMap":63},[67,609,610,612,615,617],{"class":69,"line":70},[67,611,351],{"class":73},[67,613,614],{"class":77}," mkdir",[67,616,397],{"class":113},[67,618,619],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fworkspace\n",[67,621,622,624,627,629,631],{"class":69,"line":251},[67,623,351],{"class":73},[67,625,626],{"class":77}," chown",[67,628,419],{"class":113},[67,630,422],{"class":77},[67,632,633],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.openclaw\n",[11,635,636],{},"I prefer keeping this data outside the container so configuration, auth state, and workspace files do not disappear when rebuilding images or replacing containers.",[45,638,640],{"id":639},"protect-the-environment-file","Protect the environment file",[11,642,643,644,647],{},"Once you create your ",[53,645,646],{},".env",", lock it down immediately:",[58,649,651],{"className":60,"code":650,"language":62,"meta":63,"style":63},"chmod 600 .env\n",[53,652,653],{"__ignoreMap":63},[67,654,655,657,659],{"class":69,"line":70},[67,656,429],{"class":73},[67,658,441],{"class":113},[67,660,661],{"class":77}," .env\n",[11,663,664],{},"That is just a small step, but it is one of those small steps that should always be there.",[45,666,668],{"id":667},"why-the-default-docker-compose-file-is-not-enough","Why the default Docker Compose file is not enough",[11,670,671],{},"This is where the setup starts getting more opinionated.",[11,673,674,675,678],{},"The default ",[53,676,677],{},"docker-compose.yml"," is not what I would call a hardened setup. There are a few things we want to change:",[680,681,682,689,692,695,698],"ol",{},[29,683,684,685,688],{},"We want the gateway to build from the local ",[53,686,687],{},"Dockerfile",", because we are going to tweak that file.",[29,690,691],{},"We want host networking so device pairing works reliably.",[29,693,694],{},"We want the gateway bound to loopback, not something reachable from outside.",[29,696,697],{},"We want restart policies so the service comes back after reboot or crashes.",[29,699,700,701,703],{},"We do ",[21,702,228],{}," want leftover Anthropic session variables hanging around if we are not using them.",[11,705,706,707,56],{},"If you want to edit comfortably, this is one of those cases where VS Code Remote SSH is much nicer than editing everything with ",[53,708,709],{},"nano",[11,711,712],{},"The file we are changing is:",[58,714,716],{"className":60,"code":715,"language":62,"meta":63,"style":63},"nano \u002Fhome\u002Fopenclaw\u002Fopenclaw\u002Fdocker-compose.yml\n",[53,717,718],{"__ignoreMap":63},[67,719,720,722],{"class":69,"line":70},[67,721,709],{"class":73},[67,723,724],{"class":77}," \u002Fhome\u002Fopenclaw\u002Fopenclaw\u002Fdocker-compose.yml\n",[11,726,727],{},"And this is the version I ended up using:",[58,729,733],{"className":730,"code":731,"language":732,"meta":63,"style":63},"language-yaml shiki shiki-themes github-light github-dark","services:\n  openclaw-gateway:\n    image: ${OPENCLAW_IMAGE:-openclaw:local}\n    build: .\n    environment:\n      HOME: \u002Fhome\u002Fnode\n      TERM: xterm-256color\n      OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}\n      NODE_ENV: production\n      OPENCLAW_GATEWAY_BIND: ${OPENCLAW_GATEWAY_BIND:-loopback}\n      OPENCLAW_GATEWAY_PORT: ${OPENCLAW_GATEWAY_PORT:-18789}\n      OPENCLAW_SECRET: ${OPENCLAW_SECRET}\n      XDG_CONFIG_HOME: ${XDG_CONFIG_HOME}\n    volumes:\n      - ${OPENCLAW_CONFIG_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\n      - ${OPENCLAW_WORKSPACE_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\u002Fworkspace\n    network_mode: host\n    init: true\n    restart: unless-stopped\n    command:\n      [\"node\", \"dist\u002Findex.js\", \"gateway\",\n       \"--bind\", \"${OPENCLAW_GATEWAY_BIND:-loopback}\",\n       \"--port\", \"18789\"]\n\n  openclaw-cli:\n    image: ${OPENCLAW_IMAGE:-openclaw:local}\n    environment:\n      HOME: \u002Fhome\u002Fnode\n      TERM: xterm-256color\n      OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}\n      BROWSER: echo\n    volumes:\n      - ${OPENCLAW_CONFIG_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\n      - ${OPENCLAW_WORKSPACE_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\u002Fworkspace\n    stdin_open: true\n    tty: true\n    init: true\n    entrypoint: [\"node\", \"dist\u002Findex.js\"]\n","yaml",[53,734,735,744,751,762,772,779,790,801,812,823,834,845,856,867,875,884,892,903,914,925,933,956,969,983,990,998,1007,1014,1023,1032,1041,1052,1059,1066,1073,1083,1093,1102],{"__ignoreMap":63},[67,736,737,741],{"class":69,"line":70},[67,738,740],{"class":739},"s9eBZ","services",[67,742,743],{"class":104},":\n",[67,745,746,749],{"class":69,"line":251},[67,747,748],{"class":739},"  openclaw-gateway",[67,750,743],{"class":104},[67,752,753,756,759],{"class":69,"line":264},[67,754,755],{"class":739},"    image",[67,757,758],{"class":104},": ",[67,760,761],{"class":77},"${OPENCLAW_IMAGE:-openclaw:local}\n",[67,763,764,767,769],{"class":69,"line":280},[67,765,766],{"class":739},"    build",[67,768,758],{"class":104},[67,770,771],{"class":113},".\n",[67,773,774,777],{"class":69,"line":288},[67,775,776],{"class":739},"    environment",[67,778,743],{"class":104},[67,780,782,785,787],{"class":69,"line":781},6,[67,783,784],{"class":739},"      HOME",[67,786,758],{"class":104},[67,788,789],{"class":77},"\u002Fhome\u002Fnode\n",[67,791,793,796,798],{"class":69,"line":792},7,[67,794,795],{"class":739},"      TERM",[67,797,758],{"class":104},[67,799,800],{"class":77},"xterm-256color\n",[67,802,804,807,809],{"class":69,"line":803},8,[67,805,806],{"class":739},"      OPENCLAW_GATEWAY_TOKEN",[67,808,758],{"class":104},[67,810,811],{"class":77},"${OPENCLAW_GATEWAY_TOKEN}\n",[67,813,815,818,820],{"class":69,"line":814},9,[67,816,817],{"class":739},"      NODE_ENV",[67,819,758],{"class":104},[67,821,822],{"class":77},"production\n",[67,824,826,829,831],{"class":69,"line":825},10,[67,827,828],{"class":739},"      OPENCLAW_GATEWAY_BIND",[67,830,758],{"class":104},[67,832,833],{"class":77},"${OPENCLAW_GATEWAY_BIND:-loopback}\n",[67,835,837,840,842],{"class":69,"line":836},11,[67,838,839],{"class":739},"      OPENCLAW_GATEWAY_PORT",[67,841,758],{"class":104},[67,843,844],{"class":77},"${OPENCLAW_GATEWAY_PORT:-18789}\n",[67,846,848,851,853],{"class":69,"line":847},12,[67,849,850],{"class":739},"      OPENCLAW_SECRET",[67,852,758],{"class":104},[67,854,855],{"class":77},"${OPENCLAW_SECRET}\n",[67,857,859,862,864],{"class":69,"line":858},13,[67,860,861],{"class":739},"      XDG_CONFIG_HOME",[67,863,758],{"class":104},[67,865,866],{"class":77},"${XDG_CONFIG_HOME}\n",[67,868,870,873],{"class":69,"line":869},14,[67,871,872],{"class":739},"    volumes",[67,874,743],{"class":104},[67,876,878,881],{"class":69,"line":877},15,[67,879,880],{"class":104},"      - ",[67,882,883],{"class":77},"${OPENCLAW_CONFIG_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\n",[67,885,887,889],{"class":69,"line":886},16,[67,888,880],{"class":104},[67,890,891],{"class":77},"${OPENCLAW_WORKSPACE_DIR}:\u002Fhome\u002Fnode\u002F.openclaw\u002Fworkspace\n",[67,893,895,898,900],{"class":69,"line":894},17,[67,896,897],{"class":739},"    network_mode",[67,899,758],{"class":104},[67,901,902],{"class":77},"host\n",[67,904,906,909,911],{"class":69,"line":905},18,[67,907,908],{"class":739},"    init",[67,910,758],{"class":104},[67,912,913],{"class":113},"true\n",[67,915,917,920,922],{"class":69,"line":916},19,[67,918,919],{"class":739},"    restart",[67,921,758],{"class":104},[67,923,924],{"class":77},"unless-stopped\n",[67,926,928,931],{"class":69,"line":927},20,[67,929,930],{"class":739},"    command",[67,932,743],{"class":104},[67,934,936,939,942,945,948,950,953],{"class":69,"line":935},21,[67,937,938],{"class":104},"      [",[67,940,941],{"class":77},"\"node\"",[67,943,944],{"class":104},", ",[67,946,947],{"class":77},"\"dist\u002Findex.js\"",[67,949,944],{"class":104},[67,951,952],{"class":77},"\"gateway\"",[67,954,955],{"class":104},",\n",[67,957,959,962,964,967],{"class":69,"line":958},22,[67,960,961],{"class":77},"       \"--bind\"",[67,963,944],{"class":104},[67,965,966],{"class":77},"\"${OPENCLAW_GATEWAY_BIND:-loopback}\"",[67,968,955],{"class":104},[67,970,972,975,977,980],{"class":69,"line":971},23,[67,973,974],{"class":77},"       \"--port\"",[67,976,944],{"class":104},[67,978,979],{"class":77},"\"18789\"",[67,981,982],{"class":104},"]\n",[67,984,986],{"class":69,"line":985},24,[67,987,989],{"emptyLinePlaceholder":988},true,"\n",[67,991,993,996],{"class":69,"line":992},25,[67,994,995],{"class":739},"  openclaw-cli",[67,997,743],{"class":104},[67,999,1001,1003,1005],{"class":69,"line":1000},26,[67,1002,755],{"class":739},[67,1004,758],{"class":104},[67,1006,761],{"class":77},[67,1008,1010,1012],{"class":69,"line":1009},27,[67,1011,776],{"class":739},[67,1013,743],{"class":104},[67,1015,1017,1019,1021],{"class":69,"line":1016},28,[67,1018,784],{"class":739},[67,1020,758],{"class":104},[67,1022,789],{"class":77},[67,1024,1026,1028,1030],{"class":69,"line":1025},29,[67,1027,795],{"class":739},[67,1029,758],{"class":104},[67,1031,800],{"class":77},[67,1033,1035,1037,1039],{"class":69,"line":1034},30,[67,1036,806],{"class":739},[67,1038,758],{"class":104},[67,1040,811],{"class":77},[67,1042,1044,1047,1049],{"class":69,"line":1043},31,[67,1045,1046],{"class":739},"      BROWSER",[67,1048,758],{"class":104},[67,1050,1051],{"class":77},"echo\n",[67,1053,1055,1057],{"class":69,"line":1054},32,[67,1056,872],{"class":739},[67,1058,743],{"class":104},[67,1060,1062,1064],{"class":69,"line":1061},33,[67,1063,880],{"class":104},[67,1065,883],{"class":77},[67,1067,1069,1071],{"class":69,"line":1068},34,[67,1070,880],{"class":104},[67,1072,891],{"class":77},[67,1074,1076,1079,1081],{"class":69,"line":1075},35,[67,1077,1078],{"class":739},"    stdin_open",[67,1080,758],{"class":104},[67,1082,913],{"class":113},[67,1084,1086,1089,1091],{"class":69,"line":1085},36,[67,1087,1088],{"class":739},"    tty",[67,1090,758],{"class":104},[67,1092,913],{"class":113},[67,1094,1096,1098,1100],{"class":69,"line":1095},37,[67,1097,908],{"class":739},[67,1099,758],{"class":104},[67,1101,913],{"class":113},[67,1103,1105,1108,1111,1113,1115,1117],{"class":69,"line":1104},38,[67,1106,1107],{"class":739},"    entrypoint",[67,1109,1110],{"class":104},": [",[67,1112,941],{"class":77},[67,1114,944],{"class":104},[67,1116,947],{"class":77},[67,1118,982],{"class":104},[11,1120,1121],{},"The most important line here is probably this one:",[58,1123,1125],{"className":730,"code":1124,"language":732,"meta":63,"style":63},"OPENCLAW_GATEWAY_BIND: ${OPENCLAW_GATEWAY_BIND:-loopback}\n",[53,1126,1127],{"__ignoreMap":63},[67,1128,1129,1132,1134],{"class":69,"line":70},[67,1130,1131],{"class":739},"OPENCLAW_GATEWAY_BIND",[67,1133,758],{"class":104},[67,1135,833],{"class":77},[11,1137,1138,1141,1142,1145],{},[53,1139,1140],{},"loopback"," means the gateway listens on ",[53,1143,1144],{},"127.0.0.1"," only. That is exactly what we want.",[11,1147,1148,1149,1152],{},"Another important change is removing the ",[53,1150,1151],{},"ports:"," section entirely. If you expose the gateway with normal Docker port publishing and then open it in the firewall, you have made the box much more reachable than necessary.",[45,1154,1156],{"id":1155},"modify-the-dockerfile","Modify the Dockerfile",[11,1158,1159,1160,56],{},"There is one small but practical modification I had to make in the ",[53,1161,687],{},[11,1163,1164],{},"Open it:",[58,1166,1168],{"className":60,"code":1167,"language":62,"meta":63,"style":63},"nano Dockerfile\n",[53,1169,1170],{"__ignoreMap":63},[67,1171,1172,1174],{"class":69,"line":70},[67,1173,709],{"class":73},[67,1175,1176],{"class":77}," Dockerfile\n",[11,1178,1179],{},"Near the bottom you will find:",[58,1181,1185],{"className":1182,"code":1183,"language":1184,"meta":63,"style":63},"language-dockerfile shiki shiki-themes github-light github-dark","USER node\n","dockerfile",[53,1186,1187],{"__ignoreMap":63},[67,1188,1189],{"class":69,"line":70},[67,1190,1183],{},[11,1192,1193],{},"Comment that out:",[58,1195,1197],{"className":1182,"code":1196,"language":1184,"meta":63,"style":63},"# USER node\n",[53,1198,1199],{"__ignoreMap":63},[67,1200,1201],{"class":69,"line":70},[67,1202,1196],{},[11,1204,1205],{},"Normally I prefer dropping privileges inside containers, but in this case the agent needs to install tools, manage packages, and write files in ways that quickly run into permissions friction. Leaving that line in place caused more trouble than value for my use case.",[45,1207,1209],{"id":1208},"build-and-run-onboarding","Build and run onboarding",[11,1211,1212],{},"Now build the images:",[58,1214,1216],{"className":60,"code":1215,"language":62,"meta":63,"style":63},"docker compose build\n",[53,1217,1218],{"__ignoreMap":63},[67,1219,1220,1223,1226],{"class":69,"line":70},[67,1221,1222],{"class":73},"docker",[67,1224,1225],{"class":77}," compose",[67,1227,1228],{"class":77}," build\n",[11,1230,1231],{},"Then run the onboarding wizard:",[58,1233,1235],{"className":60,"code":1234,"language":62,"meta":63,"style":63},"docker compose run --rm openclaw-cli onboard\n",[53,1236,1237],{"__ignoreMap":63},[67,1238,1239,1241,1243,1246,1249,1252],{"class":69,"line":70},[67,1240,1222],{"class":73},[67,1242,1225],{"class":77},[67,1244,1245],{"class":77}," run",[67,1247,1248],{"class":113}," --rm",[67,1250,1251],{"class":77}," openclaw-cli",[67,1253,1254],{"class":77}," onboard\n",[11,1256,1257,1258,1261,1262,1265,1266,1269],{},"I went with ",[53,1259,1260],{},"QuickStart",", then ",[53,1263,1264],{},"OpenAI (Codex OAuth + API key)",", followed by ",[53,1267,1268],{},"OpenAI Codex (ChatGPT OAuth)",", but you can pick whichever provider works for you.",[11,1271,1272],{},"The OAuth flow is slightly awkward the first time, but it is simple enough:",[680,1274,1275,1278,1281,1284,1290,1293],{},[29,1276,1277],{},"The wizard prints a URL.",[29,1279,1280],{},"Open it in your laptop browser.",[29,1282,1283],{},"Log in and approve.",[29,1285,1286,1287],{},"The browser redirects to something like ",[53,1288,1289],{},"http:\u002F\u002Flocalhost:xxxx\u002Fauth\u002Fcallback?code=...",[29,1291,1292],{},"The page may say it cannot connect. That is normal.",[29,1294,1295],{},"Copy the entire redirected URL and paste it back into the terminal.",[11,1297,1298],{},"If you want Telegram, configure it during onboarding. I skipped the optional skills, hooks, and extra API key prompts for the initial setup.",[45,1300,1302],{"id":1301},"fix-the-bind-mode-if-the-wizard-changes-it","Fix the bind mode if the wizard changes it",[11,1304,1305],{},"After onboarding, verify that the gateway bind value is still correct:",[58,1307,1309],{"className":60,"code":1308,"language":62,"meta":63,"style":63},"grep '\"bind\"' \u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fopenclaw.json\n",[53,1310,1311],{"__ignoreMap":63},[67,1312,1313,1316,1319],{"class":69,"line":70},[67,1314,1315],{"class":73},"grep",[67,1317,1318],{"class":77}," '\"bind\"'",[67,1320,1321],{"class":77}," \u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fopenclaw.json\n",[11,1323,1324],{},"It should say:",[58,1326,1330],{"className":1327,"code":1328,"language":1329,"meta":63,"style":63},"language-json shiki shiki-themes github-light github-dark","\"bind\": \"loopback\"\n","json",[53,1331,1332],{"__ignoreMap":63},[67,1333,1334,1337,1339],{"class":69,"line":70},[67,1335,1336],{"class":77},"\"bind\"",[67,1338,758],{"class":104},[67,1340,1341],{"class":77},"\"loopback\"\n",[11,1343,1344,1345,1348],{},"If it says ",[53,1346,1347],{},"lan",", change it. This is not a cosmetic setting. It controls whether the gateway stays local or starts listening more broadly than intended.",[45,1350,1352],{"id":1351},"fix-the-gateway-token-mismatch","Fix the gateway token mismatch",[11,1354,1355],{},"This was the part that felt most likely to waste time later if left unnoticed.",[11,1357,1358,1359,1362,1363,1365],{},"The onboarding wizard can write its own gateway token into ",[53,1360,1361],{},"openclaw.json",", and that token may not match the one in your ",[53,1364,646],{},". If the tokens differ, you will get weird failures later: CLI commands fail, subagents do not spawn correctly, cron jobs misbehave, and the setup feels broken in ways that are harder to reason about than they should be.",[11,1367,1368],{},"Check the token written by the wizard:",[58,1370,1372],{"className":60,"code":1371,"language":62,"meta":63,"style":63},"python3 -c \"import json; c=json.load(open('\u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fopenclaw.json')); print(c['gateway']['auth']['token'])\"\n",[53,1373,1374],{"__ignoreMap":63},[67,1375,1376,1379,1382],{"class":69,"line":70},[67,1377,1378],{"class":73},"python3",[67,1380,1381],{"class":113}," -c",[67,1383,1384],{"class":77}," \"import json; c=json.load(open('\u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fopenclaw.json')); print(c['gateway']['auth']['token'])\"\n",[11,1386,1387,1388,1390],{},"Then compare it with the token in ",[53,1389,646],{},":",[58,1392,1394],{"className":60,"code":1393,"language":62,"meta":63,"style":63},"grep OPENCLAW_GATEWAY_TOKEN \u002Fhome\u002Fopenclaw\u002Fopenclaw\u002F.env\n",[53,1395,1396],{"__ignoreMap":63},[67,1397,1398,1400,1403],{"class":69,"line":70},[67,1399,1315],{"class":73},[67,1401,1402],{"class":77}," OPENCLAW_GATEWAY_TOKEN",[67,1404,1405],{"class":77}," \u002Fhome\u002Fopenclaw\u002Fopenclaw\u002F.env\n",[11,1407,1408],{},"If they are different, edit the JSON file and make them match:",[58,1410,1412],{"className":60,"code":1411,"language":62,"meta":63,"style":63},"nano \u002Fhome\u002Fopenclaw\u002F.openclaw\u002Fopenclaw.json\n",[53,1413,1414],{"__ignoreMap":63},[67,1415,1416,1418],{"class":69,"line":70},[67,1417,709],{"class":73},[67,1419,1321],{"class":77},[45,1421,1423,1424,1426],{"id":1422},"the-final-openclawjson-gateway-shape","The final ",[53,1425,1361],{}," gateway shape",[11,1428,1429,1430,1432],{},"The gateway section in ",[53,1431,1361],{}," should look like this:",[58,1434,1436],{"className":1327,"code":1435,"language":1329,"meta":63,"style":63},"\"gateway\": {\n  \"port\": 18789,\n  \"mode\": \"local\",\n  \"bind\": \"loopback\",\n  \"auth\": {\n    \"mode\": \"token\",\n    \"token\": \"YOUR_TOKEN_FROM_ENV_FILE\",\n    \"rateLimit\": {\n      \"maxAttempts\": 10,\n      \"windowMs\": 60000,\n      \"lockoutMs\": 300000\n    }\n  },\n  \"controlUi\": {\n    \"enabled\": true,\n    \"allowInsecureAuth\": false\n  }\n}\n",[53,1437,1438,1445,1457,1469,1481,1488,1500,1512,1519,1531,1543,1553,1558,1563,1570,1582,1592,1597],{"__ignoreMap":63},[67,1439,1440,1442],{"class":69,"line":70},[67,1441,952],{"class":77},[67,1443,1444],{"class":104},": {\n",[67,1446,1447,1450,1452,1455],{"class":69,"line":251},[67,1448,1449],{"class":113},"  \"port\"",[67,1451,758],{"class":104},[67,1453,1454],{"class":113},"18789",[67,1456,955],{"class":104},[67,1458,1459,1462,1464,1467],{"class":69,"line":264},[67,1460,1461],{"class":113},"  \"mode\"",[67,1463,758],{"class":104},[67,1465,1466],{"class":77},"\"local\"",[67,1468,955],{"class":104},[67,1470,1471,1474,1476,1479],{"class":69,"line":280},[67,1472,1473],{"class":113},"  \"bind\"",[67,1475,758],{"class":104},[67,1477,1478],{"class":77},"\"loopback\"",[67,1480,955],{"class":104},[67,1482,1483,1486],{"class":69,"line":288},[67,1484,1485],{"class":113},"  \"auth\"",[67,1487,1444],{"class":104},[67,1489,1490,1493,1495,1498],{"class":69,"line":781},[67,1491,1492],{"class":113},"    \"mode\"",[67,1494,758],{"class":104},[67,1496,1497],{"class":77},"\"token\"",[67,1499,955],{"class":104},[67,1501,1502,1505,1507,1510],{"class":69,"line":792},[67,1503,1504],{"class":113},"    \"token\"",[67,1506,758],{"class":104},[67,1508,1509],{"class":77},"\"YOUR_TOKEN_FROM_ENV_FILE\"",[67,1511,955],{"class":104},[67,1513,1514,1517],{"class":69,"line":803},[67,1515,1516],{"class":113},"    \"rateLimit\"",[67,1518,1444],{"class":104},[67,1520,1521,1524,1526,1529],{"class":69,"line":814},[67,1522,1523],{"class":113},"      \"maxAttempts\"",[67,1525,758],{"class":104},[67,1527,1528],{"class":113},"10",[67,1530,955],{"class":104},[67,1532,1533,1536,1538,1541],{"class":69,"line":825},[67,1534,1535],{"class":113},"      \"windowMs\"",[67,1537,758],{"class":104},[67,1539,1540],{"class":113},"60000",[67,1542,955],{"class":104},[67,1544,1545,1548,1550],{"class":69,"line":836},[67,1546,1547],{"class":113},"      \"lockoutMs\"",[67,1549,758],{"class":104},[67,1551,1552],{"class":113},"300000\n",[67,1554,1555],{"class":69,"line":847},[67,1556,1557],{"class":104},"    }\n",[67,1559,1560],{"class":69,"line":858},[67,1561,1562],{"class":104},"  },\n",[67,1564,1565,1568],{"class":69,"line":869},[67,1566,1567],{"class":113},"  \"controlUi\"",[67,1569,1444],{"class":104},[67,1571,1572,1575,1577,1580],{"class":69,"line":877},[67,1573,1574],{"class":113},"    \"enabled\"",[67,1576,758],{"class":104},[67,1578,1579],{"class":113},"true",[67,1581,955],{"class":104},[67,1583,1584,1587,1589],{"class":69,"line":886},[67,1585,1586],{"class":113},"    \"allowInsecureAuth\"",[67,1588,758],{"class":104},[67,1590,1591],{"class":113},"false\n",[67,1593,1594],{"class":69,"line":894},[67,1595,1596],{"class":104},"  }\n",[67,1598,1599],{"class":69,"line":905},[67,1600,1601],{"class":104},"}\n",[11,1603,1604],{},"There are three fields here that matter a lot:",[26,1606,1607,1615,1621],{},[29,1608,1609,1612,1613],{},[53,1610,1611],{},"bind: \"loopback\""," keeps the gateway local to ",[53,1614,1144],{},[29,1616,1617,1620],{},[53,1618,1619],{},"allowInsecureAuth: false"," ensures you do not weaken device pairing",[29,1622,1623,1626],{},[53,1624,1625],{},"rateLimit"," gives you a basic brute-force safety net",[11,1628,1629],{},"This is the part where the setup stops being “just a local tool running in Docker” and starts becoming a service you can operate with a bit more confidence.",[45,1631,1633],{"id":1632},"final-thoughts","Final thoughts",[11,1635,1636],{},"OpenClaw is not particularly hard to get running. The hard part is resisting the temptation to stop the moment you see the happy path working.",[11,1638,1639],{},"A secure setup here is mostly about discipline:",[26,1641,1642,1645,1648,1651,1654,1657],{},[29,1643,1644],{},"do the firewall first",[29,1646,1647],{},"move off root",[29,1649,1650],{},"keep the gateway on loopback",[29,1652,1653],{},"do not open the port publicly",[29,1655,1656],{},"keep persistent data outside the container",[29,1658,1659],{},"make sure the tokens are actually in sync",[11,1661,1662],{},"If you skip those parts, the setup is technically “done”, but not in a way I would feel good about leaving on a VPS.",[11,1664,1665],{},"My general rule is simple: whenever a tool can execute commands, install packages, and read or write files, the surrounding environment matters as much as the tool itself.",[11,1667,1668],{},"That is exactly the kind of software that deserves a hardened setup from day one.",[1670,1671,1672],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":63,"searchDepth":251,"depth":251,"links":1674},[1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1687],{"id":47,"depth":251,"text":48},{"id":218,"depth":251,"text":219},{"id":322,"depth":251,"text":323},{"id":542,"depth":251,"text":543},{"id":639,"depth":251,"text":640},{"id":667,"depth":251,"text":668},{"id":1155,"depth":251,"text":1156},{"id":1208,"depth":251,"text":1209},{"id":1301,"depth":251,"text":1302},{"id":1351,"depth":251,"text":1352},{"id":1422,"depth":251,"text":1686},"The final openclaw.json gateway shape",{"id":1632,"depth":251,"text":1633},"AI","2026\u002F03\u002F23",null,"md","\u002Fimages\u002Fopenclaw.png","openclaw, vps, docker, ubuntu, secure openclaw setup, hardened server setup, ssh hardening, ufw, fail2ban",{},"\u002Fblog\u002Fopen-claw-secure-hardened-setup","☕️☕️ 12 min read",{"title":6,"description":13},"open-claw-secure-hardened-setup","blog\u002Fopen-claw-secure-hardened-setup","ai, devops, docker, security, vps","1ndE6Pq3cRoQjtThOp6cmCWCXXUcKnBO70YryaJNr70",{"id":1703,"title":1704,"body":1705,"category":2336,"date":2337,"description":2338,"excerpt":1690,"extension":1691,"image":2339,"keywords":2340,"meta":2341,"navigation":988,"path":2342,"readingTime":2343,"seo":2344,"slug":2345,"stem":2346,"tags":2347,"__hash__":2348},"blog\u002Fblog\u002Fcosmos-sdk-overview-through-the-lens-of-a-javascript-developer.md","Cosmos SDK overview through the lens of a JavaScript Developer",{"type":8,"value":1706,"toc":2323},[1707,1718,1742,1746,1757,1762,1772,1775,1795,1799,1806,1813,1835,1841,1847,1855,1861,1906,1912,1944,1947,1951,1954,1957,2009,2013,2027,2030,2033,2036,2077,2080,2085,2088,2142,2147,2161,2164,2181,2185,2188,2196,2200,2223,2262,2265,2268],[11,1708,1709,1710,1717],{},"Why does a JavaScript developer write about a framework for building application-specific blockchains? We at ",[1711,1712,1716],"a",{"href":1713,"rel":1714},"http:\u002F\u002Finjectiveprotocol.com\u002F",[1715],"nofollow","Injective Protocol"," are building our own Injective Chain on the Cosmos SDK — that alone was enough reason for me to dig into it.",[11,1719,1720,1721,1724,1725,1730,1731,1736,1737,1741],{},"So, what is the Cosmos SDK? Their official docs state that ",[21,1722,1723],{},"Cosmos SDK is the world’s most popular framework for building application-specific blockchains."," It’s written in ",[1711,1726,1729],{"href":1727,"rel":1728},"https:\u002F\u002Fgolang.org\u002F",[1715],"Go"," programming language and uses ",[1711,1732,1735],{"href":1733,"rel":1734},"https:\u002F\u002Ftendermint.com\u002F",[1715],"Tendermint"," core as a consensus algorithm. Tendermint is responsible for the networking and the consensus layer, and Cosmos SDK handles the business logic. The Cosmos SDK and Tendermint communicate using the ABCI (",[1738,1739,1740],"em",{},"Application Blockchain Interface","). Let’s get into the specifics.",[45,1743,1745],{"id":1744},"tendermint-core","Tendermint Core",[11,1747,1748,1749,1752,1753,1756],{},"Tendermint Core is a blockchain application platform; it provides the equivalent of a web-server, database, and supporting libraries for blockchain applications written in any programming language. Tendermint Core is the leading BFT (Byzantine Fault Tolerance) engine for building blockchains. The safety threshold is ",[53,1750,1751],{},"1\u002F3"," of the validator's power. Every block that is produced is considered final (i.e blocks have ",[21,1754,1755],{},"instant finality","). Also, with the ABCI interface, Tendermint Core supports state machines written in any programming language.",[1758,1759,1761],"h3",{"id":1760},"tendermint-bft","Tendermint BFT",[11,1763,1764,1765,1768,1769,56],{},"Byzantine Fault Tolerance is the feature of a distributed network to reach consensus ",[1738,1766,1767],{},"(agreement on the same value)"," even when some of the nodes in the network fail to respond or respond with incorrect information. The objective of a BFT mechanism is to safeguard against system failures by employing collective decision-making (both – correct and faulty nodes) which aims to reduce to influence of the faulty nodes. BFT is derived from ",[1738,1770,1771],{},"Byzantine Generals’ Problem",[11,1773,1774],{},"The advantages of BFT are:",[26,1776,1777,1783,1789],{},[29,1778,1779,1782],{},[21,1780,1781],{},"Energy efficiency -"," It can achieve distributed consensus without carrying out complex mathematical computations (like in Proof of Work).",[29,1784,1785,1788],{},[21,1786,1787],{},"Transaction finality",": The transactions do not require multiple confirmations (like in the case of the PoW mechanism in Bitcoin where every node individually verifies all the transactions before adding the new block to the blockchain; confirmations can take between 10-60 minutes depending upon how many entities confirm the new block) after they have been finalized and agreed upon.",[29,1790,1791,1794],{},[21,1792,1793],{},"Low reward variance",": Every node in the network takes part in responding to the request by the client and hence every node can be incentivized leading to low variance in rewarding the nodes that help in decision making.",[1758,1796,1798],{"id":1797},"bft-consensus-algorithm","BFT Consensus Algorithm",[11,1800,1801,1802,1805],{},"The participants in the protocol are called ",[21,1803,1804],{},"validators",", who take turns in proposing blocks of transactions and vote on them. These turns are assigned to the validators using round-robin scheduling according to their total amount of stake.",[11,1807,1808,1809,1812],{},"Another key participant in the protocol are the ",[21,1810,1811],{},"delegators",". Delegators are token holders that do not want to run validator operations (ie. invest in the necessary equipment to be able to participate in the consensus protocol) and, as the name suggests, they delegate their stake tokens to validators, who charge a commission on the corresponding fees obtained by the delegated tokens.",[11,1814,1815,1816,1819,1820,1823,1824,1827,1828,1831,1832,56],{},"Tendermint BFT works using three steps: ",[21,1817,1818],{},"proposing",", ",[21,1821,1822],{},"pre-voting"," and ",[21,1825,1826],{},"pre-committing."," The sequence ",[53,1829,1830],{},"(Propose -> Pre-vote -> Pre-commit)"," is called ",[21,1833,1834],{},"round",[11,1836,1837],{},[1838,1839],"img",{"alt":1798,"src":1840},"\u002Fimages\u002Fposts\u002Fbft-consensus-algorithm.png",[11,1842,1843,1844],{},"1) ",[21,1845,1846],{},"Propose step",[26,1848,1849,1852],{},[29,1850,1851],{},"A validator node from the validator set is chosen as block proposer for a given height.",[29,1853,1854],{},"The validator picks up transactions and packs them in a block. The proposed block is broadcasted to the rest of nodes.",[11,1856,1857,1858],{},"2) ",[21,1859,1860],{},"Pre-Vote step",[26,1862,1863],{},[29,1864,1865,1866,1871,1872,1875,1876,1879,1880,1883,1884,1889,1890,1892,1893,1898,1899,1902,1903,56],{},"Each node casts a ",[1738,1867,1868],{},[21,1869,1870],{},"pre-vote"," and listens until ",[53,1873,1874],{},"+2\u002F3"," pre-votes from validators' nodes have been submitted. A block can be either pre-voted as ",[1738,1877,1878],{},"a pre-vote"," (ie. valid block) or ",[53,1881,1882],{},"nil"," (when it's invalid or timeout reached). We generally call a ",[1738,1885,1886],{},[21,1887,1888],{},"Polka"," when ",[53,1891,1874],{}," of validator pre-vote for the same block. Also, in the perspective of a validator, if the validator voted for the block that is referred to in the Polka, the validator now has what's called a ",[1738,1894,1895],{},[21,1896,1897],{},"proof-of-lock-change"," or ",[21,1900,1901],{},"PoLC"," for short in that particular height and round ",[53,1904,1905],{},"(H, R)",[11,1907,1908,1909],{},"3) ",[21,1910,1911],{},"Pre-Commit step",[26,1913,1914,1932],{},[29,1915,1916,1917,1922,1923,1925,1926,1928,1929,1931],{},"Once a Polka is reached, validators submit a ",[1738,1918,1919],{},[21,1920,1921],{},"pre-commit"," block, otherwise they ",[1738,1924,1921],{}," ",[53,1927,1882],{},". After they are broadcasted, they wait for ",[53,1930,1874],{}," pre-commits from their peers.",[29,1933,1934,1935,1937,1938,1943],{},"Finally, the proposed block is committed when validators submit more than ",[53,1936,1874],{}," pre-commits (aka ",[1738,1939,1940],{},[21,1941,1942],{},"Commit",") and a new block height is reached with a new selected block proposer. If not, the network performs a new round and the process starts from the beginning (1).",[11,1945,1946],{},"One important thing to consider is that these pre-votes are included in the next block as proof that the previous block was committed - they cannot be included in the current block, as that block has already been created.",[1758,1948,1950],{"id":1949},"abci-application-blockchain-interface","ABCI (Application BlockChain Interface)",[11,1952,1953],{},"The communication between the Cosmos SDK and Tendermint goes through the ABCI interface. Tendermint only handles transaction bytes. It has no knowledge of what these bytes mean. All Tendermint does is order these transaction bytes deterministically. Tendermint passes the bytes to the application via the ABCI, and expects a return code to inform it if the messages contained in the transactions were successfully processed or not.",[11,1955,1956],{},"Here are the most important messages of the ABCI:",[26,1958,1959,1967,1975,1987],{},[29,1960,1961,1966],{},[21,1962,1963,1390],{},[53,1964,1965],{},"CheckTx"," Validate transaction. Mempool only relays valid transactions to its peers.",[29,1968,1969,1974],{},[21,1970,1971,1390],{},[53,1972,1973],{},"DeliverTx"," Deliver transaction. Validate tx against the current state, application protocol and the cryptographic credentials.",[29,1976,1977,1981,1982,1986],{},[21,1978,1979,1390],{},[53,1980,1942],{}," Persist app state to disk. Transactions are now considered final. Remain transactions in the mempool are replayed (",[21,1983,1984],{},[53,1985,1965],{},").",[29,1988,1989,1994,1995],{},[21,1990,1991,1390],{},[53,1992,1993],{},"BeginBlock\u002FEndBlock"," Run code at the beginning\u002Fend of every block. ",[21,1996,1997,1998,2000,2001,2004,2005,2008],{},"Every application module can define its own ",[53,1999,1993],{}," functions in their ",[53,2002,2003],{},"BeginBlocker"," and ",[53,2006,2007],{},"EndBlocker"," function within the module.",[45,2010,2012],{"id":2011},"cosmos-sdk","Cosmos SDK",[11,2014,2015,2016,2019,2020,2023,2024],{},"The Cosmos-SDK is an open-source framework for building multi-asset public ",[21,2017,2018],{},"Proof-of-Stake"," (PoS) blockchains, as well as permission ",[21,2021,2022],{},"Proof-Of-Authority"," (PoA) blockchains. Blockchains built with the Cosmos SDK are generally referred to as ",[21,2025,2026],{},"application-specific blockchains.",[11,2028,2029],{},"The goal of the Cosmos SDK is to allow developers to easily create custom blockchains from scratch that can natively interoperate with other blockchains. SDK-based blockchains are built out of composable modules, most of which are open source and readily available for any developers to use. Anyone can create a module for the Cosmos-SDK, and integrating already-built modules is as simple as importing them into your blockchain application.",[11,2031,2032],{},"The Cosmos SDK is a framework that facilitates the development of secure state machines on top of Tendermint. At its core, the SDK is a boilerplate implementation of the ABCI in Go. It comes with a multistore to persist data and a router to handle transactions.",[11,2034,2035],{},"The main components of a Cosmos SDK app are:",[26,2037,2038,2046,2054,2060,2071],{},[29,2039,2040,2045],{},[21,2041,2042],{},[53,2043,2044],{},"Baseapp",": Boilerplate implementation of a Cosmos SDK application. It comes with an implementation of the ABCI to handle the connection with the underlying consensus engine.",[29,2047,2048,2053],{},[21,2049,2050],{},[53,2051,2052],{},"Multistore",": The Cosmos SDK provides a multistore for persisting state. It's a store of key-value stores (KVStores).",[29,2055,2056,2059],{},[21,2057,2058],{},"App Codec",": encoding of state and client logic (Amino or Protobuf)",[29,2061,2062,2065,2066],{},[21,2063,2064],{},"Module Keepers",": hold a key to access the module's KVStore. Secured by ",[1711,2067,2070],{"href":2068,"rel":2069},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject-capability_model",[1715],"object-capability model.",[29,2072,2073,2076],{},[21,2074,2075],{},"Module manager",": List of the app modules which contain the application business logic.",[11,2078,2079],{},"Let's dig deeper into these components.",[1758,2081,2083],{"id":2082},"baseapp",[53,2084,2044],{},[11,2086,2087],{},"Boilerplate implementation of a Cosmos SDK application. It comes with an implementation of the ABCI to handle the connection with the underlying consensus engine.",[26,2089,2090,2107,2117,2126,2131],{},[29,2091,2092,758,2095,944,2097,944,2099,944,2101,944,2104],{},[21,2093,2094],{},"Tendermint ABCI",[53,2096,1965],{},[53,2098,1973],{},[53,2100,1942],{},[53,2102,2103],{},"Begin\u002FEnd Block",[53,2105,2106],{},"Info",[29,2108,2109,2112,2113,2116],{},[21,2110,2111],{},"Transaction\u002FQuery Router",": runs each ",[53,2114,2115],{},"Msg"," through the Router\u002FQuery handler for each of the modules",[29,2118,2119,2122,2123],{},[21,2120,2121],{},"Tendermint consensus parameters",": Block, Evidence, and Validators ",[1738,2124,2125],{},"(can be changed through governance)",[29,2127,2128],{},[21,2129,2130],{},"DB and Logger",[29,2132,2133,2138,2139],{},[21,2134,2135],{},[53,2136,2137],{},"AnteHandler",": special logic that is executed before the actual transaction execution happens ",[1738,2140,2141],{},"(eg: balance, signature verification, gas consumption, etc).",[1758,2143,2145],{"id":2144},"multistore",[53,2146,2052],{},[11,2148,2149,2150,2157,2158,2160],{},"Store of key-value stores (KVStore). Each module contains a key to one or more KVStore. It supports different databases ",[1738,2151,2152,2153,2156],{},"(GolevelDB ",[67,2154,2155],{},"default",", RocksDB, BoltDB, etc)",". App state persisted to disk during ABCI ",[53,2159,1942],{}," phase.",[11,2162,2163],{},"There are different types of stores:",[26,2165,2166,2172],{},[29,2167,2168,2171],{},[21,2169,2170],{},"IAVL Store",": self-balancing binary tree. Get and Set operations are O(log n).",[29,2173,2174,2177,2178,56],{},[21,2175,2176],{},"Transcient Store",": automatically discarded at the end of the block. Useful to persist information that is only relevant per-block ",[1738,2179,2180],{},"(eg: parameter changes)",[1758,2182,2184],{"id":2183},"keepers","Keepers",[11,2186,2187],{},"Gatekeepers for a given KVStore. They are usually associated with a single module. They hold a reference to a store key and a codec for de\u002Fencoding of types from\u002Fto the store.",[11,2189,2190,2191,56],{},"They can also hold a reference to other modules' ",[1711,2192,2195],{"href":2193,"rel":2194},"https:\u002F\u002Fdocs.cosmos.network\u002Fv0.42\u002Fbuilding-modules\u002Fkeeper.html#type-definition",[1715],"expected Keepers",[1758,2197,2199],{"id":2198},"module-manager","Module Manager",[11,2201,2202,2203,2206,2207,2210,2211,2214,2215,2218,2219,2222],{},"The center piece of the Cosmos SDK. The modules are a way to implement the business logic of the Cosmos Application. Usually located in the ",[53,2204,2205],{},"x"," folder ",[1738,2208,2209],{},"(stands for extensions)",". When designing and building a module, Composability ",[1738,2212,2213],{},"(can be reused)",", Specialisation ",[1738,2216,2217],{},"(manages a specific set of business logic)"," and Capabilities ",[1738,2220,2221],{},"(each module has a single reference to a KVStore, and overall security measures for the module, what it can and can't do)"," should be the key considerations.",[26,2224,2225,2231,2234,2237,2246,2259],{},[29,2226,2227,2228,56],{},"Manages a collection of ",[53,2229,2230],{},"AppModules",[29,2232,2233],{},"It also defines a common interface that modules need to expose",[29,2235,2236],{},"Register clients",[29,2238,2239,2240,2004,2243],{},"Routes for tx and queries; ",[53,2241,2242],{},"Handler",[53,2244,2245],{},"Query",[29,2247,2248,2249,944,2252,944,2255,2258],{},"ABCI Logic (",[53,2250,2251],{},"InitChain",[53,2253,2254],{},"Import\u002FExportGenesis",[53,2256,2257],{},"Begin\u002FEndBlock",")",[29,2260,2261],{},"Module invariants and simulation registration",[11,2263,2264],{},"That's about it. The next post will cover Cosmos SDK modules and then IBC. I'll continue writing on this topic as I keep exploring. ✌️",[11,2266,2267],{},"References:",[26,2269,2270,2275,2280,2288,2296,2304,2310,2317],{},[29,2271,2272],{},[1711,2273,1729],{"href":1727,"rel":2274},[1715],[29,2276,2277],{},[1711,2278,1735],{"href":1733,"rel":2279},[1715],[29,2281,2282,2287],{},[1711,2283,2286],{"href":2284,"rel":2285},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FByzantine_fault",[1715],"BFT"," (Byzantine Fault Tolerance)",[29,2289,2290,2295],{},[1711,2291,2294],{"href":2292,"rel":2293},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProof_of_stake",[1715],"PoS"," (Proof of Stake)",[29,2297,2298,2303],{},[1711,2299,2302],{"href":2300,"rel":2301},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRound-robin_scheduling",[1715],"RRA"," (Round Robin Algorithm)",[29,2305,2306],{},[1711,2307,2012],{"href":2308,"rel":2309},"https:\u002F\u002Fdocs.cosmos.network\u002Fv0.42\u002F",[1715],[29,2311,2312],{},[1711,2313,2316],{"href":2314,"rel":2315},"https:\u002F\u002Fcosmos-network.gitbooks.io\u002Fcosmos-academy\u002Fcontent\u002Fintroduction-to-the-cosmos-ecosystem\u002Ftendermint-bft-consensus-algorithm.html",[1715],"Tendermint BFT Consensus Algorithm",[29,2318,2319],{},[1711,2320,2322],{"href":2068,"rel":2321},[1715],"Object-Capability Model",{"title":63,"searchDepth":251,"depth":251,"links":2324},[2325,2330],{"id":1744,"depth":251,"text":1745,"children":2326},[2327,2328,2329],{"id":1760,"depth":264,"text":1761},{"id":1797,"depth":264,"text":1798},{"id":1949,"depth":264,"text":1950},{"id":2011,"depth":251,"text":2012,"children":2331},[2332,2333,2334,2335],{"id":2082,"depth":264,"text":2044},{"id":2144,"depth":264,"text":2052},{"id":2183,"depth":264,"text":2184},{"id":2198,"depth":264,"text":2199},"Blockchain","2021\u002F04\u002F10","Why does a JavaScript developer write about a framework for building application-specific blockchains? We at Injective Protocol are building our own Injective Chain on the Cosmos SDK — that alone was enough reason for me to dig into it.","\u002Fimages\u002Fcosmos-sdk.jpeg","cosmos, cosmos-sdk, blockchain, tendermint, bft, byzantine fault tolerance",{},"\u002Fblog\u002Fcosmos-sdk-overview-through-the-lens-of-a-javascript-developer","☕️ 10 min read",{"title":1704,"description":2338},"cosmos-sdk-overview-through-the-lens-of-a-javascript-developer","blog\u002Fcosmos-sdk-overview-through-the-lens-of-a-javascript-developer","cosmos, blockchain","AiqUZZDJCHuLCEC3LHk7631vT0nuAoesuxndELCv_lA",{"id":2350,"title":2351,"body":2352,"category":6223,"date":6224,"description":6225,"excerpt":1690,"extension":1691,"image":6226,"keywords":6227,"meta":6228,"navigation":988,"path":6229,"readingTime":6230,"seo":6231,"slug":6232,"stem":6233,"tags":6234,"__hash__":6235},"blog\u002Fblog\u002Funderstand-and-use-solid-design-principles-with-typescript.md","Understand and use SOLID Design Principles with TypeScript",{"type":8,"value":2353,"toc":6212},[2354,2361,2365,2370,2373,2377,2383,2390,2393,2397,2408,2414,2446,2449,2453,2459,2462,2757,2764,2767,3277,3283,3304,3307,3312,3334,3338,3341,3347,3366,3477,3502,3869,3888,3895,3909,4323,4328,4347,4351,4367,4370,4375,4378,4712,4735,4738,5019,5023,5030,5033,5191,5211,5387,5404,5408,5415,5418,5421,5424,5432,5443,5711,5726,5733,6110,6123,6200,6203,6206,6209],[11,2355,2356,2357,2360],{},"Yes, another take into explaining SOLID Design Principles. And yes, another blog post about making your code more maintainable and readable. But this time something is different - well, mostly nothing really is (",[1738,2358,2359],{},"you expected something here, didn't ya?",") except that it's just my perspective. A perspective gained from applying these principles in real life projects.",[45,2362,2364],{"id":2363},"what-is-a-design-principle","What is a Design Principle?",[11,2366,2367],{},[1738,2368,2369],{},"Software design principles are concerned with providing means to handle the complexity of the design process effectively. Effectively managing the complexity will not only reduce the effort needed for design but can also reduce the scope of introducing errors during design. Design Principles are standards used to organize and arrange the structural components of Software Engineering design. Methods in which these design principles are applied affect the expressive content and the working process from the start. Design principles help developers build-up common consensus about architectural knowledge, help people process with large-scale software engineering, help beginners avoid traps and pitfalls which have been detected by past experiences.",[11,2371,2372],{},"Reading the above definition might scare you if you are a beginner or confuse you if you are a seasoned developer. To explain in a concise and understandable way - the goal of a design principle is to make our software easier to understand, read, extend and maintain.",[45,2374,2376],{"id":2375},"why-do-we-need-design-principles","Why do we need Design Principles",[11,2378,2379,2380,56],{},"Imagine you are put into a position to work on a legacy codebase that didn't have proper code architecture and components within the projects are coupled together and you need to either fix some minor bug or extend the code with some functionality. You will find yourself jumping back and forth between individual components to understand how these individual components are coupled. Once you think you understood how the system is structured and how do these components behave (and are interconnected between each other), you start extending the codebase. But then again, while extending some of the things might not seem logical at first, so you have to adapt your mindset to follow the existing codebase, which will again force to you ",[21,2381,2382],{},"spend more time reading than writing code",[11,2384,2385,2386,2389],{},"These issues don't arise only on legacy codebases. Let's say you are working for a startup (or start one of your own), and you are in charge of a project. You start your project from a clean slate and you are in charge of its architecture and the whole development cycle. We all know that startups want to release new features and new products as fast as possible, so you end up spending less time thinking about the code structure and more about just building up new features. And this is okay if you have a ",[1738,2387,2388],{},"short-term"," vision for your startup. But what happens if your startup gets enough steam and is in it for the long run? How much technical debt have you introduced and how much time and money is it going to cost you while trying to scale and maintain your codebase?",[11,2391,2392],{},"Implementing these design principles is not something you will waste extra time on. Once you understand their meaning and their usage, gain a little bit of real-life experience, they will be part of your development process - making you a better and more-seniorish-like developer.",[45,2394,2396],{"id":2395},"what-does-solid-stand-for","What does SOLID stand for",[11,2398,2399,2402,2403,56],{},[21,2400,2401],{},"SOLID"," is an acronym for the first five object-oriented design (OOD) principles by Robert C. Martin, popularly known as ",[1711,2404,2407],{"href":2405,"rel":2406},"https:\u002F\u002Ftwitter.com\u002FUncleBob",[1715],"@UncleBob",[11,2409,2410,2411,2413],{},"The following 5 concepts make up our ",[21,2412,2401],{}," principles:",[680,2415,2416,2422,2428,2434,2440],{},[29,2417,2418,2421],{},[21,2419,2420],{},"S","ingle Responsibility",[29,2423,2424,2427],{},[21,2425,2426],{},"O","pen\u002FClosed",[29,2429,2430,2433],{},[21,2431,2432],{},"L","iskov Substitution",[29,2435,2436,2439],{},[21,2437,2438],{},"I","nterface Segregation",[29,2441,2442,2445],{},[21,2443,2444],{},"D","ependency Inversion",[11,2447,2448],{},"While some of these words may sound daunting, they can be easily understood with some simple code examples. In the following sections, we'll take a deep dive into what each of these principles means, along with a quick TypeScript example to illustrate each one.",[1758,2450,2452],{"id":2451},"single-responsibility-principle-srp","Single Responsibility Principle (SRP)",[11,2454,2455,2458],{},[21,2456,2457],{},"The SRP requires that a class should have only one reason to change."," A class that follows this principle performs just a few related tasks. You don’t need to limit your thinking to classes when considering the SRP. You can apply the principle to methods or modules, ensuring that they do just one thing and therefore have just one reason to change.",[11,2460,2461],{},"Let's look at an example where we violate SRP:",[58,2463,2467],{"className":2464,"code":2465,"language":2466,"meta":63,"style":63},"language-ts shiki shiki-themes github-light github-dark","import axios from \"axios\";\n\nclass User {\n  private emailServiceUrl = `emailService.example\u002Fapi`;\n  private email: string;\n  private name: string;\n\n  constructor(name: string, email: string) {\n    this.name = name;\n    this.email = email;\n  }\n\n  sendGreetingEmail() {\n    const { name, email, emailServiceUrl } = this;\n    const body = `Hey ${name}. Welcome to our app!`;\n\n    return axios(emailServiceUrl, {\n      data: {\n        email,\n        body,\n      },\n    });\n  }\n}\n\nconst user = new User(\"John\", \"email@example.com\");\nuser.sendGreetingEmail();\n","ts",[53,2468,2469,2486,2490,2501,2518,2532,2545,2549,2576,2590,2602,2606,2610,2618,2647,2666,2670,2681,2686,2691,2696,2701,2706,2710,2714,2718,2746],{"__ignoreMap":63},[67,2470,2471,2474,2477,2480,2483],{"class":69,"line":70},[67,2472,2473],{"class":163},"import",[67,2475,2476],{"class":104}," axios ",[67,2478,2479],{"class":163},"from",[67,2481,2482],{"class":77}," \"axios\"",[67,2484,2485],{"class":104},";\n",[67,2487,2488],{"class":69,"line":251},[67,2489,989],{"emptyLinePlaceholder":988},[67,2491,2492,2495,2498],{"class":69,"line":264},[67,2493,2494],{"class":163},"class",[67,2496,2497],{"class":73}," User",[67,2499,2500],{"class":104}," {\n",[67,2502,2503,2506,2510,2513,2516],{"class":69,"line":280},[67,2504,2505],{"class":163},"  private",[67,2507,2509],{"class":2508},"s4XuR"," emailServiceUrl",[67,2511,2512],{"class":163}," =",[67,2514,2515],{"class":77}," `emailService.example\u002Fapi`",[67,2517,2485],{"class":104},[67,2519,2520,2522,2525,2527,2530],{"class":69,"line":288},[67,2521,2505],{"class":163},[67,2523,2524],{"class":2508}," email",[67,2526,1390],{"class":163},[67,2528,2529],{"class":113}," string",[67,2531,2485],{"class":104},[67,2533,2534,2536,2539,2541,2543],{"class":69,"line":781},[67,2535,2505],{"class":163},[67,2537,2538],{"class":2508}," name",[67,2540,1390],{"class":163},[67,2542,2529],{"class":113},[67,2544,2485],{"class":104},[67,2546,2547],{"class":69,"line":792},[67,2548,989],{"emptyLinePlaceholder":988},[67,2550,2551,2554,2557,2560,2562,2564,2566,2569,2571,2573],{"class":69,"line":803},[67,2552,2553],{"class":163},"  constructor",[67,2555,2556],{"class":104},"(",[67,2558,2559],{"class":2508},"name",[67,2561,1390],{"class":163},[67,2563,2529],{"class":113},[67,2565,944],{"class":104},[67,2567,2568],{"class":2508},"email",[67,2570,1390],{"class":163},[67,2572,2529],{"class":113},[67,2574,2575],{"class":104},") {\n",[67,2577,2578,2581,2584,2587],{"class":69,"line":814},[67,2579,2580],{"class":113},"    this",[67,2582,2583],{"class":104},".name ",[67,2585,2586],{"class":163},"=",[67,2588,2589],{"class":104}," name;\n",[67,2591,2592,2594,2597,2599],{"class":69,"line":825},[67,2593,2580],{"class":113},[67,2595,2596],{"class":104},".email ",[67,2598,2586],{"class":163},[67,2600,2601],{"class":104}," email;\n",[67,2603,2604],{"class":69,"line":836},[67,2605,1596],{"class":104},[67,2607,2608],{"class":69,"line":847},[67,2609,989],{"emptyLinePlaceholder":988},[67,2611,2612,2615],{"class":69,"line":858},[67,2613,2614],{"class":73},"  sendGreetingEmail",[67,2616,2617],{"class":104},"() {\n",[67,2619,2620,2623,2626,2628,2630,2632,2634,2637,2640,2642,2645],{"class":69,"line":869},[67,2621,2622],{"class":163},"    const",[67,2624,2625],{"class":104}," { ",[67,2627,2559],{"class":113},[67,2629,944],{"class":104},[67,2631,2568],{"class":113},[67,2633,944],{"class":104},[67,2635,2636],{"class":113},"emailServiceUrl",[67,2638,2639],{"class":104}," } ",[67,2641,2586],{"class":163},[67,2643,2644],{"class":113}," this",[67,2646,2485],{"class":104},[67,2648,2649,2651,2654,2656,2659,2661,2664],{"class":69,"line":877},[67,2650,2622],{"class":163},[67,2652,2653],{"class":113}," body",[67,2655,2512],{"class":163},[67,2657,2658],{"class":77}," `Hey ${",[67,2660,2559],{"class":104},[67,2662,2663],{"class":77},"}. Welcome to our app!`",[67,2665,2485],{"class":104},[67,2667,2668],{"class":69,"line":886},[67,2669,989],{"emptyLinePlaceholder":988},[67,2671,2672,2675,2678],{"class":69,"line":894},[67,2673,2674],{"class":163},"    return",[67,2676,2677],{"class":73}," axios",[67,2679,2680],{"class":104},"(emailServiceUrl, {\n",[67,2682,2683],{"class":69,"line":905},[67,2684,2685],{"class":104},"      data: {\n",[67,2687,2688],{"class":69,"line":916},[67,2689,2690],{"class":104},"        email,\n",[67,2692,2693],{"class":69,"line":927},[67,2694,2695],{"class":104},"        body,\n",[67,2697,2698],{"class":69,"line":935},[67,2699,2700],{"class":104},"      },\n",[67,2702,2703],{"class":69,"line":958},[67,2704,2705],{"class":104},"    });\n",[67,2707,2708],{"class":69,"line":971},[67,2709,1596],{"class":104},[67,2711,2712],{"class":69,"line":985},[67,2713,1601],{"class":104},[67,2715,2716],{"class":69,"line":992},[67,2717,989],{"emptyLinePlaceholder":988},[67,2719,2720,2723,2726,2728,2731,2733,2735,2738,2740,2743],{"class":69,"line":1000},[67,2721,2722],{"class":163},"const",[67,2724,2725],{"class":113}," user",[67,2727,2512],{"class":163},[67,2729,2730],{"class":163}," new",[67,2732,2497],{"class":73},[67,2734,2556],{"class":104},[67,2736,2737],{"class":77},"\"John\"",[67,2739,944],{"class":104},[67,2741,2742],{"class":77},"\"email@example.com\"",[67,2744,2745],{"class":104},");\n",[67,2747,2748,2751,2754],{"class":69,"line":1009},[67,2749,2750],{"class":104},"user.",[67,2752,2753],{"class":73},"sendGreetingEmail",[67,2755,2756],{"class":104},"();\n",[11,2758,2759,2760,2763],{},"As you can see in the example above, this ",[53,2761,2762],{},"User"," class is responsible for many things. It contains user details, sending a welcome email, contains the email service url, constructs and sends the email message. Think of the code smells here. Why would the user need to know about which email service to use? Why would the user need to handle sending a welcome email?",[11,2765,2766],{},"Let's refactor the above example to conform to SRP, so each class has only one reason to change.",[58,2768,2770],{"className":2464,"code":2769,"language":2466,"meta":63,"style":63},"import axios from \"axios\";\n\nclass EmailClient {\n  private endpoint = `emailService.example\u002Fapi`;\n\n  public send(to: string, body: any) {\n    const { endpoint } = this;\n\n    return axios(endpoint, {\n      data: {\n        email: to,\n        body,\n      },\n    });\n  }\n}\n\nclass User {\n  private email: string;\n  private name: string;\n\n  constructor(name: string, email: string) {\n    this.name = name;\n    this.email = email;\n  }\n\n  getName(): string {\n    return this.name;\n  }\n\n  getEmail(): string {\n    return this.email;\n  }\n}\n\nclass WelcomeUserService {\n  private emailService: EmailClient;\n\n  constructor() {\n    this.emailService = new EmailClient();\n  }\n\n  public handle(user: User) {\n    const name = user.getName();\n    const email = user.getEmail();\n    const body = `Hey ${name}. Welcome to our app!`;\n\n    return this.emailService.send(email, body);\n  }\n}\n\nconst welcomeUserService = new WelcomeUserService();\nconst user = new User(\"John\", \"email@example.com\");\n\nwelcomeUserService.handle(user);\n",[53,2771,2772,2784,2788,2797,2810,2814,2843,2860,2864,2873,2877,2882,2886,2890,2894,2898,2902,2906,2914,2926,2938,2942,2964,2974,2984,2988,2992,3006,3015,3019,3023,3036,3045,3049,3053,3057,3066,3079,3083,3090,3106,3111,3116,3135,3152,3168,3185,3190,3206,3211,3216,3221,3237,3260,3265],{"__ignoreMap":63},[67,2773,2774,2776,2778,2780,2782],{"class":69,"line":70},[67,2775,2473],{"class":163},[67,2777,2476],{"class":104},[67,2779,2479],{"class":163},[67,2781,2482],{"class":77},[67,2783,2485],{"class":104},[67,2785,2786],{"class":69,"line":251},[67,2787,989],{"emptyLinePlaceholder":988},[67,2789,2790,2792,2795],{"class":69,"line":264},[67,2791,2494],{"class":163},[67,2793,2794],{"class":73}," EmailClient",[67,2796,2500],{"class":104},[67,2798,2799,2801,2804,2806,2808],{"class":69,"line":280},[67,2800,2505],{"class":163},[67,2802,2803],{"class":2508}," endpoint",[67,2805,2512],{"class":163},[67,2807,2515],{"class":77},[67,2809,2485],{"class":104},[67,2811,2812],{"class":69,"line":288},[67,2813,989],{"emptyLinePlaceholder":988},[67,2815,2816,2819,2822,2824,2827,2829,2831,2833,2836,2838,2841],{"class":69,"line":781},[67,2817,2818],{"class":163},"  public",[67,2820,2821],{"class":73}," send",[67,2823,2556],{"class":104},[67,2825,2826],{"class":2508},"to",[67,2828,1390],{"class":163},[67,2830,2529],{"class":113},[67,2832,944],{"class":104},[67,2834,2835],{"class":2508},"body",[67,2837,1390],{"class":163},[67,2839,2840],{"class":113}," any",[67,2842,2575],{"class":104},[67,2844,2845,2847,2849,2852,2854,2856,2858],{"class":69,"line":792},[67,2846,2622],{"class":163},[67,2848,2625],{"class":104},[67,2850,2851],{"class":113},"endpoint",[67,2853,2639],{"class":104},[67,2855,2586],{"class":163},[67,2857,2644],{"class":113},[67,2859,2485],{"class":104},[67,2861,2862],{"class":69,"line":803},[67,2863,989],{"emptyLinePlaceholder":988},[67,2865,2866,2868,2870],{"class":69,"line":814},[67,2867,2674],{"class":163},[67,2869,2677],{"class":73},[67,2871,2872],{"class":104},"(endpoint, {\n",[67,2874,2875],{"class":69,"line":825},[67,2876,2685],{"class":104},[67,2878,2879],{"class":69,"line":836},[67,2880,2881],{"class":104},"        email: to,\n",[67,2883,2884],{"class":69,"line":847},[67,2885,2695],{"class":104},[67,2887,2888],{"class":69,"line":858},[67,2889,2700],{"class":104},[67,2891,2892],{"class":69,"line":869},[67,2893,2705],{"class":104},[67,2895,2896],{"class":69,"line":877},[67,2897,1596],{"class":104},[67,2899,2900],{"class":69,"line":886},[67,2901,1601],{"class":104},[67,2903,2904],{"class":69,"line":894},[67,2905,989],{"emptyLinePlaceholder":988},[67,2907,2908,2910,2912],{"class":69,"line":905},[67,2909,2494],{"class":163},[67,2911,2497],{"class":73},[67,2913,2500],{"class":104},[67,2915,2916,2918,2920,2922,2924],{"class":69,"line":916},[67,2917,2505],{"class":163},[67,2919,2524],{"class":2508},[67,2921,1390],{"class":163},[67,2923,2529],{"class":113},[67,2925,2485],{"class":104},[67,2927,2928,2930,2932,2934,2936],{"class":69,"line":927},[67,2929,2505],{"class":163},[67,2931,2538],{"class":2508},[67,2933,1390],{"class":163},[67,2935,2529],{"class":113},[67,2937,2485],{"class":104},[67,2939,2940],{"class":69,"line":935},[67,2941,989],{"emptyLinePlaceholder":988},[67,2943,2944,2946,2948,2950,2952,2954,2956,2958,2960,2962],{"class":69,"line":958},[67,2945,2553],{"class":163},[67,2947,2556],{"class":104},[67,2949,2559],{"class":2508},[67,2951,1390],{"class":163},[67,2953,2529],{"class":113},[67,2955,944],{"class":104},[67,2957,2568],{"class":2508},[67,2959,1390],{"class":163},[67,2961,2529],{"class":113},[67,2963,2575],{"class":104},[67,2965,2966,2968,2970,2972],{"class":69,"line":971},[67,2967,2580],{"class":113},[67,2969,2583],{"class":104},[67,2971,2586],{"class":163},[67,2973,2589],{"class":104},[67,2975,2976,2978,2980,2982],{"class":69,"line":985},[67,2977,2580],{"class":113},[67,2979,2596],{"class":104},[67,2981,2586],{"class":163},[67,2983,2601],{"class":104},[67,2985,2986],{"class":69,"line":992},[67,2987,1596],{"class":104},[67,2989,2990],{"class":69,"line":1000},[67,2991,989],{"emptyLinePlaceholder":988},[67,2993,2994,2997,3000,3002,3004],{"class":69,"line":1009},[67,2995,2996],{"class":73},"  getName",[67,2998,2999],{"class":104},"()",[67,3001,1390],{"class":163},[67,3003,2529],{"class":113},[67,3005,2500],{"class":104},[67,3007,3008,3010,3012],{"class":69,"line":1016},[67,3009,2674],{"class":163},[67,3011,2644],{"class":113},[67,3013,3014],{"class":104},".name;\n",[67,3016,3017],{"class":69,"line":1025},[67,3018,1596],{"class":104},[67,3020,3021],{"class":69,"line":1034},[67,3022,989],{"emptyLinePlaceholder":988},[67,3024,3025,3028,3030,3032,3034],{"class":69,"line":1043},[67,3026,3027],{"class":73},"  getEmail",[67,3029,2999],{"class":104},[67,3031,1390],{"class":163},[67,3033,2529],{"class":113},[67,3035,2500],{"class":104},[67,3037,3038,3040,3042],{"class":69,"line":1054},[67,3039,2674],{"class":163},[67,3041,2644],{"class":113},[67,3043,3044],{"class":104},".email;\n",[67,3046,3047],{"class":69,"line":1061},[67,3048,1596],{"class":104},[67,3050,3051],{"class":69,"line":1068},[67,3052,1601],{"class":104},[67,3054,3055],{"class":69,"line":1075},[67,3056,989],{"emptyLinePlaceholder":988},[67,3058,3059,3061,3064],{"class":69,"line":1085},[67,3060,2494],{"class":163},[67,3062,3063],{"class":73}," WelcomeUserService",[67,3065,2500],{"class":104},[67,3067,3068,3070,3073,3075,3077],{"class":69,"line":1095},[67,3069,2505],{"class":163},[67,3071,3072],{"class":2508}," emailService",[67,3074,1390],{"class":163},[67,3076,2794],{"class":73},[67,3078,2485],{"class":104},[67,3080,3081],{"class":69,"line":1104},[67,3082,989],{"emptyLinePlaceholder":988},[67,3084,3086,3088],{"class":69,"line":3085},39,[67,3087,2553],{"class":163},[67,3089,2617],{"class":104},[67,3091,3093,3095,3098,3100,3102,3104],{"class":69,"line":3092},40,[67,3094,2580],{"class":113},[67,3096,3097],{"class":104},".emailService ",[67,3099,2586],{"class":163},[67,3101,2730],{"class":163},[67,3103,2794],{"class":73},[67,3105,2756],{"class":104},[67,3107,3109],{"class":69,"line":3108},41,[67,3110,1596],{"class":104},[67,3112,3114],{"class":69,"line":3113},42,[67,3115,989],{"emptyLinePlaceholder":988},[67,3117,3119,3121,3124,3126,3129,3131,3133],{"class":69,"line":3118},43,[67,3120,2818],{"class":163},[67,3122,3123],{"class":73}," handle",[67,3125,2556],{"class":104},[67,3127,3128],{"class":2508},"user",[67,3130,1390],{"class":163},[67,3132,2497],{"class":73},[67,3134,2575],{"class":104},[67,3136,3138,3140,3142,3144,3147,3150],{"class":69,"line":3137},44,[67,3139,2622],{"class":163},[67,3141,2538],{"class":113},[67,3143,2512],{"class":163},[67,3145,3146],{"class":104}," user.",[67,3148,3149],{"class":73},"getName",[67,3151,2756],{"class":104},[67,3153,3155,3157,3159,3161,3163,3166],{"class":69,"line":3154},45,[67,3156,2622],{"class":163},[67,3158,2524],{"class":113},[67,3160,2512],{"class":163},[67,3162,3146],{"class":104},[67,3164,3165],{"class":73},"getEmail",[67,3167,2756],{"class":104},[67,3169,3171,3173,3175,3177,3179,3181,3183],{"class":69,"line":3170},46,[67,3172,2622],{"class":163},[67,3174,2653],{"class":113},[67,3176,2512],{"class":163},[67,3178,2658],{"class":77},[67,3180,2559],{"class":104},[67,3182,2663],{"class":77},[67,3184,2485],{"class":104},[67,3186,3188],{"class":69,"line":3187},47,[67,3189,989],{"emptyLinePlaceholder":988},[67,3191,3193,3195,3197,3200,3203],{"class":69,"line":3192},48,[67,3194,2674],{"class":163},[67,3196,2644],{"class":113},[67,3198,3199],{"class":104},".emailService.",[67,3201,3202],{"class":73},"send",[67,3204,3205],{"class":104},"(email, body);\n",[67,3207,3209],{"class":69,"line":3208},49,[67,3210,1596],{"class":104},[67,3212,3214],{"class":69,"line":3213},50,[67,3215,1601],{"class":104},[67,3217,3219],{"class":69,"line":3218},51,[67,3220,989],{"emptyLinePlaceholder":988},[67,3222,3224,3226,3229,3231,3233,3235],{"class":69,"line":3223},52,[67,3225,2722],{"class":163},[67,3227,3228],{"class":113}," welcomeUserService",[67,3230,2512],{"class":163},[67,3232,2730],{"class":163},[67,3234,3063],{"class":73},[67,3236,2756],{"class":104},[67,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,3258],{"class":69,"line":3239},53,[67,3241,2722],{"class":163},[67,3243,2725],{"class":113},[67,3245,2512],{"class":163},[67,3247,2730],{"class":163},[67,3249,2497],{"class":73},[67,3251,2556],{"class":104},[67,3253,2737],{"class":77},[67,3255,944],{"class":104},[67,3257,2742],{"class":77},[67,3259,2745],{"class":104},[67,3261,3263],{"class":69,"line":3262},54,[67,3264,989],{"emptyLinePlaceholder":988},[67,3266,3268,3271,3274],{"class":69,"line":3267},55,[67,3269,3270],{"class":104},"welcomeUserService.",[67,3272,3273],{"class":73},"handle",[67,3275,3276],{"class":104},"(user);\n",[11,3278,3279,3280],{},"When you compare the two code snippets, the refactored one is longer. ",[21,3281,3282],{},"So let's address this before deep-diving into the refactor. Yes, the refactored code is a bit longer but that doesn't mean that you will lose more time writing this code. In fact, you will save time in the long run when you go back reading and trying to understand your code. So it's a tradeoff, and a tradeoff that you should be more than happy to make.",[11,3284,3285,3286,3288,3289,3291,3292,3295,3296,3299,3300,3303],{},"After we refactored our ",[53,3287,2762],{}," class we ended up having three classes. Now, the ",[53,3290,2762],{}," class is only responsible for keeping the user details - SRP achieved. But what happened with the other functionality? We extracted that functionality to separate classes. We have an ",[53,3293,3294],{},"EmailClient"," class, that is responsible for sending out emails (",[1738,3297,3298],{},"any kind of emails, not just welcome emails","). Now we have a class that we can reuse now only for our users but throughout our application. The ",[53,3301,3302],{},"WelcomeUserService"," class handles sending out an email to the user when he joins our app. It is responsible for generating the body for the email and sending out the email.",[11,3305,3306],{},"There we have it. We refactored one class that doesn't follow SRP to three classes that do. Extending any of these classes will be easier now because they are not tightly coupled.",[11,3308,3309],{},[21,3310,3311],{},"How to notice code that needs to be refactored using SRP",[26,3313,3314,3317,3326],{},[29,3315,3316],{},"Prefixed functions that should be extracted to their own class,",[29,3318,3319,3320,2004,3322,3325],{},"Mixing separate entities into one class (ex. encapsulating",[53,3321,2762],{},[53,3323,3324],{},"UserRole"," into one class)",[29,3327,3328,3329,2004,3331,3325],{},"Mixing separate concerns into one class (ex. encapsulating ",[53,3330,2762],{},[53,3332,3333],{},"AuthService",[1758,3335,3337],{"id":3336},"openclosed-principle-ocp","Open\u002FClosed Principle (OCP)",[11,3339,3340],{},"People usually stop reading and trying to understand the rest of the SOLID principles after going through SRP because they feel intimidated by the definitions and names of the remaining principles, but don't give up just yet!",[11,3342,3343,3346],{},[21,3344,3345],{},"The OCP requires that a class (other entities as well - functions, modules) should be open for extension but closed for modification."," This principle can be confusing at the beginning because its name is counterintuitive, but once you understand it, it's the principle that will save you the most time in terms of development in the future. Its goal is to get your application to a stable state, a state where the application's core can never be broken.",[11,3348,3349,3350,3353,3354,3357,3358,3361,3362,3365],{},"Imagine we have an ",[1738,3351,3352],{},"e-commerce"," application that needs to handle different payment methods. In the beginning, lets say we have a ",[53,3355,3356],{},"Checkout"," class that has a ",[53,3359,3360],{},"process"," method that processes a ",[53,3363,3364],{},"Cart"," using the payment method the user selected.",[58,3367,3369],{"className":2464,"code":3368,"language":2466,"meta":63,"style":63},"class Payment {\n  creditCard(cart: Cart) {\n    \u002F\u002F handle payment using credit card\n  }\n}\n\nclass Checkout {\n  private cart: Cart;\n\n  process() {\n    new Payment().creditCard(this.cart);\n  }\n}\n",[53,3370,3371,3380,3397,3403,3407,3411,3415,3424,3437,3441,3448,3469,3473],{"__ignoreMap":63},[67,3372,3373,3375,3378],{"class":69,"line":70},[67,3374,2494],{"class":163},[67,3376,3377],{"class":73}," Payment",[67,3379,2500],{"class":104},[67,3381,3382,3385,3387,3390,3392,3395],{"class":69,"line":251},[67,3383,3384],{"class":73},"  creditCard",[67,3386,2556],{"class":104},[67,3388,3389],{"class":2508},"cart",[67,3391,1390],{"class":163},[67,3393,3394],{"class":73}," Cart",[67,3396,2575],{"class":104},[67,3398,3399],{"class":69,"line":264},[67,3400,3402],{"class":3401},"sJ8bj","    \u002F\u002F handle payment using credit card\n",[67,3404,3405],{"class":69,"line":280},[67,3406,1596],{"class":104},[67,3408,3409],{"class":69,"line":288},[67,3410,1601],{"class":104},[67,3412,3413],{"class":69,"line":781},[67,3414,989],{"emptyLinePlaceholder":988},[67,3416,3417,3419,3422],{"class":69,"line":792},[67,3418,2494],{"class":163},[67,3420,3421],{"class":73}," Checkout",[67,3423,2500],{"class":104},[67,3425,3426,3428,3431,3433,3435],{"class":69,"line":803},[67,3427,2505],{"class":163},[67,3429,3430],{"class":2508}," cart",[67,3432,1390],{"class":163},[67,3434,3394],{"class":73},[67,3436,2485],{"class":104},[67,3438,3439],{"class":69,"line":814},[67,3440,989],{"emptyLinePlaceholder":988},[67,3442,3443,3446],{"class":69,"line":825},[67,3444,3445],{"class":73},"  process",[67,3447,2617],{"class":104},[67,3449,3450,3453,3455,3458,3461,3463,3466],{"class":69,"line":836},[67,3451,3452],{"class":163},"    new",[67,3454,3377],{"class":73},[67,3456,3457],{"class":104},"().",[67,3459,3460],{"class":73},"creditCard",[67,3462,2556],{"class":104},[67,3464,3465],{"class":113},"this",[67,3467,3468],{"class":104},".cart);\n",[67,3470,3471],{"class":69,"line":847},[67,3472,1596],{"class":104},[67,3474,3475],{"class":69,"line":858},[67,3476,1601],{"class":104},[11,3478,3479,3480,3483,3484,3487,3488,3490,3491,3493,3494,3497,3498,3501],{},"But, what if we want to add more payment methods - PayPal, Coupons, Bank Transfer, etc. They all have different underlying implementations, so we have to implement separate methods within our ",[53,3481,3482],{},"Payment"," class for each of these payment methods. Now, we have to add ",[53,3485,3486],{},"if"," statements within our ",[53,3489,3360],{}," method within the ",[53,3492,3356],{}," class to handle different cases. So, if we decide to add another payment method, we have to ",[21,3495,3496],{},"modify"," our ****existing implementation to add another ",[53,3499,3500],{},"else if"," statement to handle this case. Considering this principle it means that our code design is bad and we should consider another approach.",[58,3503,3505],{"className":2464,"code":3504,"language":2466,"meta":63,"style":63},"class Payment {\n  private cart: Cart;\n\n  constructor(cart: Cart) {\n    this.cart = cart;\n  }\n\n  creditCard() {\n    \u002F\u002F handle payment using credit card\n  }\n\n  payPal() {\n    \u002F\u002F handle payment using paypal\n  }\n\n  bankTransfer() {\n    \u002F\u002F handle payment using bank transfer\n  }\n\n  coupon() {\n    \u002F\u002F handle payment using bank transfer\n  }\n}\n\nclass Checkout {\n  private cart: Cart;\n\n  constructor(cart: Cart) {\n    this.cart = cart;\n  }\n\n  process() {\n    const paymentMethod = request().get(\"payment_method\"); \u002F\u002F getting user's selection\n    const payment = new Payment(this.card);\n\n    if (paymentMethod === \"credit-card\") {\n      payment.creditCard();\n    } else if (paymentMethod === \"paypal\") {\n      payment.paypal();\n    } else if (paymentMethod === \"bank-transfer\") {\n      payment.bankTransfer();\n    } else {\n      payment.coupon();\n    }\n  }\n}\n",[53,3506,3507,3515,3527,3531,3545,3557,3561,3565,3571,3575,3579,3583,3590,3595,3599,3603,3610,3615,3619,3623,3630,3634,3638,3642,3646,3654,3666,3670,3684,3694,3698,3702,3708,3736,3756,3760,3776,3785,3805,3814,3831,3840,3848,3857,3861,3865],{"__ignoreMap":63},[67,3508,3509,3511,3513],{"class":69,"line":70},[67,3510,2494],{"class":163},[67,3512,3377],{"class":73},[67,3514,2500],{"class":104},[67,3516,3517,3519,3521,3523,3525],{"class":69,"line":251},[67,3518,2505],{"class":163},[67,3520,3430],{"class":2508},[67,3522,1390],{"class":163},[67,3524,3394],{"class":73},[67,3526,2485],{"class":104},[67,3528,3529],{"class":69,"line":264},[67,3530,989],{"emptyLinePlaceholder":988},[67,3532,3533,3535,3537,3539,3541,3543],{"class":69,"line":280},[67,3534,2553],{"class":163},[67,3536,2556],{"class":104},[67,3538,3389],{"class":2508},[67,3540,1390],{"class":163},[67,3542,3394],{"class":73},[67,3544,2575],{"class":104},[67,3546,3547,3549,3552,3554],{"class":69,"line":288},[67,3548,2580],{"class":113},[67,3550,3551],{"class":104},".cart ",[67,3553,2586],{"class":163},[67,3555,3556],{"class":104}," cart;\n",[67,3558,3559],{"class":69,"line":781},[67,3560,1596],{"class":104},[67,3562,3563],{"class":69,"line":792},[67,3564,989],{"emptyLinePlaceholder":988},[67,3566,3567,3569],{"class":69,"line":803},[67,3568,3384],{"class":73},[67,3570,2617],{"class":104},[67,3572,3573],{"class":69,"line":814},[67,3574,3402],{"class":3401},[67,3576,3577],{"class":69,"line":825},[67,3578,1596],{"class":104},[67,3580,3581],{"class":69,"line":836},[67,3582,989],{"emptyLinePlaceholder":988},[67,3584,3585,3588],{"class":69,"line":847},[67,3586,3587],{"class":73},"  payPal",[67,3589,2617],{"class":104},[67,3591,3592],{"class":69,"line":858},[67,3593,3594],{"class":3401},"    \u002F\u002F handle payment using paypal\n",[67,3596,3597],{"class":69,"line":869},[67,3598,1596],{"class":104},[67,3600,3601],{"class":69,"line":877},[67,3602,989],{"emptyLinePlaceholder":988},[67,3604,3605,3608],{"class":69,"line":886},[67,3606,3607],{"class":73},"  bankTransfer",[67,3609,2617],{"class":104},[67,3611,3612],{"class":69,"line":894},[67,3613,3614],{"class":3401},"    \u002F\u002F handle payment using bank transfer\n",[67,3616,3617],{"class":69,"line":905},[67,3618,1596],{"class":104},[67,3620,3621],{"class":69,"line":916},[67,3622,989],{"emptyLinePlaceholder":988},[67,3624,3625,3628],{"class":69,"line":927},[67,3626,3627],{"class":73},"  coupon",[67,3629,2617],{"class":104},[67,3631,3632],{"class":69,"line":935},[67,3633,3614],{"class":3401},[67,3635,3636],{"class":69,"line":958},[67,3637,1596],{"class":104},[67,3639,3640],{"class":69,"line":971},[67,3641,1601],{"class":104},[67,3643,3644],{"class":69,"line":985},[67,3645,989],{"emptyLinePlaceholder":988},[67,3647,3648,3650,3652],{"class":69,"line":992},[67,3649,2494],{"class":163},[67,3651,3421],{"class":73},[67,3653,2500],{"class":104},[67,3655,3656,3658,3660,3662,3664],{"class":69,"line":1000},[67,3657,2505],{"class":163},[67,3659,3430],{"class":2508},[67,3661,1390],{"class":163},[67,3663,3394],{"class":73},[67,3665,2485],{"class":104},[67,3667,3668],{"class":69,"line":1009},[67,3669,989],{"emptyLinePlaceholder":988},[67,3671,3672,3674,3676,3678,3680,3682],{"class":69,"line":1016},[67,3673,2553],{"class":163},[67,3675,2556],{"class":104},[67,3677,3389],{"class":2508},[67,3679,1390],{"class":163},[67,3681,3394],{"class":73},[67,3683,2575],{"class":104},[67,3685,3686,3688,3690,3692],{"class":69,"line":1025},[67,3687,2580],{"class":113},[67,3689,3551],{"class":104},[67,3691,2586],{"class":163},[67,3693,3556],{"class":104},[67,3695,3696],{"class":69,"line":1034},[67,3697,1596],{"class":104},[67,3699,3700],{"class":69,"line":1043},[67,3701,989],{"emptyLinePlaceholder":988},[67,3703,3704,3706],{"class":69,"line":1054},[67,3705,3445],{"class":73},[67,3707,2617],{"class":104},[67,3709,3710,3712,3715,3717,3720,3722,3725,3727,3730,3733],{"class":69,"line":1061},[67,3711,2622],{"class":163},[67,3713,3714],{"class":113}," paymentMethod",[67,3716,2512],{"class":163},[67,3718,3719],{"class":73}," request",[67,3721,3457],{"class":104},[67,3723,3724],{"class":73},"get",[67,3726,2556],{"class":104},[67,3728,3729],{"class":77},"\"payment_method\"",[67,3731,3732],{"class":104},"); ",[67,3734,3735],{"class":3401},"\u002F\u002F getting user's selection\n",[67,3737,3738,3740,3743,3745,3747,3749,3751,3753],{"class":69,"line":1068},[67,3739,2622],{"class":163},[67,3741,3742],{"class":113}," payment",[67,3744,2512],{"class":163},[67,3746,2730],{"class":163},[67,3748,3377],{"class":73},[67,3750,2556],{"class":104},[67,3752,3465],{"class":113},[67,3754,3755],{"class":104},".card);\n",[67,3757,3758],{"class":69,"line":1075},[67,3759,989],{"emptyLinePlaceholder":988},[67,3761,3762,3765,3768,3771,3774],{"class":69,"line":1085},[67,3763,3764],{"class":163},"    if",[67,3766,3767],{"class":104}," (paymentMethod ",[67,3769,3770],{"class":163},"===",[67,3772,3773],{"class":77}," \"credit-card\"",[67,3775,2575],{"class":104},[67,3777,3778,3781,3783],{"class":69,"line":1095},[67,3779,3780],{"class":104},"      payment.",[67,3782,3460],{"class":73},[67,3784,2756],{"class":104},[67,3786,3787,3790,3793,3796,3798,3800,3803],{"class":69,"line":1104},[67,3788,3789],{"class":104},"    } ",[67,3791,3792],{"class":163},"else",[67,3794,3795],{"class":163}," if",[67,3797,3767],{"class":104},[67,3799,3770],{"class":163},[67,3801,3802],{"class":77}," \"paypal\"",[67,3804,2575],{"class":104},[67,3806,3807,3809,3812],{"class":69,"line":3085},[67,3808,3780],{"class":104},[67,3810,3811],{"class":73},"paypal",[67,3813,2756],{"class":104},[67,3815,3816,3818,3820,3822,3824,3826,3829],{"class":69,"line":3092},[67,3817,3789],{"class":104},[67,3819,3792],{"class":163},[67,3821,3795],{"class":163},[67,3823,3767],{"class":104},[67,3825,3770],{"class":163},[67,3827,3828],{"class":77}," \"bank-transfer\"",[67,3830,2575],{"class":104},[67,3832,3833,3835,3838],{"class":69,"line":3108},[67,3834,3780],{"class":104},[67,3836,3837],{"class":73},"bankTransfer",[67,3839,2756],{"class":104},[67,3841,3842,3844,3846],{"class":69,"line":3113},[67,3843,3789],{"class":104},[67,3845,3792],{"class":163},[67,3847,2500],{"class":104},[67,3849,3850,3852,3855],{"class":69,"line":3118},[67,3851,3780],{"class":104},[67,3853,3854],{"class":73},"coupon",[67,3856,2756],{"class":104},[67,3858,3859],{"class":69,"line":3137},[67,3860,1557],{"class":104},[67,3862,3863],{"class":69,"line":3154},[67,3864,1596],{"class":104},[67,3866,3867],{"class":69,"line":3170},[67,3868,1601],{"class":104},[11,3870,3871,3872,3874,3875,3877,3878,3880,3881,3883,3884,3887],{},"What would happen if we need to add another payment method? We would have to change the ",[53,3873,3360],{}," method in the ",[53,3876,3356],{}," class and include another ",[53,3879,3500],{}," statement in the ",[53,3882,3360],{}," method which violates the ",[1738,3885,3886],{},"closed for modification"," part of this principle. This calls for a code design change.",[11,3889,3890,3891,3894],{},"To refactor this code and satisfy the requirements of the principle, we can use the ",[21,3892,3893],{},"Factory Pattern",". The Factory pattern is a creational pattern that allows us to create different instances of a class on runtime (more about the Factory Pattern can be found online). This means that we don't have to care about which method is going to be called because a proper class instance will be instantiated on runtime based on the client's selection.",[11,3896,3897,3898,3901,3902,3905,3906,3908],{},"Knowing this, let's refactor the example above to follow the requirements of OCP. We can define an interface and define classes for each of the payments that extend that interface and implement its methods. We will also define a ",[53,3899,3900],{},"PayableFactory"," class that instantiates our desired ",[53,3903,3904],{},"Payable"," instance and we use that in the checkout. That way, the ",[53,3907,3356],{}," class is not going to be modified in case we need to add another payment method and is compliant with the requirements of OCP.",[58,3910,3912],{"className":2464,"code":3911,"language":2466,"meta":63,"style":63},"interface Payable {\n  pay(cart: Cart);\n}\n\nclass BankTransfer implements Payable {\n  pay(cart: Cart) {\n    \u002F\u002F Handle bank transfer payment\n  }\n}\n\nclass CreditCard implements Payable {\n  pay(cart: Cart) {\n    \u002F\u002F Handle credit card payment\n  }\n}\n\nclass PayPal implements Payable {\n  pay(cart: Cart) {\n    \u002F\u002F Handle PayPal payment\n  }\n}\n\nclass PayableFactory {\n  static make(type: string) {\n    if(paymentMethod === 'credit-card') {\n      return new CreditCard();\n    } else if (paymentMethod === 'paypal') {\n      return new PayPal();\n    } else (paymentMethod === 'bank-transfer') {\n      return new BankTransfer();\n  }\n}\n\nclass Checkout {\n  private cart: Cart;\n\n  constructor(cart: Cart) {\n    this.cart = cart;\n  }\n\n  process() {\n    const paymentMethod = request().get('payment_method') \u002F\u002F getting user's selection\n    const paymentFactory = new PaymentFactory(paymentMethod);\n\n    paymentFactory.pay(cart);\n  }\n}\n",[53,3913,3914,3924,3939,3943,3947,3961,3975,3980,3984,3988,3992,4005,4019,4024,4028,4032,4036,4049,4063,4068,4072,4076,4080,4089,4108,4122,4133,4150,4160,4175,4185,4189,4193,4197,4205,4217,4221,4235,4245,4249,4253,4259,4283,4300,4304,4315,4319],{"__ignoreMap":63},[67,3915,3916,3919,3922],{"class":69,"line":70},[67,3917,3918],{"class":163},"interface",[67,3920,3921],{"class":73}," Payable",[67,3923,2500],{"class":104},[67,3925,3926,3929,3931,3933,3935,3937],{"class":69,"line":251},[67,3927,3928],{"class":73},"  pay",[67,3930,2556],{"class":104},[67,3932,3389],{"class":2508},[67,3934,1390],{"class":163},[67,3936,3394],{"class":73},[67,3938,2745],{"class":104},[67,3940,3941],{"class":69,"line":264},[67,3942,1601],{"class":104},[67,3944,3945],{"class":69,"line":280},[67,3946,989],{"emptyLinePlaceholder":988},[67,3948,3949,3951,3954,3957,3959],{"class":69,"line":288},[67,3950,2494],{"class":163},[67,3952,3953],{"class":73}," BankTransfer",[67,3955,3956],{"class":163}," implements",[67,3958,3921],{"class":73},[67,3960,2500],{"class":104},[67,3962,3963,3965,3967,3969,3971,3973],{"class":69,"line":781},[67,3964,3928],{"class":73},[67,3966,2556],{"class":104},[67,3968,3389],{"class":2508},[67,3970,1390],{"class":163},[67,3972,3394],{"class":73},[67,3974,2575],{"class":104},[67,3976,3977],{"class":69,"line":792},[67,3978,3979],{"class":3401},"    \u002F\u002F Handle bank transfer payment\n",[67,3981,3982],{"class":69,"line":803},[67,3983,1596],{"class":104},[67,3985,3986],{"class":69,"line":814},[67,3987,1601],{"class":104},[67,3989,3990],{"class":69,"line":825},[67,3991,989],{"emptyLinePlaceholder":988},[67,3993,3994,3996,3999,4001,4003],{"class":69,"line":836},[67,3995,2494],{"class":163},[67,3997,3998],{"class":73}," CreditCard",[67,4000,3956],{"class":163},[67,4002,3921],{"class":73},[67,4004,2500],{"class":104},[67,4006,4007,4009,4011,4013,4015,4017],{"class":69,"line":847},[67,4008,3928],{"class":73},[67,4010,2556],{"class":104},[67,4012,3389],{"class":2508},[67,4014,1390],{"class":163},[67,4016,3394],{"class":73},[67,4018,2575],{"class":104},[67,4020,4021],{"class":69,"line":858},[67,4022,4023],{"class":3401},"    \u002F\u002F Handle credit card payment\n",[67,4025,4026],{"class":69,"line":869},[67,4027,1596],{"class":104},[67,4029,4030],{"class":69,"line":877},[67,4031,1601],{"class":104},[67,4033,4034],{"class":69,"line":886},[67,4035,989],{"emptyLinePlaceholder":988},[67,4037,4038,4040,4043,4045,4047],{"class":69,"line":894},[67,4039,2494],{"class":163},[67,4041,4042],{"class":73}," PayPal",[67,4044,3956],{"class":163},[67,4046,3921],{"class":73},[67,4048,2500],{"class":104},[67,4050,4051,4053,4055,4057,4059,4061],{"class":69,"line":905},[67,4052,3928],{"class":73},[67,4054,2556],{"class":104},[67,4056,3389],{"class":2508},[67,4058,1390],{"class":163},[67,4060,3394],{"class":73},[67,4062,2575],{"class":104},[67,4064,4065],{"class":69,"line":916},[67,4066,4067],{"class":3401},"    \u002F\u002F Handle PayPal payment\n",[67,4069,4070],{"class":69,"line":927},[67,4071,1596],{"class":104},[67,4073,4074],{"class":69,"line":935},[67,4075,1601],{"class":104},[67,4077,4078],{"class":69,"line":958},[67,4079,989],{"emptyLinePlaceholder":988},[67,4081,4082,4084,4087],{"class":69,"line":971},[67,4083,2494],{"class":163},[67,4085,4086],{"class":73}," PayableFactory",[67,4088,2500],{"class":104},[67,4090,4091,4094,4097,4099,4102,4104,4106],{"class":69,"line":985},[67,4092,4093],{"class":163},"  static",[67,4095,4096],{"class":73}," make",[67,4098,2556],{"class":104},[67,4100,4101],{"class":2508},"type",[67,4103,1390],{"class":163},[67,4105,2529],{"class":113},[67,4107,2575],{"class":104},[67,4109,4110,4112,4115,4117,4120],{"class":69,"line":992},[67,4111,3764],{"class":163},[67,4113,4114],{"class":104},"(paymentMethod ",[67,4116,3770],{"class":163},[67,4118,4119],{"class":77}," 'credit-card'",[67,4121,2575],{"class":104},[67,4123,4124,4127,4129,4131],{"class":69,"line":1000},[67,4125,4126],{"class":163},"      return",[67,4128,2730],{"class":163},[67,4130,3998],{"class":73},[67,4132,2756],{"class":104},[67,4134,4135,4137,4139,4141,4143,4145,4148],{"class":69,"line":1009},[67,4136,3789],{"class":104},[67,4138,3792],{"class":163},[67,4140,3795],{"class":163},[67,4142,3767],{"class":104},[67,4144,3770],{"class":163},[67,4146,4147],{"class":77}," 'paypal'",[67,4149,2575],{"class":104},[67,4151,4152,4154,4156,4158],{"class":69,"line":1016},[67,4153,4126],{"class":163},[67,4155,2730],{"class":163},[67,4157,4042],{"class":73},[67,4159,2756],{"class":104},[67,4161,4162,4164,4166,4168,4170,4173],{"class":69,"line":1025},[67,4163,3789],{"class":104},[67,4165,3792],{"class":163},[67,4167,3767],{"class":104},[67,4169,3770],{"class":163},[67,4171,4172],{"class":77}," 'bank-transfer'",[67,4174,2575],{"class":104},[67,4176,4177,4179,4181,4183],{"class":69,"line":1034},[67,4178,4126],{"class":163},[67,4180,2730],{"class":163},[67,4182,3953],{"class":73},[67,4184,2756],{"class":104},[67,4186,4187],{"class":69,"line":1043},[67,4188,1596],{"class":104},[67,4190,4191],{"class":69,"line":1054},[67,4192,1601],{"class":104},[67,4194,4195],{"class":69,"line":1061},[67,4196,989],{"emptyLinePlaceholder":988},[67,4198,4199,4201,4203],{"class":69,"line":1068},[67,4200,2494],{"class":163},[67,4202,3421],{"class":73},[67,4204,2500],{"class":104},[67,4206,4207,4209,4211,4213,4215],{"class":69,"line":1075},[67,4208,2505],{"class":163},[67,4210,3430],{"class":2508},[67,4212,1390],{"class":163},[67,4214,3394],{"class":73},[67,4216,2485],{"class":104},[67,4218,4219],{"class":69,"line":1085},[67,4220,989],{"emptyLinePlaceholder":988},[67,4222,4223,4225,4227,4229,4231,4233],{"class":69,"line":1095},[67,4224,2553],{"class":163},[67,4226,2556],{"class":104},[67,4228,3389],{"class":2508},[67,4230,1390],{"class":163},[67,4232,3394],{"class":73},[67,4234,2575],{"class":104},[67,4236,4237,4239,4241,4243],{"class":69,"line":1104},[67,4238,2580],{"class":113},[67,4240,3551],{"class":104},[67,4242,2586],{"class":163},[67,4244,3556],{"class":104},[67,4246,4247],{"class":69,"line":3085},[67,4248,1596],{"class":104},[67,4250,4251],{"class":69,"line":3092},[67,4252,989],{"emptyLinePlaceholder":988},[67,4254,4255,4257],{"class":69,"line":3108},[67,4256,3445],{"class":73},[67,4258,2617],{"class":104},[67,4260,4261,4263,4265,4267,4269,4271,4273,4275,4278,4281],{"class":69,"line":3113},[67,4262,2622],{"class":163},[67,4264,3714],{"class":113},[67,4266,2512],{"class":163},[67,4268,3719],{"class":73},[67,4270,3457],{"class":104},[67,4272,3724],{"class":73},[67,4274,2556],{"class":104},[67,4276,4277],{"class":77},"'payment_method'",[67,4279,4280],{"class":104},") ",[67,4282,3735],{"class":3401},[67,4284,4285,4287,4290,4292,4294,4297],{"class":69,"line":3118},[67,4286,2622],{"class":163},[67,4288,4289],{"class":113}," paymentFactory",[67,4291,2512],{"class":163},[67,4293,2730],{"class":163},[67,4295,4296],{"class":73}," PaymentFactory",[67,4298,4299],{"class":104},"(paymentMethod);\n",[67,4301,4302],{"class":69,"line":3137},[67,4303,989],{"emptyLinePlaceholder":988},[67,4305,4306,4309,4312],{"class":69,"line":3154},[67,4307,4308],{"class":104},"    paymentFactory.",[67,4310,4311],{"class":73},"pay",[67,4313,4314],{"class":104},"(cart);\n",[67,4316,4317],{"class":69,"line":3170},[67,4318,1596],{"class":104},[67,4320,4321],{"class":69,"line":3187},[67,4322,1601],{"class":104},[11,4324,4325],{},[21,4326,4327],{},"How to notice code that needs to be refactored using OCP",[26,4329,4330,4341,4344],{},[29,4331,4332,4333,4335,4336,4335,4338,4340],{},"Conditional Code - a lot of ",[53,4334,3486],{}," ",[53,4337,3500],{},[53,4339,3792],{}," statements,",[29,4342,4343],{},"Multiple unit tests due to multiple execution paths,",[29,4345,4346],{},"Violation of the Single Responsibility Principle,",[1758,4348,4350],{"id":4349},"liskov-substitution-principle","Liskov Substitution Principle",[11,4352,4353,4356,4357,4360,4361,4366],{},[21,4354,4355],{},"LSP states that"," r",[21,4358,4359],{},"eplacing an instance of a class with its child class should not produce any negative side effects."," This rule, introduced by ",[1711,4362,4365],{"href":4363,"rel":4364},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBarbara_Liskov",[1715],"Barbara Liskov",", ensures us that changing one part of our system does not break other parts. To make this principle less confusing, we will break it down into multiple parts.",[11,4368,4369],{},"The first thing we notice for LSP is that its main focus is class inheritance. Let’s implement a straightforward and vivid example of how we can break the above principle (this time using a meme as a starting point).",[11,4371,4372],{},[1838,4373],{"alt":4350,"src":4374},"\u002Fimages\u002Fposts\u002Fduck-liskov.jpg",[11,4376,4377],{},"Let's translate the meme into valid OOP code.",[58,4379,4381],{"className":2464,"code":4380,"language":2466,"meta":63,"style":63},"abstract class Duck {\n  quack(): string {\n    return 'The duck is quacking';\n  },\n\n  fly(): string {\n    return 'The duck is flying';\n  },\n\n  swim(): string {\n    return 'The duck is swimming';\n  }\n}\n\nclass RubberDuck extends Duck {\n  quack(): string {\n    person = new Person();\n\n    if(!person.squeezesDuck(this)) {\n      throw new Error('The rubber duck cannot swim on its own');\n    }\n\n    return 'The duck is quacking';\n  }\n\n  fly() {\n    throw new Error('The rubber duck cannot fly');\n  }\n\n  swim(): string {\n    person = new Person();\n\n    if(!person.throwDuckInBath(this)) {\n      throw new Error('The rubber duck cannot swim on its own');\n    }\n\n    return 'The duck is swimming';\n  }\n}\n",[53,4382,4383,4396,4409,4418,4422,4426,4439,4448,4452,4456,4469,4478,4482,4486,4490,4504,4516,4530,4534,4556,4573,4577,4581,4589,4593,4597,4603,4619,4623,4627,4639,4651,4655,4674,4688,4692,4696,4704,4708],{"__ignoreMap":63},[67,4384,4385,4388,4391,4394],{"class":69,"line":70},[67,4386,4387],{"class":163},"abstract",[67,4389,4390],{"class":163}," class",[67,4392,4393],{"class":73}," Duck",[67,4395,2500],{"class":104},[67,4397,4398,4401,4403,4405,4407],{"class":69,"line":251},[67,4399,4400],{"class":73},"  quack",[67,4402,2999],{"class":104},[67,4404,1390],{"class":163},[67,4406,2529],{"class":113},[67,4408,2500],{"class":104},[67,4410,4411,4413,4416],{"class":69,"line":264},[67,4412,2674],{"class":163},[67,4414,4415],{"class":77}," 'The duck is quacking'",[67,4417,2485],{"class":104},[67,4419,4420],{"class":69,"line":280},[67,4421,1562],{"class":104},[67,4423,4424],{"class":69,"line":288},[67,4425,989],{"emptyLinePlaceholder":988},[67,4427,4428,4431,4433,4435,4437],{"class":69,"line":781},[67,4429,4430],{"class":73},"  fly",[67,4432,2999],{"class":104},[67,4434,1390],{"class":163},[67,4436,2529],{"class":113},[67,4438,2500],{"class":104},[67,4440,4441,4443,4446],{"class":69,"line":792},[67,4442,2674],{"class":163},[67,4444,4445],{"class":77}," 'The duck is flying'",[67,4447,2485],{"class":104},[67,4449,4450],{"class":69,"line":803},[67,4451,1562],{"class":104},[67,4453,4454],{"class":69,"line":814},[67,4455,989],{"emptyLinePlaceholder":988},[67,4457,4458,4461,4463,4465,4467],{"class":69,"line":825},[67,4459,4460],{"class":73},"  swim",[67,4462,2999],{"class":104},[67,4464,1390],{"class":163},[67,4466,2529],{"class":113},[67,4468,2500],{"class":104},[67,4470,4471,4473,4476],{"class":69,"line":836},[67,4472,2674],{"class":163},[67,4474,4475],{"class":77}," 'The duck is swimming'",[67,4477,2485],{"class":104},[67,4479,4480],{"class":69,"line":847},[67,4481,1596],{"class":104},[67,4483,4484],{"class":69,"line":858},[67,4485,1601],{"class":104},[67,4487,4488],{"class":69,"line":869},[67,4489,989],{"emptyLinePlaceholder":988},[67,4491,4492,4494,4497,4500,4502],{"class":69,"line":877},[67,4493,2494],{"class":163},[67,4495,4496],{"class":73}," RubberDuck",[67,4498,4499],{"class":163}," extends",[67,4501,4393],{"class":73},[67,4503,2500],{"class":104},[67,4505,4506,4508,4510,4512,4514],{"class":69,"line":886},[67,4507,4400],{"class":73},[67,4509,2999],{"class":104},[67,4511,1390],{"class":163},[67,4513,2529],{"class":113},[67,4515,2500],{"class":104},[67,4517,4518,4521,4523,4525,4528],{"class":69,"line":894},[67,4519,4520],{"class":104},"    person ",[67,4522,2586],{"class":163},[67,4524,2730],{"class":163},[67,4526,4527],{"class":73}," Person",[67,4529,2756],{"class":104},[67,4531,4532],{"class":69,"line":905},[67,4533,989],{"emptyLinePlaceholder":988},[67,4535,4536,4538,4540,4543,4546,4549,4551,4553],{"class":69,"line":916},[67,4537,3764],{"class":163},[67,4539,2556],{"class":104},[67,4541,4542],{"class":163},"!",[67,4544,4545],{"class":104},"person.",[67,4547,4548],{"class":73},"squeezesDuck",[67,4550,2556],{"class":104},[67,4552,3465],{"class":113},[67,4554,4555],{"class":104},")) {\n",[67,4557,4558,4561,4563,4566,4568,4571],{"class":69,"line":927},[67,4559,4560],{"class":163},"      throw",[67,4562,2730],{"class":163},[67,4564,4565],{"class":73}," Error",[67,4567,2556],{"class":104},[67,4569,4570],{"class":77},"'The rubber duck cannot swim on its own'",[67,4572,2745],{"class":104},[67,4574,4575],{"class":69,"line":935},[67,4576,1557],{"class":104},[67,4578,4579],{"class":69,"line":958},[67,4580,989],{"emptyLinePlaceholder":988},[67,4582,4583,4585,4587],{"class":69,"line":971},[67,4584,2674],{"class":163},[67,4586,4415],{"class":77},[67,4588,2485],{"class":104},[67,4590,4591],{"class":69,"line":985},[67,4592,1596],{"class":104},[67,4594,4595],{"class":69,"line":992},[67,4596,989],{"emptyLinePlaceholder":988},[67,4598,4599,4601],{"class":69,"line":1000},[67,4600,4430],{"class":73},[67,4602,2617],{"class":104},[67,4604,4605,4608,4610,4612,4614,4617],{"class":69,"line":1009},[67,4606,4607],{"class":163},"    throw",[67,4609,2730],{"class":163},[67,4611,4565],{"class":73},[67,4613,2556],{"class":104},[67,4615,4616],{"class":77},"'The rubber duck cannot fly'",[67,4618,2745],{"class":104},[67,4620,4621],{"class":69,"line":1016},[67,4622,1596],{"class":104},[67,4624,4625],{"class":69,"line":1025},[67,4626,989],{"emptyLinePlaceholder":988},[67,4628,4629,4631,4633,4635,4637],{"class":69,"line":1034},[67,4630,4460],{"class":73},[67,4632,2999],{"class":104},[67,4634,1390],{"class":163},[67,4636,2529],{"class":113},[67,4638,2500],{"class":104},[67,4640,4641,4643,4645,4647,4649],{"class":69,"line":1043},[67,4642,4520],{"class":104},[67,4644,2586],{"class":163},[67,4646,2730],{"class":163},[67,4648,4527],{"class":73},[67,4650,2756],{"class":104},[67,4652,4653],{"class":69,"line":1054},[67,4654,989],{"emptyLinePlaceholder":988},[67,4656,4657,4659,4661,4663,4665,4668,4670,4672],{"class":69,"line":1061},[67,4658,3764],{"class":163},[67,4660,2556],{"class":104},[67,4662,4542],{"class":163},[67,4664,4545],{"class":104},[67,4666,4667],{"class":73},"throwDuckInBath",[67,4669,2556],{"class":104},[67,4671,3465],{"class":113},[67,4673,4555],{"class":104},[67,4675,4676,4678,4680,4682,4684,4686],{"class":69,"line":1068},[67,4677,4560],{"class":163},[67,4679,2730],{"class":163},[67,4681,4565],{"class":73},[67,4683,2556],{"class":104},[67,4685,4570],{"class":77},[67,4687,2745],{"class":104},[67,4689,4690],{"class":69,"line":1075},[67,4691,1557],{"class":104},[67,4693,4694],{"class":69,"line":1085},[67,4695,989],{"emptyLinePlaceholder":988},[67,4697,4698,4700,4702],{"class":69,"line":1095},[67,4699,2674],{"class":163},[67,4701,4475],{"class":77},[67,4703,2485],{"class":104},[67,4705,4706],{"class":69,"line":1104},[67,4707,1596],{"class":104},[67,4709,4710],{"class":69,"line":3085},[67,4711,1601],{"class":104},[11,4713,4714,4715,4718,4719,4722,4723,3490,4726,4728,4729,4731,4732,4734],{},"Notice how we are changing every method of the ",[53,4716,4717],{},"abstract class Duck"," in our concrete class ",[53,4720,4721],{},"RubberDuck",". This is the first clue that we are violating LSP. The second clue is the ",[53,4724,4725],{},"fly",[53,4727,4721],{}," class. It just throws an Error. This means that if we have an instance of the ",[53,4730,4721],{}," somewhere within our app and we want to call the ",[53,4733,4725],{}," method on that instance our app will fail every time, which is a signal of a bad code design.",[11,4736,4737],{},"The way we can resolve this bad code design and follow LSP is coding by contract instead of extending an abstract class. This means that our class will implement only the contracts needed for its implementation.",[58,4739,4741],{"className":2464,"code":4740,"language":2466,"meta":63,"style":63},"interface QuackableInterface {\n  swim(): string;\n}\n\ninterface FlyableInterface {\n  fly(): string;\n}\n\ninterface SwimmableInterface {\n  swim(): string;\n}\n\nclass RubberDuck implements QuackableInterface, SwimmableInterface {\n  quack(): string {\n    person = new Person();\n\n    if (!person.squeezesDuck(this)) {\n      throw new Error(\"The rubber duck cannot swim on its own\");\n    }\n\n    return \"The duck is quacking\";\n  }\n\n  swim(): string {\n    person = new Person();\n\n    if (!person.throwDuckInBath(this)) {\n      throw new Error(\"The rubber duck cannot swim on its own\");\n    }\n\n    return \"The duck is swimming\";\n  }\n}\n",[53,4742,4743,4752,4764,4768,4772,4781,4793,4797,4801,4810,4822,4826,4830,4847,4859,4871,4875,4894,4909,4913,4917,4926,4930,4934,4946,4958,4962,4980,4994,4998,5002,5011,5015],{"__ignoreMap":63},[67,4744,4745,4747,4750],{"class":69,"line":70},[67,4746,3918],{"class":163},[67,4748,4749],{"class":73}," QuackableInterface",[67,4751,2500],{"class":104},[67,4753,4754,4756,4758,4760,4762],{"class":69,"line":251},[67,4755,4460],{"class":73},[67,4757,2999],{"class":104},[67,4759,1390],{"class":163},[67,4761,2529],{"class":113},[67,4763,2485],{"class":104},[67,4765,4766],{"class":69,"line":264},[67,4767,1601],{"class":104},[67,4769,4770],{"class":69,"line":280},[67,4771,989],{"emptyLinePlaceholder":988},[67,4773,4774,4776,4779],{"class":69,"line":288},[67,4775,3918],{"class":163},[67,4777,4778],{"class":73}," FlyableInterface",[67,4780,2500],{"class":104},[67,4782,4783,4785,4787,4789,4791],{"class":69,"line":781},[67,4784,4430],{"class":73},[67,4786,2999],{"class":104},[67,4788,1390],{"class":163},[67,4790,2529],{"class":113},[67,4792,2485],{"class":104},[67,4794,4795],{"class":69,"line":792},[67,4796,1601],{"class":104},[67,4798,4799],{"class":69,"line":803},[67,4800,989],{"emptyLinePlaceholder":988},[67,4802,4803,4805,4808],{"class":69,"line":814},[67,4804,3918],{"class":163},[67,4806,4807],{"class":73}," SwimmableInterface",[67,4809,2500],{"class":104},[67,4811,4812,4814,4816,4818,4820],{"class":69,"line":825},[67,4813,4460],{"class":73},[67,4815,2999],{"class":104},[67,4817,1390],{"class":163},[67,4819,2529],{"class":113},[67,4821,2485],{"class":104},[67,4823,4824],{"class":69,"line":836},[67,4825,1601],{"class":104},[67,4827,4828],{"class":69,"line":847},[67,4829,989],{"emptyLinePlaceholder":988},[67,4831,4832,4834,4836,4838,4840,4842,4845],{"class":69,"line":858},[67,4833,2494],{"class":163},[67,4835,4496],{"class":73},[67,4837,3956],{"class":163},[67,4839,4749],{"class":73},[67,4841,944],{"class":104},[67,4843,4844],{"class":73},"SwimmableInterface",[67,4846,2500],{"class":104},[67,4848,4849,4851,4853,4855,4857],{"class":69,"line":869},[67,4850,4400],{"class":73},[67,4852,2999],{"class":104},[67,4854,1390],{"class":163},[67,4856,2529],{"class":113},[67,4858,2500],{"class":104},[67,4860,4861,4863,4865,4867,4869],{"class":69,"line":877},[67,4862,4520],{"class":104},[67,4864,2586],{"class":163},[67,4866,2730],{"class":163},[67,4868,4527],{"class":73},[67,4870,2756],{"class":104},[67,4872,4873],{"class":69,"line":886},[67,4874,989],{"emptyLinePlaceholder":988},[67,4876,4877,4879,4882,4884,4886,4888,4890,4892],{"class":69,"line":894},[67,4878,3764],{"class":163},[67,4880,4881],{"class":104}," (",[67,4883,4542],{"class":163},[67,4885,4545],{"class":104},[67,4887,4548],{"class":73},[67,4889,2556],{"class":104},[67,4891,3465],{"class":113},[67,4893,4555],{"class":104},[67,4895,4896,4898,4900,4902,4904,4907],{"class":69,"line":905},[67,4897,4560],{"class":163},[67,4899,2730],{"class":163},[67,4901,4565],{"class":73},[67,4903,2556],{"class":104},[67,4905,4906],{"class":77},"\"The rubber duck cannot swim on its own\"",[67,4908,2745],{"class":104},[67,4910,4911],{"class":69,"line":916},[67,4912,1557],{"class":104},[67,4914,4915],{"class":69,"line":927},[67,4916,989],{"emptyLinePlaceholder":988},[67,4918,4919,4921,4924],{"class":69,"line":935},[67,4920,2674],{"class":163},[67,4922,4923],{"class":77}," \"The duck is quacking\"",[67,4925,2485],{"class":104},[67,4927,4928],{"class":69,"line":958},[67,4929,1596],{"class":104},[67,4931,4932],{"class":69,"line":971},[67,4933,989],{"emptyLinePlaceholder":988},[67,4935,4936,4938,4940,4942,4944],{"class":69,"line":985},[67,4937,4460],{"class":73},[67,4939,2999],{"class":104},[67,4941,1390],{"class":163},[67,4943,2529],{"class":113},[67,4945,2500],{"class":104},[67,4947,4948,4950,4952,4954,4956],{"class":69,"line":992},[67,4949,4520],{"class":104},[67,4951,2586],{"class":163},[67,4953,2730],{"class":163},[67,4955,4527],{"class":73},[67,4957,2756],{"class":104},[67,4959,4960],{"class":69,"line":1000},[67,4961,989],{"emptyLinePlaceholder":988},[67,4963,4964,4966,4968,4970,4972,4974,4976,4978],{"class":69,"line":1009},[67,4965,3764],{"class":163},[67,4967,4881],{"class":104},[67,4969,4542],{"class":163},[67,4971,4545],{"class":104},[67,4973,4667],{"class":73},[67,4975,2556],{"class":104},[67,4977,3465],{"class":113},[67,4979,4555],{"class":104},[67,4981,4982,4984,4986,4988,4990,4992],{"class":69,"line":1016},[67,4983,4560],{"class":163},[67,4985,2730],{"class":163},[67,4987,4565],{"class":73},[67,4989,2556],{"class":104},[67,4991,4906],{"class":77},[67,4993,2745],{"class":104},[67,4995,4996],{"class":69,"line":1025},[67,4997,1557],{"class":104},[67,4999,5000],{"class":69,"line":1034},[67,5001,989],{"emptyLinePlaceholder":988},[67,5003,5004,5006,5009],{"class":69,"line":1043},[67,5005,2674],{"class":163},[67,5007,5008],{"class":77}," \"The duck is swimming\"",[67,5010,2485],{"class":104},[67,5012,5013],{"class":69,"line":1054},[67,5014,1596],{"class":104},[67,5016,5017],{"class":69,"line":1061},[67,5018,1601],{"class":104},[1758,5020,5022],{"id":5021},"interface-segregation-principle","Interface Segregation Principle",[11,5024,5025,5026,5029],{},"It is quite common to find that an interface is in essence just a description of an entire class. ISP states that ",[21,5027,5028],{},"we should write a series of smaller and more specific interfaces"," that are implemented by the class. Each interface provides a single behavior. It's also important to mention that an instance (or a method) should never be dependent on methods it doesn't use.",[11,5031,5032],{},"Let's see through an example of what does this means.",[58,5034,5036],{"className":2464,"code":5035,"language":2466,"meta":63,"style":63},"class Subscriber {\n  subscribe() {\n    \u002F\u002F\n  }\n\n  unsubscribe() {\n    \u002F\u002F\n  }\n\n  getNotifyEmail() {\n    \u002F\u002F\n  }\n}\n\nclass Notification {\n  send(subscriber: Subscriber, message: string) {\n    \u002F\u002F imagine we already have some EmailClient service\n    const emailClient = new EmailClient();\n\n    emailClient.send(subscriber.getNotifyEmail(), message);\n  }\n}\n",[53,5037,5038,5047,5054,5059,5063,5067,5074,5078,5082,5086,5093,5097,5101,5105,5109,5118,5143,5148,5163,5167,5183,5187],{"__ignoreMap":63},[67,5039,5040,5042,5045],{"class":69,"line":70},[67,5041,2494],{"class":163},[67,5043,5044],{"class":73}," Subscriber",[67,5046,2500],{"class":104},[67,5048,5049,5052],{"class":69,"line":251},[67,5050,5051],{"class":73},"  subscribe",[67,5053,2617],{"class":104},[67,5055,5056],{"class":69,"line":264},[67,5057,5058],{"class":3401},"    \u002F\u002F\n",[67,5060,5061],{"class":69,"line":280},[67,5062,1596],{"class":104},[67,5064,5065],{"class":69,"line":288},[67,5066,989],{"emptyLinePlaceholder":988},[67,5068,5069,5072],{"class":69,"line":781},[67,5070,5071],{"class":73},"  unsubscribe",[67,5073,2617],{"class":104},[67,5075,5076],{"class":69,"line":792},[67,5077,5058],{"class":3401},[67,5079,5080],{"class":69,"line":803},[67,5081,1596],{"class":104},[67,5083,5084],{"class":69,"line":814},[67,5085,989],{"emptyLinePlaceholder":988},[67,5087,5088,5091],{"class":69,"line":825},[67,5089,5090],{"class":73},"  getNotifyEmail",[67,5092,2617],{"class":104},[67,5094,5095],{"class":69,"line":836},[67,5096,5058],{"class":3401},[67,5098,5099],{"class":69,"line":847},[67,5100,1596],{"class":104},[67,5102,5103],{"class":69,"line":858},[67,5104,1601],{"class":104},[67,5106,5107],{"class":69,"line":869},[67,5108,989],{"emptyLinePlaceholder":988},[67,5110,5111,5113,5116],{"class":69,"line":877},[67,5112,2494],{"class":163},[67,5114,5115],{"class":73}," Notification",[67,5117,2500],{"class":104},[67,5119,5120,5123,5125,5128,5130,5132,5134,5137,5139,5141],{"class":69,"line":886},[67,5121,5122],{"class":73},"  send",[67,5124,2556],{"class":104},[67,5126,5127],{"class":2508},"subscriber",[67,5129,1390],{"class":163},[67,5131,5044],{"class":73},[67,5133,944],{"class":104},[67,5135,5136],{"class":2508},"message",[67,5138,1390],{"class":163},[67,5140,2529],{"class":113},[67,5142,2575],{"class":104},[67,5144,5145],{"class":69,"line":894},[67,5146,5147],{"class":3401},"    \u002F\u002F imagine we already have some EmailClient service\n",[67,5149,5150,5152,5155,5157,5159,5161],{"class":69,"line":905},[67,5151,2622],{"class":163},[67,5153,5154],{"class":113}," emailClient",[67,5156,2512],{"class":163},[67,5158,2730],{"class":163},[67,5160,2794],{"class":73},[67,5162,2756],{"class":104},[67,5164,5165],{"class":69,"line":916},[67,5166,989],{"emptyLinePlaceholder":988},[67,5168,5169,5172,5174,5177,5180],{"class":69,"line":927},[67,5170,5171],{"class":104},"    emailClient.",[67,5173,3202],{"class":73},[67,5175,5176],{"class":104},"(subscriber.",[67,5178,5179],{"class":73},"getNotifyEmail",[67,5181,5182],{"class":104},"(), message);\n",[67,5184,5185],{"class":69,"line":935},[67,5186,1596],{"class":104},[67,5188,5189],{"class":69,"line":958},[67,5190,1601],{"class":104},[11,5192,5193,5194,5196,5197,5200,5201,5204,5205,5207,5208,5210],{},"Notice here that if the implementation of the ",[53,5195,5179],{}," in the ",[53,5198,5199],{},"Subscriber"," class changes, our ",[53,5202,5203],{},"Notification"," class will need to change as well even though they are not dependant on each other. To comply with the ISP, we can create an interface that implements the ",[53,5206,5179],{}," and have the ",[53,5209,5199],{}," class implement that interface:",[58,5212,5214],{"className":2464,"code":5213,"language":2466,"meta":63,"style":63},"interface NotifiableInterface {\n  getNotifyEmail(): string;\n}\n\nclass Subscriber implements NotifiableInterface {\n  subscribe() {\n    \u002F\u002F\n  }\n\n  unsubscribe() {\n    \u002F\u002F\n  }\n\n  getNotifyEmail() {\n    \u002F\u002F\n  }\n}\n\nclass Notification {\n  send(subscriber: NotifiableInterface, message: string) {\n    \u002F\u002F imagine we already have some EmailClient service\n    const emailClient = new EmailClient();\n\n    emailClient.send(subscriber.getNotifyEmail(), message);\n  }\n}\n",[53,5215,5216,5225,5237,5241,5245,5257,5263,5267,5271,5275,5281,5285,5289,5293,5299,5303,5307,5311,5315,5323,5345,5349,5363,5367,5379,5383],{"__ignoreMap":63},[67,5217,5218,5220,5223],{"class":69,"line":70},[67,5219,3918],{"class":163},[67,5221,5222],{"class":73}," NotifiableInterface",[67,5224,2500],{"class":104},[67,5226,5227,5229,5231,5233,5235],{"class":69,"line":251},[67,5228,5090],{"class":73},[67,5230,2999],{"class":104},[67,5232,1390],{"class":163},[67,5234,2529],{"class":113},[67,5236,2485],{"class":104},[67,5238,5239],{"class":69,"line":264},[67,5240,1601],{"class":104},[67,5242,5243],{"class":69,"line":280},[67,5244,989],{"emptyLinePlaceholder":988},[67,5246,5247,5249,5251,5253,5255],{"class":69,"line":288},[67,5248,2494],{"class":163},[67,5250,5044],{"class":73},[67,5252,3956],{"class":163},[67,5254,5222],{"class":73},[67,5256,2500],{"class":104},[67,5258,5259,5261],{"class":69,"line":781},[67,5260,5051],{"class":73},[67,5262,2617],{"class":104},[67,5264,5265],{"class":69,"line":792},[67,5266,5058],{"class":3401},[67,5268,5269],{"class":69,"line":803},[67,5270,1596],{"class":104},[67,5272,5273],{"class":69,"line":814},[67,5274,989],{"emptyLinePlaceholder":988},[67,5276,5277,5279],{"class":69,"line":825},[67,5278,5071],{"class":73},[67,5280,2617],{"class":104},[67,5282,5283],{"class":69,"line":836},[67,5284,5058],{"class":3401},[67,5286,5287],{"class":69,"line":847},[67,5288,1596],{"class":104},[67,5290,5291],{"class":69,"line":858},[67,5292,989],{"emptyLinePlaceholder":988},[67,5294,5295,5297],{"class":69,"line":869},[67,5296,5090],{"class":73},[67,5298,2617],{"class":104},[67,5300,5301],{"class":69,"line":877},[67,5302,5058],{"class":3401},[67,5304,5305],{"class":69,"line":886},[67,5306,1596],{"class":104},[67,5308,5309],{"class":69,"line":894},[67,5310,1601],{"class":104},[67,5312,5313],{"class":69,"line":905},[67,5314,989],{"emptyLinePlaceholder":988},[67,5316,5317,5319,5321],{"class":69,"line":916},[67,5318,2494],{"class":163},[67,5320,5115],{"class":73},[67,5322,2500],{"class":104},[67,5324,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343],{"class":69,"line":927},[67,5326,5122],{"class":73},[67,5328,2556],{"class":104},[67,5330,5127],{"class":2508},[67,5332,1390],{"class":163},[67,5334,5222],{"class":73},[67,5336,944],{"class":104},[67,5338,5136],{"class":2508},[67,5340,1390],{"class":163},[67,5342,2529],{"class":113},[67,5344,2575],{"class":104},[67,5346,5347],{"class":69,"line":935},[67,5348,5147],{"class":3401},[67,5350,5351,5353,5355,5357,5359,5361],{"class":69,"line":958},[67,5352,2622],{"class":163},[67,5354,5154],{"class":113},[67,5356,2512],{"class":163},[67,5358,2730],{"class":163},[67,5360,2794],{"class":73},[67,5362,2756],{"class":104},[67,5364,5365],{"class":69,"line":971},[67,5366,989],{"emptyLinePlaceholder":988},[67,5368,5369,5371,5373,5375,5377],{"class":69,"line":985},[67,5370,5171],{"class":104},[67,5372,3202],{"class":73},[67,5374,5176],{"class":104},[67,5376,5179],{"class":73},[67,5378,5182],{"class":104},[67,5380,5381],{"class":69,"line":992},[67,5382,1596],{"class":104},[67,5384,5385],{"class":69,"line":1000},[67,5386,1601],{"class":104},[11,5388,5389,5390,3490,5392,5394,5395,5397,5398,5196,5400,5403],{},"Now, our ",[53,5391,3202],{},[53,5393,5203],{}," class doesn't depend on the whole ",[53,5396,5199],{}," method and only depends on the particular method ",[53,5399,5179],{},[53,5401,5402],{},"NotifiableInterface"," interface.",[1758,5405,5407],{"id":5406},"dependency-inversion-principle","Dependency Inversion Principle",[11,5409,5410,5411,5414],{},"This is the last one, I promise. The core of DIP is that ",[21,5412,5413],{},"high-level modules should not depend on the low-level modules. Instead, both of them should depend on abstractions."," This definition is way too abstract and technical, so let's break it down with examples and simplified explanations.",[11,5416,5417],{},"Let's see a real-life example of DIP. Let's say you want to charge your laptop. You can do that using your power supply adapter by plugging it into the wall socket. We don't dig a hole in the wall to find the wirings and plug the adapter directly into our power supply grid.",[11,5419,5420],{},"To translate the metaphor above coding-wise, our interface is the socket, and it provides us with the means (functions\u002Fclasses\u002Fmodules) to achieve the desired outcome. This means that we don't depend on a concrete implementation but we depend on abstractions. The most common example to take that implement DIP are database engines. You don't care what happens under the hood, what you care about is that the abstraction provided you enough functions to read and manipulate your data.",[11,5422,5423],{},"Other benefits that this principle offers:",[26,5425,5426,5429],{},[29,5427,5428],{},"Easy testing and clear boundaries of dependencies.",[29,5430,5431],{},"Low coupling, allowing for varying implementations or business requirements changing but the contract between modules remains the same.",[11,5433,5434,5435,5438,5439,5442],{},"Let's see this principle through an example. Let's build a ",[53,5436,5437],{},"SignupService"," that uses an ",[53,5440,5441],{},"HttpClient"," as a data source.",[58,5444,5446],{"className":2464,"code":5445,"language":2466,"meta":63,"style":63},"\u002F\u002F classes\u002FHttpClient.ts\nimport axios from \"axios\";\n\nexport default {\n  createUser: async (user: User) => {\n    return axios.post(\u002F* ... *\u002F);\n  },\n\n  getUserByEmail: async (email: string) => {\n    return axios.get(\u002F* ... *\u002F);\n  },\n};\n\n\u002F\u002F services\u002FSignupService.ts\nimport HttpClient from \"classes\u002FHttpClient\"; \u002F\u002F ❌ the domain depends on a concretion\n\nexport async function signup(email: string, password: string) {\n  const existingUser = await HttpClient.getUserByEmail(email);\n\n  if (existingUser) {\n    throw new Error(\"Email already used\");\n  }\n\n  return HttpClient.createUser({ email, password });\n}\n",[53,5447,5448,5453,5465,5469,5478,5503,5520,5524,5528,5551,5565,5569,5574,5578,5583,5601,5605,5637,5659,5663,5671,5686,5690,5694,5707],{"__ignoreMap":63},[67,5449,5450],{"class":69,"line":70},[67,5451,5452],{"class":3401},"\u002F\u002F classes\u002FHttpClient.ts\n",[67,5454,5455,5457,5459,5461,5463],{"class":69,"line":251},[67,5456,2473],{"class":163},[67,5458,2476],{"class":104},[67,5460,2479],{"class":163},[67,5462,2482],{"class":77},[67,5464,2485],{"class":104},[67,5466,5467],{"class":69,"line":264},[67,5468,989],{"emptyLinePlaceholder":988},[67,5470,5471,5474,5476],{"class":69,"line":280},[67,5472,5473],{"class":163},"export",[67,5475,242],{"class":163},[67,5477,2500],{"class":104},[67,5479,5480,5483,5485,5488,5490,5492,5494,5496,5498,5501],{"class":69,"line":288},[67,5481,5482],{"class":73},"  createUser",[67,5484,758],{"class":104},[67,5486,5487],{"class":163},"async",[67,5489,4881],{"class":104},[67,5491,3128],{"class":2508},[67,5493,1390],{"class":163},[67,5495,2497],{"class":73},[67,5497,4280],{"class":104},[67,5499,5500],{"class":163},"=>",[67,5502,2500],{"class":104},[67,5504,5505,5507,5510,5513,5515,5518],{"class":69,"line":781},[67,5506,2674],{"class":163},[67,5508,5509],{"class":104}," axios.",[67,5511,5512],{"class":73},"post",[67,5514,2556],{"class":104},[67,5516,5517],{"class":3401},"\u002F* ... *\u002F",[67,5519,2745],{"class":104},[67,5521,5522],{"class":69,"line":792},[67,5523,1562],{"class":104},[67,5525,5526],{"class":69,"line":803},[67,5527,989],{"emptyLinePlaceholder":988},[67,5529,5530,5533,5535,5537,5539,5541,5543,5545,5547,5549],{"class":69,"line":814},[67,5531,5532],{"class":73},"  getUserByEmail",[67,5534,758],{"class":104},[67,5536,5487],{"class":163},[67,5538,4881],{"class":104},[67,5540,2568],{"class":2508},[67,5542,1390],{"class":163},[67,5544,2529],{"class":113},[67,5546,4280],{"class":104},[67,5548,5500],{"class":163},[67,5550,2500],{"class":104},[67,5552,5553,5555,5557,5559,5561,5563],{"class":69,"line":825},[67,5554,2674],{"class":163},[67,5556,5509],{"class":104},[67,5558,3724],{"class":73},[67,5560,2556],{"class":104},[67,5562,5517],{"class":3401},[67,5564,2745],{"class":104},[67,5566,5567],{"class":69,"line":836},[67,5568,1562],{"class":104},[67,5570,5571],{"class":69,"line":847},[67,5572,5573],{"class":104},"};\n",[67,5575,5576],{"class":69,"line":858},[67,5577,989],{"emptyLinePlaceholder":988},[67,5579,5580],{"class":69,"line":869},[67,5581,5582],{"class":3401},"\u002F\u002F services\u002FSignupService.ts\n",[67,5584,5585,5587,5590,5592,5595,5598],{"class":69,"line":877},[67,5586,2473],{"class":163},[67,5588,5589],{"class":104}," HttpClient ",[67,5591,2479],{"class":163},[67,5593,5594],{"class":77}," \"classes\u002FHttpClient\"",[67,5596,5597],{"class":104},"; ",[67,5599,5600],{"class":3401},"\u002F\u002F ❌ the domain depends on a concretion\n",[67,5602,5603],{"class":69,"line":886},[67,5604,989],{"emptyLinePlaceholder":988},[67,5606,5607,5609,5612,5615,5618,5620,5622,5624,5626,5628,5631,5633,5635],{"class":69,"line":894},[67,5608,5473],{"class":163},[67,5610,5611],{"class":163}," async",[67,5613,5614],{"class":163}," function",[67,5616,5617],{"class":73}," signup",[67,5619,2556],{"class":104},[67,5621,2568],{"class":2508},[67,5623,1390],{"class":163},[67,5625,2529],{"class":113},[67,5627,944],{"class":104},[67,5629,5630],{"class":2508},"password",[67,5632,1390],{"class":163},[67,5634,2529],{"class":113},[67,5636,2575],{"class":104},[67,5638,5639,5642,5645,5647,5650,5653,5656],{"class":69,"line":905},[67,5640,5641],{"class":163},"  const",[67,5643,5644],{"class":113}," existingUser",[67,5646,2512],{"class":163},[67,5648,5649],{"class":163}," await",[67,5651,5652],{"class":104}," HttpClient.",[67,5654,5655],{"class":73},"getUserByEmail",[67,5657,5658],{"class":104},"(email);\n",[67,5660,5661],{"class":69,"line":916},[67,5662,989],{"emptyLinePlaceholder":988},[67,5664,5665,5668],{"class":69,"line":927},[67,5666,5667],{"class":163},"  if",[67,5669,5670],{"class":104}," (existingUser) {\n",[67,5672,5673,5675,5677,5679,5681,5684],{"class":69,"line":935},[67,5674,4607],{"class":163},[67,5676,2730],{"class":163},[67,5678,4565],{"class":73},[67,5680,2556],{"class":104},[67,5682,5683],{"class":77},"\"Email already used\"",[67,5685,2745],{"class":104},[67,5687,5688],{"class":69,"line":958},[67,5689,1596],{"class":104},[67,5691,5692],{"class":69,"line":971},[67,5693,989],{"emptyLinePlaceholder":988},[67,5695,5696,5699,5701,5704],{"class":69,"line":985},[67,5697,5698],{"class":163},"  return",[67,5700,5652],{"class":104},[67,5702,5703],{"class":73},"createUser",[67,5705,5706],{"class":104},"({ email, password });\n",[67,5708,5709],{"class":69,"line":992},[67,5710,1601],{"class":104},[11,5712,5713,5714],{},"This isn't ideal. We have just created a dependency from our domain to an implementation detail (HTTP) - crossing an architectural boundary and thus violating the Dependency rule. Furthermore, ",[21,5715,5716,5717,5720,5721,1819,5723,5725],{},"because ",[53,5718,5719],{},"signup"," is coupled with ",[53,5722,5441],{},[53,5724,5719],{}," it can't be unit tested.",[11,5727,5728,5729,2004,5731,56],{},"Let's reimplement it with dependency inversion this time! We're going to start by decoupling ",[53,5730,5437],{},[53,5732,5441],{},[58,5734,5736],{"className":2464,"code":5735,"language":2466,"meta":63,"style":63},"\u002F\u002F contracts\u002FApiClient.ts\nexport interface ApiClient {\n  createUser: (user: User) => Promise\u003Cvoid>;\n  getUserByEmail: (email: string) => Promise\u003CUser>;\n}\n\n\u002F\u002F classes\u002FHttpClient.ts\nimport axios from \"axios\";\nimport ApiClient from \"contracts\u002FApiClient\";\n\nexport function HttpClient(): ApiClient {\n  return {\n    createUser: async (user: User) => {\n      return axios.post(\u002F* ... *\u002F);\n    },\n    getUserByEmail: async (email: string) => {\n      return axios.get(\u002F* ... *\u002F);\n    },\n  };\n}\n\n\u002F\u002F services\u002FSignupService.ts\nimport ApiClient from \"contracts\u002FApiClient\"; \u002F\u002F ✅ the domain depends on an abstraction\n\nexport function SignupService(client: ApiClient) {\n  return async (email: string, password: string) => {\n    const existingUser = await client.getUserByEmail(email);\n\n    if (existingUser) {\n      throw new Error(\"Email already used\");\n    }\n\n    return client.createUser({ email, password });\n  };\n}\n",[53,5737,5738,5743,5755,5785,5811,5815,5819,5823,5835,5849,5853,5870,5876,5899,5913,5918,5941,5955,5959,5964,5968,5972,5976,5991,5995,6015,6043,6060,6064,6070,6084,6088,6092,6102,6106],{"__ignoreMap":63},[67,5739,5740],{"class":69,"line":70},[67,5741,5742],{"class":3401},"\u002F\u002F contracts\u002FApiClient.ts\n",[67,5744,5745,5747,5750,5753],{"class":69,"line":251},[67,5746,5473],{"class":163},[67,5748,5749],{"class":163}," interface",[67,5751,5752],{"class":73}," ApiClient",[67,5754,2500],{"class":104},[67,5756,5757,5759,5761,5763,5765,5767,5769,5771,5773,5776,5779,5782],{"class":69,"line":264},[67,5758,5482],{"class":73},[67,5760,1390],{"class":163},[67,5762,4881],{"class":104},[67,5764,3128],{"class":2508},[67,5766,1390],{"class":163},[67,5768,2497],{"class":73},[67,5770,4280],{"class":104},[67,5772,5500],{"class":163},[67,5774,5775],{"class":73}," Promise",[67,5777,5778],{"class":104},"\u003C",[67,5780,5781],{"class":113},"void",[67,5783,5784],{"class":104},">;\n",[67,5786,5787,5789,5791,5793,5795,5797,5799,5801,5803,5805,5807,5809],{"class":69,"line":280},[67,5788,5532],{"class":73},[67,5790,1390],{"class":163},[67,5792,4881],{"class":104},[67,5794,2568],{"class":2508},[67,5796,1390],{"class":163},[67,5798,2529],{"class":113},[67,5800,4280],{"class":104},[67,5802,5500],{"class":163},[67,5804,5775],{"class":73},[67,5806,5778],{"class":104},[67,5808,2762],{"class":73},[67,5810,5784],{"class":104},[67,5812,5813],{"class":69,"line":288},[67,5814,1601],{"class":104},[67,5816,5817],{"class":69,"line":781},[67,5818,989],{"emptyLinePlaceholder":988},[67,5820,5821],{"class":69,"line":792},[67,5822,5452],{"class":3401},[67,5824,5825,5827,5829,5831,5833],{"class":69,"line":803},[67,5826,2473],{"class":163},[67,5828,2476],{"class":104},[67,5830,2479],{"class":163},[67,5832,2482],{"class":77},[67,5834,2485],{"class":104},[67,5836,5837,5839,5842,5844,5847],{"class":69,"line":814},[67,5838,2473],{"class":163},[67,5840,5841],{"class":104}," ApiClient ",[67,5843,2479],{"class":163},[67,5845,5846],{"class":77}," \"contracts\u002FApiClient\"",[67,5848,2485],{"class":104},[67,5850,5851],{"class":69,"line":825},[67,5852,989],{"emptyLinePlaceholder":988},[67,5854,5855,5857,5859,5862,5864,5866,5868],{"class":69,"line":836},[67,5856,5473],{"class":163},[67,5858,5614],{"class":163},[67,5860,5861],{"class":73}," HttpClient",[67,5863,2999],{"class":104},[67,5865,1390],{"class":163},[67,5867,5752],{"class":73},[67,5869,2500],{"class":104},[67,5871,5872,5874],{"class":69,"line":847},[67,5873,5698],{"class":163},[67,5875,2500],{"class":104},[67,5877,5878,5881,5883,5885,5887,5889,5891,5893,5895,5897],{"class":69,"line":858},[67,5879,5880],{"class":73},"    createUser",[67,5882,758],{"class":104},[67,5884,5487],{"class":163},[67,5886,4881],{"class":104},[67,5888,3128],{"class":2508},[67,5890,1390],{"class":163},[67,5892,2497],{"class":73},[67,5894,4280],{"class":104},[67,5896,5500],{"class":163},[67,5898,2500],{"class":104},[67,5900,5901,5903,5905,5907,5909,5911],{"class":69,"line":869},[67,5902,4126],{"class":163},[67,5904,5509],{"class":104},[67,5906,5512],{"class":73},[67,5908,2556],{"class":104},[67,5910,5517],{"class":3401},[67,5912,2745],{"class":104},[67,5914,5915],{"class":69,"line":877},[67,5916,5917],{"class":104},"    },\n",[67,5919,5920,5923,5925,5927,5929,5931,5933,5935,5937,5939],{"class":69,"line":886},[67,5921,5922],{"class":73},"    getUserByEmail",[67,5924,758],{"class":104},[67,5926,5487],{"class":163},[67,5928,4881],{"class":104},[67,5930,2568],{"class":2508},[67,5932,1390],{"class":163},[67,5934,2529],{"class":113},[67,5936,4280],{"class":104},[67,5938,5500],{"class":163},[67,5940,2500],{"class":104},[67,5942,5943,5945,5947,5949,5951,5953],{"class":69,"line":894},[67,5944,4126],{"class":163},[67,5946,5509],{"class":104},[67,5948,3724],{"class":73},[67,5950,2556],{"class":104},[67,5952,5517],{"class":3401},[67,5954,2745],{"class":104},[67,5956,5957],{"class":69,"line":905},[67,5958,5917],{"class":104},[67,5960,5961],{"class":69,"line":916},[67,5962,5963],{"class":104},"  };\n",[67,5965,5966],{"class":69,"line":927},[67,5967,1601],{"class":104},[67,5969,5970],{"class":69,"line":935},[67,5971,989],{"emptyLinePlaceholder":988},[67,5973,5974],{"class":69,"line":958},[67,5975,5582],{"class":3401},[67,5977,5978,5980,5982,5984,5986,5988],{"class":69,"line":971},[67,5979,2473],{"class":163},[67,5981,5841],{"class":104},[67,5983,2479],{"class":163},[67,5985,5846],{"class":77},[67,5987,5597],{"class":104},[67,5989,5990],{"class":3401},"\u002F\u002F ✅ the domain depends on an abstraction\n",[67,5992,5993],{"class":69,"line":985},[67,5994,989],{"emptyLinePlaceholder":988},[67,5996,5997,5999,6001,6004,6006,6009,6011,6013],{"class":69,"line":992},[67,5998,5473],{"class":163},[67,6000,5614],{"class":163},[67,6002,6003],{"class":73}," SignupService",[67,6005,2556],{"class":104},[67,6007,6008],{"class":2508},"client",[67,6010,1390],{"class":163},[67,6012,5752],{"class":73},[67,6014,2575],{"class":104},[67,6016,6017,6019,6021,6023,6025,6027,6029,6031,6033,6035,6037,6039,6041],{"class":69,"line":1000},[67,6018,5698],{"class":163},[67,6020,5611],{"class":163},[67,6022,4881],{"class":104},[67,6024,2568],{"class":2508},[67,6026,1390],{"class":163},[67,6028,2529],{"class":113},[67,6030,944],{"class":104},[67,6032,5630],{"class":2508},[67,6034,1390],{"class":163},[67,6036,2529],{"class":113},[67,6038,4280],{"class":104},[67,6040,5500],{"class":163},[67,6042,2500],{"class":104},[67,6044,6045,6047,6049,6051,6053,6056,6058],{"class":69,"line":1009},[67,6046,2622],{"class":163},[67,6048,5644],{"class":113},[67,6050,2512],{"class":163},[67,6052,5649],{"class":163},[67,6054,6055],{"class":104}," client.",[67,6057,5655],{"class":73},[67,6059,5658],{"class":104},[67,6061,6062],{"class":69,"line":1016},[67,6063,989],{"emptyLinePlaceholder":988},[67,6065,6066,6068],{"class":69,"line":1025},[67,6067,3764],{"class":163},[67,6069,5670],{"class":104},[67,6071,6072,6074,6076,6078,6080,6082],{"class":69,"line":1034},[67,6073,4560],{"class":163},[67,6075,2730],{"class":163},[67,6077,4565],{"class":73},[67,6079,2556],{"class":104},[67,6081,5683],{"class":77},[67,6083,2745],{"class":104},[67,6085,6086],{"class":69,"line":1043},[67,6087,1557],{"class":104},[67,6089,6090],{"class":69,"line":1054},[67,6091,989],{"emptyLinePlaceholder":988},[67,6093,6094,6096,6098,6100],{"class":69,"line":1061},[67,6095,2674],{"class":163},[67,6097,6055],{"class":104},[67,6099,5703],{"class":73},[67,6101,5706],{"class":104},[67,6103,6104],{"class":69,"line":1068},[67,6105,5963],{"class":104},[67,6107,6108],{"class":69,"line":1075},[67,6109,1601],{"class":104},[11,6111,6112,6113,6115,6116,6119,6120,6122],{},"With the power of dependency inversion, our ",[53,6114,5437],{}," can now use any ",[53,6117,6118],{},"ApiClient",". Let's inject our ",[53,6121,5441],{}," for now.",[58,6124,6126],{"className":2464,"code":6125,"language":2466,"meta":63,"style":63},"\u002F\u002F index.ts\nimport SignupService from \"services\u002Fsignup\";\nimport HttpClient from \"classes\u002FHttpClient\";\n\nconst signup = SignupService(HttpClient());\n\nsignup(\"bojan@example.com\", \"password\");\n",[53,6127,6128,6133,6147,6159,6163,6180,6184],{"__ignoreMap":63},[67,6129,6130],{"class":69,"line":70},[67,6131,6132],{"class":3401},"\u002F\u002F index.ts\n",[67,6134,6135,6137,6140,6142,6145],{"class":69,"line":251},[67,6136,2473],{"class":163},[67,6138,6139],{"class":104}," SignupService ",[67,6141,2479],{"class":163},[67,6143,6144],{"class":77}," \"services\u002Fsignup\"",[67,6146,2485],{"class":104},[67,6148,6149,6151,6153,6155,6157],{"class":69,"line":264},[67,6150,2473],{"class":163},[67,6152,5589],{"class":104},[67,6154,2479],{"class":163},[67,6156,5594],{"class":77},[67,6158,2485],{"class":104},[67,6160,6161],{"class":69,"line":280},[67,6162,989],{"emptyLinePlaceholder":988},[67,6164,6165,6167,6169,6171,6173,6175,6177],{"class":69,"line":288},[67,6166,2722],{"class":163},[67,6168,5617],{"class":113},[67,6170,2512],{"class":163},[67,6172,6003],{"class":73},[67,6174,2556],{"class":104},[67,6176,5441],{"class":73},[67,6178,6179],{"class":104},"());\n",[67,6181,6182],{"class":69,"line":781},[67,6183,989],{"emptyLinePlaceholder":988},[67,6185,6186,6188,6190,6193,6195,6198],{"class":69,"line":792},[67,6187,5719],{"class":73},[67,6189,2556],{"class":104},[67,6191,6192],{"class":77},"\"bojan@example.com\"",[67,6194,944],{"class":104},[67,6196,6197],{"class":77},"\"password\"",[67,6199,2745],{"class":104},[6201,6202],"hr",{},[11,6204,6205],{},"And there you have it. I know it has been a long article, but I wanted to share my knowledge about this particular topic for a long time.",[11,6207,6208],{},"Wish everyone a happy, productive, successful and healthy New Year & Merry Christmas!",[1670,6210,6211],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":63,"searchDepth":251,"depth":251,"links":6213},[6214,6215,6216],{"id":2363,"depth":251,"text":2364},{"id":2375,"depth":251,"text":2376},{"id":2395,"depth":251,"text":2396,"children":6217},[6218,6219,6220,6221,6222],{"id":2451,"depth":264,"text":2452},{"id":3336,"depth":264,"text":3337},{"id":4349,"depth":264,"text":4350},{"id":5021,"depth":264,"text":5022},{"id":5406,"depth":264,"text":5407},"TypeScript","2020\u002F12\u002F26","Yes, another take into explaining SOLID Design Principles. And yes, another blog post about making your code more maintainable and readable. But this time something is different - well, mostly nothing really is (you expected something here, didn't ya?) except that it's just my perspective. A perspective gained from applying these principles in real life projects.","\u002Fimages\u002Fsolid-ts.jpg","typescript, design patters, solid, solid design principles, single responsibility principle, open close principle, liskov substitution principle, dependency inversion principle, interface segregation principle",{},"\u002Fblog\u002Funderstand-and-use-solid-design-principles-with-typescript","☕️☕️ 19 min read",{"title":2351,"description":6225},"understand-and-use-solid-design-principles-with-typescript","blog\u002Funderstand-and-use-solid-design-principles-with-typescript","solid, typescript, design patterns","exK7fI-uA5IXl6sv5uvrn9lmCvwjMYdH-j6KdtUp8GU",{"id":6237,"title":6238,"body":6239,"category":8818,"date":8819,"description":6243,"excerpt":1690,"extension":1691,"image":8820,"keywords":8821,"meta":8822,"navigation":988,"path":8823,"readingTime":8824,"seo":8825,"slug":8826,"stem":8827,"tags":8828,"__hash__":8829},"blog\u002Fblog\u002Fhandling-different-component-states-with-a-status-class.md","Abstracting functionality to classes - Handling different component states",{"type":8,"value":6240,"toc":8813},[6241,6244,6247,6268,6272,6275,6464,6487,6725,7076,7079,7083,7101,7280,7580,7589,7593,7596,8255,8263,8467,8785,8795,8797,8810],[11,6242,6243],{},"(Part 1)",[11,6245,6246],{},"Web apps often require a loading state (things like loading spinners) to let a user know that an operation is taking place. These operations often take time, so it's best to let the user know something is loading, which helps improve UX, prevent form resubmissions, etc.",[11,6248,6249,6250,6253,6254,944,6257,944,6260,6263,6264,6267],{},"Beyond ",[53,6251,6252],{},"loading",", our components can have other states too — ",[53,6255,6256],{},"completed",[53,6258,6259],{},"rejected",[53,6261,6262],{},"idle",", etc. There are different ways of handling these states, so let's go through them. In the end, I am going to show you my way of handling these states by using a simple ",[53,6265,6266],{},"Status"," class that is very convenient to use.",[1758,6269,6271],{"id":6270},"handling-component-state-using-booleans","Handling component state using booleans",[11,6273,6274],{},"The most common example is a button that is disabled and maybe even has a loading spinner when we submit a form.",[58,6276,6280],{"className":6277,"code":6278,"language":6279,"meta":63,"style":63},"language-vue shiki shiki-themes github-light github-dark","\u002F\u002F button-wrap.vue\n\u003Ctemplate>\n  \u003Cbutton v-bind=\"$attrs\" v-on=\"$listeners\" :disabled=\"isLoading\">\n    \u003Cspan v-if=\"isLoading\">Loading ...\u003C\u002Fspan>\n    \u003Cslot v-else>\u003C\u002Fslot>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    props: {\n      isLoading: {\n        required: false,\n        default: false,\n        type: Boolean,\n      }\n    }\n  }\n\u003C\u002Fscript>\n","vue",[53,6281,6282,6287,6297,6331,6352,6369,6378,6387,6391,6400,6409,6414,6419,6429,6438,6443,6448,6452,6456],{"__ignoreMap":63},[67,6283,6284],{"class":69,"line":70},[67,6285,6286],{"class":104},"\u002F\u002F button-wrap.vue\n",[67,6288,6289,6291,6294],{"class":69,"line":251},[67,6290,5778],{"class":104},[67,6292,6293],{"class":739},"template",[67,6295,6296],{"class":104},">\n",[67,6298,6299,6302,6305,6308,6310,6313,6316,6318,6321,6324,6326,6329],{"class":69,"line":264},[67,6300,6301],{"class":104},"  \u003C",[67,6303,6304],{"class":739},"button",[67,6306,6307],{"class":73}," v-bind",[67,6309,2586],{"class":104},[67,6311,6312],{"class":77},"\"$attrs\"",[67,6314,6315],{"class":73}," v-on",[67,6317,2586],{"class":104},[67,6319,6320],{"class":77},"\"$listeners\"",[67,6322,6323],{"class":73}," :disabled",[67,6325,2586],{"class":104},[67,6327,6328],{"class":77},"\"isLoading\"",[67,6330,6296],{"class":104},[67,6332,6333,6336,6338,6341,6343,6345,6348,6350],{"class":69,"line":280},[67,6334,6335],{"class":104},"    \u003C",[67,6337,67],{"class":739},[67,6339,6340],{"class":73}," v-if",[67,6342,2586],{"class":104},[67,6344,6328],{"class":77},[67,6346,6347],{"class":104},">Loading ...\u003C\u002F",[67,6349,67],{"class":739},[67,6351,6296],{"class":104},[67,6353,6354,6356,6359,6362,6365,6367],{"class":69,"line":288},[67,6355,6335],{"class":104},[67,6357,6358],{"class":739},"slot",[67,6360,6361],{"class":73}," v-else",[67,6363,6364],{"class":104},">\u003C\u002F",[67,6366,6358],{"class":739},[67,6368,6296],{"class":104},[67,6370,6371,6374,6376],{"class":69,"line":781},[67,6372,6373],{"class":104},"  \u003C\u002F",[67,6375,6304],{"class":739},[67,6377,6296],{"class":104},[67,6379,6380,6383,6385],{"class":69,"line":792},[67,6381,6382],{"class":104},"\u003C\u002F",[67,6384,6293],{"class":739},[67,6386,6296],{"class":104},[67,6388,6389],{"class":69,"line":803},[67,6390,989],{"emptyLinePlaceholder":988},[67,6392,6393,6395,6398],{"class":69,"line":814},[67,6394,5778],{"class":104},[67,6396,6397],{"class":739},"script",[67,6399,6296],{"class":104},[67,6401,6402,6405,6407],{"class":69,"line":825},[67,6403,6404],{"class":163},"  export",[67,6406,242],{"class":163},[67,6408,2500],{"class":104},[67,6410,6411],{"class":69,"line":836},[67,6412,6413],{"class":104},"    props: {\n",[67,6415,6416],{"class":69,"line":847},[67,6417,6418],{"class":104},"      isLoading: {\n",[67,6420,6421,6424,6427],{"class":69,"line":858},[67,6422,6423],{"class":104},"        required: ",[67,6425,6426],{"class":113},"false",[67,6428,955],{"class":104},[67,6430,6431,6434,6436],{"class":69,"line":869},[67,6432,6433],{"class":104},"        default: ",[67,6435,6426],{"class":113},[67,6437,955],{"class":104},[67,6439,6440],{"class":69,"line":877},[67,6441,6442],{"class":104},"        type: Boolean,\n",[67,6444,6445],{"class":69,"line":886},[67,6446,6447],{"class":104},"      }\n",[67,6449,6450],{"class":69,"line":894},[67,6451,1557],{"class":104},[67,6453,6454],{"class":69,"line":905},[67,6455,1596],{"class":104},[67,6457,6458,6460,6462],{"class":69,"line":916},[67,6459,6382],{"class":104},[67,6461,6397],{"class":739},[67,6463,6296],{"class":104},[11,6465,6466,6467,6470,6471,6474,6475,944,6477,6479,6480,944,6483,6486],{},"This is a simple ",[53,6468,6469],{},"button-wrap"," component that we can use within our forms and pass a ",[53,6472,6473],{},"isLoading"," boolean prop whether the button should be disabled (the form is being submitted) and show a loading spinner instead of the button label. In most cases, this is probably the easiest way to handle the loading state. But, what if we need other states like ",[53,6476,6259],{},[53,6478,6256],{},", etc, do we pass ",[53,6481,6482],{},"isRejected",[53,6484,6485],{},"isCompleted"," booleans? Maybe for this case, it doesn't make sense, but be ensured that you will need these states somewhere along the line, and you will get cluttered with booleans just to cover all of the cases. This is with what you might end up eventually:",[58,6488,6490],{"className":6277,"code":6489,"language":6279,"meta":63,"style":63},"\u002F\u002F button-wrap.vue\n\u003Ctemplate>\n  \u003Cbutton v-bind=\"$attrs\" v-on=\"$listeners\" :disabled=\"isLoading && !isCompleted && !isRejected\"\n    :class=\"{'is-valid': isCompleted, 'is-rejected': isRejected}\"\n  >\n    \u003Cspan v-if=\"isLoading && !isCompleted && !isRejected\">Loading ...\u003C\u002Fspan>\n    \u003Cslot v-else-if=\"!isLoading && !isCompleted && !isRejected\">\u003C\u002Fslot>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    props: {\n      isCompleted: {\n        required: false,\n        default: false,\n        type: Boolean,\n      },\n      isRejected: {\n        required: false,\n        default: false,\n        type: Boolean,\n      },\n      isLoading: {\n        required: false,\n        default: false,\n        type: Boolean,\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,6491,6492,6496,6504,6529,6539,6544,6563,6583,6591,6599,6603,6611,6619,6623,6628,6636,6644,6648,6652,6657,6665,6673,6677,6681,6685,6693,6701,6705,6709,6713,6717],{"__ignoreMap":63},[67,6493,6494],{"class":69,"line":70},[67,6495,6286],{"class":104},[67,6497,6498,6500,6502],{"class":69,"line":251},[67,6499,5778],{"class":104},[67,6501,6293],{"class":739},[67,6503,6296],{"class":104},[67,6505,6506,6508,6510,6512,6514,6516,6518,6520,6522,6524,6526],{"class":69,"line":264},[67,6507,6301],{"class":104},[67,6509,6304],{"class":739},[67,6511,6307],{"class":73},[67,6513,2586],{"class":104},[67,6515,6312],{"class":77},[67,6517,6315],{"class":73},[67,6519,2586],{"class":104},[67,6521,6320],{"class":77},[67,6523,6323],{"class":73},[67,6525,2586],{"class":104},[67,6527,6528],{"class":77},"\"isLoading && !isCompleted && !isRejected\"\n",[67,6530,6531,6534,6536],{"class":69,"line":280},[67,6532,6533],{"class":73},"    :class",[67,6535,2586],{"class":104},[67,6537,6538],{"class":77},"\"{'is-valid': isCompleted, 'is-rejected': isRejected}\"\n",[67,6540,6541],{"class":69,"line":288},[67,6542,6543],{"class":104},"  >\n",[67,6545,6546,6548,6550,6552,6554,6557,6559,6561],{"class":69,"line":781},[67,6547,6335],{"class":104},[67,6549,67],{"class":739},[67,6551,6340],{"class":73},[67,6553,2586],{"class":104},[67,6555,6556],{"class":77},"\"isLoading && !isCompleted && !isRejected\"",[67,6558,6347],{"class":104},[67,6560,67],{"class":739},[67,6562,6296],{"class":104},[67,6564,6565,6567,6569,6572,6574,6577,6579,6581],{"class":69,"line":792},[67,6566,6335],{"class":104},[67,6568,6358],{"class":739},[67,6570,6571],{"class":73}," v-else-if",[67,6573,2586],{"class":104},[67,6575,6576],{"class":77},"\"!isLoading && !isCompleted && !isRejected\"",[67,6578,6364],{"class":104},[67,6580,6358],{"class":739},[67,6582,6296],{"class":104},[67,6584,6585,6587,6589],{"class":69,"line":803},[67,6586,6373],{"class":104},[67,6588,6304],{"class":739},[67,6590,6296],{"class":104},[67,6592,6593,6595,6597],{"class":69,"line":814},[67,6594,6382],{"class":104},[67,6596,6293],{"class":739},[67,6598,6296],{"class":104},[67,6600,6601],{"class":69,"line":825},[67,6602,989],{"emptyLinePlaceholder":988},[67,6604,6605,6607,6609],{"class":69,"line":836},[67,6606,5778],{"class":104},[67,6608,6397],{"class":739},[67,6610,6296],{"class":104},[67,6612,6613,6615,6617],{"class":69,"line":847},[67,6614,6404],{"class":163},[67,6616,242],{"class":163},[67,6618,2500],{"class":104},[67,6620,6621],{"class":69,"line":858},[67,6622,6413],{"class":104},[67,6624,6625],{"class":69,"line":869},[67,6626,6627],{"class":104},"      isCompleted: {\n",[67,6629,6630,6632,6634],{"class":69,"line":877},[67,6631,6423],{"class":104},[67,6633,6426],{"class":113},[67,6635,955],{"class":104},[67,6637,6638,6640,6642],{"class":69,"line":886},[67,6639,6433],{"class":104},[67,6641,6426],{"class":113},[67,6643,955],{"class":104},[67,6645,6646],{"class":69,"line":894},[67,6647,6442],{"class":104},[67,6649,6650],{"class":69,"line":905},[67,6651,2700],{"class":104},[67,6653,6654],{"class":69,"line":916},[67,6655,6656],{"class":104},"      isRejected: {\n",[67,6658,6659,6661,6663],{"class":69,"line":927},[67,6660,6423],{"class":104},[67,6662,6426],{"class":113},[67,6664,955],{"class":104},[67,6666,6667,6669,6671],{"class":69,"line":935},[67,6668,6433],{"class":104},[67,6670,6426],{"class":113},[67,6672,955],{"class":104},[67,6674,6675],{"class":69,"line":958},[67,6676,6442],{"class":104},[67,6678,6679],{"class":69,"line":971},[67,6680,2700],{"class":104},[67,6682,6683],{"class":69,"line":985},[67,6684,6418],{"class":104},[67,6686,6687,6689,6691],{"class":69,"line":992},[67,6688,6423],{"class":104},[67,6690,6426],{"class":113},[67,6692,955],{"class":104},[67,6694,6695,6697,6699],{"class":69,"line":1000},[67,6696,6433],{"class":104},[67,6698,6426],{"class":113},[67,6700,955],{"class":104},[67,6702,6703],{"class":69,"line":1009},[67,6704,6442],{"class":104},[67,6706,6707],{"class":69,"line":1016},[67,6708,6447],{"class":104},[67,6710,6711],{"class":69,"line":1025},[67,6712,1557],{"class":104},[67,6714,6715],{"class":69,"line":1034},[67,6716,1596],{"class":104},[67,6718,6719,6721,6723],{"class":69,"line":1043},[67,6720,6382],{"class":104},[67,6722,6397],{"class":739},[67,6724,6296],{"class":104},[58,6726,6728],{"className":6277,"code":6727,"language":6279,"meta":63,"style":63},"\u002F\u002Fform.vue\n\u003Ctemplate>\n  \u003Cform @submit.prevent=\"onSubmit\">\n  \u002F\u002F\n  \u003Cbutton-wrap :isLoading=\"status.isLoading\"\n    :isCompleted=\"status.isCompleted\"\n    :isRejected=\"status.isRejected\">\n    Submit\n  \u003C\u002Fbutton-wrap>\n  \u003C\u002Fform>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    data: () => ({\n      status: {\n        isLoading: false,\n        isRejected: false,\n        isCompleted: false,\n      }\n    }),\n\n    methods: {\n      onSubmit() {\n        this.status.isLoading = true;\n\n        axios.post(url, data)\n          .then((response) => {\n            this.status.isCompleted = true;\n          })\n          .catch((error) => {\n            this.status.isRejected = true;\n          });\n          .finally(() => {\n            this.status.isLoading = false;\n          }\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,6729,6730,6735,6743,6760,6765,6779,6789,6801,6806,6814,6822,6830,6834,6842,6850,6863,6868,6877,6886,6895,6899,6904,6908,6913,6920,6935,6939,6949,6969,6983,6988,7006,7019,7024,7038,7051,7056,7060,7064,7068],{"__ignoreMap":63},[67,6731,6732],{"class":69,"line":70},[67,6733,6734],{"class":104},"\u002F\u002Fform.vue\n",[67,6736,6737,6739,6741],{"class":69,"line":251},[67,6738,5778],{"class":104},[67,6740,6293],{"class":739},[67,6742,6296],{"class":104},[67,6744,6745,6747,6750,6753,6755,6758],{"class":69,"line":264},[67,6746,6301],{"class":104},[67,6748,6749],{"class":739},"form",[67,6751,6752],{"class":73}," @submit.prevent",[67,6754,2586],{"class":104},[67,6756,6757],{"class":77},"\"onSubmit\"",[67,6759,6296],{"class":104},[67,6761,6762],{"class":69,"line":280},[67,6763,6764],{"class":104},"  \u002F\u002F\n",[67,6766,6767,6769,6771,6774,6776],{"class":69,"line":288},[67,6768,6301],{"class":104},[67,6770,6469],{"class":739},[67,6772,6773],{"class":73}," :isLoading",[67,6775,2586],{"class":104},[67,6777,6778],{"class":77},"\"status.isLoading\"\n",[67,6780,6781,6784,6786],{"class":69,"line":781},[67,6782,6783],{"class":73},"    :isCompleted",[67,6785,2586],{"class":104},[67,6787,6788],{"class":77},"\"status.isCompleted\"\n",[67,6790,6791,6794,6796,6799],{"class":69,"line":792},[67,6792,6793],{"class":73},"    :isRejected",[67,6795,2586],{"class":104},[67,6797,6798],{"class":77},"\"status.isRejected\"",[67,6800,6296],{"class":104},[67,6802,6803],{"class":69,"line":803},[67,6804,6805],{"class":104},"    Submit\n",[67,6807,6808,6810,6812],{"class":69,"line":814},[67,6809,6373],{"class":104},[67,6811,6469],{"class":739},[67,6813,6296],{"class":104},[67,6815,6816,6818,6820],{"class":69,"line":825},[67,6817,6373],{"class":104},[67,6819,6749],{"class":739},[67,6821,6296],{"class":104},[67,6823,6824,6826,6828],{"class":69,"line":836},[67,6825,6382],{"class":104},[67,6827,6293],{"class":739},[67,6829,6296],{"class":104},[67,6831,6832],{"class":69,"line":847},[67,6833,989],{"emptyLinePlaceholder":988},[67,6835,6836,6838,6840],{"class":69,"line":858},[67,6837,5778],{"class":104},[67,6839,6397],{"class":739},[67,6841,6296],{"class":104},[67,6843,6844,6846,6848],{"class":69,"line":869},[67,6845,6404],{"class":163},[67,6847,242],{"class":163},[67,6849,2500],{"class":104},[67,6851,6852,6855,6858,6860],{"class":69,"line":877},[67,6853,6854],{"class":73},"    data",[67,6856,6857],{"class":104},": () ",[67,6859,5500],{"class":163},[67,6861,6862],{"class":104}," ({\n",[67,6864,6865],{"class":69,"line":886},[67,6866,6867],{"class":104},"      status: {\n",[67,6869,6870,6873,6875],{"class":69,"line":894},[67,6871,6872],{"class":104},"        isLoading: ",[67,6874,6426],{"class":113},[67,6876,955],{"class":104},[67,6878,6879,6882,6884],{"class":69,"line":905},[67,6880,6881],{"class":104},"        isRejected: ",[67,6883,6426],{"class":113},[67,6885,955],{"class":104},[67,6887,6888,6891,6893],{"class":69,"line":916},[67,6889,6890],{"class":104},"        isCompleted: ",[67,6892,6426],{"class":113},[67,6894,955],{"class":104},[67,6896,6897],{"class":69,"line":927},[67,6898,6447],{"class":104},[67,6900,6901],{"class":69,"line":935},[67,6902,6903],{"class":104},"    }),\n",[67,6905,6906],{"class":69,"line":958},[67,6907,989],{"emptyLinePlaceholder":988},[67,6909,6910],{"class":69,"line":971},[67,6911,6912],{"class":104},"    methods: {\n",[67,6914,6915,6918],{"class":69,"line":985},[67,6916,6917],{"class":73},"      onSubmit",[67,6919,2617],{"class":104},[67,6921,6922,6925,6928,6930,6933],{"class":69,"line":992},[67,6923,6924],{"class":113},"        this",[67,6926,6927],{"class":104},".status.isLoading ",[67,6929,2586],{"class":163},[67,6931,6932],{"class":113}," true",[67,6934,2485],{"class":104},[67,6936,6937],{"class":69,"line":1000},[67,6938,989],{"emptyLinePlaceholder":988},[67,6940,6941,6944,6946],{"class":69,"line":1009},[67,6942,6943],{"class":104},"        axios.",[67,6945,5512],{"class":73},[67,6947,6948],{"class":104},"(url, data)\n",[67,6950,6951,6954,6957,6960,6963,6965,6967],{"class":69,"line":1016},[67,6952,6953],{"class":104},"          .",[67,6955,6956],{"class":73},"then",[67,6958,6959],{"class":104},"((",[67,6961,6962],{"class":2508},"response",[67,6964,4280],{"class":104},[67,6966,5500],{"class":163},[67,6968,2500],{"class":104},[67,6970,6971,6974,6977,6979,6981],{"class":69,"line":1025},[67,6972,6973],{"class":113},"            this",[67,6975,6976],{"class":104},".status.isCompleted ",[67,6978,2586],{"class":163},[67,6980,6932],{"class":113},[67,6982,2485],{"class":104},[67,6984,6985],{"class":69,"line":1034},[67,6986,6987],{"class":104},"          })\n",[67,6989,6990,6992,6995,6997,7000,7002,7004],{"class":69,"line":1043},[67,6991,6953],{"class":104},[67,6993,6994],{"class":73},"catch",[67,6996,6959],{"class":104},[67,6998,6999],{"class":2508},"error",[67,7001,4280],{"class":104},[67,7003,5500],{"class":163},[67,7005,2500],{"class":104},[67,7007,7008,7010,7013,7015,7017],{"class":69,"line":1054},[67,7009,6973],{"class":113},[67,7011,7012],{"class":104},".status.isRejected ",[67,7014,2586],{"class":163},[67,7016,6932],{"class":113},[67,7018,2485],{"class":104},[67,7020,7021],{"class":69,"line":1061},[67,7022,7023],{"class":104},"          });\n",[67,7025,7026,7028,7031,7034,7036],{"class":69,"line":1068},[67,7027,6953],{"class":104},[67,7029,7030],{"class":73},"finally",[67,7032,7033],{"class":104},"(() ",[67,7035,5500],{"class":163},[67,7037,2500],{"class":104},[67,7039,7040,7042,7044,7046,7049],{"class":69,"line":1075},[67,7041,6973],{"class":113},[67,7043,6927],{"class":104},[67,7045,2586],{"class":163},[67,7047,7048],{"class":113}," false",[67,7050,2485],{"class":104},[67,7052,7053],{"class":69,"line":1085},[67,7054,7055],{"class":104},"          }\n",[67,7057,7058],{"class":69,"line":1095},[67,7059,6447],{"class":104},[67,7061,7062],{"class":69,"line":1104},[67,7063,1557],{"class":104},[67,7065,7066],{"class":69,"line":3085},[67,7067,1596],{"class":104},[67,7069,7070,7072,7074],{"class":69,"line":3092},[67,7071,6382],{"class":163},[67,7073,6397],{"class":104},[67,7075,6296],{"class":163},[11,7077,7078],{},"Having three booleans for three different states feels a bit redundant when the button can have only one state at a time. Let's see a more convenient way to do this using classes.",[1758,7080,7082],{"id":7081},"handling-component-state-using-status-property","Handling component state using status property",[11,7084,7085,7086,7089,7090,7093,7094,7097,7098,7100],{},"Since a component can have only one state at a time, we can simplify by having one ",[53,7087,7088],{},"data"," property named ",[53,7091,7092],{},"status"," and use ",[53,7095,7096],{},"strings"," to represent the state that the component is currently in. Changing the value of the ",[53,7099,7092],{}," would be a notification to the component that its state changed. This implementation would look like this:",[58,7102,7104],{"className":6277,"code":7103,"language":6279,"meta":63,"style":63},"\u002F\u002F button-wrap.vue\n\u003Ctemplate>\n  \u003Cbutton v-bind=\"$attrs\" v-on=\"$listeners\" :disabled=\"status === 'loading'\"\n    :class=\"{'is-valid': status === 'completed', 'is-rejected': status === 'rejected'}\"\n  >\n    \u003Cspan v-if=\"status === 'loading'\">Loading ...\u003C\u002Fspan>\n    \u003Cslot v-else-if=\"status !== 'rejected' && status !== 'loading'\">\u003C\u002Fslot>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    props: {\n      status: {\n        required: false,\n        default: 'idle',\n        type: String,\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,7105,7106,7110,7118,7143,7152,7156,7175,7194,7202,7210,7214,7222,7230,7234,7238,7246,7255,7260,7264,7268,7272],{"__ignoreMap":63},[67,7107,7108],{"class":69,"line":70},[67,7109,6286],{"class":104},[67,7111,7112,7114,7116],{"class":69,"line":251},[67,7113,5778],{"class":104},[67,7115,6293],{"class":739},[67,7117,6296],{"class":104},[67,7119,7120,7122,7124,7126,7128,7130,7132,7134,7136,7138,7140],{"class":69,"line":264},[67,7121,6301],{"class":104},[67,7123,6304],{"class":739},[67,7125,6307],{"class":73},[67,7127,2586],{"class":104},[67,7129,6312],{"class":77},[67,7131,6315],{"class":73},[67,7133,2586],{"class":104},[67,7135,6320],{"class":77},[67,7137,6323],{"class":73},[67,7139,2586],{"class":104},[67,7141,7142],{"class":77},"\"status === 'loading'\"\n",[67,7144,7145,7147,7149],{"class":69,"line":280},[67,7146,6533],{"class":73},[67,7148,2586],{"class":104},[67,7150,7151],{"class":77},"\"{'is-valid': status === 'completed', 'is-rejected': status === 'rejected'}\"\n",[67,7153,7154],{"class":69,"line":288},[67,7155,6543],{"class":104},[67,7157,7158,7160,7162,7164,7166,7169,7171,7173],{"class":69,"line":781},[67,7159,6335],{"class":104},[67,7161,67],{"class":739},[67,7163,6340],{"class":73},[67,7165,2586],{"class":104},[67,7167,7168],{"class":77},"\"status === 'loading'\"",[67,7170,6347],{"class":104},[67,7172,67],{"class":739},[67,7174,6296],{"class":104},[67,7176,7177,7179,7181,7183,7185,7188,7190,7192],{"class":69,"line":792},[67,7178,6335],{"class":104},[67,7180,6358],{"class":739},[67,7182,6571],{"class":73},[67,7184,2586],{"class":104},[67,7186,7187],{"class":77},"\"status !== 'rejected' && status !== 'loading'\"",[67,7189,6364],{"class":104},[67,7191,6358],{"class":739},[67,7193,6296],{"class":104},[67,7195,7196,7198,7200],{"class":69,"line":803},[67,7197,6373],{"class":104},[67,7199,6304],{"class":739},[67,7201,6296],{"class":104},[67,7203,7204,7206,7208],{"class":69,"line":814},[67,7205,6382],{"class":104},[67,7207,6293],{"class":739},[67,7209,6296],{"class":104},[67,7211,7212],{"class":69,"line":825},[67,7213,989],{"emptyLinePlaceholder":988},[67,7215,7216,7218,7220],{"class":69,"line":836},[67,7217,5778],{"class":104},[67,7219,6397],{"class":739},[67,7221,6296],{"class":104},[67,7223,7224,7226,7228],{"class":69,"line":847},[67,7225,6404],{"class":163},[67,7227,242],{"class":163},[67,7229,2500],{"class":104},[67,7231,7232],{"class":69,"line":858},[67,7233,6413],{"class":104},[67,7235,7236],{"class":69,"line":869},[67,7237,6867],{"class":104},[67,7239,7240,7242,7244],{"class":69,"line":877},[67,7241,6423],{"class":104},[67,7243,6426],{"class":113},[67,7245,955],{"class":104},[67,7247,7248,7250,7253],{"class":69,"line":886},[67,7249,6433],{"class":104},[67,7251,7252],{"class":77},"'idle'",[67,7254,955],{"class":104},[67,7256,7257],{"class":69,"line":894},[67,7258,7259],{"class":104},"        type: String,\n",[67,7261,7262],{"class":69,"line":905},[67,7263,6447],{"class":104},[67,7265,7266],{"class":69,"line":916},[67,7267,1557],{"class":104},[67,7269,7270],{"class":69,"line":927},[67,7271,1596],{"class":104},[67,7273,7274,7276,7278],{"class":69,"line":935},[67,7275,6382],{"class":104},[67,7277,6397],{"class":739},[67,7279,6296],{"class":104},[58,7281,7283],{"className":6277,"code":7282,"language":6279,"meta":63,"style":63},"\u002F\u002Fform.vue\n\u003Ctemplate>\n  \u003Cform @submit.prevent=\"onSubmit\">\n  \u002F\u002F\n  \u003Cbutton-wrap :status=\"status\">\n    Submit\n  \u003C\u002Fbutton-wrap>\n  \u003C\u002Fform>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    data: () => ({\n      status: 'idle'\n    }),\n\n    methods: {\n      onSubmit() {\n        this.status = 'loading';\n\n        axios.post(url, data)\n          .then((response) => {\n            this.status = 'completed';\n          })\n          .catch((error) => {\n            this.status = 'rejected';\n          });\n          .finally(() => {\n            if(this.status !== 'rejected') {\n              this.status === 'idle';\n            }\n          }\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,7284,7285,7289,7297,7311,7315,7331,7335,7343,7351,7359,7363,7371,7379,7389,7397,7401,7405,7409,7415,7429,7433,7441,7457,7470,7474,7490,7503,7507,7519,7537,7551,7556,7560,7564,7568,7572],{"__ignoreMap":63},[67,7286,7287],{"class":69,"line":70},[67,7288,6734],{"class":104},[67,7290,7291,7293,7295],{"class":69,"line":251},[67,7292,5778],{"class":104},[67,7294,6293],{"class":739},[67,7296,6296],{"class":104},[67,7298,7299,7301,7303,7305,7307,7309],{"class":69,"line":264},[67,7300,6301],{"class":104},[67,7302,6749],{"class":739},[67,7304,6752],{"class":73},[67,7306,2586],{"class":104},[67,7308,6757],{"class":77},[67,7310,6296],{"class":104},[67,7312,7313],{"class":69,"line":280},[67,7314,6764],{"class":104},[67,7316,7317,7319,7321,7324,7326,7329],{"class":69,"line":288},[67,7318,6301],{"class":104},[67,7320,6469],{"class":739},[67,7322,7323],{"class":73}," :status",[67,7325,2586],{"class":104},[67,7327,7328],{"class":77},"\"status\"",[67,7330,6296],{"class":104},[67,7332,7333],{"class":69,"line":781},[67,7334,6805],{"class":104},[67,7336,7337,7339,7341],{"class":69,"line":792},[67,7338,6373],{"class":104},[67,7340,6469],{"class":739},[67,7342,6296],{"class":104},[67,7344,7345,7347,7349],{"class":69,"line":803},[67,7346,6373],{"class":104},[67,7348,6749],{"class":739},[67,7350,6296],{"class":104},[67,7352,7353,7355,7357],{"class":69,"line":814},[67,7354,6382],{"class":104},[67,7356,6293],{"class":739},[67,7358,6296],{"class":104},[67,7360,7361],{"class":69,"line":825},[67,7362,989],{"emptyLinePlaceholder":988},[67,7364,7365,7367,7369],{"class":69,"line":836},[67,7366,5778],{"class":104},[67,7368,6397],{"class":739},[67,7370,6296],{"class":104},[67,7372,7373,7375,7377],{"class":69,"line":847},[67,7374,6404],{"class":163},[67,7376,242],{"class":163},[67,7378,2500],{"class":104},[67,7380,7381,7383,7385,7387],{"class":69,"line":858},[67,7382,6854],{"class":73},[67,7384,6857],{"class":104},[67,7386,5500],{"class":163},[67,7388,6862],{"class":104},[67,7390,7391,7394],{"class":69,"line":869},[67,7392,7393],{"class":104},"      status: ",[67,7395,7396],{"class":77},"'idle'\n",[67,7398,7399],{"class":69,"line":877},[67,7400,6903],{"class":104},[67,7402,7403],{"class":69,"line":886},[67,7404,989],{"emptyLinePlaceholder":988},[67,7406,7407],{"class":69,"line":894},[67,7408,6912],{"class":104},[67,7410,7411,7413],{"class":69,"line":905},[67,7412,6917],{"class":73},[67,7414,2617],{"class":104},[67,7416,7417,7419,7422,7424,7427],{"class":69,"line":916},[67,7418,6924],{"class":113},[67,7420,7421],{"class":104},".status ",[67,7423,2586],{"class":163},[67,7425,7426],{"class":77}," 'loading'",[67,7428,2485],{"class":104},[67,7430,7431],{"class":69,"line":927},[67,7432,989],{"emptyLinePlaceholder":988},[67,7434,7435,7437,7439],{"class":69,"line":935},[67,7436,6943],{"class":104},[67,7438,5512],{"class":73},[67,7440,6948],{"class":104},[67,7442,7443,7445,7447,7449,7451,7453,7455],{"class":69,"line":958},[67,7444,6953],{"class":104},[67,7446,6956],{"class":73},[67,7448,6959],{"class":104},[67,7450,6962],{"class":2508},[67,7452,4280],{"class":104},[67,7454,5500],{"class":163},[67,7456,2500],{"class":104},[67,7458,7459,7461,7463,7465,7468],{"class":69,"line":971},[67,7460,6973],{"class":113},[67,7462,7421],{"class":104},[67,7464,2586],{"class":163},[67,7466,7467],{"class":77}," 'completed'",[67,7469,2485],{"class":104},[67,7471,7472],{"class":69,"line":985},[67,7473,6987],{"class":104},[67,7475,7476,7478,7480,7482,7484,7486,7488],{"class":69,"line":992},[67,7477,6953],{"class":104},[67,7479,6994],{"class":73},[67,7481,6959],{"class":104},[67,7483,6999],{"class":2508},[67,7485,4280],{"class":104},[67,7487,5500],{"class":163},[67,7489,2500],{"class":104},[67,7491,7492,7494,7496,7498,7501],{"class":69,"line":1000},[67,7493,6973],{"class":113},[67,7495,7421],{"class":104},[67,7497,2586],{"class":163},[67,7499,7500],{"class":77}," 'rejected'",[67,7502,2485],{"class":104},[67,7504,7505],{"class":69,"line":1009},[67,7506,7023],{"class":104},[67,7508,7509,7511,7513,7515,7517],{"class":69,"line":1016},[67,7510,6953],{"class":104},[67,7512,7030],{"class":73},[67,7514,7033],{"class":104},[67,7516,5500],{"class":163},[67,7518,2500],{"class":104},[67,7520,7521,7524,7526,7528,7530,7533,7535],{"class":69,"line":1025},[67,7522,7523],{"class":163},"            if",[67,7525,2556],{"class":104},[67,7527,3465],{"class":113},[67,7529,7421],{"class":104},[67,7531,7532],{"class":163},"!==",[67,7534,7500],{"class":77},[67,7536,2575],{"class":104},[67,7538,7539,7542,7544,7546,7549],{"class":69,"line":1034},[67,7540,7541],{"class":113},"              this",[67,7543,7421],{"class":104},[67,7545,3770],{"class":163},[67,7547,7548],{"class":77}," 'idle'",[67,7550,2485],{"class":104},[67,7552,7553],{"class":69,"line":1043},[67,7554,7555],{"class":104},"            }\n",[67,7557,7558],{"class":69,"line":1054},[67,7559,7055],{"class":104},[67,7561,7562],{"class":69,"line":1061},[67,7563,6447],{"class":104},[67,7565,7566],{"class":69,"line":1068},[67,7567,1557],{"class":104},[67,7569,7570],{"class":69,"line":1075},[67,7571,1596],{"class":104},[67,7573,7574,7576,7578],{"class":69,"line":1085},[67,7575,6382],{"class":163},[67,7577,6397],{"class":104},[67,7579,6296],{"class":163},[11,7581,7582,7583,7585,7586,7588],{},"What would happen if we want to introduce a new state? What if we want to change the ",[53,7584,6259],{}," class to ",[53,7587,6999],{},"?",[1758,7590,7592],{"id":7591},"handling-component-state-using-a-status-class","Handling component state using a Status class",[11,7594,7595],{},"Nothing prevents us from using JavaScript classes within our Vue components. When abstracting functionality, I always try to find the most pragmatic approach. We can achieve this by combining the best parts of both approaches above. Let's build the Status class now.",[58,7597,7601],{"className":7598,"code":7599,"language":7600,"meta":63,"style":63},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F classes\u002FStatus.js\n\nexport const STATUS = {\n  IDLE: \"idle\",\n  LOADING: \"loading\",\n  COMPLETED: \"completed\",\n  REJECTED: \"rejected\",\n};\n\nexport default class Status {\n  constructor(status = STATUS.IDLE) {\n    this.status = status;\n  }\n\n  get() {\n    return this.status;\n  }\n\n  set(status) {\n    this.status = status;\n  }\n\n  is(status) {\n    return this.status === status;\n  }\n\n  isLoading() {\n    return this.is(STATUS.LOADING);\n  }\n\n  isNotLoading() {\n    return !this.is(STATUS.LOADING);\n  }\n\n  isCompleted() {\n    return this.is(STATUS.COMPLETED);\n  }\n\n  isIdle() {\n    return this.is(STATUS.IDLE);\n  }\n\n  isRejected() {\n    return this.is(STATUS.REJECTED);\n  }\n\n  setLoading() {\n    this.set(STATUS.LOADING);\n  }\n\n  setCompleted() {\n    this.set(STATUS.COMPLETED);\n  }\n\n  setRejected() {\n    this.set(STATUS.REJECTED);\n  }\n\n  setIdle() {\n    this.set(STATUS.IDLE);\n  }\n\n  toggle() {\n    this.set(this.status === STATUS.IDLE ? STATUS.LOADING : STATUS.IDLE);\n  }\n\n  toString() {\n    return this.get();\n  }\n\n  valueOf() {\n    return this.get();\n  }\n}\n","javascript",[53,7602,7603,7608,7612,7626,7636,7646,7656,7666,7670,7674,7687,7706,7717,7721,7725,7732,7741,7745,7749,7760,7770,7774,7778,7789,7801,7805,7809,7816,7839,7843,7847,7854,7877,7881,7885,7892,7913,7917,7921,7928,7948,7952,7956,7963,7984,7988,7992,7999,8018,8022,8026,8033,8051,8055,8059,8066,8085,8090,8095,8103,8122,8127,8132,8140,8183,8188,8193,8201,8214,8219,8224,8232,8245,8250],{"__ignoreMap":63},[67,7604,7605],{"class":69,"line":70},[67,7606,7607],{"class":3401},"\u002F\u002F classes\u002FStatus.js\n",[67,7609,7610],{"class":69,"line":251},[67,7611,989],{"emptyLinePlaceholder":988},[67,7613,7614,7616,7619,7622,7624],{"class":69,"line":264},[67,7615,5473],{"class":163},[67,7617,7618],{"class":163}," const",[67,7620,7621],{"class":113}," STATUS",[67,7623,2512],{"class":163},[67,7625,2500],{"class":104},[67,7627,7628,7631,7634],{"class":69,"line":280},[67,7629,7630],{"class":104},"  IDLE: ",[67,7632,7633],{"class":77},"\"idle\"",[67,7635,955],{"class":104},[67,7637,7638,7641,7644],{"class":69,"line":288},[67,7639,7640],{"class":104},"  LOADING: ",[67,7642,7643],{"class":77},"\"loading\"",[67,7645,955],{"class":104},[67,7647,7648,7651,7654],{"class":69,"line":781},[67,7649,7650],{"class":104},"  COMPLETED: ",[67,7652,7653],{"class":77},"\"completed\"",[67,7655,955],{"class":104},[67,7657,7658,7661,7664],{"class":69,"line":792},[67,7659,7660],{"class":104},"  REJECTED: ",[67,7662,7663],{"class":77},"\"rejected\"",[67,7665,955],{"class":104},[67,7667,7668],{"class":69,"line":803},[67,7669,5573],{"class":104},[67,7671,7672],{"class":69,"line":814},[67,7673,989],{"emptyLinePlaceholder":988},[67,7675,7676,7678,7680,7682,7685],{"class":69,"line":825},[67,7677,5473],{"class":163},[67,7679,242],{"class":163},[67,7681,4390],{"class":163},[67,7683,7684],{"class":73}," Status",[67,7686,2500],{"class":104},[67,7688,7689,7691,7693,7695,7697,7699,7701,7704],{"class":69,"line":836},[67,7690,2553],{"class":163},[67,7692,2556],{"class":104},[67,7694,7092],{"class":2508},[67,7696,2512],{"class":163},[67,7698,7621],{"class":113},[67,7700,56],{"class":104},[67,7702,7703],{"class":113},"IDLE",[67,7705,2575],{"class":104},[67,7707,7708,7710,7712,7714],{"class":69,"line":847},[67,7709,2580],{"class":113},[67,7711,7421],{"class":104},[67,7713,2586],{"class":163},[67,7715,7716],{"class":104}," status;\n",[67,7718,7719],{"class":69,"line":858},[67,7720,1596],{"class":104},[67,7722,7723],{"class":69,"line":869},[67,7724,989],{"emptyLinePlaceholder":988},[67,7726,7727,7730],{"class":69,"line":877},[67,7728,7729],{"class":73},"  get",[67,7731,2617],{"class":104},[67,7733,7734,7736,7738],{"class":69,"line":886},[67,7735,2674],{"class":163},[67,7737,2644],{"class":113},[67,7739,7740],{"class":104},".status;\n",[67,7742,7743],{"class":69,"line":894},[67,7744,1596],{"class":104},[67,7746,7747],{"class":69,"line":905},[67,7748,989],{"emptyLinePlaceholder":988},[67,7750,7751,7754,7756,7758],{"class":69,"line":916},[67,7752,7753],{"class":73},"  set",[67,7755,2556],{"class":104},[67,7757,7092],{"class":2508},[67,7759,2575],{"class":104},[67,7761,7762,7764,7766,7768],{"class":69,"line":927},[67,7763,2580],{"class":113},[67,7765,7421],{"class":104},[67,7767,2586],{"class":163},[67,7769,7716],{"class":104},[67,7771,7772],{"class":69,"line":935},[67,7773,1596],{"class":104},[67,7775,7776],{"class":69,"line":958},[67,7777,989],{"emptyLinePlaceholder":988},[67,7779,7780,7783,7785,7787],{"class":69,"line":971},[67,7781,7782],{"class":73},"  is",[67,7784,2556],{"class":104},[67,7786,7092],{"class":2508},[67,7788,2575],{"class":104},[67,7790,7791,7793,7795,7797,7799],{"class":69,"line":985},[67,7792,2674],{"class":163},[67,7794,2644],{"class":113},[67,7796,7421],{"class":104},[67,7798,3770],{"class":163},[67,7800,7716],{"class":104},[67,7802,7803],{"class":69,"line":992},[67,7804,1596],{"class":104},[67,7806,7807],{"class":69,"line":1000},[67,7808,989],{"emptyLinePlaceholder":988},[67,7810,7811,7814],{"class":69,"line":1009},[67,7812,7813],{"class":73},"  isLoading",[67,7815,2617],{"class":104},[67,7817,7818,7820,7822,7824,7827,7829,7832,7834,7837],{"class":69,"line":1016},[67,7819,2674],{"class":163},[67,7821,2644],{"class":113},[67,7823,56],{"class":104},[67,7825,7826],{"class":73},"is",[67,7828,2556],{"class":104},[67,7830,7831],{"class":113},"STATUS",[67,7833,56],{"class":104},[67,7835,7836],{"class":113},"LOADING",[67,7838,2745],{"class":104},[67,7840,7841],{"class":69,"line":1025},[67,7842,1596],{"class":104},[67,7844,7845],{"class":69,"line":1034},[67,7846,989],{"emptyLinePlaceholder":988},[67,7848,7849,7852],{"class":69,"line":1043},[67,7850,7851],{"class":73},"  isNotLoading",[67,7853,2617],{"class":104},[67,7855,7856,7858,7861,7863,7865,7867,7869,7871,7873,7875],{"class":69,"line":1054},[67,7857,2674],{"class":163},[67,7859,7860],{"class":163}," !",[67,7862,3465],{"class":113},[67,7864,56],{"class":104},[67,7866,7826],{"class":73},[67,7868,2556],{"class":104},[67,7870,7831],{"class":113},[67,7872,56],{"class":104},[67,7874,7836],{"class":113},[67,7876,2745],{"class":104},[67,7878,7879],{"class":69,"line":1061},[67,7880,1596],{"class":104},[67,7882,7883],{"class":69,"line":1068},[67,7884,989],{"emptyLinePlaceholder":988},[67,7886,7887,7890],{"class":69,"line":1075},[67,7888,7889],{"class":73},"  isCompleted",[67,7891,2617],{"class":104},[67,7893,7894,7896,7898,7900,7902,7904,7906,7908,7911],{"class":69,"line":1085},[67,7895,2674],{"class":163},[67,7897,2644],{"class":113},[67,7899,56],{"class":104},[67,7901,7826],{"class":73},[67,7903,2556],{"class":104},[67,7905,7831],{"class":113},[67,7907,56],{"class":104},[67,7909,7910],{"class":113},"COMPLETED",[67,7912,2745],{"class":104},[67,7914,7915],{"class":69,"line":1095},[67,7916,1596],{"class":104},[67,7918,7919],{"class":69,"line":1104},[67,7920,989],{"emptyLinePlaceholder":988},[67,7922,7923,7926],{"class":69,"line":3085},[67,7924,7925],{"class":73},"  isIdle",[67,7927,2617],{"class":104},[67,7929,7930,7932,7934,7936,7938,7940,7942,7944,7946],{"class":69,"line":3092},[67,7931,2674],{"class":163},[67,7933,2644],{"class":113},[67,7935,56],{"class":104},[67,7937,7826],{"class":73},[67,7939,2556],{"class":104},[67,7941,7831],{"class":113},[67,7943,56],{"class":104},[67,7945,7703],{"class":113},[67,7947,2745],{"class":104},[67,7949,7950],{"class":69,"line":3108},[67,7951,1596],{"class":104},[67,7953,7954],{"class":69,"line":3113},[67,7955,989],{"emptyLinePlaceholder":988},[67,7957,7958,7961],{"class":69,"line":3118},[67,7959,7960],{"class":73},"  isRejected",[67,7962,2617],{"class":104},[67,7964,7965,7967,7969,7971,7973,7975,7977,7979,7982],{"class":69,"line":3137},[67,7966,2674],{"class":163},[67,7968,2644],{"class":113},[67,7970,56],{"class":104},[67,7972,7826],{"class":73},[67,7974,2556],{"class":104},[67,7976,7831],{"class":113},[67,7978,56],{"class":104},[67,7980,7981],{"class":113},"REJECTED",[67,7983,2745],{"class":104},[67,7985,7986],{"class":69,"line":3154},[67,7987,1596],{"class":104},[67,7989,7990],{"class":69,"line":3170},[67,7991,989],{"emptyLinePlaceholder":988},[67,7993,7994,7997],{"class":69,"line":3187},[67,7995,7996],{"class":73},"  setLoading",[67,7998,2617],{"class":104},[67,8000,8001,8003,8005,8008,8010,8012,8014,8016],{"class":69,"line":3192},[67,8002,2580],{"class":113},[67,8004,56],{"class":104},[67,8006,8007],{"class":73},"set",[67,8009,2556],{"class":104},[67,8011,7831],{"class":113},[67,8013,56],{"class":104},[67,8015,7836],{"class":113},[67,8017,2745],{"class":104},[67,8019,8020],{"class":69,"line":3208},[67,8021,1596],{"class":104},[67,8023,8024],{"class":69,"line":3213},[67,8025,989],{"emptyLinePlaceholder":988},[67,8027,8028,8031],{"class":69,"line":3218},[67,8029,8030],{"class":73},"  setCompleted",[67,8032,2617],{"class":104},[67,8034,8035,8037,8039,8041,8043,8045,8047,8049],{"class":69,"line":3223},[67,8036,2580],{"class":113},[67,8038,56],{"class":104},[67,8040,8007],{"class":73},[67,8042,2556],{"class":104},[67,8044,7831],{"class":113},[67,8046,56],{"class":104},[67,8048,7910],{"class":113},[67,8050,2745],{"class":104},[67,8052,8053],{"class":69,"line":3239},[67,8054,1596],{"class":104},[67,8056,8057],{"class":69,"line":3262},[67,8058,989],{"emptyLinePlaceholder":988},[67,8060,8061,8064],{"class":69,"line":3267},[67,8062,8063],{"class":73},"  setRejected",[67,8065,2617],{"class":104},[67,8067,8069,8071,8073,8075,8077,8079,8081,8083],{"class":69,"line":8068},56,[67,8070,2580],{"class":113},[67,8072,56],{"class":104},[67,8074,8007],{"class":73},[67,8076,2556],{"class":104},[67,8078,7831],{"class":113},[67,8080,56],{"class":104},[67,8082,7981],{"class":113},[67,8084,2745],{"class":104},[67,8086,8088],{"class":69,"line":8087},57,[67,8089,1596],{"class":104},[67,8091,8093],{"class":69,"line":8092},58,[67,8094,989],{"emptyLinePlaceholder":988},[67,8096,8098,8101],{"class":69,"line":8097},59,[67,8099,8100],{"class":73},"  setIdle",[67,8102,2617],{"class":104},[67,8104,8106,8108,8110,8112,8114,8116,8118,8120],{"class":69,"line":8105},60,[67,8107,2580],{"class":113},[67,8109,56],{"class":104},[67,8111,8007],{"class":73},[67,8113,2556],{"class":104},[67,8115,7831],{"class":113},[67,8117,56],{"class":104},[67,8119,7703],{"class":113},[67,8121,2745],{"class":104},[67,8123,8125],{"class":69,"line":8124},61,[67,8126,1596],{"class":104},[67,8128,8130],{"class":69,"line":8129},62,[67,8131,989],{"emptyLinePlaceholder":988},[67,8133,8135,8138],{"class":69,"line":8134},63,[67,8136,8137],{"class":73},"  toggle",[67,8139,2617],{"class":104},[67,8141,8143,8145,8147,8149,8151,8153,8155,8157,8159,8161,8163,8166,8168,8170,8172,8175,8177,8179,8181],{"class":69,"line":8142},64,[67,8144,2580],{"class":113},[67,8146,56],{"class":104},[67,8148,8007],{"class":73},[67,8150,2556],{"class":104},[67,8152,3465],{"class":113},[67,8154,7421],{"class":104},[67,8156,3770],{"class":163},[67,8158,7621],{"class":113},[67,8160,56],{"class":104},[67,8162,7703],{"class":113},[67,8164,8165],{"class":163}," ?",[67,8167,7621],{"class":113},[67,8169,56],{"class":104},[67,8171,7836],{"class":113},[67,8173,8174],{"class":163}," :",[67,8176,7621],{"class":113},[67,8178,56],{"class":104},[67,8180,7703],{"class":113},[67,8182,2745],{"class":104},[67,8184,8186],{"class":69,"line":8185},65,[67,8187,1596],{"class":104},[67,8189,8191],{"class":69,"line":8190},66,[67,8192,989],{"emptyLinePlaceholder":988},[67,8194,8196,8199],{"class":69,"line":8195},67,[67,8197,8198],{"class":73},"  toString",[67,8200,2617],{"class":104},[67,8202,8204,8206,8208,8210,8212],{"class":69,"line":8203},68,[67,8205,2674],{"class":163},[67,8207,2644],{"class":113},[67,8209,56],{"class":104},[67,8211,3724],{"class":73},[67,8213,2756],{"class":104},[67,8215,8217],{"class":69,"line":8216},69,[67,8218,1596],{"class":104},[67,8220,8222],{"class":69,"line":8221},70,[67,8223,989],{"emptyLinePlaceholder":988},[67,8225,8227,8230],{"class":69,"line":8226},71,[67,8228,8229],{"class":73},"  valueOf",[67,8231,2617],{"class":104},[67,8233,8235,8237,8239,8241,8243],{"class":69,"line":8234},72,[67,8236,2674],{"class":163},[67,8238,2644],{"class":113},[67,8240,56],{"class":104},[67,8242,3724],{"class":73},[67,8244,2756],{"class":104},[67,8246,8248],{"class":69,"line":8247},73,[67,8249,1596],{"class":104},[67,8251,8253],{"class":69,"line":8252},74,[67,8254,1601],{"class":104},[11,8256,8257,8258,2004,8260,8262],{},"The class is pretty straightforward, there is no complicated logic, just simple ",[53,8259,8007],{},[53,8261,3724],{}," methods for all of the states we can have. Let's now integrate this class in our simple form example:",[58,8264,8266],{"className":6277,"code":8265,"language":6279,"meta":63,"style":63},"\u002F\u002F button-wrap.vue\n\u003Ctemplate>\n  \u003Cbutton v-bind=\"$attrs\" v-on=\"$listeners\" :disabled=\"status.isLoading()\"\n  :class=\"{'is-valid': status.isCompleted(), 'is-rejected': status.isRejected()}\">\n    \u003Cspan v-if=\"status.isLoading()\">Loading ...\u003C\u002Fspan>\n    \u003Cslot v-else-if=\"status.isIdle() || status.isCompleted()\">\u003C\u002Fslot>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  import Status from '~\u002Fclasses\u002FStatus';\n\n  export default {\n    props: {\n      status: {\n        required: false,\n        default: () => new Status(),\n        type: Object,\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,8267,8268,8272,8280,8305,8317,8336,8355,8363,8371,8375,8383,8398,8402,8410,8414,8418,8426,8442,8447,8451,8455,8459],{"__ignoreMap":63},[67,8269,8270],{"class":69,"line":70},[67,8271,6286],{"class":104},[67,8273,8274,8276,8278],{"class":69,"line":251},[67,8275,5778],{"class":104},[67,8277,6293],{"class":739},[67,8279,6296],{"class":104},[67,8281,8282,8284,8286,8288,8290,8292,8294,8296,8298,8300,8302],{"class":69,"line":264},[67,8283,6301],{"class":104},[67,8285,6304],{"class":739},[67,8287,6307],{"class":73},[67,8289,2586],{"class":104},[67,8291,6312],{"class":77},[67,8293,6315],{"class":73},[67,8295,2586],{"class":104},[67,8297,6320],{"class":77},[67,8299,6323],{"class":73},[67,8301,2586],{"class":104},[67,8303,8304],{"class":77},"\"status.isLoading()\"\n",[67,8306,8307,8310,8312,8315],{"class":69,"line":280},[67,8308,8309],{"class":73},"  :class",[67,8311,2586],{"class":104},[67,8313,8314],{"class":77},"\"{'is-valid': status.isCompleted(), 'is-rejected': status.isRejected()}\"",[67,8316,6296],{"class":104},[67,8318,8319,8321,8323,8325,8327,8330,8332,8334],{"class":69,"line":288},[67,8320,6335],{"class":104},[67,8322,67],{"class":739},[67,8324,6340],{"class":73},[67,8326,2586],{"class":104},[67,8328,8329],{"class":77},"\"status.isLoading()\"",[67,8331,6347],{"class":104},[67,8333,67],{"class":739},[67,8335,6296],{"class":104},[67,8337,8338,8340,8342,8344,8346,8349,8351,8353],{"class":69,"line":781},[67,8339,6335],{"class":104},[67,8341,6358],{"class":739},[67,8343,6571],{"class":73},[67,8345,2586],{"class":104},[67,8347,8348],{"class":77},"\"status.isIdle() || status.isCompleted()\"",[67,8350,6364],{"class":104},[67,8352,6358],{"class":739},[67,8354,6296],{"class":104},[67,8356,8357,8359,8361],{"class":69,"line":792},[67,8358,6373],{"class":104},[67,8360,6304],{"class":739},[67,8362,6296],{"class":104},[67,8364,8365,8367,8369],{"class":69,"line":803},[67,8366,6382],{"class":104},[67,8368,6293],{"class":739},[67,8370,6296],{"class":104},[67,8372,8373],{"class":69,"line":814},[67,8374,989],{"emptyLinePlaceholder":988},[67,8376,8377,8379,8381],{"class":69,"line":825},[67,8378,5778],{"class":104},[67,8380,6397],{"class":739},[67,8382,6296],{"class":104},[67,8384,8385,8388,8391,8393,8396],{"class":69,"line":836},[67,8386,8387],{"class":163},"  import",[67,8389,8390],{"class":104}," Status ",[67,8392,2479],{"class":163},[67,8394,8395],{"class":77}," '~\u002Fclasses\u002FStatus'",[67,8397,2485],{"class":104},[67,8399,8400],{"class":69,"line":847},[67,8401,989],{"emptyLinePlaceholder":988},[67,8403,8404,8406,8408],{"class":69,"line":858},[67,8405,6404],{"class":163},[67,8407,242],{"class":163},[67,8409,2500],{"class":104},[67,8411,8412],{"class":69,"line":869},[67,8413,6413],{"class":104},[67,8415,8416],{"class":69,"line":877},[67,8417,6867],{"class":104},[67,8419,8420,8422,8424],{"class":69,"line":886},[67,8421,6423],{"class":104},[67,8423,6426],{"class":113},[67,8425,955],{"class":104},[67,8427,8428,8431,8433,8435,8437,8439],{"class":69,"line":894},[67,8429,8430],{"class":73},"        default",[67,8432,6857],{"class":104},[67,8434,5500],{"class":163},[67,8436,2730],{"class":163},[67,8438,7684],{"class":73},[67,8440,8441],{"class":104},"(),\n",[67,8443,8444],{"class":69,"line":905},[67,8445,8446],{"class":104},"        type: Object,\n",[67,8448,8449],{"class":69,"line":916},[67,8450,6447],{"class":104},[67,8452,8453],{"class":69,"line":927},[67,8454,1557],{"class":104},[67,8456,8457],{"class":69,"line":935},[67,8458,1596],{"class":104},[67,8460,8461,8463,8465],{"class":69,"line":958},[67,8462,6382],{"class":104},[67,8464,6397],{"class":739},[67,8466,6296],{"class":104},[58,8468,8470],{"className":6277,"code":8469,"language":6279,"meta":63,"style":63},"\u002F\u002Fform.vue\n\u003Ctemplate>\n  \u003Cform @submit.prevent=\"onSubmit\">\n  \u002F\u002F\n  \u003Cbutton-wrap :status=\"status\">\n    Submit\n  \u003C\u002Fbutton-wrap>\n  \u003C\u002Fform>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport Status from '~\u002Fclasses\u002FStatus';\n\nexport default {\n  data: () => ({\n    status: new Status()\n  }),\n\n  methods: {\n    onSubmit() {\n      this.status.toggle();\n\n      axios.post(url, data)\n        .then((response) => {\n          this.status.setCompleted();\n        })\n        .catch((error) => {\n          this.status.setRejected();\n        });\n        .finally(() => {\n          if(this.status.isNotRejected()) {\n            this.status.toggle();\n          }\n        }\n    }\n  }\n}\n\u003C\u002Fscript>\n",[53,8471,8472,8476,8484,8498,8502,8516,8520,8528,8536,8544,8548,8556,8568,8572,8580,8591,8604,8609,8613,8618,8625,8638,8642,8651,8668,8680,8685,8701,8712,8717,8729,8746,8756,8760,8765,8769,8773,8777],{"__ignoreMap":63},[67,8473,8474],{"class":69,"line":70},[67,8475,6734],{"class":104},[67,8477,8478,8480,8482],{"class":69,"line":251},[67,8479,5778],{"class":104},[67,8481,6293],{"class":739},[67,8483,6296],{"class":104},[67,8485,8486,8488,8490,8492,8494,8496],{"class":69,"line":264},[67,8487,6301],{"class":104},[67,8489,6749],{"class":739},[67,8491,6752],{"class":73},[67,8493,2586],{"class":104},[67,8495,6757],{"class":77},[67,8497,6296],{"class":104},[67,8499,8500],{"class":69,"line":280},[67,8501,6764],{"class":104},[67,8503,8504,8506,8508,8510,8512,8514],{"class":69,"line":288},[67,8505,6301],{"class":104},[67,8507,6469],{"class":739},[67,8509,7323],{"class":73},[67,8511,2586],{"class":104},[67,8513,7328],{"class":77},[67,8515,6296],{"class":104},[67,8517,8518],{"class":69,"line":781},[67,8519,6805],{"class":104},[67,8521,8522,8524,8526],{"class":69,"line":792},[67,8523,6373],{"class":104},[67,8525,6469],{"class":739},[67,8527,6296],{"class":104},[67,8529,8530,8532,8534],{"class":69,"line":803},[67,8531,6373],{"class":104},[67,8533,6749],{"class":739},[67,8535,6296],{"class":104},[67,8537,8538,8540,8542],{"class":69,"line":814},[67,8539,6382],{"class":104},[67,8541,6293],{"class":739},[67,8543,6296],{"class":104},[67,8545,8546],{"class":69,"line":825},[67,8547,989],{"emptyLinePlaceholder":988},[67,8549,8550,8552,8554],{"class":69,"line":836},[67,8551,5778],{"class":104},[67,8553,6397],{"class":739},[67,8555,6296],{"class":104},[67,8557,8558,8560,8562,8564,8566],{"class":69,"line":847},[67,8559,2473],{"class":163},[67,8561,8390],{"class":104},[67,8563,2479],{"class":163},[67,8565,8395],{"class":77},[67,8567,2485],{"class":104},[67,8569,8570],{"class":69,"line":858},[67,8571,989],{"emptyLinePlaceholder":988},[67,8573,8574,8576,8578],{"class":69,"line":869},[67,8575,5473],{"class":163},[67,8577,242],{"class":163},[67,8579,2500],{"class":104},[67,8581,8582,8585,8587,8589],{"class":69,"line":877},[67,8583,8584],{"class":73},"  data",[67,8586,6857],{"class":104},[67,8588,5500],{"class":163},[67,8590,6862],{"class":104},[67,8592,8593,8596,8599,8601],{"class":69,"line":886},[67,8594,8595],{"class":104},"    status: ",[67,8597,8598],{"class":163},"new",[67,8600,7684],{"class":73},[67,8602,8603],{"class":104},"()\n",[67,8605,8606],{"class":69,"line":894},[67,8607,8608],{"class":104},"  }),\n",[67,8610,8611],{"class":69,"line":905},[67,8612,989],{"emptyLinePlaceholder":988},[67,8614,8615],{"class":69,"line":916},[67,8616,8617],{"class":104},"  methods: {\n",[67,8619,8620,8623],{"class":69,"line":927},[67,8621,8622],{"class":73},"    onSubmit",[67,8624,2617],{"class":104},[67,8626,8627,8630,8633,8636],{"class":69,"line":935},[67,8628,8629],{"class":113},"      this",[67,8631,8632],{"class":104},".status.",[67,8634,8635],{"class":73},"toggle",[67,8637,2756],{"class":104},[67,8639,8640],{"class":69,"line":958},[67,8641,989],{"emptyLinePlaceholder":988},[67,8643,8644,8647,8649],{"class":69,"line":971},[67,8645,8646],{"class":104},"      axios.",[67,8648,5512],{"class":73},[67,8650,6948],{"class":104},[67,8652,8653,8656,8658,8660,8662,8664,8666],{"class":69,"line":985},[67,8654,8655],{"class":104},"        .",[67,8657,6956],{"class":73},[67,8659,6959],{"class":104},[67,8661,6962],{"class":2508},[67,8663,4280],{"class":104},[67,8665,5500],{"class":163},[67,8667,2500],{"class":104},[67,8669,8670,8673,8675,8678],{"class":69,"line":992},[67,8671,8672],{"class":113},"          this",[67,8674,8632],{"class":104},[67,8676,8677],{"class":73},"setCompleted",[67,8679,2756],{"class":104},[67,8681,8682],{"class":69,"line":1000},[67,8683,8684],{"class":104},"        })\n",[67,8686,8687,8689,8691,8693,8695,8697,8699],{"class":69,"line":1009},[67,8688,8655],{"class":104},[67,8690,6994],{"class":73},[67,8692,6959],{"class":104},[67,8694,6999],{"class":2508},[67,8696,4280],{"class":104},[67,8698,5500],{"class":163},[67,8700,2500],{"class":104},[67,8702,8703,8705,8707,8710],{"class":69,"line":1016},[67,8704,8672],{"class":113},[67,8706,8632],{"class":104},[67,8708,8709],{"class":73},"setRejected",[67,8711,2756],{"class":104},[67,8713,8714],{"class":69,"line":1025},[67,8715,8716],{"class":104},"        });\n",[67,8718,8719,8721,8723,8725,8727],{"class":69,"line":1034},[67,8720,8655],{"class":104},[67,8722,7030],{"class":73},[67,8724,7033],{"class":104},[67,8726,5500],{"class":163},[67,8728,2500],{"class":104},[67,8730,8731,8734,8736,8738,8740,8743],{"class":69,"line":1043},[67,8732,8733],{"class":163},"          if",[67,8735,2556],{"class":104},[67,8737,3465],{"class":113},[67,8739,8632],{"class":104},[67,8741,8742],{"class":73},"isNotRejected",[67,8744,8745],{"class":104},"()) {\n",[67,8747,8748,8750,8752,8754],{"class":69,"line":1054},[67,8749,6973],{"class":113},[67,8751,8632],{"class":104},[67,8753,8635],{"class":73},[67,8755,2756],{"class":104},[67,8757,8758],{"class":69,"line":1061},[67,8759,7055],{"class":104},[67,8761,8762],{"class":69,"line":1068},[67,8763,8764],{"class":104},"        }\n",[67,8766,8767],{"class":69,"line":1075},[67,8768,1557],{"class":104},[67,8770,8771],{"class":69,"line":1085},[67,8772,1596],{"class":104},[67,8774,8775],{"class":69,"line":1095},[67,8776,1601],{"class":104},[67,8778,8779,8781,8783],{"class":69,"line":1104},[67,8780,6382],{"class":163},[67,8782,6397],{"class":104},[67,8784,6296],{"class":163},[11,8786,8787,8788,8791,8792,8794],{},"If you are a ",[1738,8789,8790],{},"clean code"," freak like me, this code looks pretty dope. Now, you can use the ",[53,8793,6266],{}," class anywhere and for different cases like fetching data from an API, submitting a form, toggling sates based on user actions, showing UI skeletons while prefetching data, etc.",[6201,8796],{},[11,8798,8799,8800,8803,8804,8809],{},"You can also use HOC (High order components) to abstract the state logic and use that within your components, but eventually, it boils down to the first\u002Fsecond scenario that I told you about in this post. This approach is framework agnostic, meaning that you can also use it in your React apps. You can always use a ",[21,8801,8802],{},"state machine"," like ",[1711,8805,8808],{"href":8806,"rel":8807},"https:\u002F\u002Fxstate.js.org\u002F",[1715],"xstate"," but for simple logic, I find my approach way easier and more convenient that importing a whole new library. Thanks for reading. I will update this series with another post soon.",[1670,8811,8812],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":63,"searchDepth":251,"depth":251,"links":8814},[8815,8816,8817],{"id":6270,"depth":264,"text":6271},{"id":7081,"depth":264,"text":7082},{"id":7591,"depth":264,"text":7592},"JavaScript","2020\u002F05\u002F09","\u002Fimages\u002Fis-loading.png","vue, vue.js, react, status, state, component state, state booleans, is loading, loading, loading boolean",{},"\u002Fblog\u002Fhandling-different-component-states-with-a-status-class","☕️ 7 min read",{"title":6238,"description":6243},"handling-different-component-states-with-a-status-class","blog\u002Fhandling-different-component-states-with-a-status-class","vue components, advanced vue, vue, javascript","d7m_uAebX5iydSEVSgOef0iK1chHy05HUBpeQPEieNg",{"id":8831,"title":8832,"body":8833,"category":10129,"date":10130,"description":10131,"excerpt":1690,"extension":1691,"image":10132,"keywords":10133,"meta":10134,"navigation":988,"path":10135,"readingTime":10136,"seo":10137,"slug":10138,"stem":10139,"tags":10140,"__hash__":10141},"blog\u002Fblog\u002Fconvenient-way-to-use-layouts-in-inertia.md","Convenient way to use layouts in Inertia",{"type":8,"value":8834,"toc":10124},[8835,8848,8851,8855,8873,9016,9033,9037,9040,9220,9223,9258,9275,9287,9296,9421,9424,9431,9492,9548,9551,9555,9558,9589,9592,9691,9694,9697,9790,9793,9802,9879,9885,9888,10109,10111,10118,10121],[11,8836,8837,8838,8843,8844,8847],{},"If you are a Laravel developer, you have probably heard of ",[1711,8839,8842],{"href":8840,"rel":8841},"https:\u002F\u002Finertiajs.com\u002F",[1715],"Inertia.js",", the modern monolith. With Inertia, you can ",[1738,8845,8846],{},"quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers."," This makes Inertia a perfect choice for building some web apps.",[11,8849,8850],{},"Inertia gained a lot of traction in the past few months, so I have decided to give it a shot and build something with it. Installing and configuring it was easy, the documentation is awesome and I had no difficulties setting up my project. It took me a little bit of time to adapt to the mental model of how Inertia works, but after wrapping my head around it I started building the app.",[1758,8852,8854],{"id":8853},"building-my-first-page-and-the-need-for-a-layout","Building my first page and the need for a layout",[11,8856,8857,8858,8861,8862,8865,8866,8868,8869,8872],{},"Every app you have built has a layout that you wrap around your pages. Usually, the Layout has the ",[53,8859,8860],{},"Header"," the ",[53,8863,8864],{},"Footer"," and a ",[53,8867,6358],{}," (or ",[53,8870,8871],{},"router-view",") tag where you insert the content based on the route you are in. In the documentation, there is a straightforward example of a page, which you can also see attached in the code below.",[58,8874,8878],{"className":8875,"code":8876,"language":8877,"meta":63,"style":63},"language-js shiki shiki-themes github-light github-dark","  \u003Ctemplate>\n    \u003Clayout title=\"Welcome\">\n      \u003Ch1>Welcome\u003C\u002Fh1>\n      \u003Cp>Hello {{ user.name }}, welcome to your first Inertia app!\u003C\u002Fp>\n    \u003C\u002Flayout>\n  \u003C\u002Ftemplate>\n\n  \u003Cscript>\n    import Layout from '.\u002FLayout'\n\n    export default {\n      components: {\n        Layout,\n      },\n      props: {\n        user: Object,\n      },\n    }\n  \u003C\u002Fscript>\n","js",[53,8879,8880,8888,8905,8920,8933,8942,8950,8954,8962,8967,8971,8976,8981,8986,8990,8995,9000,9004,9008],{"__ignoreMap":63},[67,8881,8882,8884,8886],{"class":69,"line":70},[67,8883,6301],{"class":104},[67,8885,6293],{"class":739},[67,8887,6296],{"class":104},[67,8889,8890,8892,8895,8898,8900,8903],{"class":69,"line":251},[67,8891,6335],{"class":104},[67,8893,8894],{"class":739},"layout",[67,8896,8897],{"class":73}," title",[67,8899,2586],{"class":163},[67,8901,8902],{"class":77},"\"Welcome\"",[67,8904,6296],{"class":104},[67,8906,8907,8910,8913,8916,8918],{"class":69,"line":264},[67,8908,8909],{"class":104},"      \u003C",[67,8911,8912],{"class":739},"h1",[67,8914,8915],{"class":104},">Welcome\u003C\u002F",[67,8917,8912],{"class":739},[67,8919,6296],{"class":104},[67,8921,8922,8924,8926,8929,8931],{"class":69,"line":280},[67,8923,8909],{"class":104},[67,8925,11],{"class":739},[67,8927,8928],{"class":104},">Hello {{ user.name }}, welcome to your first Inertia app!\u003C\u002F",[67,8930,11],{"class":739},[67,8932,6296],{"class":104},[67,8934,8935,8938,8940],{"class":69,"line":288},[67,8936,8937],{"class":104},"    \u003C\u002F",[67,8939,8894],{"class":739},[67,8941,6296],{"class":104},[67,8943,8944,8946,8948],{"class":69,"line":781},[67,8945,6373],{"class":104},[67,8947,6293],{"class":739},[67,8949,6296],{"class":104},[67,8951,8952],{"class":69,"line":792},[67,8953,989],{"emptyLinePlaceholder":988},[67,8955,8956,8958,8960],{"class":69,"line":803},[67,8957,6301],{"class":104},[67,8959,6397],{"class":739},[67,8961,6296],{"class":104},[67,8963,8964],{"class":69,"line":814},[67,8965,8966],{"class":104},"    import Layout from '.\u002FLayout'\n",[67,8968,8969],{"class":69,"line":825},[67,8970,989],{"emptyLinePlaceholder":988},[67,8972,8973],{"class":69,"line":836},[67,8974,8975],{"class":104},"    export default {\n",[67,8977,8978],{"class":69,"line":847},[67,8979,8980],{"class":104},"      components: {\n",[67,8982,8983],{"class":69,"line":858},[67,8984,8985],{"class":104},"        Layout,\n",[67,8987,8988],{"class":69,"line":869},[67,8989,2700],{"class":104},[67,8991,8992],{"class":69,"line":877},[67,8993,8994],{"class":104},"      props: {\n",[67,8996,8997],{"class":69,"line":886},[67,8998,8999],{"class":104},"        user: Object,\n",[67,9001,9002],{"class":69,"line":894},[67,9003,2700],{"class":104},[67,9005,9006],{"class":69,"line":905},[67,9007,1557],{"class":104},[67,9009,9010,9012,9014],{"class":69,"line":916},[67,9011,6373],{"class":104},[67,9013,6397],{"class":739},[67,9015,6296],{"class":104},[11,9017,9018,9019,9021,9022,9025,9026,944,9029,9032],{},"As you can see from the example, you need to wrap every page with the Layout, insert that layout in the ",[53,9020,6397],{}," tag and add it to the ",[53,9023,9024],{},"components"," key. This gives you the flexibility that you might need if you are using different layouts, but what if we could find a ",[21,9027,9028],{},"better",[21,9030,9031],{},"more convenient"," way to make this happen?",[1758,9034,9036],{"id":9035},"the-first-and-flawed-approach-i-went-for","The first (and flawed) approach I went for",[11,9038,9039],{},"To use Inertia in your Vue app, you just have to copy and paste their code snippet from the documentation and you are good to go.",[58,9041,9043],{"className":8875,"code":9042,"language":8877,"meta":63,"style":63},"import { InertiaApp } from \"@inertiajs\u002Finertia-vue\";\nimport Vue from \"vue\";\n\nVue.use(InertiaApp);\n\nconst app = document.getElementById(\"app\");\n\nnew Vue({\n  render: (h) =>\n    h(InertiaApp, {\n      props: {\n        initialPage: JSON.parse(app.dataset.page),\n        resolveComponent: (name) => require(`.\u002FPages\u002F${name}`).default,\n      },\n    }),\n}).$mount(app);\n",[53,9044,9045,9059,9073,9077,9088,9092,9114,9118,9128,9144,9152,9156,9172,9201,9205,9209],{"__ignoreMap":63},[67,9046,9047,9049,9052,9054,9057],{"class":69,"line":70},[67,9048,2473],{"class":163},[67,9050,9051],{"class":104}," { InertiaApp } ",[67,9053,2479],{"class":163},[67,9055,9056],{"class":77}," \"@inertiajs\u002Finertia-vue\"",[67,9058,2485],{"class":104},[67,9060,9061,9063,9066,9068,9071],{"class":69,"line":251},[67,9062,2473],{"class":163},[67,9064,9065],{"class":104}," Vue ",[67,9067,2479],{"class":163},[67,9069,9070],{"class":77}," \"vue\"",[67,9072,2485],{"class":104},[67,9074,9075],{"class":69,"line":264},[67,9076,989],{"emptyLinePlaceholder":988},[67,9078,9079,9082,9085],{"class":69,"line":280},[67,9080,9081],{"class":104},"Vue.",[67,9083,9084],{"class":73},"use",[67,9086,9087],{"class":104},"(InertiaApp);\n",[67,9089,9090],{"class":69,"line":288},[67,9091,989],{"emptyLinePlaceholder":988},[67,9093,9094,9096,9099,9101,9104,9107,9109,9112],{"class":69,"line":781},[67,9095,2722],{"class":163},[67,9097,9098],{"class":113}," app",[67,9100,2512],{"class":163},[67,9102,9103],{"class":104}," document.",[67,9105,9106],{"class":73},"getElementById",[67,9108,2556],{"class":104},[67,9110,9111],{"class":77},"\"app\"",[67,9113,2745],{"class":104},[67,9115,9116],{"class":69,"line":792},[67,9117,989],{"emptyLinePlaceholder":988},[67,9119,9120,9122,9125],{"class":69,"line":803},[67,9121,8598],{"class":163},[67,9123,9124],{"class":73}," Vue",[67,9126,9127],{"class":104},"({\n",[67,9129,9130,9133,9136,9139,9141],{"class":69,"line":814},[67,9131,9132],{"class":73},"  render",[67,9134,9135],{"class":104},": (",[67,9137,9138],{"class":2508},"h",[67,9140,4280],{"class":104},[67,9142,9143],{"class":163},"=>\n",[67,9145,9146,9149],{"class":69,"line":825},[67,9147,9148],{"class":73},"    h",[67,9150,9151],{"class":104},"(InertiaApp, {\n",[67,9153,9154],{"class":69,"line":836},[67,9155,8994],{"class":104},[67,9157,9158,9161,9164,9166,9169],{"class":69,"line":847},[67,9159,9160],{"class":104},"        initialPage: ",[67,9162,9163],{"class":113},"JSON",[67,9165,56],{"class":104},[67,9167,9168],{"class":73},"parse",[67,9170,9171],{"class":104},"(app.dataset.page),\n",[67,9173,9174,9177,9179,9181,9183,9185,9188,9190,9193,9195,9198],{"class":69,"line":858},[67,9175,9176],{"class":73},"        resolveComponent",[67,9178,9135],{"class":104},[67,9180,2559],{"class":2508},[67,9182,4280],{"class":104},[67,9184,5500],{"class":163},[67,9186,9187],{"class":73}," require",[67,9189,2556],{"class":104},[67,9191,9192],{"class":77},"`.\u002FPages\u002F${",[67,9194,2559],{"class":104},[67,9196,9197],{"class":77},"}`",[67,9199,9200],{"class":104},").default,\n",[67,9202,9203],{"class":69,"line":869},[67,9204,2700],{"class":104},[67,9206,9207],{"class":69,"line":877},[67,9208,6903],{"class":104},[67,9210,9211,9214,9217],{"class":69,"line":886},[67,9212,9213],{"class":104},"}).",[67,9215,9216],{"class":73},"$mount",[67,9218,9219],{"class":104},"(app);\n",[11,9221,9222],{},"Let's dive into this code so you can understand why I thought of this solution first.",[11,9224,9225,9226,9229,9230,9233,9234,9237,9238,9240,9241,9243,9244,9247,9248,9250,9251,9254,9255,9257],{},"First, we include the ",[53,9227,9228],{},"InertiaApp"," and inject it in our ",[53,9231,9232],{},"Vue"," instance. Then, we get the ",[53,9235,9236],{},"selector"," we want to ",[53,9239,9216],{}," our Vue app in, and instantiate our ",[53,9242,9232],{}," instance. The object we pass to the constructor has only one property, and its the ",[53,9245,9246],{},"render"," property, which as it states, renders our the app. It has one argument, ",[53,9249,9138],{}," which is the ",[53,9252,9253],{},"render function"," we call to render our ",[53,9256,9228],{}," component.",[11,9259,9260,9261,9263,9264,9267,9268,9270,9271,9274],{},"If you never used the ",[53,9262,9246],{}," function in your Vue apps, it can take three arguments, first one is a ",[53,9265,9266],{},"Vue instance"," (Vue Component), the second one is the ",[53,9269,7088],{}," passed to that component, and the third one is an ",[53,9272,9273],{},"array"," of children included in the component.",[11,9276,9277,9278,4335,9280,9283,9284,9286],{},"As you can see in the code above, we are rendering the ",[53,9279,9228],{},[53,9281,9282],{},"render: h => h(InertiaApp, ...)"," we pass some ",[53,9285,7088],{}," as the second argument, and there are no children rendered for this component.",[11,9288,9289,9290,5196,9292,9295],{},"My initial approach was, what if we wrap the whole ",[53,9291,9228],{},[53,9293,9294],{},"Layout"," we are going to use. The end code snippet looked like this",[58,9297,9299],{"className":8875,"code":9298,"language":8877,"meta":63,"style":63},"import App from \".\u002Flayout\u002FApp\";\n\nnew Vue({\n  render: (h) =>\n    h(App, {}, [\n      InertiaApp,\n      {\n        props: {\n          initialPage: JSON.parse(app.dataset.page),\n          resolveComponent: (name) => require(`.\u002FPages\u002F${name}`).default,\n        },\n      },\n    ]),\n}).$mount(app);\n",[53,9300,9301,9315,9319,9327,9339,9346,9351,9356,9361,9374,9399,9404,9408,9413],{"__ignoreMap":63},[67,9302,9303,9305,9308,9310,9313],{"class":69,"line":70},[67,9304,2473],{"class":163},[67,9306,9307],{"class":104}," App ",[67,9309,2479],{"class":163},[67,9311,9312],{"class":77}," \".\u002Flayout\u002FApp\"",[67,9314,2485],{"class":104},[67,9316,9317],{"class":69,"line":251},[67,9318,989],{"emptyLinePlaceholder":988},[67,9320,9321,9323,9325],{"class":69,"line":264},[67,9322,8598],{"class":163},[67,9324,9124],{"class":73},[67,9326,9127],{"class":104},[67,9328,9329,9331,9333,9335,9337],{"class":69,"line":280},[67,9330,9132],{"class":73},[67,9332,9135],{"class":104},[67,9334,9138],{"class":2508},[67,9336,4280],{"class":104},[67,9338,9143],{"class":163},[67,9340,9341,9343],{"class":69,"line":288},[67,9342,9148],{"class":73},[67,9344,9345],{"class":104},"(App, {}, [\n",[67,9347,9348],{"class":69,"line":781},[67,9349,9350],{"class":104},"      InertiaApp,\n",[67,9352,9353],{"class":69,"line":792},[67,9354,9355],{"class":104},"      {\n",[67,9357,9358],{"class":69,"line":803},[67,9359,9360],{"class":104},"        props: {\n",[67,9362,9363,9366,9368,9370,9372],{"class":69,"line":814},[67,9364,9365],{"class":104},"          initialPage: ",[67,9367,9163],{"class":113},[67,9369,56],{"class":104},[67,9371,9168],{"class":73},[67,9373,9171],{"class":104},[67,9375,9376,9379,9381,9383,9385,9387,9389,9391,9393,9395,9397],{"class":69,"line":825},[67,9377,9378],{"class":73},"          resolveComponent",[67,9380,9135],{"class":104},[67,9382,2559],{"class":2508},[67,9384,4280],{"class":104},[67,9386,5500],{"class":163},[67,9388,9187],{"class":73},[67,9390,2556],{"class":104},[67,9392,9192],{"class":77},[67,9394,2559],{"class":104},[67,9396,9197],{"class":77},[67,9398,9200],{"class":104},[67,9400,9401],{"class":69,"line":836},[67,9402,9403],{"class":104},"        },\n",[67,9405,9406],{"class":69,"line":847},[67,9407,2700],{"class":104},[67,9409,9410],{"class":69,"line":858},[67,9411,9412],{"class":104},"    ]),\n",[67,9414,9415,9417,9419],{"class":69,"line":869},[67,9416,9213],{"class":104},[67,9418,9216],{"class":73},[67,9420,9219],{"class":104},[11,9422,9423],{},"Now, we don't have to include our Layout in every component. But, this approach is flawed and I am going to explain why.",[11,9425,9426,9427,9430],{},"The ",[21,9428,9429],{},"first flaw"," of this approach is because this approach is not flexible. Meaning that you will be stuck with the same layout on every Inertia page you build. This flaw can limit you in many ways.",[11,9432,9426,9433,9436,9437,9440,9441,9443,9444,9447,9448,9451,9452,9455,9456,9462,9463,9465,9466,9469,9470,9472,9473,9476,9477,9480,9481,9484,9485,9488,9489,9491],{},[21,9434,9435],{},"second, and deeper flaw"," that I have found with this approach is that you cannot access the ",[53,9438,9439],{},"$page"," property within your layout. The ",[53,9442,9439],{}," property, contains ",[21,9445,9446],{},"shared data"," you pass from your backend to your frontend. For example, if you want to show a simple ",[1738,9449,9450],{},"Welcome {username}"," message in your Header (which is in the Layout), you need to share the authenticated user from the backend to the frontend. But, if you try to access your ",[53,9453,9454],{},"$page.user"," property within your Layout you will get an error saying that ",[1738,9457,9458,9459,9461],{},"can not get property ",[53,9460,3128],{}," of undefined."," At first, this error didn't make any sense. I can use the ",[53,9464,9454],{}," property in my ",[53,9467,9468],{},"pages",", but I cannot use it in my Layout. After debugging for some time and looking at the Inertia's source code, I have found out that internally Inertia injects the ",[53,9471,9439],{}," property ",[53,9474,9475],{},"Object.defineProperty(Vue.prototype, '$page', { get: () => app.props })"," in our Vue instance, which is a ",[53,9478,9479],{},"getter"," that returns the ",[53,9482,9483],{},"props"," of the ",[53,9486,9487],{},"app",", and if you go back to the previous code snippet, we pass the shared data from our backend in our ",[53,9490,9228],{}," component as a prop",[58,9493,9495],{"className":8875,"code":9494,"language":8877,"meta":63,"style":63},"props: {\n    initialPage: JSON.parse(app.dataset.page),\n    resolveComponent: (name) => require(`.\u002FPages\u002F${name}`).default\n}\n",[53,9496,9497,9503,9518,9544],{"__ignoreMap":63},[67,9498,9499,9501],{"class":69,"line":70},[67,9500,9483],{"class":73},[67,9502,1444],{"class":104},[67,9504,9505,9508,9510,9512,9514,9516],{"class":69,"line":251},[67,9506,9507],{"class":73},"    initialPage",[67,9509,758],{"class":104},[67,9511,9163],{"class":113},[67,9513,56],{"class":104},[67,9515,9168],{"class":73},[67,9517,9171],{"class":104},[67,9519,9520,9523,9525,9527,9529,9531,9533,9535,9537,9539,9541],{"class":69,"line":264},[67,9521,9522],{"class":73},"    resolveComponent",[67,9524,9135],{"class":104},[67,9526,2559],{"class":2508},[67,9528,4280],{"class":104},[67,9530,5500],{"class":163},[67,9532,9187],{"class":73},[67,9534,2556],{"class":104},[67,9536,9192],{"class":77},[67,9538,2559],{"class":104},[67,9540,9197],{"class":77},[67,9542,9543],{"class":104},").default\n",[67,9545,9546],{"class":69,"line":280},[67,9547,1601],{"class":104},[11,9549,9550],{},"which are then parsed and injected in the Vue instance. To simplify the flaw, the Layout did not have access to the shared data passed from our server.",[1758,9552,9554],{"id":9553},"solving-the-flaws-with-a-convenient-solution","Solving the flaws with a convenient solution",[11,9556,9557],{},"Let's get back to our snippet above. We resolve the current page with this code",[58,9559,9561],{"className":8875,"code":9560,"language":8877,"meta":63,"style":63},"resolveComponent: (name) => require(`.\u002FPages\u002F${name}`).default;\n",[53,9562,9563],{"__ignoreMap":63},[67,9564,9565,9568,9570,9572,9574,9576,9578,9580,9582,9584,9586],{"class":69,"line":70},[67,9566,9567],{"class":73},"resolveComponent",[67,9569,9135],{"class":104},[67,9571,2559],{"class":2508},[67,9573,4280],{"class":104},[67,9575,5500],{"class":163},[67,9577,9187],{"class":73},[67,9579,2556],{"class":104},[67,9581,9192],{"class":77},[67,9583,2559],{"class":104},[67,9585,9197],{"class":77},[67,9587,9588],{"class":104},").default;\n",[11,9590,9591],{},"As you can see, we require the page (as a module), and return the default export from that module. Within that default export we can find all of the current page's data, including the layout. So, what if we modify the module, include the default layout and then return the modified module? Now, we don't have to wrap all of our pages with the layout. The code now looks like this",[58,9593,9595],{"className":8875,"code":9594,"language":8877,"meta":63,"style":63},"import App from \".\u002Flayout\u002FApp\";\n\n\u002F\u002F\n\nresolveComponent: (name) => {\n  const module = require(`.\u002FPages\u002F${name}`);\n\n  module.default.layout = App;\n\n  return module.default;\n};\n",[53,9596,9597,9609,9613,9618,9622,9636,9657,9661,9674,9678,9687],{"__ignoreMap":63},[67,9598,9599,9601,9603,9605,9607],{"class":69,"line":70},[67,9600,2473],{"class":163},[67,9602,9307],{"class":104},[67,9604,2479],{"class":163},[67,9606,9312],{"class":77},[67,9608,2485],{"class":104},[67,9610,9611],{"class":69,"line":251},[67,9612,989],{"emptyLinePlaceholder":988},[67,9614,9615],{"class":69,"line":264},[67,9616,9617],{"class":3401},"\u002F\u002F\n",[67,9619,9620],{"class":69,"line":280},[67,9621,989],{"emptyLinePlaceholder":988},[67,9623,9624,9626,9628,9630,9632,9634],{"class":69,"line":288},[67,9625,9567],{"class":73},[67,9627,9135],{"class":104},[67,9629,2559],{"class":2508},[67,9631,4280],{"class":104},[67,9633,5500],{"class":163},[67,9635,2500],{"class":104},[67,9637,9638,9640,9643,9645,9647,9649,9651,9653,9655],{"class":69,"line":781},[67,9639,5641],{"class":163},[67,9641,9642],{"class":113}," module",[67,9644,2512],{"class":163},[67,9646,9187],{"class":73},[67,9648,2556],{"class":104},[67,9650,9192],{"class":77},[67,9652,2559],{"class":104},[67,9654,9197],{"class":77},[67,9656,2745],{"class":104},[67,9658,9659],{"class":69,"line":792},[67,9660,989],{"emptyLinePlaceholder":988},[67,9662,9663,9666,9669,9671],{"class":69,"line":803},[67,9664,9665],{"class":113},"  module",[67,9667,9668],{"class":104},".default.layout ",[67,9670,2586],{"class":163},[67,9672,9673],{"class":104}," App;\n",[67,9675,9676],{"class":69,"line":814},[67,9677,989],{"emptyLinePlaceholder":988},[67,9679,9680,9682,9684],{"class":69,"line":825},[67,9681,5698],{"class":163},[67,9683,9642],{"class":113},[67,9685,9686],{"class":104},".default;\n",[67,9688,9689],{"class":69,"line":836},[67,9690,5573],{"class":104},[11,9692,9693],{},"Now, we have set a default Layout for all of our Inertia pages. But we still haven't solved the flaws we mentioned. Let's do that.",[11,9695,9696],{},"To solve the first flaw, we can modify the code snipped above in a way that allows us to add the default Layout only if there is no layout already defined.",[58,9698,9700],{"className":8875,"code":9699,"language":8877,"meta":63,"style":63},"resolveComponent: (name) => {\n  const module = require(`.\u002FPages\u002F${name}`);\n\n  if (!module.default.layout) {\n    \u002F\u002F there is no Layout defined, set the default layout\n    module.default.layout = App;\n  }\n\n  return module.default;\n};\n",[53,9701,9702,9716,9736,9740,9754,9759,9770,9774,9778,9786],{"__ignoreMap":63},[67,9703,9704,9706,9708,9710,9712,9714],{"class":69,"line":70},[67,9705,9567],{"class":73},[67,9707,9135],{"class":104},[67,9709,2559],{"class":2508},[67,9711,4280],{"class":104},[67,9713,5500],{"class":163},[67,9715,2500],{"class":104},[67,9717,9718,9720,9722,9724,9726,9728,9730,9732,9734],{"class":69,"line":251},[67,9719,5641],{"class":163},[67,9721,9642],{"class":113},[67,9723,2512],{"class":163},[67,9725,9187],{"class":73},[67,9727,2556],{"class":104},[67,9729,9192],{"class":77},[67,9731,2559],{"class":104},[67,9733,9197],{"class":77},[67,9735,2745],{"class":104},[67,9737,9738],{"class":69,"line":264},[67,9739,989],{"emptyLinePlaceholder":988},[67,9741,9742,9744,9746,9748,9751],{"class":69,"line":280},[67,9743,5667],{"class":163},[67,9745,4881],{"class":104},[67,9747,4542],{"class":163},[67,9749,9750],{"class":113},"module",[67,9752,9753],{"class":104},".default.layout) {\n",[67,9755,9756],{"class":69,"line":288},[67,9757,9758],{"class":3401},"    \u002F\u002F there is no Layout defined, set the default layout\n",[67,9760,9761,9764,9766,9768],{"class":69,"line":781},[67,9762,9763],{"class":113},"    module",[67,9765,9668],{"class":104},[67,9767,2586],{"class":163},[67,9769,9673],{"class":104},[67,9771,9772],{"class":69,"line":792},[67,9773,1596],{"class":104},[67,9775,9776],{"class":69,"line":803},[67,9777,989],{"emptyLinePlaceholder":988},[67,9779,9780,9782,9784],{"class":69,"line":814},[67,9781,5698],{"class":163},[67,9783,9642],{"class":113},[67,9785,9686],{"class":104},[67,9787,9788],{"class":69,"line":825},[67,9789,5573],{"class":104},[11,9791,9792],{},"We now achieved flexibility and solved the first flaw.",[11,9794,9795,9796,9799,9800,5614],{},"As for the second flaw, if we have a look at the source code for the ",[53,9797,9798],{},"inertia-vue\u002Fsrc\u002Fapp.js"," you can see that we have the following piece of code in the ",[53,9801,9246],{},[58,9803,9805],{"className":8875,"code":9804,"language":8877,"meta":63,"style":63},"if (this.component.layout) {\n  if (typeof this.component.layout === \"function\") {\n    return this.component.layout(h, child);\n  }\n\n  return h(this.component.layout, [child]);\n}\n",[53,9806,9807,9818,9839,9853,9857,9861,9875],{"__ignoreMap":63},[67,9808,9809,9811,9813,9815],{"class":69,"line":70},[67,9810,3486],{"class":163},[67,9812,4881],{"class":104},[67,9814,3465],{"class":113},[67,9816,9817],{"class":104},".component.layout) {\n",[67,9819,9820,9822,9824,9827,9829,9832,9834,9837],{"class":69,"line":251},[67,9821,5667],{"class":163},[67,9823,4881],{"class":104},[67,9825,9826],{"class":163},"typeof",[67,9828,2644],{"class":113},[67,9830,9831],{"class":104},".component.layout ",[67,9833,3770],{"class":163},[67,9835,9836],{"class":77}," \"function\"",[67,9838,2575],{"class":104},[67,9840,9841,9843,9845,9848,9850],{"class":69,"line":264},[67,9842,2674],{"class":163},[67,9844,2644],{"class":113},[67,9846,9847],{"class":104},".component.",[67,9849,8894],{"class":73},[67,9851,9852],{"class":104},"(h, child);\n",[67,9854,9855],{"class":69,"line":280},[67,9856,1596],{"class":104},[67,9858,9859],{"class":69,"line":288},[67,9860,989],{"emptyLinePlaceholder":988},[67,9862,9863,9865,9868,9870,9872],{"class":69,"line":781},[67,9864,5698],{"class":163},[67,9866,9867],{"class":73}," h",[67,9869,2556],{"class":104},[67,9871,3465],{"class":113},[67,9873,9874],{"class":104},".component.layout, [child]);\n",[67,9876,9877],{"class":69,"line":792},[67,9878,1601],{"class":104},[11,9880,9881,9882,9884],{},"This code ensures that the all of the props are transformed and injected in our app before we render the layout, so now we can access our ",[53,9883,9439],{}," property in the Layout as well.",[11,9886,9887],{},"At the end, we have ended up with the following code snippet.",[58,9889,9891],{"className":8875,"code":9890,"language":8877,"meta":63,"style":63},"import { InertiaApp } from \"@inertiajs\u002Finertia-vue\";\nimport Vue from \"vue\";\nimport App from \".\u002FLayouts\u002FApp\";\n\nVue.use(InertiaApp);\n\nconst app = document.getElementById(\"app\");\n\nnew Vue({\n  render: (h) =>\n    h(InertiaApp, {\n      props: {\n        initialPage: JSON.parse(app.dataset.page),\n        resolveComponent: (name) => {\n          const module = require(`.\u002FPages\u002F${name}`);\n\n          if (!module.default.layout) {\n            module.default.layout = App;\n          }\n\n          return module.default;\n        },\n      },\n    }),\n}).$mount(app);\n",[53,9892,9893,9905,9917,9930,9934,9942,9946,9964,9968,9976,9988,9994,9998,10010,10024,10045,10049,10061,10072,10076,10080,10089,10093,10097,10101],{"__ignoreMap":63},[67,9894,9895,9897,9899,9901,9903],{"class":69,"line":70},[67,9896,2473],{"class":163},[67,9898,9051],{"class":104},[67,9900,2479],{"class":163},[67,9902,9056],{"class":77},[67,9904,2485],{"class":104},[67,9906,9907,9909,9911,9913,9915],{"class":69,"line":251},[67,9908,2473],{"class":163},[67,9910,9065],{"class":104},[67,9912,2479],{"class":163},[67,9914,9070],{"class":77},[67,9916,2485],{"class":104},[67,9918,9919,9921,9923,9925,9928],{"class":69,"line":264},[67,9920,2473],{"class":163},[67,9922,9307],{"class":104},[67,9924,2479],{"class":163},[67,9926,9927],{"class":77}," \".\u002FLayouts\u002FApp\"",[67,9929,2485],{"class":104},[67,9931,9932],{"class":69,"line":280},[67,9933,989],{"emptyLinePlaceholder":988},[67,9935,9936,9938,9940],{"class":69,"line":288},[67,9937,9081],{"class":104},[67,9939,9084],{"class":73},[67,9941,9087],{"class":104},[67,9943,9944],{"class":69,"line":781},[67,9945,989],{"emptyLinePlaceholder":988},[67,9947,9948,9950,9952,9954,9956,9958,9960,9962],{"class":69,"line":792},[67,9949,2722],{"class":163},[67,9951,9098],{"class":113},[67,9953,2512],{"class":163},[67,9955,9103],{"class":104},[67,9957,9106],{"class":73},[67,9959,2556],{"class":104},[67,9961,9111],{"class":77},[67,9963,2745],{"class":104},[67,9965,9966],{"class":69,"line":803},[67,9967,989],{"emptyLinePlaceholder":988},[67,9969,9970,9972,9974],{"class":69,"line":814},[67,9971,8598],{"class":163},[67,9973,9124],{"class":73},[67,9975,9127],{"class":104},[67,9977,9978,9980,9982,9984,9986],{"class":69,"line":825},[67,9979,9132],{"class":73},[67,9981,9135],{"class":104},[67,9983,9138],{"class":2508},[67,9985,4280],{"class":104},[67,9987,9143],{"class":163},[67,9989,9990,9992],{"class":69,"line":836},[67,9991,9148],{"class":73},[67,9993,9151],{"class":104},[67,9995,9996],{"class":69,"line":847},[67,9997,8994],{"class":104},[67,9999,10000,10002,10004,10006,10008],{"class":69,"line":858},[67,10001,9160],{"class":104},[67,10003,9163],{"class":113},[67,10005,56],{"class":104},[67,10007,9168],{"class":73},[67,10009,9171],{"class":104},[67,10011,10012,10014,10016,10018,10020,10022],{"class":69,"line":869},[67,10013,9176],{"class":73},[67,10015,9135],{"class":104},[67,10017,2559],{"class":2508},[67,10019,4280],{"class":104},[67,10021,5500],{"class":163},[67,10023,2500],{"class":104},[67,10025,10026,10029,10031,10033,10035,10037,10039,10041,10043],{"class":69,"line":877},[67,10027,10028],{"class":163},"          const",[67,10030,9642],{"class":113},[67,10032,2512],{"class":163},[67,10034,9187],{"class":73},[67,10036,2556],{"class":104},[67,10038,9192],{"class":77},[67,10040,2559],{"class":104},[67,10042,9197],{"class":77},[67,10044,2745],{"class":104},[67,10046,10047],{"class":69,"line":886},[67,10048,989],{"emptyLinePlaceholder":988},[67,10050,10051,10053,10055,10057,10059],{"class":69,"line":894},[67,10052,8733],{"class":163},[67,10054,4881],{"class":104},[67,10056,4542],{"class":163},[67,10058,9750],{"class":113},[67,10060,9753],{"class":104},[67,10062,10063,10066,10068,10070],{"class":69,"line":905},[67,10064,10065],{"class":113},"            module",[67,10067,9668],{"class":104},[67,10069,2586],{"class":163},[67,10071,9673],{"class":104},[67,10073,10074],{"class":69,"line":916},[67,10075,7055],{"class":104},[67,10077,10078],{"class":69,"line":927},[67,10079,989],{"emptyLinePlaceholder":988},[67,10081,10082,10085,10087],{"class":69,"line":935},[67,10083,10084],{"class":163},"          return",[67,10086,9642],{"class":113},[67,10088,9686],{"class":104},[67,10090,10091],{"class":69,"line":958},[67,10092,9403],{"class":104},[67,10094,10095],{"class":69,"line":971},[67,10096,2700],{"class":104},[67,10098,10099],{"class":69,"line":985},[67,10100,6903],{"class":104},[67,10102,10103,10105,10107],{"class":69,"line":992},[67,10104,9213],{"class":104},[67,10106,9216],{"class":73},[67,10108,9219],{"class":104},[6201,10110],{},[11,10112,10113,10114,10117],{},"I didn't want to give you the solution right from the start because there was a lesson to be learned from the first approach I took, so maybe in the future you can have a ",[1738,10115,10116],{},"gotcha"," moment if you ever stumble across an issue like this.",[11,10119,10120],{},"Hope this saves you some time on your next Inertia project.",[1670,10122,10123],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":63,"searchDepth":251,"depth":251,"links":10125},[10126,10127,10128],{"id":8853,"depth":264,"text":8854},{"id":9035,"depth":264,"text":9036},{"id":9553,"depth":264,"text":9554},"Inertia","2020\u002F04\u002F21","If you are a Laravel developer, you have probably heard of Inertia.js, the modern monolith. With Inertia, you can quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers. This makes Inertia a perfect choice for building some web apps.","\u002Fimages\u002Flaravel-inertia.jpg","vue, vue.js, inertia, layout, laravel inertia, inertia layout, monolith laravel",{},"\u002Fblog\u002Fconvenient-way-to-use-layouts-in-inertia","☕️ 8 min read",{"title":8832,"description":10131},"convenient-way-to-use-layouts-in-inertia","blog\u002Fconvenient-way-to-use-layouts-in-inertia","inertia, laravel","xLM45CRyMCCEmaw3N10mOkyaTK8XdMTnAG6fKPKVOBM",{"id":10143,"title":10144,"body":10145,"category":10485,"date":10486,"description":10149,"excerpt":1690,"extension":1691,"image":10487,"keywords":10488,"meta":10489,"navigation":988,"path":10490,"readingTime":8824,"seo":10491,"slug":10492,"stem":10493,"tags":10494,"__hash__":10495},"blog\u002Fblog\u002Feverything-about-jam-stack-and-whether-you-should-have-fomo-about-it.md","Everything about JAM Stack and whether you should have FOMO about it",{"type":8,"value":10146,"toc":10476},[10147,10150,10154,10161,10178,10182,10185,10217,10220,10268,10270,10274,10281,10355,10358,10375,10379,10385,10390,10393,10397,10404,10408,10411,10415,10422,10426,10429,10433,10436,10440,10447,10451,10454,10471,10473],[11,10148,10149],{},"Working in the world of development can be a bit confusing, with lots of trends coming up to interrupt our normal lives and give us a bad case of FOMO. We‘re mostly stuck between learning, relearning and unlearning. Sometimes the new trends are not different from technologies we’ve already been using, other times, it will change your life or in this case, your stack. It’s no secret that the internet is a world of rapid, and near-constant, evolution.",[45,10151,10153],{"id":10152},"what-the-heck-is-jam-stack","What the heck is JAM stack",[11,10155,10156,10157,10160],{},"Over the past few years, one of the most recent major trends in web development has been the so-called ",[21,10158,10159],{},"JAM stack,"," a new concept for creating static websites — or sites written in pure HTML and CSS.",[11,10162,10163,10164,944,10167,944,10170,10173,10174,10177],{},"JAM stack stands for JavaScript, APIs, and Markup. It’s a new way of building websites and apps that delivers ",[21,10165,10166],{},"better performance",[21,10168,10169],{},"higher security",[21,10171,10172],{},"lower cost of scaling",", and ",[21,10175,10176],{},"better developer experience",". JAM Stack serves as an alternative to the once-prominent LAMP (Linux, Apache, MySQL, and PHP) and MEAN (MongoDB, Express.js, AngularJS, and Node.js) stacks. Let's now dive deeper into JAM Stack.",[45,10179,10181],{"id":10180},"jam-stack-and-its-superpowers","JAM stack and its superpowers",[11,10183,10184],{},"I have already highlighted some of the main benefits of using the JAM stack, but let's talk a bit more in-depth about each of them (and then some more):{class=mb-4}",[26,10186,10187,10193,10199,10205,10211],{},[29,10188,10189,10192],{},[21,10190,10191],{},"Faster Performance:"," The advantages of static websites in terms of speed can be astronomical. With HTML generated in advance and database queries eliminated, your content can be served quickly from a global content delivery network (CDN). Faster load times lead to reduced bounce rates, as visitors won’t be leaving your site due to slow loading times. This means more interactions, and ultimately, more conversions, as well.",[29,10194,10195,10198],{},[21,10196,10197],{},"Higher Security:"," JAM stack web apps are incredibly secure since you don’t have to worry about servers been hacked or database vulnerabilities.",[29,10200,10201,10204],{},[21,10202,10203],{},"Less cost of scaling:"," Cost of running a JAM stack web app is cheaper since it uses fewer resources as things like servers and databases are not necessarily needed.",[29,10206,10207,10210],{},[21,10208,10209],{},"Better Developer Experience:"," By separating the data source from the actual application, we get a greater choice of technologies. You can use any framework\u002Flibrary\u002Fservice to build a JAM stack web app.",[29,10212,10213,10216],{},[21,10214,10215],{},"Simpler model:"," JAMStack is a breath of fresh air in a world dominated by Docker, Kubernetes, Buildpacks & such.",[11,10218,10219],{},"You can also:{class=mb-4}",[26,10221,10222,10230,10243,10257,10265],{},[29,10223,10224,10225,56],{},"Manage authentication, password recovery, and more with ",[1711,10226,10229],{"href":10227,"rel":10228},"https:\u002F\u002Fwww.netlify.com\u002Fdocs\u002Fidentity\u002F",[1715],"Netlify Identity",[29,10231,10232,10233,2004,10238,56],{},"Searching through your data using ",[1711,10234,10237],{"href":10235,"rel":10236},"https:\u002F\u002Fwww.algolia.com\u002F",[1715],"Algolia",[1711,10239,10242],{"href":10240,"rel":10241},"https:\u002F\u002Flunrjs.com\u002F",[1715],"Lunr.js",[29,10244,10245,10246,10251,10252,56],{},"Handle your form submissions using ",[1711,10247,10250],{"href":10248,"rel":10249},"https:\u002F\u002Fformspree.io\u002F",[1715],"Formspree"," or ",[1711,10253,10256],{"href":10254,"rel":10255},"https:\u002F\u002Fwww.netlify.com\u002Fdocs\u002Fform-handling\u002F",[1715],"Netlify Forms",[29,10258,10259,10260],{},"Handle optimal image delivery with ",[1711,10261,10264],{"href":10262,"rel":10263},"https:\u002F\u002Fcloudinary.com\u002F",[1715],"Cloudinary",[29,10266,10267],{},"much much more ...",[6201,10269],{},[45,10271,10273],{"id":10272},"the-jam-stack-workflow","The JAM stack workflow",[11,10275,10276,10277,10280],{},"If you got this far, I think you already are feeling the ",[1738,10278,10279],{},"JAM"," stack. We already saw what does it stand for, now let's examine what is required to build an app using the JAM stack:{class=mb-4}",[26,10282,10283,10305,10338],{},[29,10284,10285,10287,10288,10293,10294,944,10299,10304],{},[21,10286,8818],{},": Any dynamic programming during the request\u002Fresponse cycle is handled by JavaScript, running entirely on the client. This could be any frontend framework like ",[1711,10289,10292],{"href":10290,"rel":10291},"https:\u002F\u002Fvuejs.org\u002F",[1715],"Vue.js",", (or a library like) ",[1711,10295,10298],{"href":10296,"rel":10297},"https:\u002F\u002Freactjs.org\u002F",[1715],"React",[1711,10300,10303],{"href":10301,"rel":10302},"https:\u002F\u002Fangular.io\u002F",[1715],"Angular",", etc. or even vanilla JavaScript.",[29,10306,10307,10310,10311,10316,10317,10251,10322,10327,10328,10251,10333],{},[21,10308,10309],{},"APIs",": All server-side processes or database actions are abstracted into reusable APIs, accessed over HTTP with JavaScript. These can be custom-built or leverage third-party services. These might include a payment processor like ",[1711,10312,10315],{"href":10313,"rel":10314},"http:\u002F\u002Fwww.stripe.com",[1715],"Stripe,"," a communication platform like ",[1711,10318,10321],{"href":10319,"rel":10320},"http:\u002F\u002Fwww.twilio.com",[1715],"Twilio",[1711,10323,10326],{"href":10324,"rel":10325},"http:\u002F\u002Fwww.sendbird.com",[1715],"Sendbird,"," or even a CMS like ",[1711,10329,10332],{"href":10330,"rel":10331},"http:\u002F\u002Fwww.prismic.io",[1715],"Prismic",[1711,10334,10337],{"href":10335,"rel":10336},"http:\u002F\u002Fwww.contentful.com",[1715],"Contentful",[29,10339,10340,10343,10344,944,10349,10354],{},[21,10341,10342],{},"Markup",": The markup should be pre-built at deploy time, usually using a site generator like ",[1711,10345,10348],{"href":10346,"rel":10347},"https:\u002F\u002Fnuxtjs.org\u002F",[1715],"Nuxt.js",[1711,10350,10353],{"href":10351,"rel":10352},"https:\u002F\u002Fwww.gatsbyjs.org\u002F",[1715],"Gatsby.js",", etc. for content sites or a build tool like Webpack, ParcelJS, etc. for web apps.",[11,10356,10357],{},"When building a project with the JAM stack, you should consider the following best practices:{class=mb-4}",[26,10359,10360,10363,10366,10369,10372],{},[29,10361,10362],{},"The entire site should be served on CDN.",[29,10364,10365],{},"Employ atomic deploys.",[29,10367,10368],{},"Instant caching invalidation.",[29,10370,10371],{},"Everything should live on Git.",[29,10373,10374],{},"Markup builds should be automated.",[1758,10376,10378],{"id":10377},"web-application-frameworks","Web application frameworks",[11,10380,10381,10382,10384],{},"Okay, having in mind everything that was written so far, few framework developers choose to build their JAM stack web apps. Having been using only ",[21,10383,10348],{}," for my projects, I can't recommend any other framework, however choosing the framework is only up to you, since they all achieve the same outcome.",[10386,10387,10389],"h4",{"id":10388},"nuxtjsclassmarginless-text-green-700","Nuxt.js{class=\"marginless text-green-700\"}",[11,10391,10392],{},"Nuxt is the dominant framework when it comes to building web applications with Vue.js. Inspired by Next.js (its counterpart in the React world), it provides a statically generated mode that will embed the data used in your pages in your HTML templates.",[10386,10394,10396],{"id":10395},"gatsbyclassmarginless-text-purple-700","Gatsby{class=\"marginless text-purple-700\"}",[11,10398,10399,10400,10403],{},"Described as ",[1738,10401,10402],{},"a blazing fast modern site generator,"," Gatsby is built in React and it allows embedding nearly any kind of data source into their internal GraphQL database for you to query. All the queried data will get statically built into the templates, making it blazing fast!",[10386,10405,10407],{"id":10406},"gridsomeclassmarginless-text-green-500","Gridsome{class=\"marginless text-green-500\"}",[11,10409,10410],{},"Gridsome is a newcomer and the Vue equivalent to Gatsby. It is still in its early stages, but it looks promising. While it lacks the bigger ecosystem Gatsby has, it has picked up on its latest features and improved on them, and I expect it to move quite fast. Their documentation is getting better every day.",[1758,10412,10414],{"id":10413},"headless-cms","Headless CMS",[11,10416,10417,10418,10421],{},"From Wikipedia: ",[1738,10419,10420],{},"A headless content management system, or headless CMS, is a back-end only content management system (CMS) built from the ground up as a content repository that makes content accessible via an API for display on any device",". We use Headless CMS to get the dynamic content we need for our JAM stack web app. I have only worked with two Headless CMS so far, and here is a quick recap about my experience with them:",[10386,10423,10425],{"id":10424},"contentfulclassmarginless-text-red-500","Contentful{class=\"marginless text-red-500\"}",[11,10427,10428],{},"Its backend is quite simple and flexible to most use cases, including support for i18n fields, content relationships, a rich HTML content editor, and more. However, it's unexpectedly expensive. They have a free plan that might work for most use cases though.",[10386,10430,10432],{"id":10431},"prismicclassmarginless-text-blue-500","Prismic{class=\"marginless text-blue-500\"}",[11,10434,10435],{},"Prismic has outstanding UI and user experience. It has support for i18n documents, associations, responsive images, and many kinds of fields. They also have tools for working with all JavaScript frameworks (libraries) a great querying API, and much more.",[1758,10437,10439],{"id":10438},"deployment-platforms","Deployment platforms",[11,10441,10442,10443,10446],{},"There are probably other platforms for deploying JAM stack web apps, but I have been using only one and there isn't any other I would recommend more than ",[21,10444,10445],{},"Netlify",". My experience using this service has been nothing but perfect from the moment I have registered. They also have a free plan that is probably enough for almost any project you are trying to build.",[10386,10448,10450],{"id":10449},"netlifyclassmarginless-text-teal-500","Netlify{class=\"marginless text-teal-500\"}",[11,10452,10453],{},"Netlify is just perfect for the JAM stack — notably, their CEO came up with the term — so expect it to support everything they need and more, including:{class=mb-4}",[26,10455,10456,10459,10462,10465,10468],{},[29,10457,10458],{},"Cached and automatic builds",[29,10460,10461],{},"Deployment previews",[29,10463,10464],{},"A\u002FB testing",[29,10466,10467],{},"Support for Serverless functions",[29,10469,10470],{},"...",[6201,10472],{},[11,10474,10475],{},"I have built several web apps on the JAM Stack and the developer experience has been unparalleled. If you haven't tried it yet, I'd encourage giving it a shot.",{"title":63,"searchDepth":251,"depth":251,"links":10477},[10478,10479,10480],{"id":10152,"depth":251,"text":10153},{"id":10180,"depth":251,"text":10181},{"id":10272,"depth":251,"text":10273,"children":10481},[10482,10483,10484],{"id":10377,"depth":264,"text":10378},{"id":10413,"depth":264,"text":10414},{"id":10438,"depth":264,"text":10439},"JAMStack","2019\u002F10\u002F25","\u002Fimages\u002Fjamstack.jpeg","javascript, jam stack, fomo jam stack, fomo",{},"\u002Fblog\u002Feverything-about-jam-stack-and-whether-you-should-have-fomo-about-it",{"title":10144,"description":10149},"everything-about-jam-stack-and-whether-you-should-have-fomo-about-it","blog\u002Feverything-about-jam-stack-and-whether-you-should-have-fomo-about-it","jam stack, javascript","PKiia15g2H4yAdyMnTe3wTDzX2qsMLGAFLv9X3NEWMQ",{"id":10497,"title":10498,"body":10499,"category":11897,"date":11898,"description":10503,"excerpt":1690,"extension":1691,"image":11899,"keywords":11900,"meta":11901,"navigation":988,"path":11902,"readingTime":11903,"seo":11904,"slug":11905,"stem":11906,"tags":11907,"__hash__":11908},"blog\u002Fblog\u002Fnaming-and-its-importance-in-programming.md","Naming and its importance in programming",{"type":8,"value":10500,"toc":11884},[10501,10504,10507,10614,10617,10621,10624,10630,10633,10636,10640,10643,10646,10650,10670,10673,10866,10873,10883,10886,10889,10893,10900,10903,10906,10954,10961,11019,11022,11041,11044,11072,11075,11101,11115,11134,11138,11145,11153,11156,11162,11166,11189,11192,11196,11199,11231,11234,11341,11344,11374,11377,11425,11429,11450,11453,11523,11526,11529,11532,11660,11667,11674,11678,11681,11751,11758,11798,11807,11810,11834,11837,11858,11861,11865,11868,11871,11874,11876,11881],[11,10502,10503],{},"If you've been writing code for any amount of time, you know that naming matters. We name our variables, functions, classes, modules — everything. We spend more time reading code than writing it, so good naming always pays off. The time you invest up front saves you and your team time repeatedly down the road.",[11,10505,10506],{},"Let's see an example of a hard to read code.",[58,10508,10510],{"className":8875,"code":10509,"language":8877,"meta":63,"style":63},"const convert = (x, y, z) => {\n  const k = Object.keys(x);\n\n  return k.map((key) => {\n    return {\n      [y]: key,\n      [z]: x[key],\n    };\n  });\n};\n",[53,10511,10512,10541,10559,10563,10584,10590,10595,10600,10605,10610],{"__ignoreMap":63},[67,10513,10514,10516,10519,10521,10523,10525,10527,10530,10532,10535,10537,10539],{"class":69,"line":70},[67,10515,2722],{"class":163},[67,10517,10518],{"class":73}," convert",[67,10520,2512],{"class":163},[67,10522,4881],{"class":104},[67,10524,2205],{"class":2508},[67,10526,944],{"class":104},[67,10528,10529],{"class":2508},"y",[67,10531,944],{"class":104},[67,10533,10534],{"class":2508},"z",[67,10536,4280],{"class":104},[67,10538,5500],{"class":163},[67,10540,2500],{"class":104},[67,10542,10543,10545,10548,10550,10553,10556],{"class":69,"line":251},[67,10544,5641],{"class":163},[67,10546,10547],{"class":113}," k",[67,10549,2512],{"class":163},[67,10551,10552],{"class":104}," Object.",[67,10554,10555],{"class":73},"keys",[67,10557,10558],{"class":104},"(x);\n",[67,10560,10561],{"class":69,"line":264},[67,10562,989],{"emptyLinePlaceholder":988},[67,10564,10565,10567,10570,10573,10575,10578,10580,10582],{"class":69,"line":280},[67,10566,5698],{"class":163},[67,10568,10569],{"class":104}," k.",[67,10571,10572],{"class":73},"map",[67,10574,6959],{"class":104},[67,10576,10577],{"class":2508},"key",[67,10579,4280],{"class":104},[67,10581,5500],{"class":163},[67,10583,2500],{"class":104},[67,10585,10586,10588],{"class":69,"line":288},[67,10587,2674],{"class":163},[67,10589,2500],{"class":104},[67,10591,10592],{"class":69,"line":781},[67,10593,10594],{"class":104},"      [y]: key,\n",[67,10596,10597],{"class":69,"line":792},[67,10598,10599],{"class":104},"      [z]: x[key],\n",[67,10601,10602],{"class":69,"line":803},[67,10603,10604],{"class":104},"    };\n",[67,10606,10607],{"class":69,"line":814},[67,10608,10609],{"class":104},"  });\n",[67,10611,10612],{"class":69,"line":825},[67,10613,5573],{"class":104},[11,10615,10616],{},"You might figure out what this function does eventually — but imagine a large feature written this way. How hard would it be for someone else, or even yourself revisiting it months later, to make sense of it?",[45,10618,10620],{"id":10619},"understanding-the-problem-you-are-trying-to-solve","Understanding the problem you are trying to solve",[11,10622,10623],{},"I challenge you to have a look at code you wrote a few months ago. Can you understand what does it achieve? Can you explain to someone easily what problem were you trying to solve? You know it's not always easy to do that.",[11,10625,10626,10627],{},"While working on some issues, we are often immersed in it to a point where it feels natural to us. As a result, what's happening is that we can find ourselves in a position where it's really hard to put the problem into words so it's easy for ",[21,10628,10629],{},"others to understand.",[11,10631,10632],{},"Yes, your focus while solving issues and writing code should be to write easily understandable code. You should view the language around your code as your proxy. Its job is to explain the problem that your code is solving to others when you're not around.",[11,10634,10635],{},"If we want to do that, we should put extra time to investigate and learn more about the problem we are trying to solve in greater detail. This will help you flesh out the language around the problem. This should make it easier for you to describe what your code is doing to others.",[45,10637,10639],{"id":10638},"good-code-is-self-explanatory","Good code is self-explanatory",[11,10641,10642],{},"The key takeaway from this post is to force yourself to write code that even non-technical users will understand. A good rule of thumb is: If an identifier name requires a comment, then it doesn't reveal the intent of the identifier. This doesn't state that you should try to avoid comments or only write code that doesn't need any explanation. Just try to explain your code through proper naming identifiers (variables, functions, classes ...).",[11,10644,10645],{},"Let's examine different situations where you can improve your code by taking some simple steps to clarify it.",[1758,10647,10649],{"id":10648},"naming-should-have-a-clear-purpose","Naming should have a clear purpose",[11,10651,10652,10653,10656,10657,944,10660,944,10663,944,10666,10669],{},"When declaring variables we should always try to name them in a way that the purpose of the variable is clear. Let's say we have a variable named ",[53,10654,10655],{},"t"," which stores time. Declaring this variable says nothing about its purpose. It does not evoke a sense of lapsed time. It would be better to choose a name that specifies what is being measured, and the unit of that measurement. Depending on the situation, we can declare variables like ",[53,10658,10659],{},"daysSinceCreation",[53,10661,10662],{},"elapsedTimesInSeconds",[53,10664,10665],{},"daysSinceClosing",[53,10667,10668],{},"personAgeInDays"," ...",[11,10671,10672],{},"Let's compare these two functions that perform the same operation.",[58,10674,10676],{"className":8875,"code":10675,"language":8877,"meta":63,"style":63},"function getList(list) {\n  const list1 = [];\n\n  for (x in list) {\n    if (x % 2 === 0) {\n      list1.push(x);\n    }\n  }\n\n  return list1;\n}\n\nfunction getOddNumbers(listOfNumbers) {\n  const oddNumbers = [];\n\n  for (number in listOfNumbers) {\n    if (isOdd(number)) {\n      oddNumbers.push(number);\n    }\n  }\n\n  return oddNumbers;\n}\n",[53,10677,10678,10693,10705,10709,10723,10743,10753,10757,10761,10765,10772,10776,10780,10794,10805,10809,10821,10833,10843,10847,10851,10855,10862],{"__ignoreMap":63},[67,10679,10680,10683,10686,10688,10691],{"class":69,"line":70},[67,10681,10682],{"class":163},"function",[67,10684,10685],{"class":73}," getList",[67,10687,2556],{"class":104},[67,10689,10690],{"class":2508},"list",[67,10692,2575],{"class":104},[67,10694,10695,10697,10700,10702],{"class":69,"line":251},[67,10696,5641],{"class":163},[67,10698,10699],{"class":113}," list1",[67,10701,2512],{"class":163},[67,10703,10704],{"class":104}," [];\n",[67,10706,10707],{"class":69,"line":264},[67,10708,989],{"emptyLinePlaceholder":988},[67,10710,10711,10714,10717,10720],{"class":69,"line":280},[67,10712,10713],{"class":163},"  for",[67,10715,10716],{"class":104}," (x ",[67,10718,10719],{"class":163},"in",[67,10721,10722],{"class":104}," list) {\n",[67,10724,10725,10727,10729,10732,10735,10738,10741],{"class":69,"line":288},[67,10726,3764],{"class":163},[67,10728,10716],{"class":104},[67,10730,10731],{"class":163},"%",[67,10733,10734],{"class":113}," 2",[67,10736,10737],{"class":163}," ===",[67,10739,10740],{"class":113}," 0",[67,10742,2575],{"class":104},[67,10744,10745,10748,10751],{"class":69,"line":781},[67,10746,10747],{"class":104},"      list1.",[67,10749,10750],{"class":73},"push",[67,10752,10558],{"class":104},[67,10754,10755],{"class":69,"line":792},[67,10756,1557],{"class":104},[67,10758,10759],{"class":69,"line":803},[67,10760,1596],{"class":104},[67,10762,10763],{"class":69,"line":814},[67,10764,989],{"emptyLinePlaceholder":988},[67,10766,10767,10769],{"class":69,"line":825},[67,10768,5698],{"class":163},[67,10770,10771],{"class":104}," list1;\n",[67,10773,10774],{"class":69,"line":836},[67,10775,1601],{"class":104},[67,10777,10778],{"class":69,"line":847},[67,10779,989],{"emptyLinePlaceholder":988},[67,10781,10782,10784,10787,10789,10792],{"class":69,"line":858},[67,10783,10682],{"class":163},[67,10785,10786],{"class":73}," getOddNumbers",[67,10788,2556],{"class":104},[67,10790,10791],{"class":2508},"listOfNumbers",[67,10793,2575],{"class":104},[67,10795,10796,10798,10801,10803],{"class":69,"line":869},[67,10797,5641],{"class":163},[67,10799,10800],{"class":113}," oddNumbers",[67,10802,2512],{"class":163},[67,10804,10704],{"class":104},[67,10806,10807],{"class":69,"line":877},[67,10808,989],{"emptyLinePlaceholder":988},[67,10810,10811,10813,10816,10818],{"class":69,"line":886},[67,10812,10713],{"class":163},[67,10814,10815],{"class":104}," (number ",[67,10817,10719],{"class":163},[67,10819,10820],{"class":104}," listOfNumbers) {\n",[67,10822,10823,10825,10827,10830],{"class":69,"line":894},[67,10824,3764],{"class":163},[67,10826,4881],{"class":104},[67,10828,10829],{"class":73},"isOdd",[67,10831,10832],{"class":104},"(number)) {\n",[67,10834,10835,10838,10840],{"class":69,"line":905},[67,10836,10837],{"class":104},"      oddNumbers.",[67,10839,10750],{"class":73},[67,10841,10842],{"class":104},"(number);\n",[67,10844,10845],{"class":69,"line":916},[67,10846,1557],{"class":104},[67,10848,10849],{"class":69,"line":927},[67,10850,1596],{"class":104},[67,10852,10853],{"class":69,"line":935},[67,10854,989],{"emptyLinePlaceholder":988},[67,10856,10857,10859],{"class":69,"line":958},[67,10858,5698],{"class":163},[67,10860,10861],{"class":104}," oddNumbers;\n",[67,10863,10864],{"class":69,"line":971},[67,10865,1601],{"class":104},[11,10867,10868,10869,10872],{},"Why is it so hard to tell what the ",[53,10870,10871],{},"getList"," function does? There are no complex expressions. The code is indented and formatted properly. Only three variables are used and there's no fancy stuff.",[11,10874,10875,10876,10879,10880,10882],{},"Now take a look at the ",[53,10877,10878],{},"getOddNumbers"," function. Did you see that the function does the same as the ",[53,10881,10871],{}," function?",[11,10884,10885],{},"Notice that the simplicity of the code has not changed. It still has the same number of operators and variables, with the same number of nesting levels. The only thing that has changed, is that the code has become much more explicit.",[11,10887,10888],{},"With some simple name changes, it is suddenly much easier to tell what this piece of code does.",[1758,10890,10892],{"id":10891},"avoid-using-plain-values","Avoid using plain values",[11,10894,10895,10896,10899],{},"The so-called ",[1738,10897,10898],{},"magic numbers"," are numbers that appear in your source code without any explanation of what they stand for (this is also applicable to other primitive types like Booleans). Usually, this is because they are used directly rather than being referenced as a variable or constant.",[11,10901,10902],{},"These plain values make your code difficult to understand as there's no explanation of what the value stands for or how it was determined.",[11,10904,10905],{},"Let's have a look at this function that calculates the potential energy given the mass and the height of an object.",[58,10907,10909],{"className":8875,"code":10908,"language":8877,"meta":63,"style":63},"function potentialEnergy(mass, height) {\n  return mass * height * 9.81;\n}\n",[53,10910,10911,10930,10950],{"__ignoreMap":63},[67,10912,10913,10915,10918,10920,10923,10925,10928],{"class":69,"line":70},[67,10914,10682],{"class":163},[67,10916,10917],{"class":73}," potentialEnergy",[67,10919,2556],{"class":104},[67,10921,10922],{"class":2508},"mass",[67,10924,944],{"class":104},[67,10926,10927],{"class":2508},"height",[67,10929,2575],{"class":104},[67,10931,10932,10934,10937,10940,10943,10945,10948],{"class":69,"line":251},[67,10933,5698],{"class":163},[67,10935,10936],{"class":104}," mass ",[67,10938,10939],{"class":163},"*",[67,10941,10942],{"class":104}," height ",[67,10944,10939],{"class":163},[67,10946,10947],{"class":113}," 9.81",[67,10949,2485],{"class":104},[67,10951,10952],{"class":69,"line":264},[67,10953,1601],{"class":104},[11,10955,10956,10957,10960],{},"As you can see, we use a ",[1738,10958,10959],{},"magic number"," in our function. Some of us probably know what does it stand for, but it will be much clear if we replace the magic number with a constant that represents what does that value stands for.",[58,10962,10964],{"className":8875,"code":10963,"language":8877,"meta":63,"style":63},"const GRAVITATIONAL_CONSTANT = 9.81;\n\nfunction potentialEnergy(mass, height) {\n  return mass * height * GRAVITATIONAL_CONSTANT;\n}\n",[53,10965,10966,10979,10983,10999,11015],{"__ignoreMap":63},[67,10967,10968,10970,10973,10975,10977],{"class":69,"line":70},[67,10969,2722],{"class":163},[67,10971,10972],{"class":113}," GRAVITATIONAL_CONSTANT",[67,10974,2512],{"class":163},[67,10976,10947],{"class":113},[67,10978,2485],{"class":104},[67,10980,10981],{"class":69,"line":251},[67,10982,989],{"emptyLinePlaceholder":988},[67,10984,10985,10987,10989,10991,10993,10995,10997],{"class":69,"line":264},[67,10986,10682],{"class":163},[67,10988,10917],{"class":73},[67,10990,2556],{"class":104},[67,10992,10922],{"class":2508},[67,10994,944],{"class":104},[67,10996,10927],{"class":2508},[67,10998,2575],{"class":104},[67,11000,11001,11003,11005,11007,11009,11011,11013],{"class":69,"line":280},[67,11002,5698],{"class":163},[67,11004,10936],{"class":104},[67,11006,10939],{"class":163},[67,11008,10942],{"class":104},[67,11010,10939],{"class":163},[67,11012,10972],{"class":113},[67,11014,2485],{"class":104},[67,11016,11017],{"class":69,"line":288},[67,11018,1601],{"class":104},[11,11020,11021],{},"We can expand this rule even further. Let's say we want to express how many milliseconds there are in an hour and use it somewhere in our code.",[58,11023,11025],{"className":8875,"code":11024,"language":8877,"meta":63,"style":63},"const MILLISECONDS_IN_ONE_HOUR = 3600000;\n",[53,11026,11027],{"__ignoreMap":63},[67,11028,11029,11031,11034,11036,11039],{"class":69,"line":70},[67,11030,2722],{"class":163},[67,11032,11033],{"class":113}," MILLISECONDS_IN_ONE_HOUR",[67,11035,2512],{"class":163},[67,11037,11038],{"class":113}," 3600000",[67,11040,2485],{"class":104},[11,11042,11043],{},"Instead of just writing the number, we can preserve the calculations inside our code instead of just using the number. This allows other developers to not only see what the number means, but also understand how it was determined.",[58,11045,11047],{"className":8875,"code":11046,"language":8877,"meta":63,"style":63},"const MILLISECONDS_IN_ONE_HOUR = 60 * 60 * 1000;\n",[53,11048,11049],{"__ignoreMap":63},[67,11050,11051,11053,11055,11057,11060,11063,11065,11067,11070],{"class":69,"line":70},[67,11052,2722],{"class":163},[67,11054,11033],{"class":113},[67,11056,2512],{"class":163},[67,11058,11059],{"class":113}," 60",[67,11061,11062],{"class":163}," *",[67,11064,11059],{"class":113},[67,11066,11062],{"class":163},[67,11068,11069],{"class":113}," 1000",[67,11071,2485],{"class":104},[11,11073,11074],{},"Moving on to an example with a boolean flag. Boolean flags can make for hard-to-understand code, so we should try to avoid them as much as possible. Consider having this function that accepts boolean as a second argument.",[58,11076,11078],{"className":8875,"code":11077,"language":8877,"meta":63,"style":63},"object.setData({ x: 1 }, true);\n",[53,11079,11080],{"__ignoreMap":63},[67,11081,11082,11085,11088,11091,11094,11097,11099],{"class":69,"line":70},[67,11083,11084],{"class":104},"object.",[67,11086,11087],{"class":73},"setData",[67,11089,11090],{"class":104},"({ x: ",[67,11092,11093],{"class":113},"1",[67,11095,11096],{"class":104}," }, ",[67,11098,1579],{"class":113},[67,11100,2745],{"class":104},[11,11102,11103,11104,11106,11107,11110,11111,11114],{},"What is the meaning of ",[53,11105,1579],{},"? We have no idea unless you dig into the source for ",[53,11108,11109],{},"setData()"," and find out. If we assume that we looked at the code and found out that it's just a flag to merge ",[53,11112,11113],{},"key:value"," pairs with existing ones within the object, what would be the best way to avoid using plain values? Using constants? Well, yeah but there is even a better way to handle this situation. Instead of adding a boolean flag, we can make a new function that merges the data by default.",[58,11116,11118],{"className":8875,"code":11117,"language":8877,"meta":63,"style":63},"object.mergeData({ x: 1 });\n",[53,11119,11120],{"__ignoreMap":63},[67,11121,11122,11124,11127,11129,11131],{"class":69,"line":70},[67,11123,11084],{"class":104},[67,11125,11126],{"class":73},"mergeData",[67,11128,11090],{"class":104},[67,11130,11093],{"class":113},[67,11132,11133],{"class":104}," });\n",[1758,11135,11137],{"id":11136},"use-names-you-can-pronounce-and-search","Use names you can pronounce and search",[11,11139,11140,11141,11144],{},"Pretend we have a variable name called ",[53,11142,11143],{},"const xsq",", which is a very important abbreviation for your company. Imagine a conversation with a colleague:",[26,11146,11147,11150],{},[29,11148,11149],{},"“Hey, what about that variable eks ess kjew?”",[29,11151,11152],{},"“You mean the access queue?”",[11,11154,11155],{},"Some developers will try to pronounce the variable as one word. Others will spell out the word.",[11,11157,11158,11159,11161],{},"Names that consist of one letter have the problem that they can't be located easily. Imagine having a variable named ",[53,11160,1711],{},", and you want to find and replace all occurrences of that variable? Sounds impossible, right?",[1758,11163,11165],{"id":11164},"dont-use-abbreviations","Don't use abbreviations",[11,11167,11168,11169,11172,11173,11176,11177,11179,11180,10251,11182,11185,11186,11188],{},"You should also try to avoid abbreviations as well. For example, let's say you're using ",[53,11170,11171],{},"cat"," instead of ",[53,11174,11175],{},"category",". Does ",[53,11178,11171],{}," mean a ",[53,11181,11175],{},[53,11183,11184],{},"catalog","? (or maybe it's an actual ",[53,11187,11171],{},"!?)",[11,11190,11191],{},"Sure, you know the answer, but it still creates ambiguity. As we saw earlier, you should avoid it as much as possible. Saving a few characters isn't worth the confusion that an abbreviation might bring.",[1758,11193,11195],{"id":11194},"replace-conditional-expression-with-functionvariables","Replace conditional expression with function\u002Fvariables",[11,11197,11198],{},"If statements with multiple operands can often be hard to understand without a comment. We can apply a similar method as above to clarify them. Have a look at this piece of code.",[58,11200,11202],{"className":8875,"code":11201,"language":8877,"meta":63,"style":63},"if (!el.offsetWidth || !el.offsetHeight) {\n  \u002F\u002F\n}\n",[53,11203,11204,11223,11227],{"__ignoreMap":63},[67,11205,11206,11208,11210,11212,11215,11218,11220],{"class":69,"line":70},[67,11207,3486],{"class":163},[67,11209,4881],{"class":104},[67,11211,4542],{"class":163},[67,11213,11214],{"class":104},"el.offsetWidth ",[67,11216,11217],{"class":163},"||",[67,11219,7860],{"class":163},[67,11221,11222],{"class":104},"el.offsetHeight) {\n",[67,11224,11225],{"class":69,"line":251},[67,11226,6764],{"class":3401},[67,11228,11229],{"class":69,"line":264},[67,11230,1601],{"class":104},[11,11232,11233],{},"What is the purpose of the code above? It can have multiple purposes, one of which is whether the element is visible in the viewport. We found the purpose, so now let's extract it to a variable or even better a reusable function.",[58,11235,11237],{"className":8875,"code":11236,"language":8877,"meta":63,"style":63},"function isVisible(el) {\n  return !el.offsetWidth || !el.offsetHeight;\n}\n\nif (isVisible(el)) {\n}\n\n\u002F** OR **\u002F\n\nconst isVisible = el.offsetWidth && el.offsetHeight;\n\nif (!isVisible) {\n}\n",[53,11238,11239,11253,11268,11272,11276,11288,11292,11296,11301,11305,11322,11326,11337],{"__ignoreMap":63},[67,11240,11241,11243,11246,11248,11251],{"class":69,"line":70},[67,11242,10682],{"class":163},[67,11244,11245],{"class":73}," isVisible",[67,11247,2556],{"class":104},[67,11249,11250],{"class":2508},"el",[67,11252,2575],{"class":104},[67,11254,11255,11257,11259,11261,11263,11265],{"class":69,"line":251},[67,11256,5698],{"class":163},[67,11258,7860],{"class":163},[67,11260,11214],{"class":104},[67,11262,11217],{"class":163},[67,11264,7860],{"class":163},[67,11266,11267],{"class":104},"el.offsetHeight;\n",[67,11269,11270],{"class":69,"line":264},[67,11271,1601],{"class":104},[67,11273,11274],{"class":69,"line":280},[67,11275,989],{"emptyLinePlaceholder":988},[67,11277,11278,11280,11282,11285],{"class":69,"line":288},[67,11279,3486],{"class":163},[67,11281,4881],{"class":104},[67,11283,11284],{"class":73},"isVisible",[67,11286,11287],{"class":104},"(el)) {\n",[67,11289,11290],{"class":69,"line":781},[67,11291,1601],{"class":104},[67,11293,11294],{"class":69,"line":792},[67,11295,989],{"emptyLinePlaceholder":988},[67,11297,11298],{"class":69,"line":803},[67,11299,11300],{"class":3401},"\u002F** OR **\u002F\n",[67,11302,11303],{"class":69,"line":814},[67,11304,989],{"emptyLinePlaceholder":988},[67,11306,11307,11309,11311,11313,11316,11319],{"class":69,"line":825},[67,11308,2722],{"class":163},[67,11310,11245],{"class":113},[67,11312,2512],{"class":163},[67,11314,11315],{"class":104}," el.offsetWidth ",[67,11317,11318],{"class":163},"&&",[67,11320,11321],{"class":104}," el.offsetHeight;\n",[67,11323,11324],{"class":69,"line":836},[67,11325,989],{"emptyLinePlaceholder":988},[67,11327,11328,11330,11332,11334],{"class":69,"line":847},[67,11329,3486],{"class":163},[67,11331,4881],{"class":104},[67,11333,4542],{"class":163},[67,11335,11336],{"class":104},"isVisible) {\n",[67,11338,11339],{"class":69,"line":858},[67,11340,1601],{"class":104},[11,11342,11343],{},"Extracting the conditional to a variable makes sense when the logic you want to clarify is very specific to a certain algorithm used only in one place. The most common use for this method is mathematical expressions.",[58,11345,11347],{"className":8875,"code":11346,"language":8877,"meta":63,"style":63},"return a * b + c \u002F d;\n",[53,11348,11349],{"__ignoreMap":63},[67,11350,11351,11354,11357,11359,11362,11365,11368,11371],{"class":69,"line":70},[67,11352,11353],{"class":163},"return",[67,11355,11356],{"class":104}," a ",[67,11358,10939],{"class":163},[67,11360,11361],{"class":104}," b ",[67,11363,11364],{"class":163},"+",[67,11366,11367],{"class":104}," c ",[67,11369,11370],{"class":163},"\u002F",[67,11372,11373],{"class":104}," d;\n",[11,11375,11376],{},"We can clarify the above by splitting the calculation:",[58,11378,11380],{"className":8875,"code":11379,"language":8877,"meta":63,"style":63},"const multiplier = a * b;\nconst divisor = c \u002F d;\nreturn multiplier + divisor;\n",[53,11381,11382,11398,11413],{"__ignoreMap":63},[67,11383,11384,11386,11389,11391,11393,11395],{"class":69,"line":70},[67,11385,2722],{"class":163},[67,11387,11388],{"class":113}," multiplier",[67,11390,2512],{"class":163},[67,11392,11356],{"class":104},[67,11394,10939],{"class":163},[67,11396,11397],{"class":104}," b;\n",[67,11399,11400,11402,11405,11407,11409,11411],{"class":69,"line":251},[67,11401,2722],{"class":163},[67,11403,11404],{"class":113}," divisor",[67,11406,2512],{"class":163},[67,11408,11367],{"class":104},[67,11410,11370],{"class":163},[67,11412,11373],{"class":104},[67,11414,11415,11417,11420,11422],{"class":69,"line":264},[67,11416,11353],{"class":163},[67,11418,11419],{"class":104}," multiplier ",[67,11421,11364],{"class":163},[67,11423,11424],{"class":104}," divisor;\n",[1758,11426,11428],{"id":11427},"class-and-module-interfaces","Class and module interfaces",[11,11430,11431,11432,944,11434,944,11437,11440,11441,944,11444,10251,11447,11449],{},"Class name and object name should be noun or noun phrase (",[53,11433,2762],{},[53,11435,11436],{},"Customer",[53,11438,11439],{},"Account",", etc). You should avoid words like ",[53,11442,11443],{},"Data",[53,11445,11446],{},"Information",[53,11448,2106],{}," (they don't give clear explanation of class or object. There can be different types of data or information). A verb as class name is highly discouraged.",[11,11451,11452],{},"The interface — that is, the public methods and properties — of a class or module can act as documentation on its usage. Let's have a look at one example.",[58,11454,11456],{"className":8875,"code":11455,"language":8877,"meta":63,"style":63},"class Box {\n  setState(state) {\n    this.state = state;\n  }\n\n  getState() {\n    return this.state;\n  }\n}\n",[53,11457,11458,11467,11479,11491,11495,11499,11506,11515,11519],{"__ignoreMap":63},[67,11459,11460,11462,11465],{"class":69,"line":70},[67,11461,2494],{"class":163},[67,11463,11464],{"class":73}," Box",[67,11466,2500],{"class":104},[67,11468,11469,11472,11474,11477],{"class":69,"line":251},[67,11470,11471],{"class":73},"  setState",[67,11473,2556],{"class":104},[67,11475,11476],{"class":2508},"state",[67,11478,2575],{"class":104},[67,11480,11481,11483,11486,11488],{"class":69,"line":264},[67,11482,2580],{"class":113},[67,11484,11485],{"class":104},".state ",[67,11487,2586],{"class":163},[67,11489,11490],{"class":104}," state;\n",[67,11492,11493],{"class":69,"line":280},[67,11494,1596],{"class":104},[67,11496,11497],{"class":69,"line":288},[67,11498,989],{"emptyLinePlaceholder":988},[67,11500,11501,11504],{"class":69,"line":781},[67,11502,11503],{"class":73},"  getState",[67,11505,2617],{"class":104},[67,11507,11508,11510,11512],{"class":69,"line":792},[67,11509,2674],{"class":163},[67,11511,2644],{"class":113},[67,11513,11514],{"class":104},".state;\n",[67,11516,11517],{"class":69,"line":803},[67,11518,1596],{"class":104},[67,11520,11521],{"class":69,"line":814},[67,11522,1601],{"class":104},[11,11524,11525],{},"This class could contain some other code in it as well. I purposely kept the example simple, to illustrate how the public interface is documentation. Can you tell how this class should be used? Maybe with a little bit of work, but it isn't very obvious on the first read.",[11,11527,11528],{},"Both of the functions have reasonable names: what they do is clear from their name. But despite this, it's not very clear how we should be using them. Most likely we would need to read more code or the documentation for the class to figure it out.",[11,11530,11531],{},"In this example, even though we named the class and its methods clearly, we can go even further.",[58,11533,11535],{"className":8875,"code":11534,"language":8877,"meta":63,"style":63},"const BOX_OPEN = \"open\";\nconst BOX_CLOSED = \"closed\";\n\nclass Box {\n  open() {\n    this.state = BOX_OPEN;\n  }\n\n  close() {\n    this.state = BOX_CLOSED;\n  }\n\n  isOpen() {\n    return this.state === BOX_OPEN;\n  }\n}\n",[53,11536,11537,11551,11565,11569,11577,11584,11596,11600,11604,11611,11623,11627,11631,11638,11652,11656],{"__ignoreMap":63},[67,11538,11539,11541,11544,11546,11549],{"class":69,"line":70},[67,11540,2722],{"class":163},[67,11542,11543],{"class":113}," BOX_OPEN",[67,11545,2512],{"class":163},[67,11547,11548],{"class":77}," \"open\"",[67,11550,2485],{"class":104},[67,11552,11553,11555,11558,11560,11563],{"class":69,"line":251},[67,11554,2722],{"class":163},[67,11556,11557],{"class":113}," BOX_CLOSED",[67,11559,2512],{"class":163},[67,11561,11562],{"class":77}," \"closed\"",[67,11564,2485],{"class":104},[67,11566,11567],{"class":69,"line":264},[67,11568,989],{"emptyLinePlaceholder":988},[67,11570,11571,11573,11575],{"class":69,"line":280},[67,11572,2494],{"class":163},[67,11574,11464],{"class":73},[67,11576,2500],{"class":104},[67,11578,11579,11582],{"class":69,"line":288},[67,11580,11581],{"class":73},"  open",[67,11583,2617],{"class":104},[67,11585,11586,11588,11590,11592,11594],{"class":69,"line":781},[67,11587,2580],{"class":113},[67,11589,11485],{"class":104},[67,11591,2586],{"class":163},[67,11593,11543],{"class":113},[67,11595,2485],{"class":104},[67,11597,11598],{"class":69,"line":792},[67,11599,1596],{"class":104},[67,11601,11602],{"class":69,"line":803},[67,11603,989],{"emptyLinePlaceholder":988},[67,11605,11606,11609],{"class":69,"line":814},[67,11607,11608],{"class":73},"  close",[67,11610,2617],{"class":104},[67,11612,11613,11615,11617,11619,11621],{"class":69,"line":825},[67,11614,2580],{"class":113},[67,11616,11485],{"class":104},[67,11618,2586],{"class":163},[67,11620,11557],{"class":113},[67,11622,2485],{"class":104},[67,11624,11625],{"class":69,"line":836},[67,11626,1596],{"class":104},[67,11628,11629],{"class":69,"line":847},[67,11630,989],{"emptyLinePlaceholder":988},[67,11632,11633,11636],{"class":69,"line":858},[67,11634,11635],{"class":73},"  isOpen",[67,11637,2617],{"class":104},[67,11639,11640,11642,11644,11646,11648,11650],{"class":69,"line":869},[67,11641,2674],{"class":163},[67,11643,2644],{"class":113},[67,11645,11485],{"class":104},[67,11647,3770],{"class":163},[67,11649,11543],{"class":113},[67,11651,2485],{"class":104},[67,11653,11654],{"class":69,"line":877},[67,11655,1596],{"class":104},[67,11657,11658],{"class":69,"line":886},[67,11659,1601],{"class":104},[11,11661,11662,11663,11666],{},"We can now see its much easier to guess the usage of our class. Notice that we only changed the public interface; the internal representation is still the same with the ",[53,11664,11665],{},"this.state"," property.",[11,11668,11669,11670,11673],{},"Now we can tell at a glance how the ",[53,11671,11672],{},"Box"," class is used. This shows that even though the first version had good names in the functions, the complete package was still confusing, and how, with simple decisions like this we can have a very big impact.",[1758,11675,11677],{"id":11676},"use-language-features-to-your-advantage","Use language features to your advantage",[11,11679,11680],{},"We can even use some features of our chosen language to better communicate the intention behind some code. A good example of this in JavaScript are the array iteration methods.",[58,11682,11684],{"className":8875,"code":11683,"language":8877,"meta":63,"style":63},"const ids = [];\n\nfor (let i = 0; i \u003C items.length; i++) {\n  ids.push(items[i].id);\n}\n",[53,11685,11686,11697,11701,11737,11747],{"__ignoreMap":63},[67,11687,11688,11690,11693,11695],{"class":69,"line":70},[67,11689,2722],{"class":163},[67,11691,11692],{"class":113}," ids",[67,11694,2512],{"class":163},[67,11696,10704],{"class":104},[67,11698,11699],{"class":69,"line":251},[67,11700,989],{"emptyLinePlaceholder":988},[67,11702,11703,11706,11708,11711,11714,11716,11718,11721,11723,11726,11729,11732,11735],{"class":69,"line":264},[67,11704,11705],{"class":163},"for",[67,11707,4881],{"class":104},[67,11709,11710],{"class":163},"let",[67,11712,11713],{"class":104}," i ",[67,11715,2586],{"class":163},[67,11717,10740],{"class":113},[67,11719,11720],{"class":104},"; i ",[67,11722,5778],{"class":163},[67,11724,11725],{"class":104}," items.",[67,11727,11728],{"class":113},"length",[67,11730,11731],{"class":104},"; i",[67,11733,11734],{"class":163},"++",[67,11736,2575],{"class":104},[67,11738,11739,11742,11744],{"class":69,"line":280},[67,11740,11741],{"class":104},"  ids.",[67,11743,10750],{"class":73},[67,11745,11746],{"class":104},"(items[i].id);\n",[67,11748,11749],{"class":69,"line":288},[67,11750,1601],{"class":104},[11,11752,11753,11754,11757],{},"The above code collects a list of IDs into a new array. However, in order to know that, we need to read the whole body of the loop. Compare it with using the ",[53,11755,11756],{},"map()"," function.",[58,11759,11761],{"className":8875,"code":11760,"language":8877,"meta":63,"style":63},"const ids = items.map(function (item) {\n  return item.id;\n});\n",[53,11762,11763,11786,11793],{"__ignoreMap":63},[67,11764,11765,11767,11769,11771,11773,11775,11777,11779,11781,11784],{"class":69,"line":70},[67,11766,2722],{"class":163},[67,11768,11692],{"class":113},[67,11770,2512],{"class":163},[67,11772,11725],{"class":104},[67,11774,10572],{"class":73},[67,11776,2556],{"class":104},[67,11778,10682],{"class":163},[67,11780,4881],{"class":104},[67,11782,11783],{"class":2508},"item",[67,11785,2575],{"class":104},[67,11787,11788,11790],{"class":69,"line":251},[67,11789,5698],{"class":163},[67,11791,11792],{"class":104}," item.id;\n",[67,11794,11795],{"class":69,"line":264},[67,11796,11797],{"class":104},"});\n",[11,11799,11800,11801,56],{},"In this case, we immediately know that this produces a new array of something, because that's the purpose of map(). This can be beneficial especially if you have more complicated looping logic. There's a list of other ",[1711,11802,11806],{"href":11803,"rel":11804,"target":11805},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FArray#Iteration_methods",[1715],"\\_blank","iteration functions on MDN",[11,11808,11809],{},"Another example with JavaScript is the const keyword. Often, you declare variables where the value is supposed to never change. A very common example is when loading modules with CommonJS:",[58,11811,11813],{"className":8875,"code":11812,"language":8877,"meta":63,"style":63},"var async = require(\"async\");\n",[53,11814,11815],{"__ignoreMap":63},[67,11816,11817,11820,11823,11825,11827,11829,11832],{"class":69,"line":70},[67,11818,11819],{"class":163},"var",[67,11821,11822],{"class":104}," async ",[67,11824,2586],{"class":163},[67,11826,9187],{"class":73},[67,11828,2556],{"class":104},[67,11830,11831],{"class":77},"\"async\"",[67,11833,2745],{"class":104},[11,11835,11836],{},"We can make the intention of never changing this even more clear:",[58,11838,11840],{"className":8875,"code":11839,"language":8877,"meta":63,"style":63},"const async = require(\"async\");\n",[53,11841,11842],{"__ignoreMap":63},[67,11843,11844,11846,11848,11850,11852,11854,11856],{"class":69,"line":70},[67,11845,2722],{"class":163},[67,11847,5611],{"class":113},[67,11849,2512],{"class":163},[67,11851,9187],{"class":73},[67,11853,2556],{"class":104},[67,11855,11831],{"class":77},[67,11857,2745],{"class":104},[11,11859,11860],{},"As an added benefit, if someone ever accidentally tries to change this, we'll now get an error.",[45,11862,11864],{"id":11863},"empathize-with-others","Empathize with others",[11,11866,11867],{},"Indeed, you're not always coding software with complex business requirements. But that doesn't mean that there isn't some merit to the idea of creating a ubiquitous language. That's what the previous example tried to show you.",[11,11869,11870],{},"Creating a ubiquitous language forces you to empathize with others. You need to find words that aren't ambiguous and that everyone can understand. The result is that it's easier for others to work with both your application and your code.",[11,11872,11873],{},"We should also empathize with our future selves. That person is often not that different from someone who has to read your code for the first time. That person and others that will read your code deserve a consistent language made up of words that are simple and unambiguous.",[6201,11875],{},[11,11877,11878],{},[21,11879,11880],{},"We often forget that writing code isn't just about solving problems. We're still writing something that others will read. That's the essence of why naming is important when you write code.",[1670,11882,11883],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":63,"searchDepth":251,"depth":251,"links":11885},[11886,11887,11896],{"id":10619,"depth":251,"text":10620},{"id":10638,"depth":251,"text":10639,"children":11888},[11889,11890,11891,11892,11893,11894,11895],{"id":10648,"depth":264,"text":10649},{"id":10891,"depth":264,"text":10892},{"id":11136,"depth":264,"text":11137},{"id":11164,"depth":264,"text":11165},{"id":11194,"depth":264,"text":11195},{"id":11427,"depth":264,"text":11428},{"id":11676,"depth":264,"text":11677},{"id":11863,"depth":251,"text":11864},"Computer Science","2019\u002F10\u002F01","\u002Fimages\u002Fnaming-in-programming.jpg","programming, naming variables, naming classes, computer science, cache invalidation, naming functions,",{},"\u002Fblog\u002Fnaming-and-its-importance-in-programming","☕️☕️ 13 min read",{"title":10498,"description":10503},"naming-and-its-importance-in-programming","blog\u002Fnaming-and-its-importance-in-programming","computer science, programming","9BGWBRx-JP7B5uGRTsmRUMMJJ4JiubOQHkWMrjzuuy0",{"id":11910,"title":11911,"body":11912,"category":13015,"date":13016,"description":11916,"excerpt":1690,"extension":1691,"image":13017,"keywords":13018,"meta":13019,"navigation":988,"path":13020,"readingTime":13021,"seo":13022,"slug":13023,"stem":13024,"tags":13025,"__hash__":13026},"blog\u002Fblog\u002Fthe-road-to-a-static-portfolio-website-with-a-blog.md","The road to a static portfolio website with a blog",{"type":8,"value":11913,"toc":13008},[11914,11917,11920,11923,11927,11930,11933,11936,11940,11943,11950,11954,11992,12001,12007,12011,12014,12035,12050,12331,12338,12682,12693,12957,12960,12964,12967,12970,12983,12992,12994,12997,13000,13005],[11,11915,11916],{},"The way you've built and designed your portfolio website says a lot about yourself as a developer. And why are we even building these websites? Well, I think it's a really nice and convenient way to showcase your profile and introduce yourself to the world. Many newcomers that join our developers' community are building their portfolio websites as a way to learn new technology and gain a little bit of experience in building websites. Also probably because everyone is tired of making a ToDo list.",[11,11918,11919],{},"I had redesigned and rebuilt my portfolio website a few times now. Since we want to showcase ourselves in the best possible way, I always listed my skills, projects I am working on, a little bit about myself and a contact form. The first version I built was built in native PHP, and it wasn't even uploaded online. After that, I wanted to experiment with Vue.js and I built the second version of my portfolio website, which at the time was a really nice looking and functional SPA (Single Page Application). However, I couldn't write any blog posts to express my thoughts on that version, so I redesigned it using Laravel and Tailwind by also adding a small dashboard for me to conveniently publish new blog posts, change the content on the website - because I really didn't want to upload a new version every time I want to change something.",[11,11921,11922],{},"So, everything looks perfect now. The website is live, changing stuff on it is easy, but ...",[45,11924,11926],{"id":11925},"sometimes-we-need-to-take-a-break","Sometimes we need to take a break",[11,11928,11929],{},"Working on personal stuff always gives you relief and keeps you motivated because you are working for yourself, and not for someone else. Sometimes, we need to take a break from brain-intensive tasks and just work on something that isn't so demanding. Building something for yourself, even if its something that you can build for a day always kept me satisfied with the work I am doing and why I choose to become a developer in the first place. If you ever find yourself stuck in a project that seems it's going on forever and doesn't feel rewarding when you are completing tasks, take the weekend and built something for yourself.",[11,11931,11932],{},"Another reason why I have decided to rebuild my website is to improve its speed and usability. I also wanted it to be more blog-focused since I have experienced a big flow of visitors to some of the blog posts I have written, which means that some people like what I write.",[11,11934,11935],{},"So, how to improve the speed and usability of the portfolio? By building a static single page application.",[45,11937,11939],{"id":11938},"the-new-way-of-building-static-single-page-application","The new way of building static single page application",[11,11941,11942],{},"A static single page application is an HTML file, with some CSS and JavaScript that doesn't require a server to load. Since we are serving static HTML pages, our visitors experience a big improvement in speed while using our website. Did you know that almost 30% of your visitors will not wait for more than 3seconds for your application to load before leaving? Sometimes, even if you optimize your web application, loading can be slower than you expected because of your server. By generating (building) a static SPA your visitors don't have to wait for the server to receive and respond to the request, all of your pages are served instantly.",[11,11944,11945,11946,11949],{},"There are tons of ways to build static SPA's (Nuxt, Gridsome, Gatsby, VuePress ...). These frameworks\u002Flibraries, give you pre-configured working environments which you can use to build your portfolio website fairly quickly. For this SPA, I am using Nuxt.js, which is a framework that gives you a boilerplate with a lot of configuration to make your development experience enjoyable. With Nuxt.js you can build universal web applications - SSR (server-side rendered) or client-side rendered. It also gives you a way to quickly generate a ",[21,11947,11948],{},"static single page application"," which is exactly what I wanted. And yes, since my preferred library for building UI's is Vue.js, adopting Nuxt.js in my workflow was pretty easy.",[45,11951,11953],{"id":11952},"generating-static-pages","Generating static pages",[11,11955,11956,11957,11960,11961,11964,11965,11967,11968,11970,11971,11974,11975,11978,11979,11982,11983,11985,11986,11988,11989,11991],{},"Nuxt includes a command ",[53,11958,11959],{},"nuxt generate",", that generates static pages for all ",[53,11962,11963],{},".vue"," files in your project's ",[53,11966,9468],{}," directory. Simple as that. Build your pages, run a simple command and you have a static SPA ready to be deployed. But, ",[53,11969,11959],{},", doesn't generate static pages for ",[1738,11972,11973],{},"dynamic"," pages. A dynamic page is a page which shows content depending on a route parameter. For example, if we want to show a blog post when a visitor navigates to ",[53,11976,11977],{},"blog\u002F{post}",", the ",[53,11980,11981],{},"{post}"," part in the URL is the ",[53,11984,5512],{}," that we want to show to the user. But, since we can type anything we want in the ",[53,11987,11981],{}," parameter (therefore the page is dynamic), the ",[53,11990,11959],{}," command doesn't generate static pages for those routes.",[11,11993,11994,11995,11997,11998,12000],{},"One way we can solve this 'problem', is to explicitly tell Nuxt to generate static pages for some values of the ",[53,11996,11981],{}," parameter. In this case, since the blog will always have a finite number of posts, we can just get all the posts, and generate a ",[53,11999,11977],{}," static page that will be served to the visitor whenever he wants to visit a particular post. Great, now we even have static pages for our dynamic post page.",[11,12002,12003,12004],{},"The next thing we need to do is to be able to write and elegantly handle blog posts. Since I don't want to use any database, every post will be stored as a single markdown file. All of the posts that I would write in the future should be handled and shown properly while also generating an appropriate static page for the new post, because doing it manually would just return me back to the previous version - and I want this one ",[21,12005,12006],{},"to be the last version of my portfolio I build.",[45,12008,12010],{"id":12009},"handing-of-new-posts-and-using-markdown-to-write-content","Handing of new posts and using markdown to write content",[11,12012,12013],{},"My final goal is to just make a new markdown file, commit the changes, push them to a repository and they are automatically shown on the website (later in this post a bit more about the past part). But, how to parse and convert markdown and render it in JavaScript?",[11,12015,12016,12017,944,12020,944,12023,12026,12027,12030,12031,12034],{},"Every post has a slug in their attributes list. When a user visits a post URL, we get the slug in the URL and try to load the markdown file for that post. When the post is loaded, we get its YAML properties (post attributes) like ",[1738,12018,12019],{},"title",[1738,12021,12022],{},"slug",[1738,12024,12025],{},"date",", etc using the ",[53,12028,12029],{},"front-matter"," package. The content of the post is converted from markdown to HTML using the ",[53,12032,12033],{},"markdownit"," package. Before we display the content, we apply to highlight for the code snippets written in the post. That's it!",[11,12036,12037],{},[1738,12038,12039,2004,12042,12045,12046,12049],{},[53,12040,12041],{},"formatReadingTime",[53,12043,12044],{},"formatPostDate"," are just functions that I use to format the posts reading time and date created. ",[53,12047,12048],{},"markdown-it-attrs"," is a package used to parse HTML attributes added your markdown.",[58,12051,12053],{"className":8875,"code":12052,"language":8877,"meta":63,"style":63},"\u002F* pages\u002Fblog\u002F_post.vue - Handling and rendering the post *\u002F\n\nimport { formatReadingTime, formatPostDate } from '~\u002Futils\u002Fformatter';\n\nconst fm = require('front-matter');\nconst md = require('markdown-it')({\n    html: true,\n    linkify: true,\n    typographer: true,\n})\n    .use(require('markdown-it-highlightjs'))\n    .use(require('markdown-it-attrs'));\n\nexport default {\n    async asyncData({ params, app: { $md } }) {\n        const result = await import(`~\u002Fposts\u002F${params.post}.md`);\n        const post = fm(result.default);\n\n        return {\n            attributes: post.attributes,\n            content: md.render(post.body),\n            readingTime: formatReadingTime(post.body),\n            date: formatPostDate(post.attributes.date),\n        };\n    },\n};\n",[53,12054,12055,12060,12064,12078,12082,12100,12119,12128,12137,12146,12151,12171,12189,12193,12201,12228,12259,12273,12277,12284,12289,12299,12308,12318,12323,12327],{"__ignoreMap":63},[67,12056,12057],{"class":69,"line":70},[67,12058,12059],{"class":3401},"\u002F* pages\u002Fblog\u002F_post.vue - Handling and rendering the post *\u002F\n",[67,12061,12062],{"class":69,"line":251},[67,12063,989],{"emptyLinePlaceholder":988},[67,12065,12066,12068,12071,12073,12076],{"class":69,"line":264},[67,12067,2473],{"class":163},[67,12069,12070],{"class":104}," { formatReadingTime, formatPostDate } ",[67,12072,2479],{"class":163},[67,12074,12075],{"class":77}," '~\u002Futils\u002Fformatter'",[67,12077,2485],{"class":104},[67,12079,12080],{"class":69,"line":280},[67,12081,989],{"emptyLinePlaceholder":988},[67,12083,12084,12086,12089,12091,12093,12095,12098],{"class":69,"line":288},[67,12085,2722],{"class":163},[67,12087,12088],{"class":113}," fm",[67,12090,2512],{"class":163},[67,12092,9187],{"class":73},[67,12094,2556],{"class":104},[67,12096,12097],{"class":77},"'front-matter'",[67,12099,2745],{"class":104},[67,12101,12102,12104,12107,12109,12111,12113,12116],{"class":69,"line":781},[67,12103,2722],{"class":163},[67,12105,12106],{"class":113}," md",[67,12108,2512],{"class":163},[67,12110,9187],{"class":73},[67,12112,2556],{"class":104},[67,12114,12115],{"class":77},"'markdown-it'",[67,12117,12118],{"class":104},")({\n",[67,12120,12121,12124,12126],{"class":69,"line":792},[67,12122,12123],{"class":104},"    html: ",[67,12125,1579],{"class":113},[67,12127,955],{"class":104},[67,12129,12130,12133,12135],{"class":69,"line":803},[67,12131,12132],{"class":104},"    linkify: ",[67,12134,1579],{"class":113},[67,12136,955],{"class":104},[67,12138,12139,12142,12144],{"class":69,"line":814},[67,12140,12141],{"class":104},"    typographer: ",[67,12143,1579],{"class":113},[67,12145,955],{"class":104},[67,12147,12148],{"class":69,"line":825},[67,12149,12150],{"class":104},"})\n",[67,12152,12153,12156,12158,12160,12163,12165,12168],{"class":69,"line":836},[67,12154,12155],{"class":104},"    .",[67,12157,9084],{"class":73},[67,12159,2556],{"class":104},[67,12161,12162],{"class":73},"require",[67,12164,2556],{"class":104},[67,12166,12167],{"class":77},"'markdown-it-highlightjs'",[67,12169,12170],{"class":104},"))\n",[67,12172,12173,12175,12177,12179,12181,12183,12186],{"class":69,"line":847},[67,12174,12155],{"class":104},[67,12176,9084],{"class":73},[67,12178,2556],{"class":104},[67,12180,12162],{"class":73},[67,12182,2556],{"class":104},[67,12184,12185],{"class":77},"'markdown-it-attrs'",[67,12187,12188],{"class":104},"));\n",[67,12190,12191],{"class":69,"line":858},[67,12192,989],{"emptyLinePlaceholder":988},[67,12194,12195,12197,12199],{"class":69,"line":869},[67,12196,5473],{"class":163},[67,12198,242],{"class":163},[67,12200,2500],{"class":104},[67,12202,12203,12206,12209,12212,12215,12217,12219,12222,12225],{"class":69,"line":877},[67,12204,12205],{"class":163},"    async",[67,12207,12208],{"class":73}," asyncData",[67,12210,12211],{"class":104},"({ ",[67,12213,12214],{"class":2508},"params",[67,12216,944],{"class":104},[67,12218,9487],{"class":2508},[67,12220,12221],{"class":104},": { ",[67,12223,12224],{"class":2508},"$md",[67,12226,12227],{"class":104}," } }) {\n",[67,12229,12230,12233,12236,12238,12240,12243,12245,12248,12250,12252,12254,12257],{"class":69,"line":886},[67,12231,12232],{"class":163},"        const",[67,12234,12235],{"class":113}," result",[67,12237,2512],{"class":163},[67,12239,5649],{"class":163},[67,12241,12242],{"class":163}," import",[67,12244,2556],{"class":104},[67,12246,12247],{"class":77},"`~\u002Fposts\u002F${",[67,12249,12214],{"class":104},[67,12251,56],{"class":77},[67,12253,5512],{"class":104},[67,12255,12256],{"class":77},"}.md`",[67,12258,2745],{"class":104},[67,12260,12261,12263,12266,12268,12270],{"class":69,"line":894},[67,12262,12232],{"class":163},[67,12264,12265],{"class":113}," post",[67,12267,2512],{"class":163},[67,12269,12088],{"class":73},[67,12271,12272],{"class":104},"(result.default);\n",[67,12274,12275],{"class":69,"line":905},[67,12276,989],{"emptyLinePlaceholder":988},[67,12278,12279,12282],{"class":69,"line":916},[67,12280,12281],{"class":163},"        return",[67,12283,2500],{"class":104},[67,12285,12286],{"class":69,"line":927},[67,12287,12288],{"class":104},"            attributes: post.attributes,\n",[67,12290,12291,12294,12296],{"class":69,"line":935},[67,12292,12293],{"class":104},"            content: md.",[67,12295,9246],{"class":73},[67,12297,12298],{"class":104},"(post.body),\n",[67,12300,12301,12304,12306],{"class":69,"line":958},[67,12302,12303],{"class":104},"            readingTime: ",[67,12305,12041],{"class":73},[67,12307,12298],{"class":104},[67,12309,12310,12313,12315],{"class":69,"line":971},[67,12311,12312],{"class":104},"            date: ",[67,12314,12044],{"class":73},[67,12316,12317],{"class":104},"(post.attributes.date),\n",[67,12319,12320],{"class":69,"line":985},[67,12321,12322],{"class":104},"        };\n",[67,12324,12325],{"class":69,"line":992},[67,12326,5917],{"class":104},[67,12328,12329],{"class":69,"line":1000},[67,12330,5573],{"class":104},[11,12332,12333,12334,12337],{},"How about finding and browsing through all of the posts? Building the blog index page is simple. We just get all of the posts within our ",[53,12335,12336],{},"posts"," directory, get the needed attributes to display them on the page, sort them by creation date to show latest posts first and we are good to go. We can even add searching\u002Ffiltering of the posts using Vue.js.",[58,12339,12341],{"className":8875,"code":12340,"language":8877,"meta":63,"style":63},"\u002F* blog\u002Findex.vue - Showing all posts *\u002F\n\nconst fm = require('front-matter');\nimport { formatReadingTime, formatPostDate } from '~\u002Futils\u002Fformatter';\n\nexport default {\n    async asyncData() {\n        const resolve = require.context('~\u002Fposts\u002F', true, \u002F\\.md$\u002F);\n        const posts = resolve\n            .keys()\n            .map(key => {\n                const [, name] = key.match(\u002F\\\u002F(.+)\\.md$\u002F);\n                return resolve(key);\n            })\n            .map(post => {\n                const result = fm(post.default);\n                return {\n                    ...result.attributes,\n                    readingTime: formatReadingTime(result.body),\n                    date: formatPostDate(result.attributes.date),\n                };\n            })\n            .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());\n\n        return {\n            posts,\n        };\n    };\n",[53,12342,12343,12348,12352,12368,12380,12384,12392,12400,12444,12456,12465,12480,12526,12536,12541,12555,12568,12574,12582,12592,12602,12607,12611,12659,12663,12669,12674,12678],{"__ignoreMap":63},[67,12344,12345],{"class":69,"line":70},[67,12346,12347],{"class":3401},"\u002F* blog\u002Findex.vue - Showing all posts *\u002F\n",[67,12349,12350],{"class":69,"line":251},[67,12351,989],{"emptyLinePlaceholder":988},[67,12353,12354,12356,12358,12360,12362,12364,12366],{"class":69,"line":264},[67,12355,2722],{"class":163},[67,12357,12088],{"class":113},[67,12359,2512],{"class":163},[67,12361,9187],{"class":73},[67,12363,2556],{"class":104},[67,12365,12097],{"class":77},[67,12367,2745],{"class":104},[67,12369,12370,12372,12374,12376,12378],{"class":69,"line":280},[67,12371,2473],{"class":163},[67,12373,12070],{"class":104},[67,12375,2479],{"class":163},[67,12377,12075],{"class":77},[67,12379,2485],{"class":104},[67,12381,12382],{"class":69,"line":288},[67,12383,989],{"emptyLinePlaceholder":988},[67,12385,12386,12388,12390],{"class":69,"line":781},[67,12387,5473],{"class":163},[67,12389,242],{"class":163},[67,12391,2500],{"class":104},[67,12393,12394,12396,12398],{"class":69,"line":792},[67,12395,12205],{"class":163},[67,12397,12208],{"class":73},[67,12399,2617],{"class":104},[67,12401,12402,12404,12407,12409,12412,12415,12417,12420,12422,12424,12427,12430,12434,12437,12440,12442],{"class":69,"line":803},[67,12403,12232],{"class":163},[67,12405,12406],{"class":113}," resolve",[67,12408,2512],{"class":163},[67,12410,12411],{"class":104}," require.",[67,12413,12414],{"class":73},"context",[67,12416,2556],{"class":104},[67,12418,12419],{"class":77},"'~\u002Fposts\u002F'",[67,12421,944],{"class":104},[67,12423,1579],{"class":113},[67,12425,12426],{"class":104},",",[67,12428,12429],{"class":77}," \u002F",[67,12431,12433],{"class":12432},"snhLl","\\.",[67,12435,1691],{"class":12436},"sA_wV",[67,12438,12439],{"class":163},"$",[67,12441,11370],{"class":77},[67,12443,2745],{"class":104},[67,12445,12446,12448,12451,12453],{"class":69,"line":814},[67,12447,12232],{"class":163},[67,12449,12450],{"class":113}," posts",[67,12452,2512],{"class":163},[67,12454,12455],{"class":104}," resolve\n",[67,12457,12458,12461,12463],{"class":69,"line":825},[67,12459,12460],{"class":104},"            .",[67,12462,10555],{"class":73},[67,12464,8603],{"class":104},[67,12466,12467,12469,12471,12473,12475,12478],{"class":69,"line":836},[67,12468,12460],{"class":104},[67,12470,10572],{"class":73},[67,12472,2556],{"class":104},[67,12474,10577],{"class":2508},[67,12476,12477],{"class":163}," =>",[67,12479,2500],{"class":104},[67,12481,12482,12485,12488,12490,12493,12495,12498,12501,12503,12505,12508,12510,12512,12514,12516,12518,12520,12522,12524],{"class":69,"line":847},[67,12483,12484],{"class":163},"                const",[67,12486,12487],{"class":104}," [, ",[67,12489,2559],{"class":113},[67,12491,12492],{"class":104},"] ",[67,12494,2586],{"class":163},[67,12496,12497],{"class":104}," key.",[67,12499,12500],{"class":73},"match",[67,12502,2556],{"class":104},[67,12504,11370],{"class":77},[67,12506,12507],{"class":12432},"\\\u002F",[67,12509,2556],{"class":12436},[67,12511,56],{"class":113},[67,12513,11364],{"class":163},[67,12515,2258],{"class":12436},[67,12517,12433],{"class":12432},[67,12519,1691],{"class":12436},[67,12521,12439],{"class":163},[67,12523,11370],{"class":77},[67,12525,2745],{"class":104},[67,12527,12528,12531,12533],{"class":69,"line":858},[67,12529,12530],{"class":163},"                return",[67,12532,12406],{"class":73},[67,12534,12535],{"class":104},"(key);\n",[67,12537,12538],{"class":69,"line":869},[67,12539,12540],{"class":104},"            })\n",[67,12542,12543,12545,12547,12549,12551,12553],{"class":69,"line":877},[67,12544,12460],{"class":104},[67,12546,10572],{"class":73},[67,12548,2556],{"class":104},[67,12550,5512],{"class":2508},[67,12552,12477],{"class":163},[67,12554,2500],{"class":104},[67,12556,12557,12559,12561,12563,12565],{"class":69,"line":886},[67,12558,12484],{"class":163},[67,12560,12235],{"class":113},[67,12562,2512],{"class":163},[67,12564,12088],{"class":73},[67,12566,12567],{"class":104},"(post.default);\n",[67,12569,12570,12572],{"class":69,"line":894},[67,12571,12530],{"class":163},[67,12573,2500],{"class":104},[67,12575,12576,12579],{"class":69,"line":905},[67,12577,12578],{"class":163},"                    ...",[67,12580,12581],{"class":104},"result.attributes,\n",[67,12583,12584,12587,12589],{"class":69,"line":916},[67,12585,12586],{"class":104},"                    readingTime: ",[67,12588,12041],{"class":73},[67,12590,12591],{"class":104},"(result.body),\n",[67,12593,12594,12597,12599],{"class":69,"line":927},[67,12595,12596],{"class":104},"                    date: ",[67,12598,12044],{"class":73},[67,12600,12601],{"class":104},"(result.attributes.date),\n",[67,12603,12604],{"class":69,"line":935},[67,12605,12606],{"class":104},"                };\n",[67,12608,12609],{"class":69,"line":958},[67,12610,12540],{"class":104},[67,12612,12613,12615,12618,12620,12622,12624,12627,12629,12631,12633,12636,12639,12642,12645,12648,12650,12652,12655,12657],{"class":69,"line":971},[67,12614,12460],{"class":104},[67,12616,12617],{"class":73},"sort",[67,12619,6959],{"class":104},[67,12621,1711],{"class":2508},[67,12623,944],{"class":104},[67,12625,12626],{"class":2508},"b",[67,12628,4280],{"class":104},[67,12630,5500],{"class":163},[67,12632,2730],{"class":163},[67,12634,12635],{"class":73}," Date",[67,12637,12638],{"class":104},"(b.date).",[67,12640,12641],{"class":73},"getTime",[67,12643,12644],{"class":104},"() ",[67,12646,12647],{"class":163},"-",[67,12649,2730],{"class":163},[67,12651,12635],{"class":73},[67,12653,12654],{"class":104},"(a.date).",[67,12656,12641],{"class":73},[67,12658,6179],{"class":104},[67,12660,12661],{"class":69,"line":985},[67,12662,989],{"emptyLinePlaceholder":988},[67,12664,12665,12667],{"class":69,"line":992},[67,12666,12281],{"class":163},[67,12668,2500],{"class":104},[67,12670,12671],{"class":69,"line":1000},[67,12672,12673],{"class":104},"            posts,\n",[67,12675,12676],{"class":69,"line":1009},[67,12677,12322],{"class":104},[67,12679,12680],{"class":69,"line":1016},[67,12681,10604],{"class":104},[11,12683,12684,12685,12688,12689,12692],{},"The last step for us to do is to configure Nuxt to generate static pages for all of the posts we have. To do this, we modify the ",[53,12686,12687],{},"nuxt.config.js"," config by extending the ",[53,12690,12691],{},"generate"," key to the default exported object.",[58,12694,12696],{"className":8875,"code":12695,"language":8877,"meta":63,"style":63},"\u002F** nuxt.config.js **\u002F\nconst glob = require('glob');\nconst path = require('path');\n\nconst getDynamicPaths = files => {\n    return [].concat(\n        ...Object.keys(files).map(url => {\n            var filepathGlob = files[url];\n            return glob\n                .sync(filepathGlob, { cwd: __dirname })\n                .map(filepath => `${url}\u002F${path.basename(filepath, '.md')}`);\n        }),\n    );\n};\n\nconst dynamicRoutes = getDynamicPaths({ '\u002Fblog': 'posts\u002F*.md' });\n\nexport default {\n  \u002F** ...  **\u002F\n  generate: {\n      routes: dynamicRoutes,\n      fallback: true,\n  },\n}\n",[53,12697,12698,12703,12721,12739,12743,12759,12772,12796,12809,12817,12828,12872,12877,12882,12886,12890,12913,12917,12925,12930,12935,12940,12949,12953],{"__ignoreMap":63},[67,12699,12700],{"class":69,"line":70},[67,12701,12702],{"class":3401},"\u002F** nuxt.config.js **\u002F\n",[67,12704,12705,12707,12710,12712,12714,12716,12719],{"class":69,"line":251},[67,12706,2722],{"class":163},[67,12708,12709],{"class":113}," glob",[67,12711,2512],{"class":163},[67,12713,9187],{"class":73},[67,12715,2556],{"class":104},[67,12717,12718],{"class":77},"'glob'",[67,12720,2745],{"class":104},[67,12722,12723,12725,12728,12730,12732,12734,12737],{"class":69,"line":264},[67,12724,2722],{"class":163},[67,12726,12727],{"class":113}," path",[67,12729,2512],{"class":163},[67,12731,9187],{"class":73},[67,12733,2556],{"class":104},[67,12735,12736],{"class":77},"'path'",[67,12738,2745],{"class":104},[67,12740,12741],{"class":69,"line":280},[67,12742,989],{"emptyLinePlaceholder":988},[67,12744,12745,12747,12750,12752,12755,12757],{"class":69,"line":288},[67,12746,2722],{"class":163},[67,12748,12749],{"class":73}," getDynamicPaths",[67,12751,2512],{"class":163},[67,12753,12754],{"class":2508}," files",[67,12756,12477],{"class":163},[67,12758,2500],{"class":104},[67,12760,12761,12763,12766,12769],{"class":69,"line":781},[67,12762,2674],{"class":163},[67,12764,12765],{"class":104}," [].",[67,12767,12768],{"class":73},"concat",[67,12770,12771],{"class":104},"(\n",[67,12773,12774,12777,12780,12782,12785,12787,12789,12792,12794],{"class":69,"line":792},[67,12775,12776],{"class":163},"        ...",[67,12778,12779],{"class":104},"Object.",[67,12781,10555],{"class":73},[67,12783,12784],{"class":104},"(files).",[67,12786,10572],{"class":73},[67,12788,2556],{"class":104},[67,12790,12791],{"class":2508},"url",[67,12793,12477],{"class":163},[67,12795,2500],{"class":104},[67,12797,12798,12801,12804,12806],{"class":69,"line":803},[67,12799,12800],{"class":163},"            var",[67,12802,12803],{"class":104}," filepathGlob ",[67,12805,2586],{"class":163},[67,12807,12808],{"class":104}," files[url];\n",[67,12810,12811,12814],{"class":69,"line":814},[67,12812,12813],{"class":163},"            return",[67,12815,12816],{"class":104}," glob\n",[67,12818,12819,12822,12825],{"class":69,"line":825},[67,12820,12821],{"class":104},"                .",[67,12823,12824],{"class":73},"sync",[67,12826,12827],{"class":104},"(filepathGlob, { cwd: __dirname })\n",[67,12829,12830,12832,12834,12836,12839,12841,12844,12846,12849,12852,12854,12857,12859,12861,12863,12866,12868,12870],{"class":69,"line":836},[67,12831,12821],{"class":104},[67,12833,10572],{"class":73},[67,12835,2556],{"class":104},[67,12837,12838],{"class":2508},"filepath",[67,12840,12477],{"class":163},[67,12842,12843],{"class":77}," `${",[67,12845,12791],{"class":104},[67,12847,12848],{"class":77},"}\u002F${",[67,12850,12851],{"class":104},"path",[67,12853,56],{"class":77},[67,12855,12856],{"class":73},"basename",[67,12858,2556],{"class":77},[67,12860,12838],{"class":104},[67,12862,944],{"class":77},[67,12864,12865],{"class":77},"'.md'",[67,12867,2258],{"class":77},[67,12869,9197],{"class":77},[67,12871,2745],{"class":104},[67,12873,12874],{"class":69,"line":847},[67,12875,12876],{"class":104},"        }),\n",[67,12878,12879],{"class":69,"line":858},[67,12880,12881],{"class":104},"    );\n",[67,12883,12884],{"class":69,"line":869},[67,12885,5573],{"class":104},[67,12887,12888],{"class":69,"line":877},[67,12889,989],{"emptyLinePlaceholder":988},[67,12891,12892,12894,12897,12899,12901,12903,12906,12908,12911],{"class":69,"line":886},[67,12893,2722],{"class":163},[67,12895,12896],{"class":113}," dynamicRoutes",[67,12898,2512],{"class":163},[67,12900,12749],{"class":73},[67,12902,12211],{"class":104},[67,12904,12905],{"class":77},"'\u002Fblog'",[67,12907,758],{"class":104},[67,12909,12910],{"class":77},"'posts\u002F*.md'",[67,12912,11133],{"class":104},[67,12914,12915],{"class":69,"line":894},[67,12916,989],{"emptyLinePlaceholder":988},[67,12918,12919,12921,12923],{"class":69,"line":905},[67,12920,5473],{"class":163},[67,12922,242],{"class":163},[67,12924,2500],{"class":104},[67,12926,12927],{"class":69,"line":916},[67,12928,12929],{"class":3401},"  \u002F** ...  **\u002F\n",[67,12931,12932],{"class":69,"line":927},[67,12933,12934],{"class":104},"  generate: {\n",[67,12936,12937],{"class":69,"line":935},[67,12938,12939],{"class":104},"      routes: dynamicRoutes,\n",[67,12941,12942,12945,12947],{"class":69,"line":958},[67,12943,12944],{"class":104},"      fallback: ",[67,12946,1579],{"class":113},[67,12948,955],{"class":104},[67,12950,12951],{"class":69,"line":971},[67,12952,1562],{"class":104},[67,12954,12955],{"class":69,"line":985},[67,12956,1601],{"class":104},[11,12958,12959],{},"Now the only thing remaining to be completed is to show the changes when we push to our repository. Netlify and it's CI\u002FCD tools are out of this world - which is why I am going to use their services to deploy any changes to my website automatically.",[45,12961,12963],{"id":12962},"netlify-is-magic","Netlify is ... magic",[11,12965,12966],{},"If you are following any trends in the developers' community, you have probably heard of Netlify. Netlify is a unified platform that automates your code to create performant, easily maintainable sites and web apps. They provide continuous deployment, an intelligent, global CDN; full DNS. automated HTTPS, asset acceleration, and much more.",[11,12968,12969],{},"Within this post, I will just explain how I use Netlify for this project specifically. There will be probably a more detailed post about Netlify in the future and its features.",[11,12971,9426,12972,12974,12975,12978,12979,12982],{},[53,12973,11959],{}," command generates a ",[53,12976,12977],{},"dist"," folder that contains all of the assets we need to deploy our website. What we need to do using Netlify is to connect it to our repository and the branch we want to ",[1738,12980,12981],{},"watch",". Meaning - whenever we push to that branch on our repository, Netlify will trigger the actions we told it do perform.",[11,12984,12985,12986,12988,12989],{},"So, we set up to run the ",[53,12987,11959],{}," command when we push to our repository's branch and serve the dist folder as our publish directory. Everything else is handled by Netlify for us. I told ya - ",[21,12990,12991],{},"magic!",[6201,12993],{},[11,12995,12996],{},"Everything is configured now. All of the changes I make are almost instantly shown on the live version of the website. I just need to commit and that it - which is extremely easy and convenient.",[11,12998,12999],{},"As a final note - I prefer dark themes. I think they are better for your eyes and there won't be any light theme for this website. I might also open-source the project some day.",[11,13001,13002],{},[21,13003,13004],{},"Thanks for reading. I hope this post will inspire you to build your own static portfolio website 😁",[1670,13006,13007],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":63,"searchDepth":251,"depth":251,"links":13009},[13010,13011,13012,13013,13014],{"id":11925,"depth":251,"text":11926},{"id":11938,"depth":251,"text":11939},{"id":11952,"depth":251,"text":11953},{"id":12009,"depth":251,"text":12010},{"id":12962,"depth":251,"text":12963},"Web Development","2019\u002F09\u002F29","\u002Fimages\u002Fportfolio-final.png","nuxt, vue, static, jam stack, netlify, static blog, markdown blog, portfolio",{},"\u002Fblog\u002Fthe-road-to-a-static-portfolio-website-with-a-blog","☕️ 11 min read",{"title":11911,"description":11916},"the-road-to-a-static-portfolio-website-with-a-blog","blog\u002Fthe-road-to-a-static-portfolio-website-with-a-blog","web development, netlify, nuxt, vue","nX5VO1oWgjc79W6TLhF0RMMMDFr3T_S0YSts6xXMesk",{"id":13028,"title":13029,"body":13030,"category":13122,"date":13123,"description":13034,"excerpt":1690,"extension":1691,"image":13124,"keywords":13125,"meta":13126,"navigation":988,"path":13127,"readingTime":13128,"seo":13129,"slug":13130,"stem":13131,"tags":13132,"__hash__":13133},"blog\u002Fblog\u002Fflux-model-and-its-benefits.md","Flux model and its benefits",{"type":8,"value":13031,"toc":13119},[13032,13035,13046,13049,13052,13055,13069,13074,13085,13087,13091,13096,13099,13102,13116],[11,13033,13034],{},"To understand why Flux matters, it helps to go back to the traditional MVC (Model View Controller) design pattern. In general, the MVC design pattern has three parts:",[680,13036,13037,13040,13043],{},[29,13038,13039],{},"Model - responsible for managing the behavior and the data of the application,",[29,13041,13042],{},"View - responsible for representing the data to the end-user,",[29,13044,13045],{},"Controller - responsible for manipulating models based on the user’s input and updating the data on the UI (UI - User Interface).",[11,13047,13048],{},"Each of the three parts is separated, which provides code separation that makes code easier to maintain. The data flow in this pattern is pretty straightforward. In the past, the backend and the frontend of the application weren't separated, which made this approach very effective. Basically, the Model and the View didn't need to know anything about each other, since the communication between them went through the Controller. However, as we keep moving forward with the development of more and more complicated UI’s, separation of the client-side and the server-side is something that is considered a good practice. In large codebases, changes in the data can happen from multiple sources, and reflecting those changes in the view can get complicated — which is why developers started looking for alternatives.",[11,13050,13051],{},"Reacting to changes in the data across different parts of the application requires unidirectional data flow. Flux is all about controlling the data flow inside the application itself. With Flux, sharing data between different application components is relatively easy and simple to understand.",[11,13053,13054],{},"The Flux design pattern consists of four parts (four different actions that need to be taken in order to achieve needed unidirectional data flow):",[680,13056,13057,13060,13063,13066],{},[29,13058,13059],{},"Actions - Objects (methods) that have a type and data that is eventually passed to the dispatcher,",[29,13061,13062],{},"Stores - Containers for application state and logic for a particular domain within the application. They are different from the Models in MVC since the Model in the MVC pattern is usually responsible for only one entity (object), while stores can store basically everything,",[29,13064,13065],{},"The Dispatcher - Registry for all callbacks to all stores in the application. It acts as a central hub and processes actions that eventually change the stores. It's different from the Controller in the MVC pattern since it doesn't have a lot of logic, and can be reused within different parts of our application.",[29,13067,13068],{},"Views - Same as the views in the MVC pattern with one key difference - they also act like controllers (View-Controllers) and they listen for changes in the stores and re-render themselves accordingly. Within the views, users can invoke actions to the dispatcher.",[11,13070,13071],{},[21,13072,13073],{},"To summarize, the main benefits are:",[26,13075,13076,13079,13082],{},[29,13077,13078],{},"The flow of the app - There are strict rules that are enforced by the dispatcher that help conserve the unidirectional flow of data within the application.",[29,13080,13081],{},"Unidirectional flow of data - Every change of the data goes through the dispatcher. Changing a particular store must be done within an action. A store cannot access another store directly.",[29,13083,13084],{},"Stores - Stores don't need to model a particular object. Storing any data in the stores is possible and common.",[6201,13086],{},[45,13088,13090],{"id":13089},"vuex","Vuex",[11,13092,13093],{},[1738,13094,13095],{},"Vuex is a state management pattern + library for Vue.js applications.",[11,13097,13098],{},"Vuex is a centralized store for all parts (components) in an application, that provides unidirectional flow of data between different parts of the application and sets rules which ensure that the data can be mutated only in a predictable way (ensuring the “one-way data flow”). Using Vuex is recommended when we build large scale SPA (SPA - Single Page Application). Even though in Vue, each component can have its own state, sharing data between components can be extremely challenging in large scale applications, which is why for better developer experience and separation of logic it's useful to implement Vuex. With Vuex we are ensuring that updating the state is unidirectional, sharing of the data between components is consistent and precise, which reflects to showing proper data in the views.",[11,13100,13101],{},"Since it's based on the Flux design pattern, Vuex has four parts:",[680,13103,13104,13107,13110,13113],{},[29,13105,13106],{},"Actions - Responsible for helping with updating the state,",[29,13108,13109],{},"State - The data that is shared within our application,",[29,13111,13112],{},"Getters - Responsible for accessing filtered, derived or computed state,",[29,13114,13115],{},"Mutations - Responsible for updating the state,",[11,13117,13118],{},"Actions call mutations to update the data — that's the reason they're described as \"helping\" with updates. They can also contain arbitrary asynchronous operations.",{"title":63,"searchDepth":251,"depth":251,"links":13120},[13121],{"id":13089,"depth":251,"text":13090},"Design Patterns","2019\u002F09\u002F15","\u002Fimages\u002Fflux.png","flux, unidirectional data flow, data flow, bidirectional data flow, mvc, design pattern, vuex, vue, redux, react",{},"\u002Fblog\u002Fflux-model-and-its-benefits","☕️ 5 min read",{"title":13029,"description":13034},"flux-model-and-its-benefits","blog\u002Fflux-model-and-its-benefits","vuex, vue, flux, design patterns","RjlJ_9rgN7U-MFqLk8hW4Arj5aMA8EFQkBMq1ejmMlY",{"id":13135,"title":13136,"body":13137,"category":10292,"date":13971,"description":13972,"excerpt":1690,"extension":1691,"image":13973,"keywords":13974,"meta":13975,"navigation":988,"path":13976,"readingTime":13128,"seo":13977,"slug":13978,"stem":13979,"tags":13980,"__hash__":13981},"blog\u002Fblog\u002Fbuilding-a-controlled-vue-input.md","Building a controlled Vue input component",{"type":8,"value":13138,"toc":13968},[13139,13156,13163,13182,13331,13334,13439,13445,13458,13686,13689,13846,13871,13874,13878,13891,13956,13959,13962,13965],[11,13140,13141,13142,13145,13146,13149,13150,2004,13152,13155],{},"First, let's explain what a ",[21,13143,13144],{},"controlled component"," is. A controlled component is one that takes its ",[21,13147,13148],{},"current value"," from ",[21,13151,9483],{},[21,13153,13154],{},"notifies the parent of changes to that value using an event."," Which means that usually, controlled components are form fields.",[11,13157,13158,13159,13162],{},"We all know that in every modern application there is a form that needs to be filled. Every form has fields, which is why we want to build ",[21,13160,13161],{},"reusable input components"," that we can use in all of our forms.",[11,13164,13165,13166,944,13169,944,13172,2004,13174,13177,13178,13181],{},"Imagine we have a big Vue component that is used for user registration. We would at least need 4 input fields there, ",[1738,13167,13168],{},"Name",[1738,13170,13171],{},"Email",[1738,13173,5630],{},[1738,13175,13176],{},"password confirmation",". We can bind each of the inputs to some data in the Vue component using the ",[21,13179,13180],{},"v-model"," directive. Basically, the component would look something like this:",[58,13183,13185],{"className":6277,"code":13184,"language":6279,"meta":63,"style":63},"\u003Ctemplate>\n  \u002F* ... *\u002F\n  \u003Cinput type=\"text\" v-model=\"name\" ...>\n  \u003Cinput type=\"email\" v-model=\"email\" ...>\n  \u002F* ... *\u002F\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    data() {\n      return {\n        name: '',\n        email: '',\n        ...\n    };\n  }\n\u003C\u002Fscript>\n",[53,13186,13187,13195,13200,13227,13250,13254,13262,13266,13274,13282,13288,13294,13304,13313,13318,13322,13326],{"__ignoreMap":63},[67,13188,13189,13191,13193],{"class":69,"line":70},[67,13190,5778],{"class":104},[67,13192,6293],{"class":739},[67,13194,6296],{"class":104},[67,13196,13197],{"class":69,"line":251},[67,13198,13199],{"class":104},"  \u002F* ... *\u002F\n",[67,13201,13202,13204,13207,13210,13212,13215,13218,13220,13223,13225],{"class":69,"line":264},[67,13203,6301],{"class":104},[67,13205,13206],{"class":739},"input",[67,13208,13209],{"class":73}," type",[67,13211,2586],{"class":104},[67,13213,13214],{"class":77},"\"text\"",[67,13216,13217],{"class":73}," v-model",[67,13219,2586],{"class":104},[67,13221,13222],{"class":77},"\"name\"",[67,13224,10669],{"class":73},[67,13226,6296],{"class":104},[67,13228,13229,13231,13233,13235,13237,13240,13242,13244,13246,13248],{"class":69,"line":280},[67,13230,6301],{"class":104},[67,13232,13206],{"class":739},[67,13234,13209],{"class":73},[67,13236,2586],{"class":104},[67,13238,13239],{"class":77},"\"email\"",[67,13241,13217],{"class":73},[67,13243,2586],{"class":104},[67,13245,13239],{"class":77},[67,13247,10669],{"class":73},[67,13249,6296],{"class":104},[67,13251,13252],{"class":69,"line":288},[67,13253,13199],{"class":104},[67,13255,13256,13258,13260],{"class":69,"line":781},[67,13257,6382],{"class":104},[67,13259,6293],{"class":739},[67,13261,6296],{"class":104},[67,13263,13264],{"class":69,"line":792},[67,13265,989],{"emptyLinePlaceholder":988},[67,13267,13268,13270,13272],{"class":69,"line":803},[67,13269,5778],{"class":104},[67,13271,6397],{"class":739},[67,13273,6296],{"class":104},[67,13275,13276,13278,13280],{"class":69,"line":814},[67,13277,6404],{"class":163},[67,13279,242],{"class":163},[67,13281,2500],{"class":104},[67,13283,13284,13286],{"class":69,"line":825},[67,13285,6854],{"class":73},[67,13287,2617],{"class":104},[67,13289,13290,13292],{"class":69,"line":836},[67,13291,4126],{"class":163},[67,13293,2500],{"class":104},[67,13295,13296,13299,13302],{"class":69,"line":847},[67,13297,13298],{"class":104},"        name: ",[67,13300,13301],{"class":77},"''",[67,13303,955],{"class":104},[67,13305,13306,13309,13311],{"class":69,"line":858},[67,13307,13308],{"class":104},"        email: ",[67,13310,13301],{"class":77},[67,13312,955],{"class":104},[67,13314,13315],{"class":69,"line":869},[67,13316,13317],{"class":163},"        ...\n",[67,13319,13320],{"class":69,"line":877},[67,13321,10604],{"class":104},[67,13323,13324],{"class":69,"line":886},[67,13325,1596],{"class":104},[67,13327,13328],{"class":69,"line":894},[67,13329,13330],{"class":104},"\u003C\u002Fscript>\n",[11,13332,13333],{},"But usually, our form fields don't look like this. They have some wrapper, they have labels, they have some classes etc etc. So, let's see what a typical form field looks like (in a real app, for example using Bootstrap):",[58,13335,13339],{"className":13336,"code":13337,"language":13338,"meta":63,"style":63},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv class=\"col-md-6\">\n  \u003Cdiv class=\"form-group\">\n    \u003Cinput type=\"text\" class=\"form-control\" ... >\n    \u003Clabel for=\"name\">Name \u003Csub*\u003C\u002Fsub>\u003C\u002Flabel>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n","html",[53,13340,13341,13357,13372,13396,13423,13431],{"__ignoreMap":63},[67,13342,13343,13345,13348,13350,13352,13355],{"class":69,"line":70},[67,13344,5778],{"class":104},[67,13346,13347],{"class":739},"div",[67,13349,4390],{"class":73},[67,13351,2586],{"class":104},[67,13353,13354],{"class":77},"\"col-md-6\"",[67,13356,6296],{"class":104},[67,13358,13359,13361,13363,13365,13367,13370],{"class":69,"line":251},[67,13360,6301],{"class":104},[67,13362,13347],{"class":739},[67,13364,4390],{"class":73},[67,13366,2586],{"class":104},[67,13368,13369],{"class":77},"\"form-group\"",[67,13371,6296],{"class":104},[67,13373,13374,13376,13378,13380,13382,13384,13386,13388,13391,13393],{"class":69,"line":264},[67,13375,6335],{"class":104},[67,13377,13206],{"class":739},[67,13379,13209],{"class":73},[67,13381,2586],{"class":104},[67,13383,13214],{"class":77},[67,13385,4390],{"class":73},[67,13387,2586],{"class":104},[67,13389,13390],{"class":77},"\"form-control\"",[67,13392,10669],{"class":73},[67,13394,13395],{"class":104}," >\n",[67,13397,13398,13400,13403,13406,13408,13410,13413,13417,13419,13421],{"class":69,"line":280},[67,13399,6335],{"class":104},[67,13401,13402],{"class":739},"label",[67,13404,13405],{"class":73}," for",[67,13407,2586],{"class":104},[67,13409,13222],{"class":77},[67,13411,13412],{"class":104},">Name \u003C",[67,13414,13416],{"class":13415},"s7hpK","sub*\u003C\u002Fsub",[67,13418,6364],{"class":104},[67,13420,13402],{"class":739},[67,13422,6296],{"class":104},[67,13424,13425,13427,13429],{"class":69,"line":288},[67,13426,6373],{"class":104},[67,13428,13347],{"class":739},[67,13430,6296],{"class":104},[67,13432,13433,13435,13437],{"class":69,"line":781},[67,13434,6382],{"class":104},[67,13436,13347],{"class":739},[67,13438,6296],{"class":104},[11,13440,13441,13442],{},"Looking at this form field, one thing is for sure - we don't want to write this code every time we want to write a text input field, so we use the most important concept from Vue - ",[21,13443,13444],{},"building reusable components.",[11,13446,13447,13448,13450,13451,13454,13455],{},"Now, if we want to wrap our heads around this issue, we can see that if we wrap this input field in a component, we want it to be a ",[21,13449,13144],{}," — receiving a value and emitting changes to the parent. So, let's do that. We create a new ",[21,13452,13453],{},"SFC (single file component)"," and start implementing our desired functionality. This is how it would look like. Please note that all of our state lives in the parent component. ",[1738,13456,13457],{},"(Note: You cannot bind v-model to a prop, because Vue will let you know that you cannot change immutable objects. Instead, you should bind the value to the prop itself, and emit changes to the parent.)",[58,13459,13461],{"className":6277,"code":13460,"language":6279,"meta":63,"style":63},"\u002F\u002F TextInput.vue\n\n\u003Ctemplate>\n  \u003Cdiv class=\"col-md-6\">\n    \u003Cdiv class=\"form-group\">\n      \u003Cinput :value=\"value\" @input=\"handleChange\" ... >\n      \u003Clabel for=\"name\">Name \u003Csub*\u003C\u002Fsub>\u003C\u002Flabel>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    props: {\n      value: {\n        required: true,\n        type: String,\n      }\n    },\n    methods: {\n      handleChange(e) {\n        this.$emit('handleChange', e.target.value);\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,13462,13463,13468,13472,13480,13494,13508,13534,13560,13568,13576,13584,13588,13596,13604,13608,13613,13621,13625,13629,13633,13637,13649,13666,13670,13674,13678],{"__ignoreMap":63},[67,13464,13465],{"class":69,"line":70},[67,13466,13467],{"class":104},"\u002F\u002F TextInput.vue\n",[67,13469,13470],{"class":69,"line":251},[67,13471,989],{"emptyLinePlaceholder":988},[67,13473,13474,13476,13478],{"class":69,"line":264},[67,13475,5778],{"class":104},[67,13477,6293],{"class":739},[67,13479,6296],{"class":104},[67,13481,13482,13484,13486,13488,13490,13492],{"class":69,"line":280},[67,13483,6301],{"class":104},[67,13485,13347],{"class":739},[67,13487,4390],{"class":73},[67,13489,2586],{"class":104},[67,13491,13354],{"class":77},[67,13493,6296],{"class":104},[67,13495,13496,13498,13500,13502,13504,13506],{"class":69,"line":288},[67,13497,6335],{"class":104},[67,13499,13347],{"class":739},[67,13501,4390],{"class":73},[67,13503,2586],{"class":104},[67,13505,13369],{"class":77},[67,13507,6296],{"class":104},[67,13509,13510,13512,13514,13517,13519,13522,13525,13527,13530,13532],{"class":69,"line":781},[67,13511,8909],{"class":104},[67,13513,13206],{"class":739},[67,13515,13516],{"class":73}," :value",[67,13518,2586],{"class":104},[67,13520,13521],{"class":77},"\"value\"",[67,13523,13524],{"class":73}," @input",[67,13526,2586],{"class":104},[67,13528,13529],{"class":77},"\"handleChange\"",[67,13531,10669],{"class":73},[67,13533,13395],{"class":104},[67,13535,13536,13538,13540,13542,13544,13546,13548,13551,13554,13556,13558],{"class":69,"line":792},[67,13537,8909],{"class":104},[67,13539,13402],{"class":739},[67,13541,13405],{"class":73},[67,13543,2586],{"class":104},[67,13545,13222],{"class":77},[67,13547,13412],{"class":104},[67,13549,13550],{"class":739},"sub*",[67,13552,13553],{"class":13415},"\u003C\u002Fsub",[67,13555,6364],{"class":104},[67,13557,13402],{"class":739},[67,13559,6296],{"class":104},[67,13561,13562,13564,13566],{"class":69,"line":803},[67,13563,8937],{"class":104},[67,13565,13347],{"class":739},[67,13567,6296],{"class":104},[67,13569,13570,13572,13574],{"class":69,"line":814},[67,13571,6373],{"class":104},[67,13573,13347],{"class":739},[67,13575,6296],{"class":104},[67,13577,13578,13580,13582],{"class":69,"line":825},[67,13579,6382],{"class":104},[67,13581,6293],{"class":739},[67,13583,6296],{"class":104},[67,13585,13586],{"class":69,"line":836},[67,13587,989],{"emptyLinePlaceholder":988},[67,13589,13590,13592,13594],{"class":69,"line":847},[67,13591,5778],{"class":104},[67,13593,6397],{"class":739},[67,13595,6296],{"class":104},[67,13597,13598,13600,13602],{"class":69,"line":858},[67,13599,6404],{"class":163},[67,13601,242],{"class":163},[67,13603,2500],{"class":104},[67,13605,13606],{"class":69,"line":869},[67,13607,6413],{"class":104},[67,13609,13610],{"class":69,"line":877},[67,13611,13612],{"class":104},"      value: {\n",[67,13614,13615,13617,13619],{"class":69,"line":886},[67,13616,6423],{"class":104},[67,13618,1579],{"class":113},[67,13620,955],{"class":104},[67,13622,13623],{"class":69,"line":894},[67,13624,7259],{"class":104},[67,13626,13627],{"class":69,"line":905},[67,13628,6447],{"class":104},[67,13630,13631],{"class":69,"line":916},[67,13632,5917],{"class":104},[67,13634,13635],{"class":69,"line":927},[67,13636,6912],{"class":104},[67,13638,13639,13642,13644,13647],{"class":69,"line":935},[67,13640,13641],{"class":73},"      handleChange",[67,13643,2556],{"class":104},[67,13645,13646],{"class":2508},"e",[67,13648,2575],{"class":104},[67,13650,13651,13653,13655,13658,13660,13663],{"class":69,"line":958},[67,13652,6924],{"class":113},[67,13654,56],{"class":104},[67,13656,13657],{"class":73},"$emit",[67,13659,2556],{"class":104},[67,13661,13662],{"class":77},"'handleChange'",[67,13664,13665],{"class":104},", e.target.value);\n",[67,13667,13668],{"class":69,"line":971},[67,13669,6447],{"class":104},[67,13671,13672],{"class":69,"line":985},[67,13673,1557],{"class":104},[67,13675,13676],{"class":69,"line":992},[67,13677,1596],{"class":104},[67,13679,13680,13682,13684],{"class":69,"line":1000},[67,13681,6382],{"class":104},[67,13683,6397],{"class":739},[67,13685,6296],{"class":104},[11,13687,13688],{},"What we did in this component is that we always emit the changed value to the parent, then the parent caches that changed event and updates its state, which then triggers the value on our controlled component to change since we pass it as a prop. This is tricky to understand, but let me show you how the parent component could look like, so you can better understand this concept.",[58,13690,13692],{"className":13336,"code":13691,"language":13338,"meta":63,"style":63},"\u002F\u002F Form.vue\n\n\u003Ctemplate>\n  \u002F* ... *\u002F\n  \u003Ctext-input :value=\"value\" @handleChange=\"handleChange($event)\">\n  \u002F* ... *\u002F\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n  export default {\n    data() {\n      return {\n        value: '',\n      }\n    },\n    methods: {\n      handleChange(payload) {\n        this.value = payload;\n      }\n    }\n  }\n\u003C\u002Fscript>\n",[53,13693,13694,13699,13703,13711,13715,13738,13742,13750,13754,13762,13770,13776,13782,13791,13795,13799,13803,13814,13826,13830,13834,13838],{"__ignoreMap":63},[67,13695,13696],{"class":69,"line":70},[67,13697,13698],{"class":104},"\u002F\u002F Form.vue\n",[67,13700,13701],{"class":69,"line":251},[67,13702,989],{"emptyLinePlaceholder":988},[67,13704,13705,13707,13709],{"class":69,"line":264},[67,13706,5778],{"class":104},[67,13708,6293],{"class":739},[67,13710,6296],{"class":104},[67,13712,13713],{"class":69,"line":280},[67,13714,13199],{"class":104},[67,13716,13717,13719,13722,13724,13726,13728,13731,13733,13736],{"class":69,"line":288},[67,13718,6301],{"class":104},[67,13720,13721],{"class":739},"text-input",[67,13723,13516],{"class":73},[67,13725,2586],{"class":104},[67,13727,13521],{"class":77},[67,13729,13730],{"class":73}," @handleChange",[67,13732,2586],{"class":104},[67,13734,13735],{"class":77},"\"handleChange($event)\"",[67,13737,6296],{"class":104},[67,13739,13740],{"class":69,"line":781},[67,13741,13199],{"class":104},[67,13743,13744,13746,13748],{"class":69,"line":792},[67,13745,6382],{"class":104},[67,13747,6293],{"class":739},[67,13749,6296],{"class":104},[67,13751,13752],{"class":69,"line":803},[67,13753,989],{"emptyLinePlaceholder":988},[67,13755,13756,13758,13760],{"class":69,"line":814},[67,13757,5778],{"class":104},[67,13759,6397],{"class":739},[67,13761,6296],{"class":104},[67,13763,13764,13766,13768],{"class":69,"line":825},[67,13765,6404],{"class":163},[67,13767,242],{"class":163},[67,13769,2500],{"class":104},[67,13771,13772,13774],{"class":69,"line":836},[67,13773,6854],{"class":73},[67,13775,2617],{"class":104},[67,13777,13778,13780],{"class":69,"line":847},[67,13779,4126],{"class":163},[67,13781,2500],{"class":104},[67,13783,13784,13787,13789],{"class":69,"line":858},[67,13785,13786],{"class":104},"        value: ",[67,13788,13301],{"class":77},[67,13790,955],{"class":104},[67,13792,13793],{"class":69,"line":869},[67,13794,6447],{"class":104},[67,13796,13797],{"class":69,"line":877},[67,13798,5917],{"class":104},[67,13800,13801],{"class":69,"line":886},[67,13802,6912],{"class":104},[67,13804,13805,13807,13809,13812],{"class":69,"line":894},[67,13806,13641],{"class":73},[67,13808,2556],{"class":104},[67,13810,13811],{"class":2508},"payload",[67,13813,2575],{"class":104},[67,13815,13816,13818,13821,13823],{"class":69,"line":905},[67,13817,6924],{"class":113},[67,13819,13820],{"class":104},".value ",[67,13822,2586],{"class":163},[67,13824,13825],{"class":104}," payload;\n",[67,13827,13828],{"class":69,"line":916},[67,13829,6447],{"class":104},[67,13831,13832],{"class":69,"line":927},[67,13833,1557],{"class":104},[67,13835,13836],{"class":69,"line":935},[67,13837,1596],{"class":104},[67,13839,13840,13842,13844],{"class":69,"line":958},[67,13841,6382],{"class":104},[67,13843,6397],{"class":739},[67,13845,6296],{"class":104},[11,13847,13848,13849,13852,13853,13856,13857,2004,13860,13863,13864,13867,13868,56],{},"Let's break down what we do here. We send the changed value from the child component ",[53,13850,13851],{},"this.$emit('handleChange', e.target.value);"," and then in the parent we ",[21,13854,13855],{},"catch the fired event"," using ",[53,13858,13859],{},"@handleChange=\"handleChange($event)\"",[21,13861,13862],{},"assign the payload"," we send (Vue passes the payload we send from the child component in the ",[53,13865,13866],{},"$event"," variable) to the value we want to update in our parent component's state. ",[1738,13869,13870],{},"(Note: payload can also be objects, arrays ..)",[11,13872,13873],{},"And that's it. Now we have a fully functional controlled component that we can reuse.",[1758,13875,13877],{"id":13876},"but-what-if-i-told-you-we-can-simplify-this-even-further","But what if I told you we can simplify this even further?",[11,13879,13880,13881,13883,13884,2004,13887,13890],{},"Vue's ",[21,13882,13180],{}," directive is basically a combination of ",[53,13885,13886],{},":value=\"value\"",[53,13888,13889],{},"@input=\"value = $event.target.value\"",". So, what we can do in our controlled component is implement those exact attributes\u002Fevents and we can then use v-model in our parent component. Let me show you what i mean.",[58,13892,13894],{"className":13336,"code":13893,"language":13338,"meta":63,"style":63},"\u002F\u002F TextInput.vue\n\u003Cinput type=\"text\" :value=\"value\" @input=\"$emit('input', $event.target.value)\" ... \u002F>\n\n\u002F\u002F Form.vue\n\u003Ctext-input v-model=\"value\">\u003C\u002Ftext-input>\n",[53,13895,13896,13900,13930,13934,13938],{"__ignoreMap":63},[67,13897,13898],{"class":69,"line":70},[67,13899,13467],{"class":104},[67,13901,13902,13904,13906,13908,13910,13912,13914,13916,13918,13920,13922,13925,13927],{"class":69,"line":251},[67,13903,5778],{"class":104},[67,13905,13206],{"class":739},[67,13907,13209],{"class":73},[67,13909,2586],{"class":104},[67,13911,13214],{"class":77},[67,13913,13516],{"class":73},[67,13915,2586],{"class":104},[67,13917,13521],{"class":77},[67,13919,13524],{"class":73},[67,13921,2586],{"class":104},[67,13923,13924],{"class":77},"\"$emit('input', $event.target.value)\"",[67,13926,10669],{"class":73},[67,13928,13929],{"class":104}," \u002F>\n",[67,13931,13932],{"class":69,"line":264},[67,13933,989],{"emptyLinePlaceholder":988},[67,13935,13936],{"class":69,"line":280},[67,13937,13698],{"class":104},[67,13939,13940,13942,13944,13946,13948,13950,13952,13954],{"class":69,"line":288},[67,13941,5778],{"class":104},[67,13943,13721],{"class":739},[67,13945,13217],{"class":73},[67,13947,2586],{"class":104},[67,13949,13521],{"class":77},[67,13951,6364],{"class":104},[67,13953,13721],{"class":739},[67,13955,6296],{"class":104},[11,13957,13958],{},"Using this approach what we basically did is mimic the v-model's implementation for our custom component and simplified our controlled component even more. Seems pretty neat right? It's basically the same as a normal input field, but packaged as a reusable component with classes, labels, and whatever else you need.",[11,13960,13961],{},"This concept can be used also for custom check boxes and custom select form fields. Actually its best use is for custom form fields like these.",[11,13963,13964],{},"This concept can be extended to custom checkboxes, selects, and any other form field where you want reusability without sacrificing control.",[1670,13966,13967],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":63,"searchDepth":251,"depth":251,"links":13969},[13970],{"id":13876,"depth":264,"text":13877},"2019\u002F08\u002F12","First, let's explain what a controlled component is. A controlled component is one that takes its current value from props and notifies the parent of changes to that value using an event. Which means that usually, controlled components are form fields.","\u002Fimages\u002Fvue-controller-component.jpg","vue, vue.js, controlled component, vue component, advanced vue component design, advanced vue, vue model, vue v-model",{},"\u002Fblog\u002Fbuilding-a-controlled-vue-input",{"title":13136,"description":13972},"building-a-controlled-vue-input","blog\u002Fbuilding-a-controlled-vue-input","vue components, advanced vue, vue","ZfLj7Zul6NgBOhSoS016P2pJ_9aOeO-gJtmiMMRtE34",{"id":13983,"title":13984,"body":13985,"category":14174,"date":14175,"description":14176,"excerpt":1690,"extension":1691,"image":14177,"keywords":14178,"meta":14179,"navigation":988,"path":14180,"readingTime":14181,"seo":14182,"slug":14183,"stem":14184,"tags":14185,"__hash__":14186},"blog\u002Fblog\u002Fconfiguring-new-laravel-application-with-tailwindcss.md","Configuring new Laravel application with TailwindCSS",{"type":8,"value":13986,"toc":14172},[13987,14006,14020,14042,14052,14059,14076,14083,14106,14113,14156,14169],[11,13988,13989,13994,13995,13998,13999,14002,14003,56],{},[1711,13990,13993],{"href":13991,"rel":13992},"https:\u002F\u002Ftailwindcss.com",[1715],"Tailwind"," is something that you have to adopt in your ",[21,13996,13997],{},"daily coding",". It will make writing CSS like a breeze. Not only it will speed up your ",[21,14000,14001],{},"design development",", but it will also make you a better ",[21,14004,14005],{},"UI designer",[11,14007,14008,14011,14012,14015,14016,14019],{},[1738,14009,14010],{},"Tailwind is a utility-first CSS framework for rapidly building custom user interfaces."," This is how they described Tailwind. It's not like any other ",[21,14013,14014],{},"CSS framework"," available (Bootstrap, Bulma, ...), it doesn't have any predefined components, but instead you build every component on your own. Sounds difficult right? ",[21,14017,14018],{},"It's not."," If you have your website to have its own identity, and not look like any other bootstrap based website online - use Tailwind.",[11,14021,14022,14023,14026,14027,14030,14031,14034,14035,14038,14039,56],{},"Setting up Tailwind for Laravel is simple. Using your terminal, craft a new Laravel application using ",[53,14024,14025],{},"laravel new \u003Capp_name>"," command. Then, enter the directory using ",[53,14028,14029],{},"cd \u003Capp_name>",", and run ",[53,14032,14033],{},"composer install",". After composer did its thing, using ",[53,14036,14037],{},"yarn"," install node modules. After node modules are in place, install tailwind using ",[53,14040,14041],{},"yarn add tailwindcss",[11,14043,14044,14045,14048,14049,56],{},"After that, run ",[53,14046,14047],{},".\u002Fnode_modules\u002F.bin\u002Ftailwind init"," to init a configuration file for tailwind. You can configure the colors, modules, and other stuff for tailwind in tailwind.js file that was generated for you ",[1738,14050,14051],{},"(don't overthink this right now — you'll learn how to customize Tailwind to fit your needs after you play with it for a while)",[11,14053,14054,14055,14058],{},"In your main ",[53,14056,14057],{},".scss"," file, add these two lines at the beginning:",[58,14060,14064],{"className":14061,"code":14062,"language":14063,"meta":63,"style":63},"language-scss shiki shiki-themes github-light github-dark","@tailwind preflight\n@tailwind utilities;\n","scss",[53,14065,14066,14071],{"__ignoreMap":63},[67,14067,14068],{"class":69,"line":70},[67,14069,14070],{"class":104},"@tailwind preflight\n",[67,14072,14073],{"class":69,"line":251},[67,14074,14075],{"class":104},"@tailwind utilities;\n",[11,14077,14078,14079,14082],{},"Open ",[53,14080,14081],{},"webpack.min.js"," and add this line to the top of the file",[58,14084,14086],{"className":8875,"code":14085,"language":8877,"meta":63,"style":63},"let tailwindcss = require('tailwindcss');\n",[53,14087,14088],{"__ignoreMap":63},[67,14089,14090,14092,14095,14097,14099,14101,14104],{"class":69,"line":70},[67,14091,11710],{"class":163},[67,14093,14094],{"class":104}," tailwindcss ",[67,14096,2586],{"class":163},[67,14098,9187],{"class":73},[67,14100,2556],{"class":104},[67,14102,14103],{"class":77},"'tailwindcss'",[67,14105,2745],{"class":104},[11,14107,14108,14109,14112],{},"And lastly, append to the ",[53,14110,14111],{},".scss()"," function this postCSS method:",[58,14114,14116],{"className":8875,"code":14115,"language":8877,"meta":63,"style":63},".options({\n      processCssUrls: false,\n      postCss: [tailwindcss('.\u002Ftailwind.js')],\n});\n",[53,14117,14118,14127,14136,14152],{"__ignoreMap":63},[67,14119,14120,14122,14125],{"class":69,"line":70},[67,14121,56],{"class":104},[67,14123,14124],{"class":73},"options",[67,14126,9127],{"class":104},[67,14128,14129,14132,14134],{"class":69,"line":251},[67,14130,14131],{"class":104},"      processCssUrls: ",[67,14133,6426],{"class":113},[67,14135,955],{"class":104},[67,14137,14138,14141,14144,14146,14149],{"class":69,"line":264},[67,14139,14140],{"class":104},"      postCss: [",[67,14142,14143],{"class":73},"tailwindcss",[67,14145,2556],{"class":104},[67,14147,14148],{"class":77},"'.\u002Ftailwind.js'",[67,14150,14151],{"class":104},")],\n",[67,14153,14154],{"class":69,"line":280},[67,14155,11797],{"class":104},[11,14157,14158,14159,14162,14163,14168],{},"Now, run ",[53,14160,14161],{},"yarn run dev"," and you can start using Tailwind in your Laravel application. Their ",[1711,14164,14167],{"href":14165,"rel":14166},"https:\u002F\u002Ftailwindcss.com\u002Fdocs",[1715],"documentation"," covers everything you need to get productive.",[1670,14170,14171],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":251,"depth":251,"links":14173},[],"Laravel","2019\u002F08\u002F07","Tailwind is something that you have to adopt in your daily coding. It will make writing CSS like a breeze. Not only it will speed up your design development, but it will also make you a better UI designer.","\u002Fimages\u002Ftailwind.png","tailwind, laravel with tailwind, configure tailwind, configure laravel with tailwind",{},"\u002Fblog\u002Fconfiguring-new-laravel-application-with-tailwindcss","☕️ 2 min read",{"title":13984,"description":14176},"configuring-new-laravel-application-with-tailwindcss","blog\u002Fconfiguring-new-laravel-application-with-tailwindcss","laravel, tailwind","9HBxRw6CpRgX2gE46QWzNfzpO8FYH4Q1I2BFLuAb_XE",{"id":14188,"title":14189,"body":14190,"category":14174,"date":14506,"description":14507,"excerpt":1690,"extension":1691,"image":14508,"keywords":14509,"meta":14510,"navigation":988,"path":14511,"readingTime":14512,"seo":14513,"slug":14514,"stem":14515,"tags":14516,"__hash__":14517},"blog\u002Fblog\u002Fpragmatic-and-lightweight-search-for-laravel-models.md","Pragmatic and lightweight search for Laravel Models",{"type":8,"value":14191,"toc":14502},[14192,14209,14216,14238,14241,14245,14255,14278,14289,14308,14311,14330,14337,14383,14397,14400,14419,14423,14436,14481,14496,14499],[11,14193,14194,14195,14200,14201,14204,14205,14208],{},"You can always install ",[1711,14196,14199],{"href":14197,"rel":14198},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F5.9\u002Fscout",[1715],"Laravel Scout"," and use some of its engines for ",[21,14202,14203],{},"searching",". However sometimes we don't need that heavy-on-server search, so we can use this simple approach for ",[21,14206,14207],{},"searching data"," in your databases.",[11,14210,14211,14212,14215],{},"If you want to search for some data in the database, for example search for some ",[1738,14213,14214],{},"Users"," by their username or email, you can perform a search using Eloquent like this:",[58,14217,14221],{"className":14218,"code":14219,"language":14220,"meta":63,"style":63},"language-php shiki shiki-themes github-light github-dark","$users = User::where('username', 'LIKE', \"%{$username}%\")\n  ->orWhere('email', 'LIKE', \"%{$email}%\")\n  ->get();\n","php",[53,14222,14223,14228,14233],{"__ignoreMap":63},[67,14224,14225],{"class":69,"line":70},[67,14226,14227],{},"$users = User::where('username', 'LIKE', \"%{$username}%\")\n",[67,14229,14230],{"class":69,"line":251},[67,14231,14232],{},"  ->orWhere('email', 'LIKE', \"%{$email}%\")\n",[67,14234,14235],{"class":69,"line":264},[67,14236,14237],{},"  ->get();\n",[11,14239,14240],{},"There are two ways to do this, so I am going to cover both of them now and explain when to use which approach.",[45,14242,14244],{"id":14243},"first-approach-using-macros","First approach: Using macros",[11,14246,14247,14248],{},"This approach is good when you want to include the search across all of your models, and not just one of them. Using macros you can ",[1738,14249,14250,14251,14254],{},"simply create a function that will give you the possibility of chaining ",[21,14252,14253],{},"Eloquent query"," into one function and calling it anywhere within your application.",[11,14256,14257,14258,14261,14262,14265,14266,14269,14270,14273,14274,14277],{},"If you want to define a macro, you have to do it in a ",[21,14259,14260],{},"service provider",". You can either add the macro to ",[53,14263,14264],{},"AppServiceProvider.php"," or create a new service provider called (for example) ",[53,14267,14268],{},"MacroServiceProvider.php"," and place the macro in the ",[53,14271,14272],{},"boot"," method of the service provider. If you create a new service provider, don't forget to add it to the service providers array in ",[53,14275,14276],{},"config\u002Fapp.php"," .",[11,14279,14280,14281,14284,14285,14288],{},"To define a macro, you simply use the ",[21,14282,14283],{},"macro static method"," on the class you want to define the macro to. We need to define a macro for the ",[21,14286,14287],{},"Eloquent class",", so we can extend it like this (in the boot method of the service provider):",[58,14290,14292],{"className":14218,"code":14291,"language":14220,"meta":63,"style":63},"Builder::macro('whereLike', function($column, $search) {\n  return $this->where($column, 'LIKE', \"%{$search}%\");\n});\n",[53,14293,14294,14299,14304],{"__ignoreMap":63},[67,14295,14296],{"class":69,"line":70},[67,14297,14298],{},"Builder::macro('whereLike', function($column, $search) {\n",[67,14300,14301],{"class":69,"line":251},[67,14302,14303],{},"  return $this->where($column, 'LIKE', \"%{$search}%\");\n",[67,14305,14306],{"class":69,"line":264},[67,14307,11797],{},[11,14309,14310],{},"The way we can use this macro now is simple:",[58,14312,14314],{"className":14218,"code":14313,"language":14220,"meta":63,"style":63},"User::whereLike('username', $username)\n  ->whereLike('email', $email)\n  ->get();\n",[53,14315,14316,14321,14326],{"__ignoreMap":63},[67,14317,14318],{"class":69,"line":70},[67,14319,14320],{},"User::whereLike('username', $username)\n",[67,14322,14323],{"class":69,"line":251},[67,14324,14325],{},"  ->whereLike('email', $email)\n",[67,14327,14328],{"class":69,"line":264},[67,14329,14237],{},[11,14331,14332,14333,14336],{},"We can still improve the macro we just wrote. This macro only covers if we want to search only one column. So, if we want to search multiple columns then we have to extend this macro to support multiple columns. If we want to perform multiple column search we are going to use the ",[53,14334,14335],{},"orWhere"," Eloquent's method. Let's do that.",[58,14338,14340],{"className":14218,"code":14339,"language":14220,"meta":63,"style":63},"Builder::macro('whereLike', function($columns, $search) {\n  $this->where(function($query) use ($columns, $search) {\n    foreach(array_wrap($columns) as $column) {\n      $query->orWhere($column, $search);\n    }\n  });\n\n  return $this;\n});\n",[53,14341,14342,14347,14352,14357,14362,14366,14370,14374,14379],{"__ignoreMap":63},[67,14343,14344],{"class":69,"line":70},[67,14345,14346],{},"Builder::macro('whereLike', function($columns, $search) {\n",[67,14348,14349],{"class":69,"line":251},[67,14350,14351],{},"  $this->where(function($query) use ($columns, $search) {\n",[67,14353,14354],{"class":69,"line":264},[67,14355,14356],{},"    foreach(array_wrap($columns) as $column) {\n",[67,14358,14359],{"class":69,"line":280},[67,14360,14361],{},"      $query->orWhere($column, $search);\n",[67,14363,14364],{"class":69,"line":288},[67,14365,1557],{},[67,14367,14368],{"class":69,"line":781},[67,14369,10609],{},[67,14371,14372],{"class":69,"line":792},[67,14373,989],{"emptyLinePlaceholder":988},[67,14375,14376],{"class":69,"line":803},[67,14377,14378],{},"  return $this;\n",[67,14380,14381],{"class":69,"line":814},[67,14382,11797],{},[11,14384,14385,14386,14389,14390,14393,14394,14396],{},"So now, if we pass a single column (using the array_wrap function we convert it to an array), and search that column, but if we add multiple columns in an array than we loop through all of them and search the search term in all of those columns. Everything is wrapped in a ",[53,14387,14388],{},"where"," query because we don't want the ",[53,14391,14392],{},"whereLike"," query to mess up any other ",[53,14395,14388],{}," queries we can perform on the Eloquent model.",[11,14398,14399],{},"You can use this macro now like this:",[58,14401,14403],{"className":14218,"code":14402,"language":14220,"meta":63,"style":63},"User::whereLike(['username', 'email'], $search)\n  ->where('enabled', true)\n  ->get();\n",[53,14404,14405,14410,14415],{"__ignoreMap":63},[67,14406,14407],{"class":69,"line":70},[67,14408,14409],{},"User::whereLike(['username', 'email'], $search)\n",[67,14411,14412],{"class":69,"line":251},[67,14413,14414],{},"  ->where('enabled', true)\n",[67,14416,14417],{"class":69,"line":264},[67,14418,14237],{},[45,14420,14422],{"id":14421},"second-approach-using-scopes","Second approach: Using scopes",[11,14424,14425,14426,14431,14432,14435],{},"If you don't need the 'search' functionality in all of your models, you can define a ",[1711,14427,14430],{"href":14428,"rel":14429},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F5.0\u002Feloquent#query-scopes",[1715],"scope"," for your model. If we want to do this on the User model, open the ",[53,14433,14434],{},"User.php"," model and add the scope like this:",[58,14437,14439],{"className":14218,"code":14438,"language":14220,"meta":63,"style":63},"public function scopeWhereLike($query, $columns, $search) {\n  $query->where(function($q) use ($columns, $search) {\n    foreach(array_wrap($columns) as $column) {\n      $q->orWhere($column, $search);\n    }\n  });\n\n  return $query;\n}\n",[53,14440,14441,14446,14451,14455,14460,14464,14468,14472,14477],{"__ignoreMap":63},[67,14442,14443],{"class":69,"line":70},[67,14444,14445],{},"public function scopeWhereLike($query, $columns, $search) {\n",[67,14447,14448],{"class":69,"line":251},[67,14449,14450],{},"  $query->where(function($q) use ($columns, $search) {\n",[67,14452,14453],{"class":69,"line":264},[67,14454,14356],{},[67,14456,14457],{"class":69,"line":280},[67,14458,14459],{},"      $q->orWhere($column, $search);\n",[67,14461,14462],{"class":69,"line":288},[67,14463,1557],{},[67,14465,14466],{"class":69,"line":781},[67,14467,10609],{},[67,14469,14470],{"class":69,"line":792},[67,14471,989],{"emptyLinePlaceholder":988},[67,14473,14474],{"class":69,"line":803},[67,14475,14476],{},"  return $query;\n",[67,14478,14479],{"class":69,"line":814},[67,14480,1601],{},[11,14482,14483,14484,944,14488,944,14491,56],{},"There are many options if you need a more advanced search. Here are some of them like ",[1711,14485,14199],{"href":14486,"rel":14487},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F5.7\u002Fscout",[1715],[1711,14489,10237],{"href":10235,"rel":14490},[1715],[1711,14492,14495],{"href":14493,"rel":14494},"https:\u002F\u002Fwww.elastic.co\u002F",[1715],"Elasticsearch",[11,14497,14498],{},"Pick the approach that fits your project scope. If you need search on most models, go with macros. If it's isolated to one or two, scopes keep things tidy.",[1670,14500,14501],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":63,"searchDepth":251,"depth":251,"links":14503},[14504,14505],{"id":14243,"depth":251,"text":14244},{"id":14421,"depth":251,"text":14422},"2019\u002F07\u002F16","You can always install Laravel Scout and use some of its engines for searching. However sometimes we don't need that heavy-on-server search, so we can use this simple approach for searching data in your databases.","\u002Fimages\u002Flaravel-eloquent.jpeg","laravel eloquent, eloquent search, model search, eloquent models, eloquent scopes, larvel scopes, laravel macros, eloquent macros",{},"\u002Fblog\u002Fpragmatic-and-lightweight-search-for-laravel-models","☕️ 4 min read",{"title":14189,"description":14507},"pragmatic-and-lightweight-search-for-laravel-models","blog\u002Fpragmatic-and-lightweight-search-for-laravel-models","laravel, advanced laravel, laravel's eloquent","JJOyRjkNLlsYxmS76Lz1zTroOLsCA3IJOhXzerQ6xDU",1774945272080]