Browse Source

3.5: union types and 'type guards' functions.

Frederic G. MARAND 5 years ago
parent
commit
83d0baeb53

+ 161 - 69
.idea/workspace.xml

@@ -2,8 +2,15 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="c6f02f4e-af06-43d1-8bc0-507af01d0573" name="Default Changelist" comment="">
+      <change afterPath="$PROJECT_DIR$/course/src/app/types.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/base-report.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/base-report.component.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/create-report-item.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/create-report-item.component.ts" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/course/src/app/create-report.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/create-report.component.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/edit-report.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/edit-report.component.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/reports.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/reports.component.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/services/messenger.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/services/messenger.service.ts" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/course/src/app/services/reportData.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/services/reportData.service.ts" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/course/src/app/services/reportItem.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/course/src/app/services/reportItem.service.ts" afterDir="false" />
     </list>
     <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
     <option name="SHOW_DIALOG" value="false" />
@@ -28,7 +35,7 @@
           <entry key="gitignore" value="2" />
           <entry key="groovy" value="1" />
           <entry key="json" value="16" />
-          <entry key="ts" value="37" />
+          <entry key="ts" value="59" />
           <entry key="txt" value="1" />
         </counts>
       </usages-collector>
@@ -38,20 +45,20 @@
           <entry key="JSON" value="3" />
           <entry key="PLAIN_TEXT" value="2" />
           <entry key="Scratch" value="3" />
-          <entry key="TypeScript" value="37" />
+          <entry key="TypeScript" value="59" />
           <entry key="tsconfig" value="13" />
         </counts>
       </usages-collector>
       <usages-collector id="statistics.file.extensions.edit">
         <counts>
           <entry key="json" value="62" />
-          <entry key="ts" value="2482" />
+          <entry key="ts" value="3768" />
         </counts>
       </usages-collector>
       <usages-collector id="statistics.file.types.edit">
         <counts>
           <entry key="JSON" value="5" />
-          <entry key="TypeScript" value="2482" />
+          <entry key="TypeScript" value="3768" />
           <entry key="tsconfig" value="57" />
         </counts>
       </usages-collector>
@@ -70,40 +77,49 @@
   <component name="FileEditorManager">
     <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
       <file pinned="false" current-in-tab="true">
-        <entry file="file://$PROJECT_DIR$/course/src/app/services/reportData.service.ts">
+        <entry file="file://$PROJECT_DIR$/course/src/app/types.ts">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="193">
-              <caret line="47" column="76" lean-forward="true" selection-start-line="47" selection-start-column="76" selection-end-line="47" selection-end-column="76" />
+            <state relative-caret-position="413">
+              <caret line="58" column="37" selection-start-line="58" selection-start-column="37" selection-end-line="58" selection-end-column="37" />
             </state>
           </provider>
         </entry>
       </file>
       <file pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/course/src/app/create-report.component.ts">
+        <entry file="file://$PROJECT_DIR$/course/src/app/reports.component.ts">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="24">
-              <caret line="3" column="27" selection-start-line="3" selection-start-column="27" selection-end-line="3" selection-end-column="27" />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/course/src/app/services/reportItem.service.ts">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="318">
-              <caret line="38" column="29" selection-start-line="38" selection-start-column="29" selection-end-line="38" selection-end-column="29" />
+            <state relative-caret-position="352">
+              <caret line="46" column="45" lean-forward="true" selection-start-line="46" selection-start-column="45" selection-end-line="46" selection-end-column="45" />
             </state>
           </provider>
         </entry>
       </file>
     </leaf>
   </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="TypeScript File" />
+      </list>
+    </option>
+  </component>
   <component name="FindInProjectRecents">
     <findStrings>
       <find>beha</find>
       <find>beh</find>
       <find>.id!</find>
+      <find>PS-report</find>
+      <find>reportitem</find>
+      <find>reportsdatasource</find>
+      <find>tabl</find>
+      <find>&quot;</find>
     </findStrings>
+    <replaceStrings>
+      <replace>'</replace>
+    </replaceStrings>
+    <dirStrings>
+      <dir>$PROJECT_DIR$/course</dir>
+    </dirStrings>
   </component>
   <component name="Git.Settings">
     <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
@@ -112,17 +128,18 @@
     <option name="CHANGED_PATHS">
       <list>
         <option value="$PROJECT_DIR$/tsconfig.json" />
-        <option value="$PROJECT_DIR$/course/src/app/create-report-item.component.ts" />
         <option value="$PROJECT_DIR$/course/src/app/app-routing.module.ts" />
-        <option value="$PROJECT_DIR$/course/src/app/services/messenger.service.ts" />
         <option value="$PROJECT_DIR$/course/src/app/app.module.ts" />
-        <option value="$PROJECT_DIR$/course/src/app/base-report.component.ts" />
-        <option value="$PROJECT_DIR$/course/src/app/edit-report.component.ts" />
-        <option value="$PROJECT_DIR$/course/src/app/reports.component.ts" />
         <option value="$PROJECT_DIR$/course/tsconfig.json" />
+        <option value="$PROJECT_DIR$/course/src/app/services/messenger.service.ts" />
         <option value="$PROJECT_DIR$/course/src/app/services/reportItem.service.ts" />
         <option value="$PROJECT_DIR$/course/src/app/create-report.component.ts" />
+        <option value="$PROJECT_DIR$/course/src/app/create-report-item.component.ts" />
+        <option value="$PROJECT_DIR$/course/src/app/base-report.component.ts" />
+        <option value="$PROJECT_DIR$/course/src/app/edit-report.component.ts" />
         <option value="$PROJECT_DIR$/course/src/app/services/reportData.service.ts" />
+        <option value="$PROJECT_DIR$/course/src/app/reports.component.ts" />
+        <option value="$PROJECT_DIR$/course/src/app/types.ts" />
       </list>
     </option>
   </component>
@@ -194,15 +211,34 @@
               <item name="Pluralsight-GetTheMostFromTypescript" type="462c0819:PsiDirectoryNode" />
               <item name="course" type="462c0819:PsiDirectoryNode" />
               <item name="node_modules" type="462c0819:PsiDirectoryNode" />
-              <item name="typescript" type="462c0819:PsiDirectoryNode" />
+              <item name="@angular" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="Pluralsight-GetTheMostFromTypescript" type="b2602c69:ProjectViewProjectNode" />
+              <item name="Pluralsight-GetTheMostFromTypescript" type="462c0819:PsiDirectoryNode" />
+              <item name="course" type="462c0819:PsiDirectoryNode" />
+              <item name="node_modules" type="462c0819:PsiDirectoryNode" />
+              <item name="@angular" type="462c0819:PsiDirectoryNode" />
+              <item name="material" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="Pluralsight-GetTheMostFromTypescript" type="b2602c69:ProjectViewProjectNode" />
+              <item name="Pluralsight-GetTheMostFromTypescript" type="462c0819:PsiDirectoryNode" />
+              <item name="course" type="462c0819:PsiDirectoryNode" />
+              <item name="node_modules" type="462c0819:PsiDirectoryNode" />
+              <item name="@angular" type="462c0819:PsiDirectoryNode" />
+              <item name="material" type="462c0819:PsiDirectoryNode" />
+              <item name="table" type="462c0819:PsiDirectoryNode" />
             </path>
             <path>
               <item name="Pluralsight-GetTheMostFromTypescript" type="b2602c69:ProjectViewProjectNode" />
               <item name="Pluralsight-GetTheMostFromTypescript" type="462c0819:PsiDirectoryNode" />
               <item name="course" type="462c0819:PsiDirectoryNode" />
               <item name="node_modules" type="462c0819:PsiDirectoryNode" />
-              <item name="typescript" type="462c0819:PsiDirectoryNode" />
-              <item name="lib" type="462c0819:PsiDirectoryNode" />
+              <item name="@angular" type="462c0819:PsiDirectoryNode" />
+              <item name="material" type="462c0819:PsiDirectoryNode" />
+              <item name="table" type="462c0819:PsiDirectoryNode" />
+              <item name="typings" type="462c0819:PsiDirectoryNode" />
             </path>
             <path>
               <item name="Pluralsight-GetTheMostFromTypescript" type="b2602c69:ProjectViewProjectNode" />
@@ -242,7 +278,7 @@
     <property name="configurable.Global.libraries.is.expanded" value="true" />
     <property name="js.eslint.eslintPackage" value="/usr/local/lib/node_modules/eslint" />
     <property name="js.eslint.nodeInterpreter" value="/usr/local/bin/node" />
-    <property name="last_opened_file_path" value="$PROJECT_DIR$/../../../go/src/github.com" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/course" />
     <property name="node.js.detected.package.tslint" value="true" />
     <property name="node.js.path.for.package.tslint" value="project" />
     <property name="node.js.selected.package.tslint" value="$PROJECT_DIR$/node_modules/tslint" />
@@ -297,19 +333,19 @@
       <updated>1535304175302</updated>
       <workItem from="1535304176395" duration="75000" />
       <workItem from="1535304274687" duration="6343000" />
-      <workItem from="1535393199806" duration="6198000" />
+      <workItem from="1535393199806" duration="10540000" />
     </task>
     <servers />
   </component>
   <component name="TimeTrackingManager">
-    <option name="totallyTimeSpent" value="12616000" />
+    <option name="totallyTimeSpent" value="16958000" />
   </component>
   <component name="ToolWindowManager">
     <frame x="0" y="23" width="1440" height="877" extended-state="6" />
     <editor active="true" />
     <layout>
       <window_info id="Favorites" order="0" side_tool="true" />
-      <window_info active="true" id="Structure" order="1" side_tool="true" visible="true" weight="0.1795422" />
+      <window_info id="Structure" order="1" side_tool="true" visible="true" weight="0.1795422" />
       <window_info id="npm" order="2" side_tool="true" />
       <window_info anchor="bottom" id="Event Log" order="0" sideWeight="0.5007153" side_tool="true" weight="0.3299363" />
       <window_info anchor="bottom" id="Version Control" order="1" />
@@ -330,7 +366,7 @@
       <window_info anchor="right" id="Commander" order="2" weight="0.4" />
       <window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
       <window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
-      <window_info anchor="right" content_ui="combo" id="Project" order="5" sideWeight="0.49582636" visible="true" weight="0.27110156" />
+      <window_info active="true" anchor="right" content_ui="combo" id="Project" order="5" sideWeight="0.49582636" visible="true" weight="0.25894135" />
       <window_info anchor="right" id="Remote Host" order="6" show_stripe_button="false" />
       <window_info anchor="right" x="0" y="0" width="310" height="677" id="Documentation" order="7" sideWeight="0.50417364" side_tool="true" weight="0.27110156" />
     </layout>
@@ -377,13 +413,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/create-report-item.component.ts">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="255">
-          <caret line="17" column="6" lean-forward="true" selection-start-line="17" selection-start-column="6" selection-end-line="17" selection-end-column="6" />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/course/src/app/app-routing.module.ts">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="210">
@@ -398,80 +427,143 @@
         </state>
       </provider>
     </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/app/app.module.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="384">
+          <caret line="44" column="12" lean-forward="true" selection-start-line="44" selection-start-column="12" selection-end-line="44" selection-end-column="12" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/tsconfig.app.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="165">
+          <caret line="11" column="3" lean-forward="true" selection-start-line="11" selection-start-column="3" selection-end-line="11" selection-end-column="3" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/tsconfig.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="195">
+          <caret line="13" column="29" selection-start-line="13" selection-start-column="29" selection-end-line="13" selection-end-column="29" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/node_modules/typescript/lib/lib.es5.d.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="253">
+          <caret line="978" lean-forward="true" selection-start-line="978" selection-end-line="978" />
+        </state>
+      </provider>
+    </entry>
     <entry file="file://$PROJECT_DIR$/course/src/app/services/messenger.service.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="220">
-          <caret line="40" column="4" selection-start-line="40" selection-start-column="4" selection-end-line="40" selection-end-column="4" />
+        <state relative-caret-position="240">
+          <caret line="15" lean-forward="true" selection-start-line="15" selection-end-line="15" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/app.module.ts">
+    <entry file="file://$PROJECT_DIR$/course/src/app/services/window.service.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="384">
-          <caret line="44" column="12" lean-forward="true" selection-start-line="44" selection-start-column="12" selection-end-line="44" selection-end-column="12" />
+        <state relative-caret-position="45">
+          <caret line="3" lean-forward="true" selection-start-line="3" selection-end-line="3" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/base-report.component.ts">
+    <entry file="file://$PROJECT_DIR$/course/src/app/create-report-item.component.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="349">
-          <caret line="62" column="51" lean-forward="true" selection-start-line="62" selection-start-column="51" selection-end-line="62" selection-end-column="51" />
+        <state relative-caret-position="300">
+          <caret line="21" column="34" lean-forward="true" selection-start-line="21" selection-start-column="34" selection-end-line="21" selection-end-column="34" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/reports.component.ts">
+    <entry file="file://$PROJECT_DIR$/course/src/app/create-report.component.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="295">
+          <caret line="20" column="5" lean-forward="true" selection-start-line="20" selection-start-column="5" selection-end-line="20" selection-end-column="5" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/app/services/reportItem.service.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="15">
+          <caret line="1" column="16" selection-start-line="1" selection-start-column="16" selection-end-line="1" selection-end-column="16" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/app/base-report.component.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="77">
-          <caret line="69" column="5" lean-forward="true" selection-start-line="69" selection-start-column="5" selection-end-line="69" selection-end-column="5" />
+        <state relative-caret-position="525">
+          <caret line="35" column="13" lean-forward="true" selection-start-line="35" selection-start-column="13" selection-end-line="35" selection-end-column="13" />
         </state>
       </provider>
     </entry>
     <entry file="file://$PROJECT_DIR$/course/src/app/edit-report.component.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="341">
-          <caret line="31" column="53" selection-start-line="31" selection-start-column="53" selection-end-line="31" selection-end-column="53" />
+        <state relative-caret-position="210">
+          <caret line="15" column="25" lean-forward="true" selection-start-line="15" selection-start-column="25" selection-end-line="15" selection-end-column="25" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/tsconfig.app.json">
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/table/typings/table.d.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="165">
-          <caret line="11" column="3" lean-forward="true" selection-start-line="11" selection-start-column="3" selection-end-line="11" selection-end-column="3" />
+        <state relative-caret-position="90">
+          <caret line="6" lean-forward="true" selection-start-line="6" selection-end-line="6" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/tsconfig.json">
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/table/typings/public-api.d.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="195">
-          <caret line="13" column="29" selection-start-line="13" selection-start-column="29" selection-end-line="13" selection-end-column="29" />
+        <state relative-caret-position="135">
+          <caret line="9" column="21" lean-forward="true" selection-start-line="9" selection-start-column="21" selection-end-line="9" selection-end-column="21" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/services/reportItem.service.ts">
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/table/typings/index.d.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="318">
-          <caret line="38" column="29" selection-start-line="38" selection-start-column="29" selection-end-line="38" selection-end-column="29" />
+        <state relative-caret-position="45">
+          <caret line="3" column="26" lean-forward="true" selection-start-line="3" selection-start-column="26" selection-end-line="3" selection-end-column="26" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/node_modules/typescript/lib/lib.es5.d.ts">
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/table/index.d.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="253">
-          <caret line="978" lean-forward="true" selection-start-line="978" selection-end-line="978" />
+        <state relative-caret-position="105">
+          <caret line="7" column="29" lean-forward="true" selection-start-line="7" selection-start-column="29" selection-end-line="7" selection-end-column="29" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/course/src/app/create-report.component.ts">
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/table.d.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="24">
-          <caret line="3" column="27" selection-start-line="3" selection-start-column="27" selection-end-line="3" selection-end-column="27" />
+        <state relative-caret-position="105">
+          <caret line="7" column="27" lean-forward="true" selection-start-line="7" selection-start-column="27" selection-end-line="7" selection-end-column="27" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/node_modules/@angular/material/material.d.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="459">
+          <caret line="39" column="21" selection-start-line="39" selection-start-column="17" selection-end-line="39" selection-end-column="21" />
         </state>
       </provider>
     </entry>
     <entry file="file://$PROJECT_DIR$/course/src/app/services/reportData.service.ts">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="193">
-          <caret line="47" column="76" lean-forward="true" selection-start-line="47" selection-start-column="76" selection-end-line="47" selection-end-column="76" />
+        <state relative-caret-position="146">
+          <caret line="43" lean-forward="true" selection-start-line="43" selection-end-line="43" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/app/reports.component.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="352">
+          <caret line="46" column="45" lean-forward="true" selection-start-line="46" selection-start-column="45" selection-end-line="46" selection-end-column="45" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/course/src/app/types.ts">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="413">
+          <caret line="58" column="37" selection-start-line="58" selection-start-column="37" selection-end-line="58" selection-end-column="37" />
         </state>
       </provider>
     </entry>

+ 1 - 1
course/src/app/base-report.component.ts

@@ -6,7 +6,7 @@ import { BehaviorSubject } from "rxjs/BehaviorSubject";
 
 import { ReportDataService } from "./services/reportData.service";
 import { CreateReportItemDialogComponent } from "./create-report-item.component";
-import { ReportItem } from "./services/reportItem.service";
+import {ReportItem} from "./types";
 
 export abstract class BaseReportComponent {
     abstract title: string;

+ 2 - 1
course/src/app/create-report-item.component.ts

@@ -1,6 +1,7 @@
 import { Component } from '@angular/core';
 import { MatDialogRef } from '@angular/material';
-import { ReportItem, ReportItemType, ReportItemService } from './services/reportItem.service';
+import { ReportItemService } from './services/reportItem.service';
+import {ReportItem, ReportItemType} from "./types";
 
 @Component({
     selector: 'ps-create-report-item',

+ 2 - 1
course/src/app/create-report.component.ts

@@ -1,9 +1,10 @@
 import { Component } from '@angular/core';
 import { Location } from '@angular/common';
 import { MatDialog } from '@angular/material';
-import {ReportDataService, NewReport} from './services/reportData.service';
+import {ReportDataService} from './services/reportData.service';
 import 'rxjs/add/operator/switchMap';
 import { BaseReportComponent } from './base-report.component';
+import {NewReport} from "./types";
 
 @Component({
     selector: 'ps-report',

+ 2 - 1
course/src/app/edit-report.component.ts

@@ -3,8 +3,9 @@ import { MatDialog } from "@angular/material";
 import { Location } from '@angular/common';
 import { ActivatedRoute } from "@angular/router";
 
-import { ReportDataService, Report } from "./services/reportData.service";
+import { ReportDataService } from "./services/reportData.service";
 import { BaseReportComponent } from "./base-report.component";
+import {Report} from "./types";
 
 @Component({
     selector: 'ps-report',

+ 15 - 20
course/src/app/reports.component.ts

@@ -1,29 +1,14 @@
 import { Component, Inject } from '@angular/core';
-import { DataSource } from '@angular/cdk/collections';
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/merge';
 import 'rxjs/add/observable/of';
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/take';
-import { Report, ReportDataService } from './services/reportData.service';
+import { ReportDataService } from './services/reportData.service';
 import { ActivatedRoute, ParamMap } from '@angular/router';
 import { WindowService } from './services/window.service';
-
-export class ReportsDataSource extends DataSource<any> {
-
-    constructor(private dataSource: ReportDataService) {
-        super();
-    }
-
-    connect(): Observable<Report[]> {
-        return this
-            .dataSource
-            .dataChange
-            .map(data => data);
-    }
-
-    disconnect() { }
-}
+import {isRemoteDataError, isRemoteDataLoading, isRemoteDataOk, Report} from "./types";
+import {MatTableDataSource} from "@angular/material";
 
 function mapUser(params: ParamMap): boolean {
     const userName = params.get('user') || '';
@@ -48,15 +33,25 @@ function toggleApprovedStyle(this: HTMLElement, approved: boolean) {
     styleUrls: ['./reports.component.css']
 })
 export class ReportsComponent {
-    reports: ReportsDataSource;
+    reports: MatTableDataSource<Report>;
     isAdmin = Observable.of(false);
     displayedColumns = ['id', 'description', 'date', 'amount', 'approved', 'actions'];
 
   constructor(private route: ActivatedRoute,
         private reportDataService: ReportDataService,
         @Inject(WindowService) private _window: Window) {
-    this.reports = new ReportsDataSource(reportDataService);
+    this.reports = new MatTableDataSource([]);
         this.isAdmin = this.route.queryParamMap.map(mapUser);
+
+        reportDataService.dataChange.subscribe(remoteData => {
+            if (isRemoteDataOk(remoteData)) {
+                this.reports.data = remoteData.data;
+            } else if (isRemoteDataError(remoteData)) {
+                alert(remoteData.error);
+            } else if (isRemoteDataLoading(remoteData)) {
+                // display loading spinner.
+            }
+        });
     }
 
     approve(report: Report) {

+ 2 - 5
course/src/app/services/messenger.service.ts

@@ -1,8 +1,5 @@
-import { ReportDataService, Report } from "./reportData.service";
-
-interface Messenger {
-    deliveryMessage(report: Report, user: string): void;
-}
+import { ReportDataService } from "./reportData.service";
+import {Messenger, Report} from "../types";
 
 class ReportCreatedMessenger implements Messenger {
     deliveryMessage(report: Report, user: string): void {

+ 61 - 40
course/src/app/services/reportData.service.ts

@@ -1,74 +1,95 @@
 import { Injectable, Inject } from '@angular/core';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
-import { ReportItem } from './reportItem.service';
 import { WindowService } from './window.service';
 import { Subject } from 'rxjs/Subject';
-
-export type Report = {
-    id: number;
-    date: Date;
-    approved: boolean;
-    description: string;
-    items: ReportItem[];
-    amount: number;
-};
-
-export type NewReport = Pick<Report, 'date' | 'approved' | 'description' | 'items' >
-
-const psReportsKey = 'ps-reports';
+import {isRemoteDataOk, NewReport, PS_REPORTS_KEY, RemoteData, Report} from '../types';
 
 @Injectable()
 export class ReportDataService {
-    dataChange = new BehaviorSubject<Report[]>([]);
+    dataChange = new BehaviorSubject<RemoteData<Report[]>>({ kind: 'notFetched' });
     reportCreated: Subject<Report> = new Subject();
     reportApprovedReject: Subject<{ report: Report, user: string }> = new Subject();
 
     constructor(@Inject(WindowService) private _window: Window) {
-        const stringfiedReports = _window.localStorage.getItem(psReportsKey);
+        this.fetch();
+        const stringfiedReports = _window.localStorage.getItem(PS_REPORTS_KEY);
         if (stringfiedReports) {
             this.dataChange.next(JSON.parse(stringfiedReports));
         }
     }
 
-    get data(): Report[] {
+    fetch() {
+        this.dataChange.next({ kind: 'loading' });
+        setTimeout(() => {
+            const random = Math.floor((Math.random() * 10) + 1);
+            const stringifiedReports = this._window.localStorage.getItem(PS_REPORTS_KEY);
+            if (random > 3 && stringifiedReports) {
+                this.dataChange.next({
+                    kind: 'ok',
+                    data: JSON.parse(stringifiedReports),
+                });
+            } else {
+                // some error.
+                this.dataChange.next({
+                    kind: 'error',
+                    error: 'Loading the data has failed',
+                });
+            }
+        }, 0);
+    }
+
+    get remoteData(): RemoteData<Report[]> {
         return this.dataChange.value;
     }
 
     getReport(reportId: number): Promise<Report | undefined> {
-        return Promise.resolve(this.data.find(r => r.id === reportId));
+        if (isRemoteDataOk(this.remoteData)) {
+            return Promise.resolve(this.remoteData.data.find(r => r.id === reportId));
+        } else {
+            return Promise.resolve(undefined);
+        }
     }
 
     add(report: NewReport) {
-        const newReport = {
-            ...report,
-            // Since this.data: Report[], rep is a Report, so it does have an id
-            // property we can use, although Report.id is an optional number.
-            // Which means we can tell the compiler it exists with a "!".
-            id: this.data.reduce((biggestId, rep) => biggestId > rep.id ? biggestId : rep.id, 0) + 1,
-            amount: report.items.reduce((sum, item) => sum + item.amount, 0)
-        };
-        const newData = [...this.data, newReport];
-        this.updateData(newData);
-        this.reportCreated.next(newReport);
+        if (isRemoteDataOk(this.remoteData)) {
+            const newReport = {
+                ...report,
+                // Since this.data: Report[], rep is a Report, so it does have an id
+                // property we can use, although Report.id is an optional number.
+                // Which means we can tell the compiler it exists with a '!'.
+                id: this.remoteData.data.reduce((biggestId, rep) => biggestId > rep.id ? biggestId : rep.id, 0) + 1,
+                amount: report.items.reduce((sum, item) => sum + item.amount, 0)
+            };
+            const newData = [...this.remoteData.data, newReport];
+            this.updateData(newData);
+            this.reportCreated.next(newReport);
+        }
     }
 
     save(report: Report) {
-        const reportIndex = this.data.findIndex(r => r.id === report.id);
-        const newData = [...this.data];
-        newData.splice(reportIndex, 1, report);
-        this.updateData(newData);
+        if (isRemoteDataOk(this.remoteData)) {
+            const reportIndex = this.remoteData.data.findIndex(r => r.id === report.id);
+            const newData = [...this.remoteData.data];
+            newData.splice(reportIndex, 1, report);
+            this.updateData(newData);
+        }
     }
 
     toggleApproval(report: Report, user: string) {
-        const reportIndex = this.data.findIndex(r => r.id === report.id);
-        const newData = [...this.data];
-        newData.splice(reportIndex, 1, report);
-        this.updateData(newData);
-        this.reportApprovedReject.next({ report, user });
+        if (isRemoteDataOk(this.remoteData)) {
+            const reportIndex = this.remoteData.data.findIndex(r => r.id === report.id);
+            const newData = [...this.remoteData.data];
+            newData.splice(reportIndex, 1, report);
+            this.updateData(newData);
+            this.reportApprovedReject.next({report, user});
+        }
     }
 
     private updateData(reports: Report[]) {
-        this._window.localStorage.setItem(psReportsKey, JSON.stringify(reports));
-        this.dataChange.next(reports);
+        this._window.localStorage.setItem(PS_REPORTS_KEY, JSON.stringify(reports));
+        this.dataChange.next({
+            kind: 'ok',
+            data: reports,
+        });
     }
 }

+ 1 - 46
course/src/app/services/reportItem.service.ts

@@ -1,52 +1,7 @@
 import { Injectable } from '@angular/core';
+import {ReportItem, ReportItemType} from "../types";
 // import {bootstrapItem} from "../../../node_modules/@angular/cli/lib/ast-tools";
 
-export enum ReportItemType {
-    food = 'Food',
-    travel = 'Travel',
-    training = 'Training',
-    transport = 'Transport',
-    unselected = ''
-}
-
-export type ReportItem = {
-    description: string;
-    amount: number;
-    hasReceipt: boolean;
-    type: ReportItemType;
-    date: Date;
-};
-
-// A ReportItem with all keys optional.
-export type ReportItem2 = {
-    // All properties same as ReportItem.
-    // [P in keyof ReportItem]?: ReportItem[P]
-
-    // All properties optional
-    [P in keyof ReportItem]?: ReportItem[P]
-}
-
-// A Generic for any object with all keys optional.
-type Optional<T> = {
-    // All properties optional
-    [P in keyof T]?: T[P]
-}
-
-// Same as ReportItem2 but using the generic.
-export type ReportItem3 = Optional<ReportItem>;
-
-// Same again, using the TS builtin for this.
-export type ReportItem4 = Partial<ReportItem>;
-
-// All keys readonly.
-export type ReportItem5 = Readonly<ReportItem>;
-
-// ReportItem again.
-export type ReportItem6 = Required<ReportItem3>;
-
-// Only keep 3 keys
-export type ReportItem7 = Pick<ReportItem, 'date' | 'amount' | 'description' >;
-
 function validateRequired(item: ReportItem): boolean {
     // false is a valid value for these, so exclude them from check.
     const exclude: (keyof ReportItem)[] = ['hasReceipt'];

+ 66 - 0
course/src/app/types.ts

@@ -0,0 +1,66 @@
+export type Report = {
+    id: number;
+    date: Date;
+    approved: boolean;
+    description: string;
+    items: ReportItem[];
+    amount: number;
+};
+
+export type NewReport = Pick<Report, 'date' | 'approved' | 'description' | 'items' >
+
+export enum ReportItemType {
+    food = 'Food',
+    travel = 'Travel',
+    training = 'Training',
+    transport = 'Transport',
+    unselected = ''
+}
+
+export type ReportItem = {
+    description: string;
+    amount: number;
+    hasReceipt: boolean;
+    type: ReportItemType;
+    date: Date;
+};
+
+export interface Messenger {
+    deliveryMessage(report: Report, user: string): void;
+}
+
+export const PS_REPORTS_KEY = 'ps-reports';
+
+type RemoteDataNotFetched = {
+    kind: "notFetched",
+};
+
+type RemoteDataLoading = {
+    kind: "loading",
+};
+
+type RemoteDataOk<T> = {
+    kind: "ok",
+    data: T,
+}
+
+type RemoteDataError = {
+    kind: 'error',
+    error: string,
+}
+
+export type RemoteData<T> =
+    RemoteDataNotFetched |
+    RemoteDataLoading |
+    RemoteDataOk<T> |
+    RemoteDataError;
+
+export function isRemoteDataError<T>(remoteData: RemoteData<T>): remoteData is RemoteDataError {
+    return remoteData.kind === 'error';
+}
+export function isRemoteDataLoading<T>(remoteData: RemoteData<T>): remoteData is RemoteDataLoading {
+    return remoteData.kind === 'loading';
+}
+export function isRemoteDataOk<T>(remoteData: RemoteData<T>): remoteData is RemoteDataOk<T> {
+    return remoteData.kind === 'ok';
+}