# 05 Consuming web services Async [toc] # Theory subject * Understanding the **async** and **await** keywords in C# * Consuming web services asynchronously * We follow the official Xamarin university course [https://docs.microsoft.com/nl-nl/learn/modules/consume-rest-services/](https://docs.microsoft.com/nl-nl/learn/modules/consume-rest-services/) # Lab subjects * The **JsonProperty** attribute * **GET** JSON data, load in a collection. * **PUT** and **POST** JSON data. * Use existing XAML layout to get **input** from user, and to display feedback. * *(see also: ‘what did we learn today’ at the end of the document)* # Assignments Assignment repository to clone: [https://classroom.github.com/a/U3fOEwjU](https://classroom.github.com/a/U3fOEwjU) How? See <i class="fa fa-book"></i>note: [Join the classroom](https://hackmd.io/@frewaeyaert/deviceprogramming/%2FrDOTGvcaTm6RYC05z2TdHw#Join-the-classroom) ## Ex01 – Trello Trello is a collaboration tool that organizes your projects into boards. In one glance, Trello tells you what's being worked on, who's working on what, and where something is in a process. We will use Trello’s publicly available API to display some (filtered) content. The app should also allow the user to add new cards or change their state. ![](https://hackmd.io/_uploads/HJhdsy7Qj.png) ### Prepare the project * Create a project Ex01 in this lab's repository * To analyse the json data and convert it to our Models, we will use the Newtonsoft.Json package. Make sure you install this as a **nuget package for solution; install on all your projects:** ![](https://hackmd.io/_uploads/BJp6i177o.png) * Create a folder "**Repositories**" and add a class called “**TrelloRepository**”. This class will contain all static async methods we need to get, post and put data from and to Trello. * Make the class **static**: ``` public static class TrelloRepository ``` * Add a **constant** (meaning: can never change value) value called “APIKEY”. ``` private const string _APIKEY = ""; ``` * Add a second constant value called “USERTOKEN”: ``` private const string _USERTOKEN = ""; ``` * *Later in this assignment, you will set this api key and usertoken to your personal keys.* * Create a folder "**Views**" and add the following content pages (just leave them empty for now): * TrelloListsPage * TrelloCardsPage * SingleCardPage ### Create your API key To be able to access an API, you often need a key. In case of our Trello API, we need both a key and a user token to be able to access our own (private) Trello boards. * Create an API **key**: * make sure you are logged in: [https://trello.com/app-key](https://trello.com/app-key) * Paste this key in the APIKEY constant in your TrelloRepository class * Use the above generated application key to request a **user token**. Reading the URL properly, you can see that we request a token to read and write that never expires: ``` https://trello.com/1/authorize?key=<application_key_here>&scope=read%2Cwrite&name=DEVPROGLAB05&expiration=never&response_type=token ``` * Paste this token in the USERTOKEN constant in TrelloRepository ### Create Models #### Documentation & json The first step we need before we can access the API to GET, POST or PUT data, is to set up the models we need. There are two ways to do this: **using the API’s documentation**, or by **analyzing the json data**. All this information for our Trello API can be found here: [https://developers.trello.com/v1.0/reference](https://developers.trello.com/v1.0/reference) #### Model 'TrelloList' * Create a folder called “**Models**”, and inside this folder create a public class “**TrelloList**”. * Often you get a lot of data and you don’t need all of it, always **make a selection of the properties you need**. It will keep your classes a lot more structured and readable. * Go to the trello reference (url above) and check out the List object. Use it to create your class. The properties we need are highlighted in the screenshot below. ![](https://hackmd.io/_uploads/HkHxUd0Qi.png) * We might want to use a different name than the one used in json. Use Newtonsoft's JsonProperty attribute to translate the JSON property 'id' to a C# propertycalled "ListId". ``` using Newtonsoft.Json; ``` ![](https://hackmd.io/_uploads/Sy6gAyXmo.png) #### Model 'TrelloBoard' * Follow the same steps to create the TrelloBoard model. * Go to Board - Board Object * We need **BoardId, Name** and **IsFavorite** (= 'starred' in json). #### Model 'TrelloCard' * Finally, build the card model using the same steps (Card – Card Object). * The properties we need are **CardId**, **Name** and **IsClosed**. --- ### GetHttpClient, GetTrelloBoardsAsync + Test We will create methods to **GET** a list of Trello boards, cards and lists. Add all methods in the **TrelloRepository** class. #### **GetHttpClient()** * Each request requires an **HttpClient** with the correct headers set. * Don't forget to import the correct namespace: `using system.net.http;` :::warning See [self study material](https://hackmd.io/@frewaeyaert/deviceprogramming/%2FQbOJTvERTx68bbLIyO9y9A) to build this function. ::: #### **GetTrelloBoardsAsync()** * We need a **static** method that **returns a list of** our personal **Trello boards**, which would result in a function header that looks like this: `public static List<TrelloBoard> GetTrelloBoards() { .... }` * **However**, we want to access data from our API, which we will need to do asynchronously (ref. theory): * We have to **wrap** the return type **in a Task<T>** object because it isn’t returned immediately!! `using Threading.Tasks;` ![](https://hackmd.io/_uploads/Skf0nvdQo.png) * **Please use this url to get a list of all boards:** [https://trello.com/1/members/my/boards](https://trello.com/1/members/my/boards) * Now continue building this function based on the code in your self study material.  * Take note that you need to add the **key** and **token** in every request url as is stated in the introduction of the API: ==Only add the key and token as highlighted in yellow, not the rest of the url!!== [https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/](https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/) ![](https://hackmd.io/_uploads/rkmKpP_Xs.png) #### **Call / test the function** _If the test board you created in Trello itself is not yet set as your favorite (= starred) board, please go to the trello website first to star this board._ * Go to **MainPage.xaml.cs** and create a test function * Create a new method (type void) called **TestTrelloRepository** and call this method in the **constructor**. _The following screenshots use the name "TrelloManager", but this refers to your "TrelloRepository"._ ![](https://hackmd.io/_uploads/HJpdAP_mi.png) * Read and try to understand the compiler error as you will see it a lot in the future: ![](https://hackmd.io/_uploads/rJRqRv_Xj.png) * The result of GetTrelloBoardsAsync() returns a **Task**<List<TrelloBoard>**>**, while we try to put directly in a List<TrelloBoard> * Solve this error by adding the **await** keyword: ![](https://hackmd.io/_uploads/HJqy1__mo.png) * This solves one error, yet raises another: ![](https://hackmd.io/_uploads/SyF-y_umj.png) * We literally do what the error above suggests and then we **set a breakpoint** just after calling the async method. ![](https://hackmd.io/_uploads/rkuMJOd7s.png) * Finally, we get a **warning** if we call TestTrelloRepository() in the constructor: ![](https://hackmd.io/_uploads/rkjL-OOQo.png) * We can not make a constructor asynchronous  * This is a warning, not an error. You can **ignore** this warning.  * _DO NOT CHANGE THE FUNCTION IN private async void INSTEAD OF private async Task!_ * **Run** the application so it stops at the breakpoint, then check your local variables. The boardsLists should be filled with your personal Trello boards. When you open a collapsed TrelloBoard object in this view, all properties should be filled (the Xamarin board in the screenshot below has been starred): ![](https://hackmd.io/_uploads/H15jZ_u7j.png) * Finally, in the test code, select one favorite board (IsFavorite) from this list using a foreach loop (or LINQ's 'Where' function instead of a foreach **if** you're up for a challenge) * _Should you await GetTrelloBoardsAsync or not?_ * TEST! --- ### GetTrelloListsAsync, GetTrelloCardsAsync + Test #### **Base url** Voor het ophalen van de Trello boards was een iets atypische endpoint nodig, die gebruik je niet meer voor de volgende calls. Alle komende urls starten van deze base url: [https://api.trello.com/1/](https://api.trello.com/1/) #### **GetTrelloListsAsync (string boardId)** Now try to follow the same steps to create a function that returns the trello lists of a certain board (which you get as a parameter). * Think about the **return type**! * To build the correct url, have a look at your API reference: [https://developer.atlassian.com/cloud/trello/rest/api-group-boards/#api-boards-id-lists-get](https://developer.atlassian.com/cloud/trello/rest/api-group-boards/#api-boards-id-lists-get) We need to GET the lists of a BOARD: ![](https://hackmd.io/_uploads/Bk48Mddms.png) * The parameters may remain the same as in the screenshot (make sure you understand them!). Copy the url and do not forget to: * **add key and token** like we did in GetTrelloBoardsAsync. * use the actual **boardId** to replace 'id' in the url * Do not forget to choose the correct **type** in **DeserializeObject<T>.** * Do not forget to set a **breakpoint** at the 'throw' line. #### **Expand the TestTrelloRepository function** * In the previous test code we selected our favorite Trello board. Use this board in combination with the GetTrelloListsAsync function to get a list of the TrelloList objects that belong to this board. * Add a breakpoint, test. * If it works, select a **random** list object from this full list and put it in a variable called "selectedList". Test one more time. #### **GetTrelloCardsAsync (string listId)** Try to do the same to **get all cards of a certain list**. * Look in the API to build the url (see steps above; try to do this step on your own as it is a very important one to understand!!). * Do not forget to add the key and token. * Do not forget to set a breakpoint at the 'throw' line. #### **GetTrelloCardByIdAsync(string cardId)** Last but not least, create a function that returns a **card object based on the card's id.** * Look in the API to build the url (see steps above; try to do this step on your own as it is a very important one to understand!!). * Do not forget to add the key and token. * Do not forget to set a breakpoint at the 'throw' line. #### **Expand the TestTrelloRepository function again** Now expand the TestTrelloRepository function again to test the third GET function that gets all cards from a list: * Use the selectedList variable from the previous step to **get all trello cards** from this list * Select **a random card** from this list * use the id of this card to test the GetCardByIdAsync function. * after each function, **set a breakpoint** * **Test** if it works by checking the locals at runtime. ### **POST/ PUT Functions** Go back to the **TrelloRepository** class: we want to add the ability to **change existing cards (PUT)** in a certain TrelloList, and **add new cards (POST)** to it. _Before you continue, check the properties in TrelloCard: you might not have foreseen translation between the 'name' (json) and 'Name' (C#) property. This raises no issue as long as you GET the data, but will cause problems the other way around when you use it for POST/PUT. **Add the jsonproperty attribute for the name/Name property.**_ #### **UpdateCardAsync (Card card)** * Update = **PUT** * The function does not need to return anything. However, it is async. An "async void" is translated into a "Task" object: * first, try to find the correct url in the reference and try to build it (do not forget to add user key & token!) * Go to Card, then find the PUT url: ![](https://hackmd.io/_uploads/HyEiNudQi.png) * Next, use the url to PUT the card object. Do not forget to set the breakpoints: ![](https://hackmd.io/_uploads/rkQh4_Omi.png) #### **AddCardAsync (string listId, Card card)** Now try to make this method to add a new card on your own. Posting data is almost exactly the same as putting data; the only difference is that we use PostAsync instead of PutAsync. * Add = POST * The function does **not** need to **return** anything. * Find the correct **url** in the reference and add key/token to it. * Next, use the url to **POST** the card object. Do not forget to set the **breakpoints**. ### POST/ PUT Functions TEST Time to expand our **TestTrelloRepository** function in the MainPage.xaml.cs page. #### **Test update (PUT)** * select a card from the list of cards you previously filled in TestTrelloRepository * change one or more properties of the card * update the card and check if it worked * the fastest way is to check it in your browser * the alternative is to get the list of cards in code again and check if it changed by looking at the locals * Result: ![](https://hackmd.io/_uploads/rJY4SuOQj.png) #### **Test add (POST)** Follow the same steps to test the AddCardAsync (POST) function. #### **Finish testing** **Comment out** the call of the **TestTrelloRepository** function so that it is no longer called when your app starts. Now that the **models** and **repository** class are finished, we can continue with the **front end.** ### FRONT END - Main Page (boards) ![](https://hackmd.io/_uploads/r1RdrO_Qo.png) #### **ListView with boards** * copy the given xaml code to MainPage.xaml ```cs <ListView x:Name="lvwBoards" SeparatorColor="White" > <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="45" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Frame CornerRadius="7" Margin="8" BackgroundColor='{Binding ColorHex}' /> <Label Grid.Column="1" Text="{Binding Name}" TextColor="Black" VerticalOptions="Center" /> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> ``` * Set the title to "Choose a board" * Call a new async function **LoadBoardsAsync** in your constructor * In this function, set the ItemsSource of your listview to the Trello boards (load them via TrelloRepository) * _**Ignore** the **'ColorHex'** binding for now; we will handle it in a later stage of this lab._ * Test run. #### **Navigate to TrelloListsPage** * Catch the **ItemSelected** event of the listview in xaml.  * In this event, we need to navigate to **TrelloListsPage**. However, this page **needs the selected board** to know which lists he needs to show. * In the **constructor of TrelloListPage**, add a **parameter** of type **TrelloBoard** and save the value in a **global variable** called **'BoardContent'** of the same type in TrelloListPage. * This way, we force to **pass a TrelloBoard object** during navigation. * So, when we navigate **in MainPage**, we pass the selected board **via the constructor** (do this in the generated ItemSelected event) ### FRONT END - TrelloListsPage ![](https://hackmd.io/_uploads/rkbBd_uQs.png) #### **ListView with TrelloLists** * Copy the given xaml code to TrelloListsPage.xaml ```cs <ListView x:Name="lvwTrelloLists" RowHeight="50" SeparatorColor="Transparent"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Frame BackgroundColor="WhiteSmoke" Margin="8" Padding="8,0"> <Grid> <Label Text="{Binding Name}" TextColor="Black" VerticalOptions="Center" /> <Label Text="..." TextColor="Gray" VerticalOptions="Center" HorizontalOptions="End" /> </Grid> </Frame> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> ``` * **Save** the TrelloBoard parameter from the constructor in a property called **BoardContent** (type TrelloBoard). * **Load** the Trello lists that belong to this TrelloBoard **asynchronously** in a method called **LoadTrelloListsAsync**. * Set the **Title** of this page to the Name of the TrelloBoard. #### **Navigate to TrelloCardsPage** * Handle the **ItemSelected** event of the ListView so that it navigates to the **TrelloCardsPage** and **pass both the BoardContext AND the selected TrelloList as parameters.** * To be able to do this, you need to change the **constructor** of TrelloCardsPage. * Don't forget to **reset** the selected item. * (Take a look at how you did this in the previous MainPage step if you are in doubt.) ### FRONT END - TrelloCardsPage ![](https://hackmd.io/_uploads/S1YktOuXj.png) #### **ListView with TrelloCards** * Copy the given xaml code to TrelloCardsPage.xaml ```cs+ <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <StackLayout> <Frame BackgroundColor="WhiteSmoke" Margin="8" Padding="0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="35" /> <RowDefinition /> <RowDefinition Height="35" /> </Grid.RowDefinitions> <Label x:Name="lblListName" Text="(list name)" TextColor="#333333" FontAttributes="Bold" VerticalOptions="Center" Margin="12,0" /> <!--<StackLayout Orientation="Horizontal" HorizontalOptions="End" VerticalOptions="Center"> <Label Text="Closed cards:" FontSize="Micro" VerticalOptions="Center" /> <Switch x:Name="switchClosedCards" Toggled="switchClosedCards_Toggled" /> </StackLayout>--> <ListView x:Name="lvwCards" Grid.Row="1" BackgroundColor="#e2e4e6" RowHeight="65" SeparatorColor="Transparent"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Frame BackgroundColor="White" Margin="4" Padding="8,0"> <Grid RowSpacing="0"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="18" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Label Text="{Binding Name}" VerticalOptions="Center" TextColor="#333333" /> <!-- handle this button click to create a PUT request that changes the card's closed state --> <Button Text="CLOSE" FontSize="Micro" x:Name="btnCloseCard" Grid.Column="1" Grid.RowSpan="2" VerticalOptions="Center" HorizontalOptions="Center" /> <StackLayout Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal"> <!--<Image Source="http://www.iconsdb.com/icons/preview/gray/comments-xxl.png" VerticalOptions="Center" HeightRequest="9" />--> <Label FontSize="Micro" Text="comments:" /> <Label Text="{Binding CardInfo.NumComments}" TextColor="Gray" FontSize="Micro" VerticalOptions="Start" /> <!--<Image Source="http://www.iconsdb.com/icons/preview/gray/attach-2-xxl.png" VerticalOptions="Center" HeightRequest="9" />--> <Label FontSize="Micro" Text="attachments:" /> <Label Text="{Binding CardInfo.NumAttachments}" TextColor="Gray" FontSize="Micro" VerticalOptions="Start" /> </StackLayout> </Grid> </Frame> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <Label x:Name="lblAddCard" Grid.Row="2" VerticalOptions="Center" Margin="12,0" TextColor="Gray" Text="Add a card..." /> </Grid> </Frame> </StackLayout> <Grid Grid.Row="2" Margin="4,0"> <Button x:Name="btnGoBack" Clicked="btnGoBack_Clicked" Text="Back to lists" /> </Grid> </Grid> ``` * **hide** the **navigation bar** (= attribute in the ContentPage tag) * _**Ignore** the **bindings to CardInfo** for now (numcomments, numattachments); we will handle this in a later stage of the lab._ * **Save** the parameters from the constructor in properties called **BoardContext** (type TrelloBoard) and **ListContext** (type TrelloList). * **Load** the Trello cards that belong to the given TrelloList **asynchronously** in a method called **LoadTrelloCardsAsync**. * Set the **Text** of **lblTitle** to the Name of the TrelloList. #### **Navigate back to lists** Clicking the "BACK TO LISTS" button should take the user back to the previous (TrelloLists)Page. We can easily do this with the built in **PopAsync** function. #### **Close a card (pressing the 'CLOSE' button)** * add a clicked event to the btnCloseCard button in xaml * when we click this closed button with **a** card in the list, we want to close this card. Question is: how can we now **which** card's close button was clicked, since they all have the same close button?? * We need to ask for the context of the button and translate it to a TrelloCard object: ```cs= //get listview item in which this button was clicked + convert to trello card TrelloCard card = (sender as Button).BindingContext as TrelloCard; ``` * Next, change the IsClosed property of the card object to **true**. * Call the **UpdateTrelloCardAsync** function to save this card * **After** the update (hint: await), load all cards again so the list is updated. The closed card should now be gone from the list. #### **Add a card (navigate to SingleCardPage** * We will use the same SingleCardPage for adding a new card or changing an existing one. * The navigation for a new card should happen **when the label is tapped**. However, a label does not have a click event.  We will solve this by adding a **TapGesture** to the label: ![](https://hackmd.io/_uploads/B1ZZUAiXi.png) * In this event, navigates to the **SingleCardPage** * Add a constructor in SingleCardPage to pass the **BoardContext** and **ListContext** as a parameter _(no card should be passed in this case, since a new one will be created)_ ### **FRONT END - SingleCardPage** ![](https://hackmd.io/_uploads/HkPd-tRQs.png) The screenshot shows the situation when to **add** a new card (card **name** editor field is **empt**). **Page initialization** * Copy the given xaml code to SingleCardPage.xaml ```cs+ <Grid VerticalOptions="Start"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Label x:Name="lblList" Text="List name" TextColor="White" FontSize="Large" Margin="19,8" Grid.Row="0" /> <Label x:Name="lblBoard" Text="Board name" BackgroundColor="Wheat" HorizontalOptions="Start" FontSize="Small" Margin="19,4,8,8" Grid.Row="1" /> <Grid Margin="8" Padding="8" BackgroundColor="White" Grid.Row="2"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Frame OutlineColor="Gray" Grid.RowSpan="2" Padding="0" WidthRequest="52" > <Image x:Name="imgAvatar" Source="https://qsf.ec.quoracdn.net/-3-images.new_grid.profile_pic_default_small.png-26-902da2b339fedf49.png" VerticalOptions="Fill" HorizontalOptions="Fill" /> </Frame> <Label x:Name="lblFullName" Text="Full Name" FontSize="Large" Grid.Column="1" /> <StackLayout Orientation="Horizontal" Grid.Row="1" Grid.Column="1"> <Label Text="@XamarinUser" /> <Label x:Name="lblUsername" Text="Xamarin username" /> </StackLayout> <Editor x:Name="editName" Keyboard="Text" FontSize="Medium" VerticalOptions="Fill" HorizontalOptions="Fill" Grid.ColumnSpan="2" Grid.Row="2" /> </Grid> <Grid Grid.Row="3" Margin="8,0,8,8"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button x:Name="btnCancel" Text="Cancel" /> <Button x:Name="btnSave" Text="Save" Grid.Column="1" /> </Grid> </Grid> ``` * in the constructor, save the TrelloBoard and TrelloList objects that you get as a parameter in new properties **BoardContent** and **ListContent**. * Set the text of lblList and lblBoard to the name of these objects * Change the title of this page to 'Add a new card'. #### **Cancel button** * A click on the 'CANCEL' button takes you back to the previous page. #### **Save button** * **add a new card:** * create a new TrelloCard object and set its name to the text in the **editName** editor field * save it via the TrelloRepository (AddTrelloCardAsync) * **after** that, return to the previous page #### **Refresh TrelloCardsPage** When we go back to the previous TrelloCardsPage, the data is not loaded again and so the list is not updated. The reason for this is that the **page already exists**. Therefore, the **constructor** which loads the data **is not being executed** again.  The solution for this is to load the data in the **OnAppearing** event of the ContextPage instead of in the constructor. Override this existing event in your TrelloCardsPage and move the method call for LoadCardsAsync from the constructor to this event. * This OnAppearing event happens the first time the page is loaded (after the constructor) * Every time the page appears again, the event is also fired again ### JSON : select a sub element / nested data Until now, we used the given JSON data in the most simple way; no nested data (sub element) has been processed so far. However, in reality, almost always at least some of the returned data will be nested. Let's have a look at some Trello examples. ### TrelloBoard - Select single property Let's have a look at the screenshot below: each board item frame in the listview (MainPage) now gets the background color of the board in Trello. However, this background color preference is wrapped in a nested object called "prefs". **We do not need the entire prefs object**; we only need backgroundColor. ![](https://hackmd.io/_uploads/HJ-Rgrnmi.png) To select this object, we need to take a few steps in our **TrelloBoard model**: 1. add extra using statements: ```cs+ using System.Runtime.Serialization; using Newtonsoft.Json.Linq; ``` 1. create a property **without a JsonProperty attribute** for the nested property AND create a private **property that contains all tokens** in the JSON file that were not processed: ![](https://hackmd.io/_uploads/H1cfbS27s.png) 1. process these extra properties to fill the remaining ones. You can choose whichever method name you please, as long as the parameter is of type StreamingContext: ![](https://hackmd.io/_uploads/S1g4bBnXi.png) _**Note:** SelectToken(....) can also get a full path as a parameter (subelem.subsubelem.subsubsubelem), should the object be nested even deeper._ 1. Look at the MainPage where the frames are already binded to this ColorHex property! Try to run the application; the square frames should get the required color. #### TrelloCard - Select entire nested object In the previous example, we selected a nested property and stored it as a normal property inside the class. Sometimes however, to keep things structured, you might want to **create a nested property** yourself (using **object composition**). Take the TrelloCard, for example. We wish to show the number of comments (NumComments) an number of attachments (NumAttachments) and show it on each card object in the list: ![](https://hackmd.io/_uploads/SkoObSh7i.png) * To accomplish this, use object composition: ![](https://hackmd.io/_uploads/rkJ9bShmi.png) * Note that we refer to this nested properties already in your TrelloCardsPage.xaml bindings like this (<nested_propname>.<propname>): ![](https://hackmd.io/_uploads/ryKiZShXs.png) ### Check Connectivity We will set up an environment based on the knowledge you acquired during the first demo. * While the application starts, check if a connectivity is available. If so start the **MainPage**, otherwise display a **NoNetworkPage**. * Test by putting your device or emulator in airplane mode, and then starting the app * The same thing should happen if the connection is lost while the application is running * Test this by starting the app while airplane mode is turned **off**. Your mainpage should appear. * While the app is running, turn airplane mode back **on**. * Check if the app behaves as it should (NoNetworkPage should appear). * Turning airplane mode back **off** should result in the MainPage being displayed again. * Make sure you end this step in turning airplane mode **off** ## Call for experts ### Member data in SingleCardsPage Zorg ervoor dat de member data getoond wordt in SingleCardPage.  * Maak een model (klasse) **TrelloMember** aan. De gegevens die we nodig hebben zijn de full name ('FullName'), de username ('UserName') en de avatar hash ('AvatarImg'). * De avatar image wordt een full prop die in de getter de url teruggeeft waarin de hash verwerkt zit, en size 30 (zie trello member reference) * In de trello Repository class geef je de member data van de huidige gebruiker terug (tip: huidige gebruiker heeft als id "me") adhv een functie **GetMemberDataAsync()**. * In de SingleCardPage laad je de member data in en toon je die in lblFullName, lblUserName en imgAvatar. ![](https://hackmd.io/_uploads/SymTGSh7j.png) :::success You can always ask the teachers for feedback on these exercises! Just add an ‘Issue’ to your repository and mention your teacher’s name (@teachername). ::: ### Board prefs across pages Zorg dat de board preferences, zoals de achtergrondkleur, over de pagina's heen gebruikt worden. ![](https://hackmd.io/_uploads/SkCAzH3mj.png) # What did we learn? * You master the techniques to access **REST data using GET, POST or PUT**: * You understand the **JSON structure** and how you can handle them in your own way, even if there are **different property names**. * You can **analyze** **existing REST services** to find the correct request urls and json formats. * You can play around with the **async and await** keywords to make sure all data handling happens asynchronously. * It is clear to you how you can get the **input** of the user and transfer it through a web service using PUT or POST. * Notifying the user when there is **no** **connection** when the application **starts**, are while the applications is **running**, is no issue for you. # Commit your work (GitHub) Please commit your final working project to GitHub before your next lab starts. :::success **You can always ask the teachers for feedback on the exercises if you’re not sure of the result! Just add an ‘Issue’ to your repository and mention your teacher’s name (@teachername).** ::: * Finish your assignment in time and commit: * open a command window * browse to your solution folder * execute these commands, using the comment “**finished lab**”: ![](https://hackmd.io/_uploads/HJLE_SjZo.png) ![](https://hackmd.io/_uploads/ByMBuBo-s.png) ![](https://hackmd.io/_uploads/S1zU_SiWi.png) # Extra official documentation on the subject * Ebook 'Creating Mobile Apps with Xamarin.Forms book' https://learn.microsoft.com/en-us/xamarin/xamarin-forms/creating-mobile-apps-xamarin-forms/ * Chapter 15 - The interactive interface ###### tags: `Device Programming` `MCT` `Syllabus`