-
-
Save bradygaster/3d1fcf43d1d1e73ea5d6c1b5aab40130 to your computer and use it in GitHub Desktop.
| endpoints.MapGet("/products", (context) => | |
| { | |
| var products = app.ApplicationServices.GetService<JsonFileProductService>().GetProducts(); | |
| var json = JsonSerializer.Serialize<IEnumerable<Product>>(products); | |
| return context.Response.WriteAsync(json); | |
| }); |
| public void AddRating(string productId, int rating) | |
| { | |
| var products = GetProducts(); | |
| var query = products.First(x => x.Id == productId); | |
| if(query.Ratings == null) | |
| { | |
| query.Ratings = new int[] { rating }; | |
| } | |
| else | |
| { | |
| var ratings = query.Ratings.ToList(); | |
| ratings.Add(rating); | |
| query.Ratings = ratings.ToArray(); | |
| } | |
| using(var outputStream = File.OpenWrite(JsonFileName)) | |
| { | |
| JsonSerializer.Serialize<IEnumerable<Product>>( | |
| new Utf8JsonWriter(outputStream, new JsonWriterOptions | |
| { | |
| SkipValidation = true, | |
| Indented = true | |
| }), | |
| products | |
| ); | |
| } | |
| } |
| app.UseEndpoints(endpoints => | |
| { | |
| endpoints.MapRazorPages(); | |
| endpoints.MapControllers(); | |
| endpoints.MapBlazorHub(); | |
| }); |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| services.AddRazorPages(); | |
| services.AddTransient<JsonFileProductService>(); | |
| } |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| services.AddRazorPages(); | |
| services.AddServerSideBlazor(); | |
| services.AddControllers(); | |
| services.AddTransient<JsonFileProductService>(); | |
| } |
| curl https://localhost:5001/products -k | jq .[0] |
| curl -X PATCH --header "Accept: application/json" --header "Content-Type: application/json" -d '{"productId" : "jenlooper-cactus", "rating" : 5}' https://localhost:5001/products -k |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> |
| <link href="https://fonts.googleapis.com/css?family=Yellowtail&display=swap" rel="stylesheet"> | |
| <link href="https://fonts.googleapis.com/css?family=Nunito&display=swap" rel="stylesheet"> |
| @page | |
| @using ContosoCrafts.WebSite.Components | |
| @model IndexModel | |
| @{ | |
| ViewData["Title"] = "Home page"; | |
| } | |
| @(await Html.RenderComponentAsync<ProductList>(RenderMode.ServerPrerendered)) | |
| <script src="_framework/blazor.server.js"></script> |
| @page | |
| @model IndexModel | |
| @{ | |
| ViewData["Title"] = "Home page"; | |
| } | |
| <div class="card-columns"> | |
| @foreach (var product in Model.Products) | |
| { | |
| <div class="card"> | |
| <div class="card-img" style="background-image: url('@product.Image');"> | |
| </div> | |
| <div class="card-body"> | |
| <h5 class="card-title">@product.Title</h5> | |
| </div> | |
| </div> | |
| } | |
| </div> |
| public IndexModel(ILogger<IndexModel> logger, | |
| JsonFileProductService productService) | |
| { | |
| _logger = logger; | |
| ProductService = productService; | |
| } | |
| public JsonFileProductService ProductService { get; } | |
| public IEnumerable<Product> Products { get; private set; } | |
| public void OnGet() | |
| { | |
| Products = ProductService.GetProducts(); | |
| } |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Text.Json; | |
| using ContosoCrafts.WebSite.Models; | |
| using Microsoft.AspNetCore.Hosting; | |
| namespace ContosoCrafts.WebSite.Services | |
| { | |
| public class JsonFileProductService | |
| { | |
| public JsonFileProductService(IWebHostEnvironment webHostEnvironment) | |
| { | |
| WebHostEnvironment = webHostEnvironment; | |
| } | |
| public IWebHostEnvironment WebHostEnvironment { get; } | |
| private string JsonFileName | |
| { | |
| get { return Path.Combine(WebHostEnvironment.WebRootPath, "data", "products.json"); } | |
| } | |
| public IEnumerable<Product> GetProducts() | |
| { | |
| using(var jsonFileReader = File.OpenText(JsonFileName)) | |
| { | |
| return JsonSerializer.Deserialize<Product[]>(jsonFileReader.ReadToEnd(), | |
| new JsonSerializerOptions | |
| { | |
| PropertyNameCaseInsensitive = true | |
| }); | |
| } | |
| } | |
| } | |
| } |
| using System.Text.Json; | |
| using System.Text.Json.Serialization; | |
| namespace ContosoCrafts.WebSite.Models | |
| { | |
| public class Product | |
| { | |
| public string Id { get; set; } | |
| public string Maker { get; set; } | |
| [JsonPropertyName("img")] | |
| public string Image { get; set; } | |
| public string Url { get; set; } | |
| public string Title { get; set; } | |
| public string Description { get; set; } | |
| public int[] Ratings { get; set; } | |
| public override string ToString() => JsonSerializer.Serialize<Product>(this); | |
| } | |
| } |
| @using Microsoft.AspNetCore.Components.Web | |
| @using ContosoCrafts.WebSite.Models | |
| @using ContosoCrafts.WebSite.Services | |
| @inject JsonFileProductService ProductService | |
| <div class="card-columns"> | |
| @foreach (var product in ProductService.GetProducts()) | |
| { | |
| <div class="card"> | |
| <div class="card-img" style="background-image: url('@product.Image');"> | |
| </div> | |
| <div class="card-body"> | |
| <h5 class="card-title">@product.Title</h5> | |
| </div> | |
| <div class="card-footer"> | |
| <small class="text-muted"><button @onclick="(e => SelectProduct(product.Id))" | |
| data-toggle="modal" data-target="#productModal" class="btn btn-primary">More Info</button> | |
| </small> | |
| </div> | |
| </div> | |
| } | |
| </div> | |
| @code | |
| { | |
| Product selectedProduct; | |
| string selectedProductId; | |
| void SelectProduct(string productId) | |
| { | |
| selectedProductId = productId; | |
| selectedProduct = ProductService.GetProducts().First(x => x.Id == productId); | |
| } | |
| } |
| @if(selectedProduct != null) | |
| { | |
| <div class="modal fade" id="productModal" tabindex="-1" role="dialog" aria-labelledby="productTitle" aria-hidden="true"> | |
| <div class="modal-dialog modal-dialog-centered" role="document"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title" id="productTitle">@selectedProduct.Title</h5> | |
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
| <span aria-hidden="true">×</span> | |
| </button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="card"> | |
| <div class="card-img" style="background-image: url('@selectedProduct.Image');"> | |
| </div> | |
| <div class="card-body"> | |
| <p class="card-text">@selectedProduct.Description</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| } |
| [ | |
| { | |
| "id" : "jenlooper-cactus", | |
| "maker" : "@jenlooper", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567048-13938600-aa33-11e9-9cfd-712191013192.jpeg", | |
| "url" : "https://www.hackster.io/agent-hawking-1/the-quantified-cactus-an-easy-plant-soil-moisture-sensor-e65393", | |
| "title" : "The Quantified Cactus: An Easy Plant Soil Moisture Sensor", | |
| "description" : "This project is a good learning project to get comfortable with soldering and programming an Arduino." | |
| }, | |
| { | |
| "id" : "jenlooper-light", | |
| "maker" : "jenlooper", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567049-13938600-aa33-11e9-9c69-a4184bf8e524.jpeg", | |
| "url" : "https://www.hackster.io/agent-hawking-1/book-light-dee7e4", | |
| "title" : "A beautiful switch-on book light", | |
| "description" : "Use craft items you have around the house, plus two LEDs and a LilyPad battery holder, to create a useful book light for reading in the dark." | |
| }, | |
| { | |
| "id" : "jenlooper-lightshow", | |
| "maker" : "@jenlooper", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567053-13938600-aa33-11e9-9780-104fe4019659.png", | |
| "url" : "https://www.hackster.io/agent-hawking-1/bling-your-laptop-with-an-internet-connected-light-show-30e4db", | |
| "title" : "Bling your Laptop with an Internet-Connected Light Show", | |
| "description" : "Create a web-connected light-strip API controllable from your website, using the Particle.io." | |
| }, | |
| { | |
| "id" : "jenlooper-survival", | |
| "maker" : "jenlooper", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567051-13938600-aa33-11e9-8ae7-0b5c19aafab4.jpeg", | |
| "url" : "https://www.hackster.io/agent-hawking-1/create-a-compact-survival-kit-38bfdb", | |
| "title" : "Create a Compact Survival Kit with LED Track Lighting", | |
| "description" : "Use an Altoids tin with Chibitronics sticker LEDs to create a light-up compact that doubles as a survival kit for the young hipster" | |
| }, | |
| { | |
| "id" : "sailorhg-bubblesortpic", | |
| "maker" : "sailorhg", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567054-13938600-aa33-11e9-9163-eec98e239b7a.png", | |
| "url" : "https://twitter.com/sailorhg/status/1090107740049952770", | |
| "title" : "Bubblesort Visualization", | |
| "description" : "Visualization of sailor scouts sorted by bubblesort algorithm by their planet's distance from the sun" | |
| }, | |
| { | |
| "id" : "sailorhg-corsage", | |
| "maker" : "sailorhg", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567055-142c1c80-aa33-11e9-96ff-9fbac6413625.png", | |
| "url" : "https://twitter.com/sailorhg/status/1090113666911891456", | |
| "title" : "Light-up Corsage", | |
| "description" : "Light-up corsage I made with my summer intern." | |
| }, | |
| { | |
| "id" : "sailorhg-kit", | |
| "maker" : "sailorhg", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567056-142c1c80-aa33-11e9-8682-10065d338145.png", | |
| "url" : "https://twitter.com/sailorhg/status/1090122822007963648", | |
| "title" : "Pastel hardware kit", | |
| "description" : "Pastel hardware kits complete with custom manufactured pastel alligator clips." | |
| }, | |
| { | |
| "id" : "sailorhg-led", | |
| "maker" : "sailorhg", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567052-13938600-aa33-11e9-9a88-cd842073ba44.jpg", | |
| "url" : "https://twitter.com/sailorhg/status/1090117277540745216", | |
| "title" : "Heart-shaped LED", | |
| "description" : "custom molded heart shaped LED with sprinkles." | |
| }, | |
| { | |
| "id" : "selinazawacki-soi-shirt", | |
| "maker" : "selinazawacki", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567060-142c1c80-aa33-11e9-8188-5a4803844a9e.png", | |
| "url" : "https://www.instagram.com/p/BNvESj-j8PI/", | |
| "title" : "Black Sweatshirt", | |
| "description" : "Black sweatshirt hoody with the Sick of the Internet logo." | |
| }, | |
| { | |
| "id" : "selinazawacki-soi-pins", | |
| "maker" : "selinazawacki", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567059-142c1c80-aa33-11e9-939b-2ecf4492786d.png", | |
| "url" : "https://www.instagram.com/p/BNm6hZzDoEF/", | |
| "title" : "Sick of the Internet Pins", | |
| "description" : "Still some time to enter the pin/sticker giveaway! " | |
| }, | |
| { | |
| "id" : "vogueandcode-hipster-dev-bro", | |
| "maker" : "vogueandcode", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567061-14c4b300-aa33-11e9-9fee-63ff2c0c9823.png", | |
| "url" : "https://www.vogueandcode.com/shop/hipster-dev-bro", | |
| "title" : "Hipster Dev", | |
| "description" : "Hipster Dev is busy coding away while styled in a camo jacket and orange beanie." | |
| }, | |
| { | |
| "id" : "vogueandcode-pretty-girls-code-tee", | |
| "maker" : "vogueandcode", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567062-14c4b300-aa33-11e9-9dcd-8bfed4ece810.png", | |
| "url" : "https://www.vogueandcode.com/shop/pretty-girls-code-tee", | |
| "title" : "Pretty Girls Code Tee", | |
| "description" : "Everyone’s favorite design is finally here on a tee! The Pretty Girls Code crew-neck tee is available in a soft pink with red writing." | |
| }, | |
| { | |
| "id" : "vogueandcode-ruby-sis-2", | |
| "maker" : "vogueandcode", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567063-14c4b300-aa33-11e9-8515-bcb866da9ea3.png", | |
| "url" : "https://www.vogueandcode.com/shop/ruby-sis-2", | |
| "title" : "Ruby Sis", | |
| "description" : "Styled in a dashiki, Ruby Sis is listening to music while coding in her favorite language, Ruby!" | |
| }, | |
| { | |
| "id" : "selinazawacki-moon", | |
| "maker" : "selinazawacki", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567057-142c1c80-aa33-11e9-9781-9e442418eaab.png", | |
| "url" : "https://www.instagram.com/p/BFktVYPinKQ/", | |
| "title" : "Holographic Dark Moon Necklace", | |
| "description" : "Not sure if I'll be making more, get it while I have it in the store." | |
| }, | |
| { | |
| "id" : "selinazawacki-shirt", | |
| "maker" : "selinazawacki", | |
| "img" : "https://user-images.githubusercontent.com/41929050/61567058-142c1c80-aa33-11e9-89fb-b4f30d84d69d.png", | |
| "url" : "https://www.instagram.com/p/BEXlpiZCnJ3/", | |
| "title" : "Floppy Crop", | |
| "description" : "Used up the Diskette fabric today to make 2 of these crops." | |
| } | |
| ] |
| [HttpPatch] | |
| public ActionResult Patch([FromBody] RatingRequest request) | |
| { | |
| ProductService.AddRating(request.ProductId, request.Rating); | |
| return Ok(); | |
| } | |
| public class RatingRequest | |
| { | |
| public string ProductId { get; set; } | |
| public int Rating { get; set; } | |
| } |
| using System.Collections.Generic; | |
| using ContosoCrafts.WebSite.Models; | |
| using ContosoCrafts.WebSite.Services; | |
| using Microsoft.AspNetCore.Mvc; | |
| namespace ContosoCrafts.WebSite.Controllers | |
| { | |
| [ApiController] | |
| [Route("[controller]")] | |
| public class ProductsController : ControllerBase | |
| { | |
| public ProductsController(JsonFileProductService productService) | |
| { | |
| ProductService = productService; | |
| } | |
| public JsonFileProductService ProductService { get; } | |
| [HttpGet] | |
| public IEnumerable<Product> Get() | |
| { | |
| return ProductService.GetProducts(); | |
| } | |
| } | |
| } |
| int currentRating = 0; | |
| int voteCount = 0; | |
| string voteLabel; | |
| void GetCurrentRating() | |
| { | |
| if(selectedProduct.Ratings == null) | |
| { | |
| currentRating = 0; | |
| voteCount = 0; | |
| } | |
| else | |
| { | |
| voteCount = selectedProduct.Ratings.Count(); | |
| voteLabel = voteCount > 1 ? "Votes" : "Vote"; | |
| currentRating = selectedProduct.Ratings.Sum() / voteCount; | |
| } | |
| System.Console.WriteLine($"Current rating for {selectedProduct.Id}: {currentRating}"); | |
| } | |
| void SubmitRating(int rating) | |
| { | |
| System.Console.WriteLine($"Rating received for {selectedProduct.Id}: {rating}"); | |
| ProductService.AddRating(selectedProductId, rating); | |
| SelectProduct(selectedProductId); | |
| } |
| <div class="modal-footer"> | |
| @if(voteCount == 0) | |
| { | |
| <span>Be the first to vote!</span> | |
| } | |
| else | |
| { | |
| <span>@voteCount @voteLabel</span> | |
| } | |
| @for(int i=1; i<6; i++) | |
| { | |
| var currentStar = i; | |
| if(i<=currentRating) | |
| { | |
| <span class="fa fa-star checked" @onclick="(e => SubmitRating(currentStar))"></span> | |
| } | |
| else | |
| { | |
| <span class="fa fa-star" @onclick="(e => SubmitRating(currentStar))"></span> | |
| } | |
| } | |
| </div> |
| void SelectProduct(string productId) | |
| { | |
| selectedProductId = productId; | |
| selectedProduct = ProductService.GetProducts().First(x => x.Id == productId); | |
| GetCurrentRating(); | |
| } |
| .checked { | |
| color: orange; | |
| } | |
| .fa-star { | |
| cursor: pointer; | |
| } | |
| /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification | |
| for details on configuring this project to bundle and minify static web assets. */ | |
| .card-columns .card:hover .card-img { | |
| opacity: 1; | |
| } | |
| .card-columns .card-body { | |
| height: 100px; | |
| font-family: 'Nunito', sans-serif; | |
| background: #fbfafd; | |
| } | |
| .card-columns .card-img { | |
| height: 330px; | |
| vertical-align: bottom; | |
| background-position: center; /* Center the image */ | |
| background-repeat: no-repeat; /* Do not repeat the image */ | |
| background-size: cover; /* Resize the background image to cover the entire container */ | |
| opacity: .8; | |
| } | |
| .modal .card-img { | |
| height: 500px; | |
| vertical-align: bottom; | |
| background-position: center; /* Center the image */ | |
| background-repeat: no-repeat; /* Do not repeat the image */ | |
| background-size: cover; /* Resize the background image to cover the entire container */ | |
| } | |
| .card-columns .card:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 10px 20px rgba(37,33,82,.12), 0 4px 8px rgba(37,33,82,.06); | |
| } | |
| a.navbar-brand { | |
| white-space: normal; | |
| font-family: 'Yellowtail', cursive; | |
| font-size: xx-large; | |
| text-align: center; | |
| word-break: break-all; | |
| } | |
| /* Provide sufficient contrast against white background */ | |
| a { | |
| color: #0366d6; | |
| } | |
| .btn-primary { | |
| color: #fff; | |
| background-color: #1b6ec2; | |
| border-color: #1861ac; | |
| } | |
| .nav-pills .nav-link.active, .nav-pills .show > .nav-link { | |
| color: #fff; | |
| background-color: #1b6ec2; | |
| border-color: #1861ac; | |
| } | |
| /* Sticky footer styles | |
| -------------------------------------------------- */ | |
| html { | |
| font-size: 14px; | |
| } | |
| @media (min-width: 768px) { | |
| html { | |
| font-size: 16px; | |
| } | |
| } | |
| .border-top { | |
| border-top: 1px solid #e5e5e5; | |
| } | |
| .border-bottom { | |
| border-bottom: 1px solid #e5e5e5; | |
| } | |
| .box-shadow { | |
| box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); | |
| } | |
| button.accept-policy { | |
| font-size: 1rem; | |
| line-height: inherit; | |
| } | |
| /* Sticky footer styles | |
| -------------------------------------------------- */ | |
| html { | |
| position: relative; | |
| min-height: 100%; | |
| } | |
| body { | |
| /* Margin bottom by footer height */ | |
| margin-bottom: 60px; | |
| } | |
| p { | |
| font-family: 'Nunito', sans-serif; | |
| } | |
| h1 { | |
| font-family: 'Nunito', sans-serif; | |
| } | |
| ul { | |
| font-family: 'Nunito', sans-serif; | |
| } | |
| .footer { | |
| position: absolute; | |
| bottom: 0; | |
| width: 100%; | |
| white-space: nowrap; | |
| line-height: 60px; /* Vertically center the text there */ | |
| font-family: 'Nunito', sans-serif; | |
| } | |
| /*Backgrounds*/ | |
| .bg-navbar { | |
| background: -webkit-linear-gradient(110deg, #e9a8a6 60%, #252152 60%); | |
| background: -o-linear-gradient(110deg, #e9a8a6 60%, #252152 60%); | |
| background: -moz-linear-gradient(110deg, #e9a8a6 60%, #252152 60%); | |
| background: linear-gradient(110deg, #e9a8a6 60%, #252152 60%); | |
| } | |
| .bg-footer { | |
| background: -webkit-linear-gradient(110deg, #252152 60%, #e9a8a6 60%); | |
| background: -o-linear-gradient(110deg, #252152 60%, #e9a8a6 60%); | |
| background: -moz-linear-gradient(110deg, #252152 60%, #e9a8a6 60%); | |
| background: linear-gradient(110deg, #252152 60%, #e9a8a6 60%); | |
| } |
| "args": [ | |
| "watch", | |
| "--project", | |
| "${workspaceFolder}/ContosoCrafts.WebSite/ContosoCrafts.WebSite.csproj", | |
| "run", | |
| "/property:GenerateFullPaths=true", | |
| "/consoleloggerparameters:NoSummary" | |
| ], |
I don't get startup class file in vs 2022, what should i do
You got Program.cs which is equivalent to Startup.cs file. Any doubt don't exited.
I don't get startup class file in vs 2022, what should i do
apparently after .net6 startup is not made by default to add the service shown in the videos you could add "builder.Services.AddTransient();" in the program.cs file instead of "services.AddTransient();" in the startup file
More Info button not working on firefox
I am using Dotnet 8.0 and firefox browser. I have followed along the codes using vscode! However, the more info button is not working at my side. I have searched different sites for solution, but nothing is working. The button is not event firing any events
I was able to solve the modal not popping up. The series was made using Bootstrap 4 or before, so if you're using a newer version, you need to change "data-toggle" and "data-target" to "data-bs-toggle" and "data-bs-target" in ProductList.razor
Source: https://stackoverflow.com/questions/74185184/bootstrap-modal-doesnt-popup-in-razor-pages
where is the code in ### startup.cs
where is the code in ### startup.cs
use card-groups class
or
enclose inside