deeper typescript integration
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'semistandard'
|
||||
]
|
||||
};
|
||||
@@ -1,12 +1,13 @@
|
||||
@model IEnumerable<Recipe>
|
||||
|
||||
@{
|
||||
string InputValue = ViewBag.Query ?? String.Empty;
|
||||
string InputValue = ViewBag.Query ?? "";
|
||||
string InitialMessage = ViewBag.Query == null ? "Search for a recipe" : $"Search results for '{ViewBag.Query}'";
|
||||
}
|
||||
|
||||
<div>
|
||||
<div id="search-page-header" class="mb-8 border-white border-b">
|
||||
<h3 id="query-message"></h3>
|
||||
<h3 id="query-message">@InitialMessage</h3>
|
||||
<input value="@InputValue" class="text-black" id="new-search-query" type="text" />
|
||||
<button id="execute-search">Search</button>
|
||||
</div>
|
||||
@@ -18,28 +19,4 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// dom elements
|
||||
const query = window.location.search?.split("=")[1];
|
||||
const message = document.getElementById("query-message");
|
||||
const textField = document.getElementById("new-search-query");
|
||||
const searchButton = document.getElementById("execute-search");
|
||||
const searchResultsContainer = document.getElementById("search-results");
|
||||
|
||||
console.log(query);
|
||||
|
||||
// set initial message value
|
||||
message.innerHTML = query ? `Viewing ${@Model.ToArray().Length} results for: ${query}` : "Enter a new search term below:";
|
||||
|
||||
// handle search updates on client side
|
||||
searchButton.onclick = () => {
|
||||
// const newResult = await fetch(`/api/recipe/search?q=${textField.value}`);
|
||||
|
||||
// console.log(newResult);
|
||||
|
||||
// const newResultJson = await newResult.json();
|
||||
window.location.search = `?q=${textField.value}`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script href="~/js/searchRecipes.js"></script>
|
||||
@@ -1,4 +1,4 @@
|
||||
export async function getAllIngredients () {
|
||||
async function getAllIngredients () {
|
||||
const ingredients = await fetch('/api/ingredients');
|
||||
return await ingredients.json();
|
||||
}
|
||||
|
||||
52
Unbinder/wwwroot/ts/searchRecipes.ts
Normal file
52
Unbinder/wwwroot/ts/searchRecipes.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { isRecipeArray, type Recipe } from "./types";
|
||||
|
||||
function setSearchResultMessage(model: Recipe[]): void {
|
||||
const message = document.getElementById("query-message");
|
||||
message.innerHTML = `Viewing ${model.length} results for: ${window.location.search.split("=")[1]}`;
|
||||
}
|
||||
|
||||
async function handleSearch(textField?: HTMLInputElement) {
|
||||
if (!textField) {
|
||||
console.log("NO TEXT FIELD!");
|
||||
return;
|
||||
}
|
||||
|
||||
const newResult = await fetch(`/api/recipe/search?q=${textField.value}`);
|
||||
const newResultJson = await newResult.json();
|
||||
const searchResultsContainer = document.getElementById("search-results");
|
||||
|
||||
if (isRecipeArray(newResultJson)) {
|
||||
let updatedHTML = "";
|
||||
for (const recipe of newResultJson) {
|
||||
updatedHTML += `
|
||||
<p>${recipe.Name}</p>
|
||||
<p>${recipe.ShortDescription}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(updatedHTML);
|
||||
|
||||
setSearchResultMessage(newResultJson);
|
||||
searchResultsContainer.innerHTML = updatedHTML;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log("Loaded DOM");
|
||||
|
||||
// dom elements
|
||||
const searchButton = document.getElementById("execute-search");
|
||||
|
||||
const inputNodes = document.querySelectorAll("input");
|
||||
let textField: HTMLInputElement | null = null;
|
||||
|
||||
inputNodes.forEach(node => {
|
||||
if (node.id == "new-search-query") {
|
||||
textField = node;
|
||||
console.log("Found text field");
|
||||
}
|
||||
});
|
||||
|
||||
// handle search updates on client side
|
||||
searchButton.onclick = async () => handleSearch(textField);
|
||||
});
|
||||
2
Unbinder/wwwroot/ts/types/index.ts
Normal file
2
Unbinder/wwwroot/ts/types/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./models";
|
||||
export * from "./validators";
|
||||
25
Unbinder/wwwroot/ts/types/models.ts
Normal file
25
Unbinder/wwwroot/ts/types/models.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export type Recipe = {
|
||||
RecipeId: number,
|
||||
Name: string,
|
||||
ShortDescription: string,
|
||||
S3Url?: string,
|
||||
Author?: string,
|
||||
RecipeText?: string,
|
||||
MainImageUrl?: string,
|
||||
}
|
||||
|
||||
export type Ingredient = {
|
||||
IngredientId: number,
|
||||
Name: string,
|
||||
Description?: string
|
||||
}
|
||||
|
||||
export type RecipeIngredient = {
|
||||
RecipeIngredientId: number,
|
||||
RecipeId: number,
|
||||
IngredientId: number,
|
||||
Amount?: number,
|
||||
Unit?: string,
|
||||
}
|
||||
|
||||
export type IngredientWithDetails = Ingredient & Pick<RecipeIngredient, 'Amount' | 'Unit'>
|
||||
37
Unbinder/wwwroot/ts/types/validators.ts
Normal file
37
Unbinder/wwwroot/ts/types/validators.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Ingredient, Recipe } from "./models";
|
||||
|
||||
export function isRecipe(input: unknown): input is Recipe {
|
||||
return (
|
||||
typeof input == 'object'
|
||||
&& input != null
|
||||
&& 'RecipeId' in input
|
||||
&& 'Name' in input
|
||||
&& 'ShortDescription' in input
|
||||
&& (input as Recipe).RecipeId != undefined
|
||||
&& (input as Recipe).Name != undefined
|
||||
&& (input as Recipe).ShortDescription != undefined
|
||||
);
|
||||
}
|
||||
|
||||
export function isArrayOfType<T>(input: unknown, isType: (input: unknown) => input is T): input is T[] {
|
||||
return Array.isArray(input) && input.every(isType);
|
||||
}
|
||||
|
||||
export function isRecipeArray(input: unknown): input is Recipe[] {
|
||||
return isArrayOfType(input, isRecipe);
|
||||
}
|
||||
|
||||
export function isIngredient(input: unknown): input is Ingredient {
|
||||
return (
|
||||
typeof input == 'object'
|
||||
&& input != null
|
||||
&& 'IngredientId' in input
|
||||
&& 'Name' in input
|
||||
&& (input as Ingredient).IngredientId != undefined
|
||||
&& (input as Ingredient).Name != undefined
|
||||
);
|
||||
}
|
||||
|
||||
export function isIngredientArray(input: unknown): input is Ingredient[] {
|
||||
return isArrayOfType(input, isIngredient);
|
||||
}
|
||||
Reference in New Issue
Block a user