Blog

Office Scrpits podstawy. Odsłona pierwsza

Druga odsłona krótkiego wprowadzenia do Office Scripts, czyli narzędzia pozwalającego automatyzować zadania w Excelu dla przeglądarek internetowych.

Office Scrpits podstawy. Odsłona pierwsza

Dzisiaj będzie krótko - na miarę moich grafomańskich zapędów - o podstawach tworzenia skryptów w webowej wersji Excela, czy raczej o pewnej ogólnej “filozofii”, jaka stoi za Office Scripts. Tym samym za wiele o kodowaniu jako takim nie należy się spodziewać, choć trudno omawiać pewne kwestie bez pokazania choćby kawałeczka instrukcji, więc siłą rzeczy coś tam w dalszej części tekstu zademonstruję. Natomiast z całą pewnością dzisiejszy tekst nie będzie to wpisem o TypeScript jako takim i to przynajmniej z dwóch powodów.

Przede wszystkim, gdy planowałem zajęciem się tematem Office Scripts, od samego początku zakładałem, że w efekcie powstanie szereg wpisów skierowanych w pierwszej kolejności do osób, które jak dotąd nie zajmowały się kodowaniem wcale lub co najwyżej mają nader skromne doświadczenia, ale z innymi niż TypeScript (dalej: TS) językami programowania. Dlatego w niedalekiej przyszłości z pewnością poświęcę jeden czy dwa wpisy, by omówić pobieżnie podstawowe konstrukcje dostępne w ramach TS. Tym niemniej przeglądając przykładowe skrypty dostępne w dokumentacji zamieszczonej na stronie Microsoftu, odniosłem wrażenie, że da się sprawnie korzystać z Office Scripts - oczywiście do pewnego poziomu złożoności zadania - bez wchodzenia w tajniki wzmiankowanego języka, choć oczywiście trzeba przy tym rozumieć podstawowe koncepcje programistyczne takiej jak pętla czy instrukcje warunkowe i w efekcie przyswoić sobie konkretny sposób, w jaki zostały one zaimplementowane właśnie w TS.

Dodatkowo wydaje mi się, że mimo wszystko - niezależnie od doświadczenia w kodowaniu - największym wyzwaniem z punktu widzenia adepta Office Scripts, jest dobra znajomość podstawowych obiektów udostępnionych przez Microsoft w ramach tego narzędzia oraz metod operowania na ich właściwościach. Szczerze powiedziawszy przy pierwszym kontakcie z dokumentacją API dla Office Scripts można się poczuć trochę przytłoczonym przez całe multum metod i obiektów, o których można tam przeczytać. Tym niemniej - tak samo jak w przypadku Excela - wydaje się, że na ogół w praktyce wykorzystywać będziemy tylko niewielką część tych funkcjonalności.

Budowa skryptu

Każdy skrypt pakietu Office musi zawierać w sobie funkcję główną (main). Zwyczajowo posiada ona parametr nazywany “workbook” z przypisanym do niego typem Workbook. Ten ostatni to obiekt najwyższego typu dla API Excel, który zawiera pozostałe obiekty takie jak arkusze, tabele czy zakresy. Tym sposobem w ramach funkcji main będziemy mogli się odwoływać do wszystkich elementów skoroszytu Excela.

Oczywiście nic nie stoi na przeszkodzie, by ochrzcić ten parametr (worbook) inaczej, natomiast ja podążał będę za tą konwencją w zgodzie z dokumentacją Microsoftu, ale jak ktoś woli nazwę “skoroszyt” czy inną bardziej swojską, to może wedle uznania wstawiać w w to miejsce własne określenie. Ważne by przypisać do niego ten nadrzędny obiekt Workbook (ExcelScript.Workbook).

Sama funkcja na najwyższy poziomie ogólności powinna wyglądać w sposób zaprezentowany poniżej.

function main(workbook: ExcelScript.Workbook) {
  // Miejsce na Twój kod
}

Należy dodać, że funkcja main nie musi być jedyną w naszym skrypcie, choć podejrzewam, iż w praktyce bardzo rzadko będziemy mieli potrzebę czy sposobność, by wychodzić poza jej zakres. Tym niemniej to właśnie od niej zaczyna się przetwarzanie całości kodu przez narzędzie Microsoftu i żaden skrypt nie zadziała bez umieszczenia tak nazwanej funkcji. Jeśli nie zastosujemy się do tej reguły, wówczas dostaniemy następujący opis problemu: No function of name 'main' found. Ensure the function exists and is not nested in anything.

Z tego wszystkiego, co napisałem wyżej należy na ten moment zapamiętać:

function main(){
 
let workbookMain = secondary()
let sheet = workbookMain.getActiveWorksheet()
 
}
 
function secondary(workbookSec: ExcelScript.Workbook) {
  return workbookSec
}

Obiektowy model działania

W przypadku API dla Office Scripts obiektem jest ściśle określony element składowy aplikacji (tutaj Excela), czyli np. arkusz, zakres komórek czy wreszcie tabela bądź wykres. By sprawnie pisać skrypty należy rozumieć w jakich relacjach do siebie one pozostają, co na najbardziej podstawowym poziomie nie powinno raczej sprawić większej trudności każdemu kompetentnemu użytkownikowi arkusza kalkulacyjnego od Microsoftu. Nie jest bowiem żadną wiedzą tajemną fakt, że każdy skoroszyt grupuje w sobie jakieś arkusze, podczas gdy te ostatnie zbudowane są z zakresów komórek, które z kolei mogą przechowywać jakieś wartości oraz posiadać określony styl. W ten sposób rysuje się nam pewna struktura hierarchiczna znana nam z obcowania z Excelem.

Oczywiście każdy obiekt może - a w przypadku Excela na ogół powinien - posiadać jakieś charakterystyczne właściwości oraz metody. Te pierwsze to pewne cechy obiektu, czyli dla zakresu komórek (obiekt Range) właściwością może być tak samo zawartość pojedynczej komórki, jak i jakiś element jej stylu (na przykład ustawiona czcionka). Z kolei metoda jest ściśle określonym działaniem na tych właściwościach, który prowadzić mają do osiągnięcia zakładanego efektu.

Dla osób bez jakiegokolwiek doświadczenia w programowaniu może to brzmieć nie do końca klarownie, ale wierzę, iż w większości przypadków nie powinno być problemów z praktycznym ogarnięciem jak to wszystko się wzajemnie zazębia. Dlatego spójrzmy na ten kawałeczek kodu:

function main(workbook: ExcelScript.Workbook){
  
  let sheet = workbook.getActiveWorksheet() //pobranie aktywnego arkusza
  console.log(sheet) //wyświetlanie informacji o zmiennej sheet
  console.log(sheet.getName()) //wyświetlenie w konsoli/danych wyjściowych nazwy aktywnego arkusza  
  
  let cell = sheet.getCell(2,2) //odwołanie do komórki "C3"
  console.log(cell.getValue()) //wyświetlenie zawartości komórki "C3"

  cell.setValue('test') //wstawienie do komórki "C3" wartości "test"
  console.log(cell.getValue())

}

Wyżej widzimy typową funkcję dla Office Scripts, o której pisałem w pierwszej części dzisiejszego tekstu. W jej ciele pomiędzy klamrami jest kilka prostych instrukcji, dzięki którym będziemy chcieli pobrać wartość z komórki C3 dla aktywnego arkusza, a następnie ją nadpisać, czyli wstawić do niej słowo "test". Ale po kolei.

W pierwszej linijce widzimy instrukcje przypisania do zmiennej o nazwie “sheet” obiektu o typie Worksheet, który odpowiada aktywnemu arkuszowi w naszym skoroszycie, czyli tego, na którym obecnie pracujemy. Do tego celu użyta  została metoda getActiveWorksheet, która jest dostępna dla obiektu naszego obiektu “workbook”. Zauważcie, że stawiając kropkę po słowie “workbook” edytor pokaże nam listę metod, które są z poziomu tego obiektu dostępne. To bardzo duża pomoc, ponieważ szereg z nich (tych metod) ma nazwy na tyle intuicyjne, że bez konieczności sięgania do dokumentacji będziemy mogli zgadnąć, do czego jedna z drugą służy.

Dwie kolejne linijki to odwołanie się do konsoli, dzięki której możemy podejrzeć pewne rzeczy. W przypadku narzędzia Microsoft widoczne będzie to w sekcji “Dane wyjściowe”, która pojawi się po uruchomieniu skryptu. W pierwszym wywołaniu chodzi o pokazanie, że zmienna “sheet” to obiekt o typie Worksheet. W drugim przypadku dzięki metodzie getName uda się nam wyświetlić nazwę aktywnego arkusza (w moim przypadku to było “Arkusz 1”).

Potem następuję przypisanie do zmiennej “cell” komórki C3 z naszego arkusza. Do tego celu wykorzystana została metoda getCell, dla której musimy podać jako argument pozycję interesującej nas komórki, przy czym pierwszy z nich odpowiada wierszom a drugi kolumnie. Ważne jest to, by mieć świadomość, że liczymy od zera a nie jedynki, dlatego 3 wiersz i kolumna w arkuszu zapisany został jako (2,2).

Dalej znowu korzystamy z dobrodziejstw konsoli i prezentujemy wartość wpisaną w komórkę C3, którą pobieramy przy pomocy metody getValue. Ponieważ w moim przypadku był to świeżutki arkusz, to w konsoli niczego nie uświadczymy, bo komórka była zwyczajnie pusta. By ten stan rzeczy zmienić przy pomocy metody setValue wstawiamy w C3 ciąg znaków “test” i już na sam koniec efekt prezentujemy w konsoli. Rzecz jasna jak spojrzymy na nasz arkusz to faktycznie dojrzymy stosowną zmianę.

Nie wiem, na ile to co zostało opisane wyżej jest czytelne i zrozumiałe, bo dla piszącego te słowa ciężko było w bardziej intuicyjny sposób to przybliżyć (chyba wyobraźni mi nie starcza). Dlatego dla osób zupełnie zielonych sugerowałbym zajrzeć do dokumentacji na stronie projektu i po prostu wykonywanie prostych zadań polegających na pobraniu jakiś danych czy ewentualnie zmianie danej wartości. Zapewne z biegiem czasu okaże się, że jest grupa pewnych podstawowych metod i obiektów, na których będziemy w większości przypadków pracować.

Ja już na sam koniec chciałbym zwrócić uwagę na jeszcze jedną rzecz. Osoby spostrzegawcze zapewne zauważyły, że każda z metod zaczynała się od słowa “get” albo “set” pisanych małymi literami, po czym następuje specyficzna nazwa danej metody. Tym niemniej nie chcę się pochylać nad wielkością użytych liter, ale jak ktoś chce poczytać trochę o tej konwencji to wystarczy, że wpisze do przeglądarki “camelCase” na ten przykład. Dużo bardziej przydatne może okazać się coś innego, a mianowicie właśnie te “przedrosteki” set i get. To inna szeroko stosowana konwencja w przypadku tworzenia wielu API, które wskazują niejako jakie zadanie dana metoda (czy request do niej się odwołujący) spełnia. Otóż przyjęło się, że metoda get ma za zadanie pobrać i zwrócić jakąś wartość czy obecny stan procesu, zaś set taką wartość dopiero przypisać czy też - bardziej ogólnie - wywołać jakąś akcję/zdarzenie. Dlatego getValue dla obiektu Cell (w naszym przypadku przypisanego do zmiennej “cell”) zwraca jakąś wartość, a metoda setValue taką wartość do tego obiektu (tu komórki) wstawia.