Componente pagini web SVG pentru IoT și producători (Partea 1)
Publicat: 2022-03-10Piața IoT este încă în fazele sale incipiente, dar își asumă abur. Ne aflăm la un vârf în istoria IoT. Piețele se multiplică de patru ori pe parcursul a cinci ani, 2015 până în 2020. Pentru dezvoltatorii web, această creștere IoT este semnificativă. Există deja o cerere mare pentru tehnici web IoT.
Multe dispozitive vor fi răspândite geospațial, iar proprietarii lor vor dori control și management de la distanță. Trebuie realizate stive web complete pentru a crea canale pentru teleoperare. De asemenea, interacțiunea se va face cu unul sau mai multe dispozitive IoT simultan. Interacțiunea trebuie să fie în timpul real al lumii fizice.
Această discuție analizează cerințele de interfață folosind Vue.js ca catalizator și ilustrează o metodă de comunicare între paginile web și dispozitivul din mai multe substituții.
Iată câteva dintre obiectivele planificate pentru această discuție:
- Creați o singură pagină web aplicație SPWA care găzduiește grupuri de interfețe IoT om-mașină (putem numi aceste „grupuri de panouri”);
- Afișează liste de identificatori de grup de panouri ca rezultat al interogării unui server;
- Afișează panourile unui grup selectat ca rezultat al unei interogări;
- Asigurați-vă că afișajul panoului este încărcat leneș și devine animat rapid;
- Asigurați-vă că panourile se sincronizează cu dispozitivele IoT.
IoT și creșterea rapidă a paginilor web
Prezentarea graficelor pentru vizualizarea și controlul de la distanță al hardware-ului împreună cu sincronizarea paginilor web cu procese fizice în timp real sunt în domeniul rezolvării problemelor paginilor web inerente acestui viitor IoT.
Mulți dintre noi încep să căutăm tehnici de prezentare IoT, dar există câteva standarde web împreună cu câteva tehnici de prezentare pe care le putem începe acum. Pe măsură ce explorăm împreună aceste standarde și tehnici, ne putem alătura acestui val IoT.
Tablourile de bord și vizualizarea datelor sunt solicitate. În plus, cererea de a depăși paginile web care oferă formulare sau afișează liste sau conținut textual este mare. Tablourile de bord pentru IoT trebuie să fie pictografice, animate. Animațiile trebuie să fie sincronizate cu procesele fizice în timp real pentru a oferi utilizatorilor o vedere veridică a stării mașinii. Starea mașinii, cum ar fi o flacără care arde sau nu, depășește starea aplicației și oferă informații critice operatorilor, poate chiar informații de siguranță.
Tablourile de bord necesită mai mult decât vizualizarea datelor. Trebuie să ținem cont de faptul că partea de lucruri a IoT este dispozitivele care nu au doar senzori, ci și interfețe de control. În implementările hardware, MCU-urile sunt extinse cu comutatoare, comutatoare de prag, setări de parametri și multe altele. Cu toate acestea, paginile web pot lua locul acelor componente de control hardware .
Nimic nou. Interfețele de computer pentru hardware există de mult timp, dar creșterea rapidă a utilizării paginilor web pentru aceste interfețe face parte din experiența noastră actuală. WebRTC și Speech API se află pe o cale de dezvoltare care a început în 2012. WebSockets s-a dezvoltat într-un interval de timp similar.
IoT este în mintea noastră de mult timp. IoT a făcut parte din dialogul uman din 1832. Dar, IoT și wireless, așa cum vom cunoaște, au fost imaginate de Tesla în jurul anului 1926. Forbes 2018 State of Iot ne spune focalizarea actuală pe piață pentru IoT. De interes pentru dezvoltatorii web, articolul prezintă tablouri de bord:
„Adoptatorii timpurii sau susținătorii IoT acordă prioritate tablourilor de bord, raportărilor, cazurilor de utilizare IoT care oferă fluxuri de date integrale pentru analiză, vizualizare avansată și extragere de date.”
Piața IoT este uriașă. Acest articol Market Size oferă o predicție pentru numărul de dispozitive care vor apărea: 2018: 23,14 miliarde ⇒ 2025: 75,44 miliarde. Și încearcă să-și pună o cifră financiară: 2014: \2,99 trilioane USD ⇒ 2020: 8,90 trilioane USD. Cererea de competențe IoT va avea cea mai rapidă creștere: IoT în cerere.
Pe măsură ce dezvoltăm interfețe clare pentru controlul și monitorizarea dispozitivelor, întâmpinăm o nouă problemă pentru dezvoltarea interfețelor noastre. Toate miliardele de dispozitive vor fi deținute de mulți oameni (sau organizații). De asemenea, fiecare persoană poate deține orice număr de dispozitive. Poate chiar și unele dintre dispozitive vor fi partajate.
Interfețele moderne care au fost realizate pentru comenzile mașinilor au adesea un aspect bine definit specific unei anumite mașini sau instalării câtorva mașini. De exemplu, într-o casă inteligentă, un sistem high-end va avea un LCD cu panouri pentru dispozitivele amplasate cu grijă. Dar, pe măsură ce creștem cu versiunea web a IoT, vor exista orice număr de panouri pentru un flux dinamic și chiar mobil de dispozitive.
Gestionarea panourilor pentru dispozitive devine similară cu gestionarea conexiunilor sociale pe site-urile web de socializare.
„Interfețele noastre de utilizator vor trebui să fie dinamice în gestionarea panoului foarte animat în timp real care trebuie afișat la un moment dat pentru fiecare utilizator anume.”
Tabloul de bord este o aplicație web SPWA cu o singură pagină. Și, ne putem imagina o bază de date de panouri. Deci, dacă un singur utilizator are de gând să acceseze un număr de panouri și configurații pentru dispozitivele sale împrăștiate pe planetă, SPWA trebuie să acceseze componentele panoului la cerere. Panourile și unele dintre JavaScript-ul compatibil vor trebui să se încarce leneș.
„Interfețele noastre vor trebui să funcționeze cu cadre de pagini web care pot permite încorporarea de legături de componente asincrone fără a reinițializa cadrele lor.”
Să folosim Vue.js, WebSockets, MQTT și SVG pentru a face un pas pe piața IoT.
Lectură recomandată : Construirea unui infografic interactiv cu Vue.js
Arhitectură la nivel înalt pentru o aplicație web IoT
Când proiectați interfața pentru pagina web IoT, aveți întotdeauna multe opțiuni. O opțiune ar putea fi să dedici o singură pagină unui singur dispozitiv. Pagina ar putea fi redată chiar și pe partea serverului. Serverul ar avea sarcina de a interoga dispozitivul pentru a obține valorile senzorului și apoi de a pune valorile în locurile adecvate din șirul HTML.
Mulți dintre noi sunt familiarizați cu instrumente care permit să fie scrise șabloane HTML cu marcatori speciali care indică unde să pună valorile variabile. Vederea {{temperature}}
într-un astfel de șablon ne spune nouă și motorului de vizualizare să luăm temperatura interogată de pe un dispozitiv și să înlocuim simbolul {{temperature}}
cu acesta. Deci, după ce a așteptat ca serverul să interogheze dispozitivul, dispozitivul răspunzând, redând pagina și livrând pagina, utilizatorul va putea în sfârșit să vadă temperatura raportată de dispozitiv.
Pentru această pagină pe arhitectură de dispozitiv, utilizatorul poate dori apoi să trimită o comandă către dispozitiv. Nicio problemă, el poate completa un formular HTML și trimite. Serverul ar putea avea chiar o rută doar pentru dispozitiv, sau poate puțin mai inteligent, o rută pentru tipul de dispozitiv și ID-ul dispozitivului. Serverul ar traduce apoi datele formularului într-un mesaj pe care să-l trimită pe dispozitiv, să le scrie la un handler de dispozitiv și să aștepte o confirmare. Apoi, serverul poate răspunde în sfârșit la cererea de postare și poate spune utilizatorului că totul este în regulă cu dispozitivul.
Multe CMS-uri funcționează în acest fel pentru actualizarea intrărilor de blog și altele asemenea. Nimic nu pare ciudat. Se pare că HTML peste HTTP a avut întotdeauna designul pentru obținerea paginilor care au fost redate și pentru trimiterea datelor din formular pentru a fi gestionate de serverul web. În plus, există mii de CMS-uri din care să alegeți. Așadar, pentru a pune în funcțiune sistemul nostru IoT, pare rezonabil să trecem prin acele mii de CMS-uri pentru a vedea care este potrivit pentru job. Sau, pentru început, am putea aplica un filtru pe CMS-uri.
Trebuie să luăm în considerare natura în timp real a ceea ce avem de-a face. Deci, în timp ce HTML în forma sa originală este destul de bun pentru multe sarcini de întreprindere, are nevoie de puțin ajutor pentru a deveni mecanismul de livrare pentru managementul IoT. Deci, avem nevoie de un CMS sau de un server web personalizat care să ajute HTML să facă această treabă IoT. De asemenea, ne putem gândi doar la server, deoarece presupunem că CMS-urile oferă funcționalitate server. Trebuie doar să ținem cont de faptul că serverul trebuie să ofere animație bazată pe evenimente, astfel încât pagina nu poate fi imprimată 100% static finalizată.
Iată câțiva parametri care ar putea ghida alegerile pentru pagina noastră web legată de dispozitiv, lucruri pe care ar trebui să le facă:
- Primiți datele senzorului și alte mesaje de stare a dispozitivului în mod asincron ;
- Redați datele senzorului pentru pagina din client (aproape corolarul 1);
- Publicați comenzi pentru un anumit dispozitiv sau grup de dispozitive în mod asincron ;
- Opțional, trimiteți comenzi prin server sau ocoliți-l.
- Menține în siguranță relația de proprietate între dispozitiv și utilizator;
- Gestionați funcționarea critică a dispozitivului fie fără interferență, fie suprascriind.
Lista ne vine în minte când ne gândim la o singură pagină care acționează ca interfață pentru un dispozitiv selectat . Dorim să putem comunica liber cu dispozitivul când vine vorba de comenzi și date.
În ceea ce privește pagina, trebuie să cerem o singură dată serverului web. Ne-am aștepta ca serverul web (sau aplicația asociată) să ofere o cale de comunicare sigură. Și, calea nu trebuie să fie prin server, sau poate ar trebui să evite serverul cu totul, deoarece serverul poate avea sarcini cu prioritate mai mare, altele decât să aibă grijă de comunicarea unei pagini pentru datele care provin de la senzori.
De fapt, ne putem imagina date care vin de la un senzor o dată pe secundă și nu ne-am aștepta ca serverul web în sine să ofere o secundă constantă la a doua actualizare pentru mii de fluxuri de senzori individuale multiplicate cu mii de spectatori. Desigur, un server web poate fi partiționat sau configurat într-un cadru de echilibrare a sarcinii, dar există și alte servicii care sunt personalizate pentru livrarea senzorilor și comenzile de marshaling către hardware.
Serverul web va trebui să livreze un pachet, astfel încât pagina să poată stabili canale de comunicație sigure cu dispozitivul. Trebuie să fim atenți la trimiterea de mesaje pe canale care nu oferă o anumită gestionare a tipurilor de mesaje care trec. Trebuie să existe anumite cunoștințe dacă un dispozitiv se află într-un mod care poate fi întrerupt sau poate exista o cerere de acțiune a utilizatorului dacă un dispozitiv este scăpat de sub control. Deci, serverul web poate ajuta clientul să obțină resursele adecvate care pot afla mai multe despre dispozitiv. Mesageria ar putea fi făcută cu ceva de genul unui server MQTT. Și, ar putea exista unele servicii pentru pregătirea serverului MQTT care pot fi inițiate atunci când utilizatorul obține acces la panoul său prin intermediul serverului web.
Datorită lumii fizice cu cerințele sale în timp real și din cauza unor considerații suplimentare de securitate, diagrama noastră devine puțin diferită de cea originală.
Nu ne putem opri aici. Configurarea unei singure pagini pe dispozitiv, chiar dacă este receptivă și gestionează bine comunicarea, nu este ceea ce am cerut. Trebuie să presupunem că un utilizator se va conecta la contul său și va accesa tabloul de bord. De acolo, va cere o listă de proiecte de conținut (cel mai probabil proiecte la care lucrează). Fiecare element din listă se va referi la un număr de resurse. Când selectează un articol făcând clic sau atingând, el va avea acces la o colecție de panouri, fiecare dintre ele va avea câteva informații despre o anumită resursă sau dispozitiv IoT.
Orice număr de panouri livrate ca răspuns la interogarea generată ca urmare a acțiunii interfeței utilizatorului poate fi acele panouri care interacționează cu dispozitivele live. Deci, de îndată ce apare un panou, va fi de așteptat să arate activitatea în timp real și să poată trimite o comandă către un dispozitiv.
Modul în care sunt văzute panourile pe pagină este o decizie de proiectare. Acestea pot fi ferestre plutitoare sau pot fi casete pe un fundal care poate fi derulat. Oricum ar fi prezentat, panourile vor marca timpul, temperatura, presiunea, viteza vântului sau orice altceva vă puteți imagina. Ne așteptăm ca panourile să fie animate în funcție de diferite scări grafice. Temperatura poate fi prezentată ca un termometru, viteza ca un indicator de viteză semicircular, sunetul ca o formă de undă în flux și așa mai departe.
Serverul web are sarcina de a furniza panourile potrivite utilizatorului potrivit, având în vedere interogările unei baze de date de panouri și având în vedere că dispozitivele trebuie să fie disponibile fizic. În plus, având în vedere că vor exista multe tipuri diferite de dispozitive, panourile pentru fiecare dispozitiv vor fi probabil diferite. Deci, serverul web ar trebui să poată furniza informațiile pictografice necesare pentru redarea unui panou. Cu toate acestea, pagina HTML pentru tabloul de bord nu ar trebui să fie încărcată cu toate panourile posibile. Nu există idee despre câți vor fi.
Iată câțiva parametri care ar putea ghida alegerile pentru pagina noastră de bord, lucruri pe care ar trebui să le facă:
- Prezentați o modalitate de selectare a grupurilor de panouri de dispozitive asociate;
- Utilizați mecanisme de comunicare simultană a dispozitivelor pentru un anumit număr de dispozitive;
- Activați panourile dispozitivelor atunci când utilizatorul le solicită;
- Încorporați elemente grafice încărcate leneș pentru modele unice de panouri;
- Utilizați jetoane și parametri de securitate pentru fiecare panou;
- Mențineți sincronicitatea cu toate dispozitivele aflate sub inspecția utilizatorului.
Putem începe să vedem cum se schimbă jocul, dar în lumea designului tabloului de bord, jocul se schimbă puțin ici și acolo de ceva timp. Trebuie doar să ne limităm la câteva instrumente actualizate și utile de dezvoltare a paginii pentru a ne pune în funcțiune.
Să începem cu cum putem reda panourile. Aceasta pare deja o treabă mare. Ne imaginăm multe tipuri diferite de panouri. Dar, dacă ați folosit vreodată un DAW muzical, ați vedea cum au folosit grafica pentru a face panourile să arate ca dispozitivele analogice folosite de trupele de demult. Toate panourile din DAW-uri sunt desenate de pluginurile care operează pe sunet. De fapt, multe dintre aceste pluginuri DAW ar putea folosi SVG pentru a-și reda interfețele. Așadar, ne limităm la manipularea interfețelor SVG, care, la rândul lor, pot fi orice grafic pe care ni-l putem imagina.
Alegerea SVG pentru panouri
Desigur, îmi plac DAW-urile și aș folosi asta ca exemplu, dar SVG este un standard pentru pagini web. SVG este un standard W3C. Este pentru a transporta desene pe paginile web. SVG era un cetățean de clasa a doua pe pagina web, necesar pentru a trăi în iFrames. Dar, de la HTML5, a fost un cetățean de primă clasă. Poate că, atunci când va apărea SVG2, va putea folosi elemente de formular. Deocamdată, elementele de formular sunt obiecte străine în SVG. Dar asta nu ar trebui să ne împiedice să facem din SVG substratul pentru panouri.
SVG poate fi desenat, stocat pentru afișare și poate fi încărcat leneș. De fapt, pe măsură ce vom explora sistemul de componente, vom vedea că SVG poate fi folosit pentru șabloanele de componente. În această discuție, vom folosi Vue.js pentru a face componente pentru panouri.
Desenarea SVG nu este dificilă, deoarece există multe programe de desenare care sunt ușor de obținut. Dacă cheltuiți banii, puteți obține Adobe Illustrator, care exportă SVG. Inkscape a fost de ceva vreme o bază pentru crearea SVG. Este open source și funcționează bine pe Linux, dar poate fi rulat și pe Mac și Windows. Apoi, există mai multe programe de editare SVG a paginilor web care sunt open source, precum și unele versiuni SaaS.
Am căutat în jur un editor SVG bazat pe web, open-source. După ce m-am uitat ceva în jur, am dat de SVG-Edit. Îl puteți include în propriile pagini web, poate dacă faceți un blog bazat pe SVG sau așa ceva.
Când vă salvați munca într-un fișier, SVG-Edit o descarcă în browser și puteți prelua fișierul din directorul de descărcare.
Imaginea pe care am desenat-o arată o poartă AND care controlează un integrator. Nu este ceea ce te-ai aștepta de obicei să vezi într-un panou pentru un MCU. Panoul ar putea avea un buton pentru a alimenta una dintre intrările de poartă AND, probabil. Apoi ar putea avea un afișaj de la un ADC care citește ieșirea integratorului. Poate că va fi o diagramă cu linii pe o axă a timpului. Cele mai multe panouri vor avea elemente grafice care permit utilizatorului să se raporteze la ceea ce se întâmplă în interiorul MCU. Și, dacă circuitul nostru va trăi oriunde, va fi în MCU.
Cu toate acestea, diagrama noastră electronică poate fi folosită pentru a discuta despre animație. Ceea ce vrem să facem este să aruncăm o privire la SVG și să vedem unde putem ajunge la unele dintre etichetele DOM pe care am dori să le schimbăm într-un fel. Apoi putem anima SVG-ul folosind puțin JavaScript vanilie și un cronometru. Să facem poarta AND să clipească în diferite culori.
SVG-ul pe care îl căutăm se află în următoarea casetă de cod. Nu pare foarte prietenos pentru programator, deși utilizatorul va fi destul de mulțumit. Cu toate acestea, există încă câteva indicii de continuat pentru a găsi pe ce element DOM dorim să operam. În primul rând, majoritatea instrumentelor de desen SVG au o modalitate de a ajunge la proprietățile obiectului, în special, atributul id
. SVG-Edit are și o modalitate. În editor, selectați poarta AND și observați bara de instrumente. Veți vedea un câmp pentru id
și class
CSS, de asemenea.
Dacă nu puteți ajunge la un instrument de editare dintr-un motiv oarecare, puteți deschide SVG-ul într-un browser și puteți inspecta DOM-ul. În orice caz, am descoperit că poarta noastră avea id
= „svg_1”.
<svg width="640" height="480" xmlns="https://www.w3.org/2000/svg" xmlns:svg="https://www.w3.org/2000/svg"> <g class="layer"> <title>Layer 1</title> <path d="m80.59881,87.020171l14.714795,0m-14.714793,-11.938687l14.714797,0.000004m-0.033867,-6.543869l0,24.758504c42.377882,2.221929 43.364812,-27.139117 0,-24.758504zm47.366321,12.333056l-15.303943,0m-48.188699,-6.489897l1.454753,0l0,1.454751l-1.454753,0l0,-1.454751zm-0.068425,11.869359l1.454753,0l0,1.454753l-1.454753,0l0,-1.454753zm63.545246,-6.089294l1.454751,0l0,1.454751l-1.454751,0l0,-1.454751z" fill="#FF0000" stroke="#000000"/> <path d="m48.58886,119.662231l18.234678,0l2.523043,-7.173309l4.128604,13.808613l4.587337,-13.987948l4.013933,13.808613l4.35797,-13.629278l4.35797,13.718944l2.408353,-6.72497l18.349357,0m-64.482612,-0.623112l1.515724,0l0,1.515728l-1.515724,0l0,-1.515728zm64.484275,-0.103111l1.515721,0l0,1.515728l-1.515721,0l0,-1.515728z" fill="#FF0000" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="rotate(90.3367 80.0675 119.304)"/> <polygon cx="108.5" cy="79.5" edge="0" fill="#ffffff" orient="x" shape="regularPoly" sides="3" strokeWidth="null" strokecolor="#000000"/> <polygon cx="215.5" cy="192.5" edge="0" fill="#ffffff" orient="x" shape="regularPoly" sides="3" strokeWidth="null" strokecolor="none"/> <polygon cx="165.5" cy="164.5" edge="0" fill="#ffffff" orient="x" shape="regularPoly" sides="3" strokeWidth="null" strokecolor="none"/> <polygon cx="161.5" cy="138.5" edge="0" fill="#ffffff" orient="x" shape="regularPoly" sides="3" strokeWidth="null" strokecolor="none"/> <polygon cx="160.5" cy="161.5" edge="0" fill="#ffffff" orient="x" shape="regularPoly" sides="3" strokeWidth="null" strokecolor="none"/> <g> <path d="m225.016923,53.008793l0,3.419331m-4.558966,-1.709666l9.11791,0m10.303228,4.235512l-25.770656,0m-34.429182,0l24.544724,0m0.220544,-4.058194l1.543807,0l0,8.164451l-1.543807,0l0,-8.164451zm7.939567,-4.473673l1.543805,0l0,16.999955l-1.543805,0l0,-16.999955zm-34.176663,8.126854l1.474036,0l0,0.747515l-1.474036,0l0,-0.747515zm61.677552,0.018809l1.474038,0l0,0.747515l-1.474038,0l0,-0.747515z" fill="#FF0000" sides="3" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null"/> <polygon cx="171.5" cy="159.5" edge="43.256342" fill="#ffffff" orient="x" points="223.47406005859375,91.5 186.01296997070312,113.128173828125 186.01296997070312,69.871826171875 223.47406005859375,91.5 " shape="regularPoly" sides="3" stroke="#000000" stroke-width="null" strokeWidth="null" strokecolor="#000000"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="171" x2="186" y1="103.5" y2="103.5"/> <path d="m130.801817,80.659041l15.333707,0l2.12165,-4.564833l3.47178,8.787299l3.857534,-8.901421l3.375353,8.787299l3.664657,-8.673176l3.664657,8.730237l2.025206,-4.279526l15.430142,0m-54.224016,-0.396526l1.274586,0l0,0.964554l-1.274586,0l0,-0.964554zm54.225414,-0.065616l1.274584,0l0,0.964554l-1.274584,0l0,-0.964554z" fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="171.5" x2="171.5" y1="103.75" y2="135.388167"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="177.75" x2="177.75" y1="58.75" y2="80.255951"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="223.75" x2="266.854524" y1="91.75" y2="91.75"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="241.75" x2="241.75" y1="59.75" y2="91.754167"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="168.25" x2="180.75" y1="135.75" y2="135.75"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="null" x1="169.75" x2="179.25" y1="138.5" y2="138.5"/> <line fill="none" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" x1="171" x2="179.75" y1="141.25" y2="141.25"/> </g> </g> </svg>
Tot ce ne trebuie acum este puțin JavaScript. Luăm în considerare mai întâi că atributul elementului „fill” este prezent. Apoi există doar programul simplu care urmează:
<html> <head> </head> <body> <!-- ALL THE SVG FROM ABOVE GOES HERE --> </body> <html> </svg> <script> // Set up a timer interval flash the color. var gateElement = document.getElementById("svg_1"); if ( gateElement ) { setInterval( () => { var fillC = gateElement.getAttribute("fill"); gateElement.setAttribute("fill", (fillC == "#00FF00") ? "#FF0000" : "#00FF00" ); }, 2000 ) } </script>
Observați că ceea ce avem este o pagină HTML minimă. Puteți tăia și lipi codul în editorul dvs. preferat. Și apoi nu uitați să tăiați și să lipiți SVG-ul pentru a înlocui comentariul. Versiunea mea de Chrome necesită ca pagina să fie HTML pentru a avea secțiunea JavaScript. Deci, acesta este un browser care încă tratează SVG ca pe ceva separat. Dar, este departe de zilele <iframe>
.
Dacă tăiați și lipiți corect, puteți deschide pagina și puteți vedea că poarta ȘI trece de la roșu la verde iar și iar.
Lectură recomandată : Descompunerea cercului SVG în căi
Panouri de construcție din VUE Components
Suntem deja pe cale de a face ca orice panou să prindă viață, dar dacă dorim să gestionăm colecții mari de panouri în moduri raționale, ne-ar avea treaba pentru noi. Acesta ar fi mai ales cazul dacă ne-am construi pur și simplu pe primul nostru exemplu.
În timp ce primul exemplu ne arată cum putem schimba în mod asincron o vizualizare a unui obiect, nu ne arată cum să legăm vizualizarea de starea oricărui obiect de date, cu atât mai puțin unul care administrează o mașină. Putem înțelege cu siguranță cum demonstrația setInterval
poate fi înlocuită cu un handler de fetch
, dar s-ar putea să nu obținem nici măcar starea unei mașini de la serverul web care servește pagina care conține SVG. De asemenea, atunci când obținem datele, programele noastre sunt acum obligate să cunoască structura DOM a paginii date.
Din fericire, cadre precum Vue au devenit populare și ne pot economisi multă muncă.
Este ușor să afli despre Vue. Documentația Vue este foarte accesibilă. Deci, dacă această discuție trece prea mult înainte, atunci s-ar putea să petreceți ceva timp învățând despre Vue pe propriul site web. Dar, există discuții foarte bune în cadrul paginilor Smashing. Krutie Patel a scris un articol uimitor despre realizarea unui infografic. Souvik Sarkar ne spune cum să construim un tablou de bord meteo cu Vue.
Selectarea grupului de panouri conexe
Pentru primul pas, ar trebui să ne referim la căutarea grupurilor de panouri. Un motiv pentru a face acest lucru mai întâi este că se află la nivelul cadru al interacțiunilor noastre umane.
Utilizatorul caută ceva care îl interesează. Poate că este interesat de toate dispozitivele din locații dintr-un oraș. Poate că are multe loturi de produse lichide și vrea să se reducă la un singur tip de produs, fiecare lot fiind guvernat de o mică colecție de dispozitive IoT. Deci, utilizatorul va căuta mai întâi pentru a obține o listă mică.
Iată procesul:
- Căutați grupuri de panouri după caracteristici/parametri.
- Vizualizați o listă de pictograme care reprezintă grupuri.
- Selectați o pictogramă (clic/atingeți).
- Începeți să utilizați panouri identificate cu pictograma atunci când apar.
Un alt motiv pentru care acesta este un prim pas bun este că putem folosi Vue în cea mai simplă formă. Nu sunt necesare instrumente de construcție. Vom include doar vue.js
cu o etichetă de script în HTML. De fapt, nici nu trebuie să-l descarcăm. Există un site unde este difuzată o copie de lucru a vue.js
Tot ce ne trebuie este următoarea etichetă:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
Am copiat eticheta de script direct din documentația Vue despre instalare.
Acum, avem nevoie de o pagină web care să poată încărca pictograme și să le transforme în ceva care dă clic. Vue face asta foarte ușor. De fapt, tocmai am scris o mică aplicație pentru a gestiona o listă Twitter folosind Vue. Acesta gestionează doar câmpurile de text. Deoarece este puțin mai simplu decât un SPWA care folosește pictograme, îl putem arunca o privire și apoi îl putem schimba pentru a fi cadrul de aplicație dorit pentru o singură pagină.
Iată o parte din cum arată pagina:
Aceasta pare o pagină destul de simplă. Fiecare intrare numerică exterioară este un interval orar cu unul sau două tweet-uri în ea. Al doilea tweet este opțional. Dacă editați un tweet, mecanismele Vue actualizează un obiect JavaScript. Această pagină lasă utilizatorului să facă clic pe butonul „actualizare intrări” pentru a spune serverului că ceva s-a schimbat, prin intermediul funcției sale de gestionare a butoanelor.
Pentru ca handlerul de buton să transmită date către server, trebuie să schimbe obiectul de date Vue într-un șir JSON. Acum, s-ar putea să vă întrebați cât de dificilă va fi traducerea unui obiect Vue în JSON. Se dovedește a fi o singură linie de cod. Puteți găsi linia în următorul cod sursă, dar dacă doriți să o găsiți mai repede, este evidențiată în paragraful de după codul sursă.
Pagina pare simplă. Aparentele inseala. Desigur, pagina pare simplă, dar codul este simplu? Da, într-adevăr este! Folosind Vue, pagina gestionează conținutul câmpurilor aproape magic. Iată codul:
<!DOCTYPE html> <html lang="en" prefix="og: https://ogp.me/ns#"> <!-- define microdata scope and type --> <head itemscope itemtype="https://schema.org/Article"> <title>Tweet Keeper</title> <style> body { margin: 2em; } .entryart { border: solid 1px navy; width: 80%; padding: 2px; padding-left: 6px; margin-bottom: 3px; background-color: #EEF4EE; } </style> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> </head> <body onload="GetTweets()"> <!-- some old fashioned handling --> <!-- The Vue app starts here. This is the HTML part of the Vue object --> <div> <!-- Recognize the name from the Vue doc --> <div itemscope itemtype="https://schema.org/Article"> <h1 itemprop="name">mangage tweets</h1> <p itemprop="description">My personal Tweet engine. This page accesses a personal tweet page that belongs to {{tweetOwner}}.</p> <!-- {{tweetOwner}} is in the data model. --> <button>Update Entries</button> </div> <!-- Here is a Vue loop for generating a lit --> <ol> <li v-for="tweet in tweets"> <!-- here is the first tweet represented as an object with a lable and tweet text --> <div class="entryart"> <input v-model="tweet.def[0].label" /> <input v-model="tweet.def[0].tweet" /> </div> <!-- here is the second tweet in the slot. But, notice that it is optional. --> <div class="entryart" v-if="tweet.def.length > 1"> <input v-model="tweet.def[1].label"/> <input v-model="tweet.def[1].tweet"/> </div> </li> </ol> </div> <script> var twtApp = new Vue({ el: '#tweetAppDiv', data: { tweets: [ // Where is the data? Still on the server.s ], tweetOwner : "Lucky Dude" // picked a name for demo } }); </script> </body> </html> <script> // Notice that you don't have to do everything in the Vue framework. // Here we are using some native API calls var gDefaultPostInfo = { // there server is beyond simple - an example from node.js docs method: 'POST', // or 'PUT' mode: "cors", // no-cors, cors, *same-origin cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached credentials: "same-origin", // include, *same-origin, omit redirect: "follow", // manual, *follow, error referrer: "no-referrer", // no-referrer, *client body: "", headers:{ 'Content-Type': 'application/json' } } // // // recall the "onload" function GetTweets(event) { var url = "https://localhost:8080/twitlist1.json" // We have a fixed file name. fetch(url).then((response) => { // this is now browser native response.text().then((text) => { var newData = JSON.parse(text); // DATA UPDATE! This is it. twtApp.tweets = newData // the page update right away with new data. }); }); } function sendTweets() { // recall the button up above. This is not a Vue style button, but still in the Vue app. var url = "https://localhost:8080/" var data = twtApp.tweets; // GET THE DATA OUT OF VUE. That's all folks. // // so happens that Vue pulls out the right data and stringifies it. var jdata = JSON.stringify(data); // data can be `string` or {object}! // gDefaultPostInfo.body = jdata; // that's for fetch - not Vue related // fetch(url,gDefaultPostInfo).then(res => { // We use fetch to POST as well as GET res.json() }).then(response => { console.log('Success:', JSON.stringify(response)) // promises }).catch(error => { console.error('Error:', error) }); } // // // </script>
Deci, doar pentru a evidenția liniile uimitoare care vorbesc despre puterea cadrului, să repetăm aici:
A. Acest lucru este extragerea datelor.
postOptionsObject.body = JSON.stringify(twtApp.tweets);
B. Aceasta înseamnă introducerea datelor în Vue și vizualizarea actualizării ecranului:
twtApp.tweets = JSON.parse(text) // text is the server response
Câtă muncă este asta?
Se pare că va exista o modalitate frumoasă de a exprima modul în care datele vor actualiza panourile pentru IoT.
Acum, să transformăm tweet-urile în pictograme pe care se poate face clic, concepute pentru a prelua componente de pe serverul web.
De la tweeturi la pictograme de preluare a panoului
Oamenilor le place să folosească SVG pentru pictograme. Le place această utilizare pentru SVG mai mult decât pentru alte lucruri, din câte îmi dau seama. Mă duc doar la numărul de site-uri web care vând sau oferă pictograme realizate în SVG. Momentul de vânzare este că grafica în linie are mai puțini octeți decât imaginile. Și, dacă aveam de gând să cer liste de imagini cu un comportament asemănător unui buton, s-ar putea să fi luat PNG-uri sau JPEG-uri în zilele în care SVG era în iframes. But, we can even find libraries in the Vue contributor lists that help us to a serving of icons.
We can turn the tweets page into an icon list returned as a search result. Just a little code has to be changed. Of course, there are a few things to be careful about if we want SVG icons to be loaded as buttons. Vue provides mechanisms for putting HTML into the application. These mechanisms have to be used or DOM elements fetched from the server don't get interpreted.
Here is the kind of rendering you can get from view if you follow your first impulse in creating a handlebars style variable location in the application DOM.
Here is the code that produces the result in the picture:
<div> <div class="entryart"> <span class="oneItem" v-for="icon in iconList"> {{icon}} </span> </div> </div> <script> var iconApp = new Vue({ el: '#iconAppTry', data: { iconList: [ // Where is the data? Still on the server. ], queryToken : "Thermo Batches" // picked a name for demo } }); </script>
Notice that we have gone from looping over tweets to looping over icons. tweet in tweets
changed into icon in iconList
. Our twtApp
hooks into the DOM element #tweetAppDiv
, while our iconApp
hooks into the DOM element #iconAppTry
. Within the Vue option object, the data
subobject has a tweets
in the first app, and iconList
in the second. The fields are both empty arrays that receive data when the fetch
routine does its job.
But, we have imitated our tweet app too closely. In the code above, the iconList is an array, and the server is expected to send an array of strings. So, let's say the server has sent us HTML, and we have it properly decoded with the array assigned to data.iconList
. Then, the picture above can be seen.
Now, let's change the code just a little. In this revised code, we can see the following:
v-html="icon">
Vue responds to the v-html syntax by putting in the DOM of the icon
element. Notice that the syntax is included after the loop directive as another attribute to the span
tag.
By removing the handlebars
syntax and using v-html
, our picture changes to something more comprehensible:
<div> <div class="entryart"> <span class="oneItem" v-for="icon in iconList" v-html="icon"> </span> </div> </div> <script> var iconApp = new Vue({ el: '#iconAppTry2', data: { iconList: [ // Where is the data? Still on the server. ], queryToken : "Thermo Batches" // picked a name for demo } }); </script>
While v-html
is a quick way to do things, the Vue team recommends using components to get the desired HTML into the page. That seems like a good idea, and we shall soon set about doing that.
But, let's use the v-html
syntax for our next example.
It's time to set up our working example for fetching SVG icons. Let's have those icons be responsive to a button click. Once those are working, we can get the panels associated with an icon.
Let's suppose that the SVG required for icons is stored in a database. For our example, we can just fetch a JSON file from the server. The grown-up version of the icon server would store many such files in a database, and deliver them to the page with the same mechanisms.
Also, it's best if the SVG arrives on the page URL encoded since we will be using JSON parse. The SVG can be decoded by calling JavaScript's decodeURIComponent
function.
In order to simulate the response to searching, we can make use of several JSON files. The page can have one button for each file. Here is the code for the page:
<!DOCTYPE html> <html lang="en" prefix="og: https://ogp.me/ns#"> <!-- define microdata scope and type --> <head itemscope itemtype="https://schema.org/Article"> <title>Search Bar</title> <style> body { margin: 2em; } div { margin: 6px; } .entryart { border: solid 1px navy; width: 80%; padding: 2px; padding-left: 6px; margin: 2px; margin-bottom: 3px; background-color: #EEF4EE; } .oneItem { background-color: #EEFFFF; margin: 2px; padding: 4px; border: solid 1px purple; } </style> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> </head> <body> <!-- some old fashioned handling --> <!-- The Vue app starts here. This is the HTML part of the Vue object --> <div> <!-- Recognize the name from the Vue doc --> <div> <h2 itemprop="name">Request MCU Groups</h2> <p itemprop="description">These are groups satistfying this query: {{queryToken}}.</p> <!-- {{tweetOwner}} is in the data model. --> <button>Find All</button> <button>Find 5 Point</button> <button>Find 6 Point</button> </div> <!-- Here is a Vue loop for generating a lit --> <div class="entryart"> <button v-for="iconEntry in iconList" @click="goGetPanel(iconEntry.name)" > <div v-html="iconEntry.icon"> </div> </button> </div> </div> <script> var iconApp = new Vue({ el: '#iconAppTry', data: { iconList: [ // Where is the data? Still on the server. ], queryToken : "Thermo Batches" // picked a name for demo }, methods : { goGetPanel: (pname) => { // `this` inside methods points to the Vue instance alert('Hello ' + pname + '!') } } }); </script> </body> </html> <script> // // recall the "onclick" on the <buttons> function GetIcons(points) { // special file names instead of search parameters // var url = (points == 11) ? "https://localhost:8080/batchQuery-all.json" : ((points == 5) ? "https://localhost:8080/batchQuery-five.json" : "https://localhost:8080/batchQuery-six.json") fetch(url).then((response) => { // this is now browser native response.text().then((text) => { var newData = JSON.parse(text); // DATA UPDATE! This is it. newData = newData.map(obj => { obj.icon = decodeURIComponent(obj.icon); return(obj) }); iconApp.iconList = newData; // the page update right away with new data. }); }); } </script>
Here is one display of icons that have been fetched from the server:
The data being sent is an array with the following kind of structure:
{ "style" : { "color" : "red", "backgroundColor" : "yellow" }, "icon" : svg1, "name" : "thermos" },
Aici, svg1
este SVG preluat dintr-un fișier. Desigur, un server corect ar fi preluat structura dintr-o bază de date, unde SVG-ul ar fi stocat în structură.
Iată un fragment din codul de mai sus. Acesta este codul care preia JSON și plasează matricea de structuri în aplicația Vue. Puteți vedea structura promisiunii a fetch
în uz. Textul este analizat, iar în rândul următor, SVG-ul codificat este decodat. Încă o linie, iar Vue actualizează pagina. Numărul de butoane din bara de butoane va fi egal cu lungimea matricei JSON.
fetch(url).then((response) => { // this is now browser native response.text().then((text) => { var newData = JSON.parse(text); // DATA UPDATE! This is it. newData = newData.map(obj => { obj.icon = decodeURIComponent(obj.icon); return(obj) }); // the page update right away with new data. iconApp.iconList = newData; }); });
Acum, încă două fragmente. Aplicația Vue. Cititorul va observa că directiva @click
a fost inclusă pe butoane. Elementul de date, iconEntry.name
, este transmis unei metode între ghilimele.
Metoda este definită în aplicația Vue:
<div class="entryart"> <button v-for="iconEntry in iconList" @click="goGetPanel(iconEntry.name)" > <div v-html="iconEntry.icon"> </div> </button> </div> </div>
Iată fragmentul pentru definirea metodelor. Obiectul methods
este adăugat imediat după obiectul de data
din obiectul parametru al aplicației:
, methods: { goGetPanel: (pname) => { // `this` inside methods points to the Vue instance alert('Hello ' + pname + '!') } }
Cititorul ar trebui să găsească definiția goGetPanel
, iar utilizarea acesteia a fost indicată pentru handlerul @click
. În aplicația noastră finală, apelul de alert
poate fi înlocuit cu o funcție care preia panouri de pe server.
O bibliotecă de componente pentru panouri IoT
Am putea decide doar că panourile pe care le preluăm de pe server pot fi desene HMTL sau doar SVG, dar dacă vor exista multe tipuri de panouri, am spera că munca de creare a panourilor ar putea fi simplificată prin existența unor biblioteci de componente pentru alege din. Ne putem imagina că editorii SVG ar putea fi îmbunătățiți pentru a permite ca componentele bibliotecii să fie aruncate în imagini ca parte a editării. Apoi, dacă editorul SVG ar putea scoate o versiune a imaginii cu etichete componente, atunci utilizarea Vue ar permite crearea imaginii, asigurând în același timp că automatizarea și animația JavaScript sunt țesute bine împreună. Pentru discuția noastră, câteva editări manuale ne pot ajuta să ajungem acolo.
Dacă vrem să creăm panouri din componentele Vue, atunci ar fi mai bine să ne dăm seama cum să facem componentele și apoi să le adunăm împreună în ceva util. Va trebui să trecem la utilizarea instrumentelor de linie de comandă oferite de Vue și să ne organizăm fluxul de lucru.
Componente
Documentația Vue subliniază că secțiunea de data
componente (subobiect) a definiției componentei trebuie să fie o funcție care returnează date. Motivul pentru aceasta este că Vue trebuie să păstreze datele separate între instanțe. Deci, în trecerea de la inițializarea unei aplicații Vue la o definiție de componentă, există o altă mică schimbare de cod.
În acest prim fragment de cod, o aplicație Vue este inițializată:
var iconApp = new Vue({ el: '#iconApp', data: { // this is the data field that can be easily updated }, methods : { ... } });
În acest nou fragment de cod, o componentă este definită și înregistrată. În primul rând, observați că, în loc să creați o new Vue
, o componentă numită iconic
este înregistrată. Apoi, câmpul de data
returnează date personalizate pentru orice instanță iconic
creată de aplicația Vue. În cele din urmă, câmpul template
este prezent la sfârșitul înregistrării componentei. Orice HTML care ar fi putut fi scris pe pagina web pentru a afișa componenta poate face parte din template
.
Vue.component('iconic', data: () => { var instanceData = { // data fields named for the // variables appearing in the template onevar : "test" } return(instanceData); }, methods : { ... }, template: '<div>This appears in every instance {{onevar}}</div>' });
Deci, ne putem imagina un panou cu termometre. Deci, dacă cineva ar furniza o componentă de thermometer
, ne-am aștepta la o definiție a componentei undeva în codul nostru. Ca atare:
Vue.component('thermometer', data: () => { var instanceData = { // data fields named for the // variables appearing in the template temperature : 0 } return(instanceData); }, methods : { ... }, template: '<div>Some SVG will go here</div>' });
Încercăm să creăm ceva care să arate astfel:
Componenta termometrului este foarte asemănătoare cu primele componente pe care le veți întâlni în tutorialele Vue. Dar, este puțin dificil să-ți dai seama cum să-l actualizezi. Există o modalitate mai bună de a defini componenta pentru reactivitate folosind proprietăți. Și asta este în următoarele:
Vue.component('thermometer', { props: ['temperature'], computed : { y: function() { var t = this.temperature/100; var h = 54.724472; var y_bar = 41.176476 // starts near the top // pretend the scale is 1 to 100, so that the temperature is a precentage return((1 - t)*h + y_bar) }, height : function() { var t = this.temperature/100; var h = 54.724472; // as high as the whole range var y_bar = 41.176476 // pretend the scale is 1 to 100, so that the temperature is a precentage return(t*h) } }, template: '#thermometer-template' })
Deci, în loc să reprezinte temperatura ca element de date. Este reprezentat ca o proprietate sub props
. Apoi, există o nouă secțiune, calculată , care oferă variabile care sunt funcții ale proprietății. Vedem this.temperature
fiind folosită atât pentru y
cât și pentru height
. Aceste variabile calculate sunt folosite în SVG ca atribute pentru un dreptunghi.
În SVG, y
crește de sus în jos. Deci, atunci când dorim ca dreptunghiul să fie mic în partea de jos a termometrului, y
-ul cutiei roșii trebuie să fie mai mic, iar înălțimea trebuie redusă astfel încât ( y + height
) să rămână la zero.
Observați câmpul template
în definiția componentelor. Este, de fapt, un ID de element de document. Elementul la care se face referire este o secțiune de script cu tipul special: type="text/x-template"
. Elementul de script este locul unde se află SVG-ul pentru termometre. Și, SVG utilizează variabilele Vue și termenii de control, astfel încât reactivitatea să poată fi definită.
Iată câteva dintre SVG:
<script type="text/x-template"> <svg xmlns:svg="https://www.w3.org/2000/svg" xmlns="https://www.w3.org/2000/svg" width="20" height="70" version="1.1" > <g transform="translate(0,-180)"> <g transform="matrix(2.0111869,0,0,1.0489665,-215.11053,144.5592)"> <rect stroke-linecap="null" stroke-linejoin="null" width="2.9665921" height="54.724472" x="111.90748" y="41.176476" /> <rect stroke-linecap="null" stroke-linejoin="null" width="2.9665921" x="111.90748" :height="height" :y="y" /> <g transform="matrix(0.76503813,0,0,1,26.586929,0)"> <line y2="57.306953" y1="57.306953" x2="113.15423" x1="107.22105" stroke-linejoin="null" stroke-linecap="null" /> <line y2="74.408356" y1="74.408356" x2="113.15423" x1="107.22105" stroke-linejoin="null" stroke-linecap="null"
Cititorul poate găsi id="thermometer-template"
în partea de sus și, privind mai jos, la elementele rect
, pot fi găsite variabilele calculate.
Aici utilizările variabile sunt separate. Sintaxa scurtă Vue pentru v-bind
este în uz, cu :height="height"
și același lucru pentru y
:
x="111.90748" :height="height" :y="y"
Când părintele elementelor SVG setează variabile care acționează ca intrare pentru temperatura proprietății temperature
, Vue recalculează height
și y
. Ca urmare, poziția și înălțimea casetei roșii se modifică.
Este de ajutor să aveți o listă a aplicației Vue care utilizează termometrul.
<body> <!-- The Vue app starts here. This is the HTML part of the Vue object --> <div> <!-- Recognize the name from the Vue doc --> <div> <h2 itemprop="name">Set Temperature</h2> <p itemprop="description">These are groups satistfying this query: {{queryToken}}.</p> <!-- {{tweetOwner}} is in the data model. --> <button @click="updateTemp(50,50)">mid</button> <button @click="updateTemp(20,80)">low</button> <button @click="updateTemp(80,20)">high</button> </div> <thermometer :temperature="temp1" ></thermometer> <thermometer :temperature="temp2" ></thermometer> </div> <script> var thermoApp = new Vue({ el: '#thermoApp', data: { temp1 : 30, temp2 : 60, queryToken : "HEAT" }, methods : { updateTemp: function (tval1,tval2) { this.temp1 = tval1; this.temp2 = tval2; } } }); </script> </body>
Asta este totul. Există trei butoane care apelează metoda updateTemp
a aplicației thermoApp
Vue. Secțiunea de date are două variabile de temperatură. Și, fiecare thermometer
își actualizează temperatura atunci când se schimbă valorile.
Codul celor două termometre menționate mai jos poate fi găsit în HTML-ul atribuit aplicației Vue.
<thermometer :temperature="temp1" ></thermometer> <thermometer :temperature="temp2" ></thermometer>
Observați că aplicația folosește formalismul function
pentru definirea metodei. Definirea updateTemp
în acest fel updateTemp: function (tval1,tval2)
permite accesarea variabilei de instanță this
.
De asemenea, definind updateTemp
acest fel updateTemp: (tval1,tval2) =>
atribuie this
unei structuri de date interne care nu reactioneaza si actualizeaza vizualizarea.
Asamblarea unui panou
Fiecare panou IoT poate fi o componentă. Vue oferă o modalitate de definire a componentelor cu subcomponente. În mod alternativ, există un mecanism de slot care poate fi folosit pentru a produce o componentă care poate cuprinde orice conținut HTML.
În următoarele câteva paragrafe, să ne uităm la realizarea unui panou din subcomponente. Există două forme care decurg rapid din exemplele noastre. Într-un caz, termometrele pot fi subcomponente numite în JavaScript. Într-un alt caz, componentele sunt definite independent, dar sunt menționate în HTML.
În ambele cazuri, același HTML poate fi folosit pentru șablon. Iată panoul nostru ca șablon:
<script type="text/x-template"> <div> <thermometer :temperature="temp1" ></thermometer> <thermometer :temperature="temp2" ></thermometer> </div> </script>
Singura diferență între primele detalii ale aplicației este că un element div
înconjoară cele două termometre. Vue va genera o eroare dacă șablonului îi lipsește un element DOM de nivel superior. div
-ul trece cerința Vue și mai multe elemente pot fi incluse în interiorul acestuia.
Acum, putem vedea cele două termometre unul lângă altul. Trecerea temperaturilor de sus la termometrul final are valori în cascadă. La nivelul superior, panoul se alătură aplicației atunci când o singură linie este inclusă în aplicația DOM.
<themo-panel :temp1="temp1" :temp2="temp2" ></themo-panel>
Șablonul pentru panou, deși simplu, pare să indice faptul că panourile pot fi proiectate cu ușurință din punct de vedere al componentelor. Este ca și cum ar fi posibil un limbaj doar pentru componentele IoT.
Acum, definiția șablonului pentru panou este suficient de simplă. Iată-l cu subcomponentele definite independent:
Vue.component('thermo-panel', { props: ['temp1','temp2'], template: '#thermo-panel-template' });
Este cam atât cât este necesar pentru ca panoul să funcționeze. Este adevărat că această versiune se bazează pe o listă lungă de proprietăți pentru definirea valorilor care urmează să fie actualizate pe măsură ce mesajele intră în pagină. Dar, acesta este un început bun. Actualizarea obiectului de data
la nivelul superior face treaba de a anima termometrele. Cu toate acestea, pe măsură ce panourile devin complicate, poate fi necesară o altă metodă pentru a arăta schimbarea.
După ce am menționat celelalte moduri de specificare a subcomponentelor, pentru panou, ar trebui să ne uităm la el. Iată-l:
Vue.component('thermo-panel', { props: ['temp1','temp2'], template: '#thermo-panel-template', components: { // a sub component for the labels 'thermometer': { props: { temperature: Number, }, template: '#thermometer-template', computed : { y: function() { var t = this.temperature/100; var h = 54.724472; var y_bar = 41.176476 // starts near the top // pretend the scale is 1 to 100, so that the temperature is a precentage return((1 - t)*h + y_bar) }, height : function() { var t = this.temperature/100; var h = 54.724472; // as high as the whole range var y_bar = 41.176476 // pretend the scale is 1 to 100, so that the temperature is a precentage return(t*h) } } } } });
Există cu siguranță mai mult cod, dar asta se datorează faptului că JavaScript pentru componenta thermometer
este inclus în lista de componente a thermo-panel
. Cele două abordări fac aceeași treabă, dar oferă moduri diferite de definire a componentelor de ambalare.
Momentan, preferința mea este pentru prima cale. Ar trebui să fie considerabil mai ușor să revizuiți panourile și să le recuperați dinamic dacă este necesară doar modificarea șablonului și a proprietăților. În acest scop, componentele definite independent formează o bibliotecă de componente. Dar, deși pare mai bine, în cele ce urmează devine mai convenabil să folosiți a doua modalitate, aparent mai verbosă.
Având în vedere că putem face panouri receptive din componente în moduri clar definite, voi explica cum le putem gestiona ca o bază de date care poate face interogări simple în următoarea parte a articolului meu.