{"id":6344,"date":"2018-11-26T10:34:41","date_gmt":"2018-11-26T09:34:41","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=6344"},"modified":"2022-12-21T13:19:03","modified_gmt":"2022-12-21T12:19:03","slug":"sharepoint-framework-przykladowy-web-part","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/sharepoint-framework-przykladowy-web-part\/","title":{"rendered":"SharePoint Framework \u2013 przyk\u0142adowy web part"},"content":{"rendered":"\n<p>W cz\u0119\u015bci drugiej swojej serii &#8222;SharePoint Framework&#8221; przedstawi\u0119 Ci zestaw porad i fragment\u00f3w kodu, kt\u00f3re pomog\u0105 Ci zacz\u0105\u0107 przygod\u0119 z SPFx. Je\u017celi jeszcze nie zaznajomi\u0142e\u015b\/a\u015b si\u0119 z cz\u0119\u015bci\u0105 pierwsz\u0105 &#8222;<a href=\"https:\/\/sii.pl\/blog\/sharepoint-framework-konfiguracja\/?category=development-na-twardo&amp;tag=sharepoint%2Coffice-365&amp;preview_id=6355&amp;preview_nonce=5bbeaca82e&amp;preview=true&amp;_thumbnail_id=6361\" target=\"_blank\" rel=\"noreferrer noopener\">SharePoint Framework &#8211; konfiguracja<\/a>&#8221; serdecznie do tego zapraszam. Znajdziesz tam kluczowe informacje dotycz\u0105ce konfiguracji oraz struktury projektu.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>W celu u\u0142atwienia pracy, u\u017cyjemy bibliotek mobx oraz mobx-react. Aby zainstalowa\u0107 paczki, w konsoli wpisujemy npm i mobx mox-react.<\/p>\n\n\n\n<p>W pliku NavigationWebPart.ts odnajdujemy interfejs INavigationWebPartProps i uzupe\u0142niamy go takimi samymi zmiennymi jakie podali\u015bmy w pliku manifest.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nexport interface INavigationWebPartProps { items: IItemModel&#x5B;]; }\n<\/pre><\/div>\n\n\n<p>Kolejnym krokiem, kt\u00f3ry mo\u017ce by\u0107 bardzo \u0142atwo pomini\u0119ty, a jest niezwykle wa\u017cny, jest skonfigurowanie mobx\u2019a. Nadpisujemy funkcj\u0119 onInit():<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\npublic onInit(): Promise&lt;void&gt; {\n \nreturn super.onInit().then(_ =&gt; {\n \nconfigure({ isolateGlobalState: true})\n \n });\n \n}\n<\/pre><\/div>\n\n\n<p>Poniewa\u017c nie wiemy jakich web part\u00f3w w przysz\u0142o\u015bci u\u017cyjemy my lub inni u\u017cytkownicy, dobrze jest zabezpieczy\u0107 nasz web part przed interferencj\u0105 z innymi rozwi\u0105zaniami u\u017cywaj\u0105cymi mobx\u2019a. Konfiguracja isolateGlobalState zapobiegnie problemom w przysz\u0142o\u015bci.<\/p>\n\n\n\n<p>Przygotowanie nawigacji sk\u0142ada\u0107 si\u0119 b\u0119dzie z nast\u0119puj\u0105cych cz\u0119\u015bci:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Stworzenie pliku store na potrzeby u\u017cycia mobx\u2019a,<\/li><li>Utworzenie komponent\u00f3w potrzebnych do wy\u015bwietlenia nawigacji,<\/li><li>Dodanie nowej kontrolki do PropertyPane,<\/li><li>Dodanie styli do rozwi\u0105zania.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Plik store<\/h2>\n\n\n\n<p>Tutaj przechowywa\u0107 b\u0119dziemy dane, na kt\u00f3rych aktualnie pracujemy oraz kt\u00f3rych zmiana powodowa\u0107 b\u0119dzie zmian\u0119 wy\u015bwietlanej zawarto\u015bci. Umie\u015bcimy tutaj dane ka\u017cdego kafelka nawigacji, aktualnie wybrany kafelek oraz informacj\u0119, czy web part jest w trybie edycji. Ca\u0142o\u015b\u0107 zostanie wsparta kilkoma operacjami na elementach nawigacji:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nimport { observable } from \"mobx\";\n \nimport IItemModel from \"..\/model\/IItemModel\";\n \n \nexport default class NavigationStore {\n \nconstructor(items: IItemModel&#x5B;]) {\n \nif (items) {\n \nthis.items = items;\n \n} else {\n \nthis.items = &#x5B;];\n \n}\n \nthis.currentItem = -1;\n \n}\n \n@observable public items: IItemModel&#x5B;];\n \n@observable public editMode: boolean;\n \n@observable public currentItem: number;\n \n \npublic addItem() {\n \nthis.currentItem = this.items.push({\n \nTitle: '',\n \nImageUrl: '',\n \nSection: '',\n \nUrl: ''\n \n}) - 1;\n \n}\n \npublic deleteItem(index: number): void {\n \nif (index === this.currentItem) {\n \nthis.currentItem = -1;\n \n} else {\n \nthis.currentItem = this.currentItem - 1;\n \n}\n \nthis.items.splice(index, 1);\n \n}\n \n}\n<\/pre><\/div>\n\n\n<p>W kodzie u\u017cyty zosta\u0142 dekorator @observable. Warto\u015b\u0107 zmiennej tak oznaczonej b\u0119dzie obserwowana przez mobx, a po jej zmianie wszystkie obserwuj\u0105ce j\u0105 komponenty ponownie wykonaj\u0105 funkcj\u0119 render().<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Komponenty nawigacji<\/h2>\n\n\n\n<p>Przygotujmy teraz komponenty nawigacji. Podstawowy plik Navigation.tsx zosta\u0142 ju\u017c wygenerowany. Wystarczy go tylko oczy\u015bci\u0107 z przyk\u0142adowej implementacji i zast\u0105pi\u0107 nasz\u0105 w\u0142asn\u0105. Zacznijmy od pliku INavigationProps.ts. Jest to interfejs, w kt\u00f3rym deklarujemy jakie dane mog\u0105 lub musz\u0105 zosta\u0107 przekazane do komponentu. W naszym wypadku ograniczymy si\u0119 do klasy store:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nimport NavigationStore from \"..\/..\/..\/store\/NavigationStore\";\n \n \nexport interface INavigationProps {\n \nstore: NavigationStore;\n \n}\n<\/pre><\/div>\n\n\n<p>W pliku Navigation.tsx wystarczy nam prosty render komponentu React. Zwr\u00f3\u0107 uwag\u0119 na u\u017cyte dekoratory:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>@autobind \u2013 przypisuj\u0105 aktualn\u0105 instancj\u0119 web parta do this. Dzi\u0119ki temu nasz this b\u0119dzie obiektem klasy Navigation nawet je\u015bli odwo\u0142ujemy si\u0119 do niego z funkcji, kt\u00f3re b\u0119d\u0105 wywo\u0142ane spoza klasy Navigation, np. funkcje przekazane do event\u00f3w, filtr\u00f3w, mapowa\u0144,<\/li><li>@observer \u2013 dekorator, kt\u00f3ry wykona za nas ca\u0142\u0105 magi\u0119 i sprawi, \u017ce obiekty tak oznaczonej klasy b\u0119d\u0105 reagowa\u0107 na zmian\u0119 warto\u015bci obserwowanych w store zmiennych.\u00a0\ud83d\ude42<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;\/pre&gt;\n \nimport * as React from &#039;react&#039;;\n \nimport styles from &#039;.\/Navigation.module.scss&#039;;\n \nimport { INavigationProps } from &#039;.\/INavigationProps&#039;;\n \nimport { escape } from &#039;@microsoft\/sp-lodash-subset&#039;;\n \nimport IItemModel from &#039;..\/..\/..\/model\/IItemModel&#039;;\n \nimport Tile from &#039;.\/tile\/Tile&#039;;\n \nimport { observer } from &#039;mobx-react&#039;;\n \nimport { DefaultButton } from &#039;office-ui-fabric-react\/lib\/Button&#039;;\n \nimport { autobind } from &#039;@uifabric\/utilities&#039;;\n \n \n@observer export default class Navigation extends \nReact.Component&lt;INavigationProps, {}&gt; {\n \n@autobind private addNewTile() {\n \nthis.props.store.addItem();\n \n}\n \npublic render(): React.ReactElement&lt;INavigationProps&gt; {\n \nreturn &lt;div&gt;\n \n&lt;div className={styles.navigation}&gt;\n \n{this.renderTiles()}\n \n \n \n{this.renderEditMode()}\n \n&lt;\/div&gt;\n}\n \nprivate renderEditMode() {\n \nreturn this.props.store.editMode ? &lt;DefaultButton\n \nprimary={true}\n \ntext=&quot;Add new tile&quot;\n \nonClick={this.addNewTile}\n \n\/&gt; : null;\n \n}\n \n \n@autobind private renderTile(item: IItemModel, i: number): JSX.Element {\n \nreturn &lt;Tile id={i} store={this.props.store} \/&gt;;\n \n}\n \n \nprivate renderTiles(): JSX.Element&#x5B;] {\n \nreturn this.props.store.items.map(this.renderTile);\n \n}\n \n}\n \n&lt;pre&gt;\n<\/pre><\/div>\n\n\n<p>W przedostatniej funkcji zauwa\u017cy\u0142e\u015b pewnie element JSX nazwany Tile. To jest nasz kolejny komponent. Przekazuje do niego store oraz id po kt\u00f3rym komponent b\u0119dzie si\u0119 odwo\u0142ywa\u0142 do konkretnego kafelka. Implementacj\u0119 element\u00f3w nawigacji pozostawiam ju\u017c Twojej inwencji. Przedstawi\u0119 Ci tylko kr\u00f3tk\u0105 koncepcj\u0119 z jakiej ja korzystam:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Zastosowa\u0142em dekorator @observer, kt\u00f3ry zadzia\u0142a tak, jak ten opisany w klasie Navigation.tsx,<\/li><li>Do danych kafelka odwo\u0142uj\u0119 si\u0119 w ni\u017cej przedstawiony spos\u00f3b:<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;span\nclassName={styles.title}&gt;{this.props.store.items&#x5B;this.props.id].Title}&lt;\/span&gt;;\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Kontrolka PropertyPane<\/h2>\n\n\n\n<p>Dodanie kontrolki PropertyPane pozwoli nam w \u0142atwy spos\u00f3b edytowa\u0107 i tworzy\u0107 nowe elementy nawigacji z poziomu ustawie\u0144 web partu. Je\u017celi chcesz dowiedzie\u0107 si\u0119 w jaki spos\u00f3b tak\u0105 kontrolk\u0119 przygotowa\u0107, zach\u0119cam Ci\u0119 do zapoznania si\u0119 z\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sharepoint\/dev\/spfx\/web-parts\/guidance\/build-custom-property-pane-controls\" rel=\"nofollow\" >artyku\u0142em na stronie Microsoftu<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Style CSS<\/h2>\n\n\n\n<p>Zatrzymajmy si\u0119 teraz na chwil\u0119 przy stylach CSS. Warto tworzy\u0107 je osobno dla ka\u017cdego z komponent\u00f3w w odpowiadaj\u0105cych im plikach SCSS. Ka\u017cdy ma sw\u00f3j ulubiony spos\u00f3b na przygotowanie styli. Chcia\u0142bym Ci tutaj jedynie zaproponowa\u0107 wykorzystanie gotowych zmiennych kolor\u00f3w. Dzi\u0119ki temu Tw\u00f3j web part b\u0119dzie zmienia\u0142 kolory wraz ze zmian\u0105 motywu! Oto przyk\u0142ad, jak zastosowa\u0107 zmienn\u0105 koloru:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nbackground: linear-gradient(to right, \"&#x5B;theme: themePrimary, default: black]\", rgba(0, 0, 0, 0));\n<\/pre><\/div>\n\n\n<p>Gdy nasz kod ju\u017c jest gotowy, mo\u017cemy podejrze\u0107 efekt ko\u0144cowy. W tym celu przy u\u017cyciu gulp serve w konsoli uruchamiamy proces budowania i startowania rozwi\u0105zania, do kt\u00f3rych mamy wgl\u0105d w workbenchu:<\/p>\n\n\n\n<p>https:\/\/localhost:5432\/workbench<\/p>\n\n\n\n<p>&lt;URL Twojego site&gt;\/_layouts\/15\/workbench.aspx<\/p>\n\n\n\n<p>Wykorzystanie drugiego linka pozwoli przetestowa\u0107 web part na danych z SharePointa. Dla przyk\u0142adu \u2013 m\u00f3j web part pobra\u0142 zielony kolor z ustawionego motywu:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowanie<\/h2>\n\n\n\n<p>Przedstawi\u0142em Ci zestaw porad i fragment\u00f3w kodu, kt\u00f3re pomog\u0105 Ci zacz\u0105\u0107 przygod\u0119 z SPFx. Zach\u0119cam do eksperymentowania! Je\u017celi natomiast masz pomys\u0142 na projekt, kt\u00f3ry chcesz wdro\u017cy\u0107 w swoim SharePoincie, zach\u0119cam do kontaktu.<\/p>\n\n\n\n<p>Na koniec zostawiam kilka porad, kt\u00f3re mog\u0105 okaza\u0107 si\u0119 pomocne przy pracy:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Uwa\u017caj przy importowaniu plik\u00f3w. Je\u017celi w folderze projektu znajduj\u0105 si\u0119 ju\u017c zbudowane pliki, VS Code cz\u0119sto traktuje je jako domy\u015blny import. Wywo\u0142a to p\u00f3\u017aniej problemy przy ponownym budowaniu projektu lub przy pr\u00f3bie uruchomienia go na innym \u015brodowisku. Tak wi\u0119c \u2013 importuj\u0105c, unikaj \/lib\/ w \u015bcie\u017ckach do twoich plik\u00f3w.<\/li><li>Je\u017celi masz problemy z pobraniem paczek do projektu przy pomocy npm install, skasuj folder node_modules oraz plik package-lock.json. Mo\u017cliwe, \u017ce znajduj\u0105 si\u0119 tam elementy, kt\u00f3re uniemo\u017cliwiaj\u0105 instalacj\u0119.<\/li><li>Zastan\u00f3w si\u0119, czy instalowana paczka pochodzi z zaufanego \u017ar\u00f3d\u0142a.<\/li><li>Nie przesadzaj z dodatkowymi paczkami. Staraj si\u0119 importowa\u0107 tylko te komponenty, kt\u00f3re s\u0105 niezb\u0119dne, a Twoje rozwi\u0105zanie nie rozro\u015bnie si\u0119 do du\u017cych rozmiar\u00f3w.<\/li><\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;6344&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;2&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 2)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;SharePoint Framework \u2013 przyk\u0142adowy web part&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 2)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W cz\u0119\u015bci drugiej swojej serii &#8222;SharePoint Framework&#8221; przedstawi\u0119 Ci zestaw porad i fragment\u00f3w kodu, kt\u00f3re pomog\u0105 Ci zacz\u0105\u0107 przygod\u0119 z &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/sharepoint-framework-przykladowy-web-part\/\">Continued<\/a><\/p>\n","protected":false},"author":18,"featured_media":6369,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":3,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[497,56],"class_list":["post-6344","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-office-365","tag-sharepoint"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2018\/11\/ai-close-up-code-160107.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6344"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=6344"}],"version-history":[{"count":1,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6344\/revisions"}],"predecessor-version":[{"id":18002,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/6344\/revisions\/18002"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/6369"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=6344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=6344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=6344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}