نسخه با پشتیبانی RTL رو از اینجا دریافت کنید:
پاسخ به بازخوردهای پروژهها
> create-react-app state-management-with-mobx-part4 > cd state-management-with-mobx-part4
> npm install --save bootstrap mobx mobx-react mobx-react-devtools mobx-state-tree
> npm install --save-dev babel-eslint customize-cra eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort react-app-rewired typescript
const products = [ { id: 1, name: "Item 1", price: 850 }, { id: 2, name: "Item 2", price: 900 }, { id: 3, name: "Item 3", price: 1500 }, { id: 4, name: "Item 4", price: 1000 } ]; export default products;
import React from "react"; import products from "../services/productsService"; const ShopItemsList = ({ onAdd }) => { return ( <table className="table table-hover"> <thead className="thead-light"> <tr> <th>Name</th> <th>Price</th> <th>Action</th> </tr> </thead> <tbody> {products.map(product => ( <tr key={product.id}> <td>{product.name}</td> <td>{product.price}</td> <td> <button className="btn btn-sm btn-info" onClick={() => onAdd(product)} > Add </button> </td> </tr> ))} </tbody> </table> ); }; export default ShopItemsList;
import React from "react"; const BasketItemsList = ({ items, totalPrice, onRemove }) => { return ( <> <table className="table table-hover"> <thead className="thead-light"> <tr> <th>Name</th> <th>Price</th> <th>Count</th> <th>Action</th> </tr> </thead> <tbody> {items.map(item => ( <tr key={item.id}> <td>{item.name}</td> <td>{item.price}</td> <td>{item.count}</td> <td> <button className="btn btn-sm btn-danger" onClick={() => onRemove(item.id)} > Remove </button> </td> </tr> ))} <tr> <td align="right"> <strong>Total: </strong> </td> <td> <strong>{totalPrice}</strong> </td> <td></td> <td></td> </tr> </tbody> </table> </> ); }; export default BasketItemsList;
const BasketItemsList = ({ items, totalPrice, onRemove }) => {
import React, { Component } from "react"; class BasketItemsCounter extends Component { render() { const { count, onRemoveAll } = this.props; return ( <div> <h1>Total items: {count}</h1> <button type="button" className="btn btn-sm btn-danger" onClick={() => onRemoveAll()} > Empty Basket </button> </div> ); } } export default BasketItemsCounter;
import React, { Component } from "react"; import BasketItemsCounter from "./components/BasketItemsCounter"; import BasketItemsList from "./components/BasketItemsList"; import ShopItemsList from "./components/ShopItemsList"; class App extends Component { render() { return ( <main className="container"> <div className="row"> <BasketItemsCounter /> </div> <hr /> <div className="row"> <h2>Products</h2> <ShopItemsList /> </div> <div className="row"> <h2>Basket</h2> <BasketItemsList /> </div> </main> ); } } export default App;
import { action, observable } from "mobx"; export default class CounterStore { @observable totalNumbersInBasket = 0; constructor(rootStore) { this.rootStore = rootStore; } @action increase = () => { this.totalNumbersInBasket++; }; @action decrease = () => { this.totalNumbersInBasket--; }; }
import { action, computed, observable } from "mobx"; export default class MarketStore { @observable basketItems = []; constructor(rootStore) { this.rootStore = rootStore; } @action add = product => { const selectedItem = this.basketItems.find(item => item.id === product.id); if (selectedItem) { selectedItem.count++; } else { this.basketItems.push({ ...product, count: 1 }); } this.rootStore.counterStore.increase(); }; @action remove = id => { const selectedItem = this.basketItems.find(item => item.id === id); selectedItem.count--; if (selectedItem.count === 0) { this.basketItems.remove(selectedItem); } this.rootStore.counterStore.decrease(); }; @action removeAll = () => { this.basketItems = []; this.rootStore.counterStore.totalNumbersInBasket = 0; }; @computed get totalPrice() { return this.basketItems.reduce((previous, current) => { return previous + current.price * current.count; }, 0); } }
import CounterStore from "./counter"; import MarketStore from "./market"; class RootStore { counterStore = new CounterStore(this); marketStore = new MarketStore(this); } export default RootStore;
export default class MarketStore { @observable basketItems = []; constructor(rootStore) { this.rootStore = rootStore; } @action removeAll = () => { this.basketItems = []; this.rootStore.counterStore.totalNumbersInBasket = 0; }; }
import "./index.css"; import "bootstrap/dist/css/bootstrap.css"; import makeInspectable from "mobx-devtools-mst"; import { Provider } from "mobx-react"; import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; import * as serviceWorker from "./serviceWorker"; import RootStore from "./stores"; const rootStore = new RootStore(); if (process.env.NODE_ENV === "development") { makeInspectable(rootStore); // https://github.com/mobxjs/mobx-devtools } ReactDOM.render( <Provider {...rootStore}> <App /> </Provider>, document.getElementById("root") ); serviceWorker.unregister();
<Provider counterStore={rootStore.counterStore} marketStore={rootStore.marketStore}>
import { inject, observer } from "mobx-react"; import React from "react"; import products from "../services/productsService"; const ShopItemsList = ({ onAdd }) => { return ( // ... ); }; export default inject(({ marketStore }) => ({ onAdd: marketStore.add }))(observer(ShopItemsList));
import { inject, observer } from "mobx-react"; import React from "react"; const BasketItemsList = ({ items, totalPrice, onRemove }) => { return ( // ... ); }; export default inject(({ marketStore }) => ({ items: marketStore.basketItems, totalPrice: marketStore.totalPrice, onRemove: marketStore.remove }))(observer(BasketItemsList));
import { inject, observer } from "mobx-react"; import React, { Component } from "react"; @inject(rootStore => ({ count: rootStore.counterStore.totalNumbersInBasket, onRemoveAll: rootStore.marketStore.removeAll })) @observer class BasketItemsCounter extends Component { render() { const { count, onRemoveAll } = this.props; return ( // ... ); } } export default BasketItemsCounter;
using System; using System.Collections.Generic; namespace MaterialAspNetCoreBackend.DomainClasses { public class User { public User() { UserNotes = new HashSet<UserNote>(); } public int Id { set; get; } public DateTimeOffset BirthDate { set; get; } public string Name { set; get; } public string Avatar { set; get; } public string Bio { set; get; } public ICollection<UserNote> UserNotes { set; get; } } }
using System; namespace MaterialAspNetCoreBackend.DomainClasses { public class UserNote { public int Id { set; get; } public DateTimeOffset Date { set; get; } public string Title { set; get; } public User User { set; get; } public int UserId { set; get; } } }
public interface IUsersService { Task<List<User>> GetAllUsersIncludeNotesAsync(); Task<User> GetUserIncludeNotesAsync(int id); }
namespace MaterialAspNetCoreBackend.WebApp.Controllers { [Route("api/[controller]")] public class UsersController : Controller { private readonly IUsersService _usersService; public UsersController(IUsersService usersService) { _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); } [HttpGet] public async Task<IActionResult> Get() { return Ok(await _usersService.GetAllUsersIncludeNotesAsync()); } [HttpGet("{id:int}")] public async Task<IActionResult> Get(int id) { return Ok(await _usersService.GetUserIncludeNotesAsync(id)); } } }
https://localhost:5001/api/users
"build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "../MaterialAspNetCoreBackend/MaterialAspNetCoreBackend.WebApp/wwwroot",
ng build --no-delete-output-path --watch
dotnet watch run
ng g i contact-manager/models/user ng g i contact-manager/models/user-note
export interface UserNote { id: number; title: string; date: Date; userId: number; }
import { UserNote } from "./user-note"; export interface User { id: number; birthDate: Date; name: string; avatar: string; bio: string; userNotes: UserNote[]; }
ng g s contact-manager/services/user --no-spec
import { Injectable } from "@angular/core"; @Injectable({ providedIn: "root" }) export class UserService { constructor() { } }
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { catchError, map } from "rxjs/operators"; import { User } from "../models/user"; @Injectable({ providedIn: "root" }) export class UserService { constructor(private http: HttpClient) { } getAllUsersIncludeNotes(): Observable<User[]> { return this.http .get<User[]>("/api/users").pipe( map(response => response || []), catchError((error: HttpErrorResponse) => throwError(error)) ); } getUserIncludeNotes(id: number): Observable<User> { return this.http .get<User>(`/api/users/${id}`).pipe( map(response => response || {} as User), catchError((error: HttpErrorResponse) => throwError(error)) ); } }
<?xml version="1.0" encoding="utf-8"?> <svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <svg viewBox="0 0 128 128" height="100%" width="100%" pointer-events="none" display="block" id="user1" >
import { Component } from "@angular/core"; import { MatIconRegistry } from "@angular/material"; import { DomSanitizer } from "@angular/platform-browser"; @Component() export class ContactManagerAppComponent { constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) { iconRegistry.addSvgIconSet(sanitizer.bypassSecurityTrustResourceUrl("assets/avatars.svg")); } }
iconRegistry.addSvgIcon( "unicorn", this.domSanitizer.bypassSecurityTrustResourceUrl("assets/unicorn_icon.svg") );
<mat-icon svgIcon="unicorn"></mat-icon>
import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component() export class SidenavComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) { } ngOnInit() { this.userService.getAllUsersIncludeNotes() .subscribe(data => this.users = data); } }
<mat-list> <mat-list-item>Item 1</mat-list-item> <mat-list-item>Item 2</mat-list-item> <mat-list-item>Item 3</mat-list-item> </mat-list>
<mat-nav-list> <mat-list-item *ngFor="let user of users"> <a matLine href="#"> <mat-icon svgIcon="{{user.avatar}}"></mat-icon> {{ user.name }} </a> </mat-list-item> </mat-nav-list>
<mat-nav-list> <mat-list-item *ngFor="let user of users"> <a matLine [routerLink]="['/contactmanager', user.id]"> <mat-icon svgIcon="{{user.avatar}}"></mat-icon> {{ user.name }} </a> </mat-list-item> </mat-nav-list>
const routes: Routes = [ { path: "", component: ContactManagerAppComponent, children: [ { path: ":id", component: MainContentComponent }, { path: "", component: MainContentComponent } ] }, { path: "**", redirectTo: "" } ];
import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component({ selector: "app-main-content", templateUrl: "./main-content.component.html", styleUrls: ["./main-content.component.css"] }) export class MainContentComponent implements OnInit { user: User; constructor(private route: ActivatedRoute, private userService: UserService) { } ngOnInit() { this.route.params.subscribe(params => { this.user = null; const id = params["id"]; if (!id) { return; } this.userService.getUserIncludeNotes(id) .subscribe(data => this.user = data); }); } }
<div *ngIf="!user"> <mat-spinner></mat-spinner> </div> <div *ngIf="user"> <mat-card> <mat-card-header> <mat-icon mat-card-avatar svgIcon="{{user.avatar}}"></mat-icon> <mat-card-title> <h2>{{ user.name }}</h2> </mat-card-title> <mat-card-subtitle> Birthday {{ user.birthDate | date:'d LLLL' }} </mat-card-subtitle> </mat-card-header> <mat-card-content> <mat-tab-group> <mat-tab label="Bio"> <p> {{user.bio}} </p> </mat-tab> <!-- <mat-tab label="Notes"></mat-tab> --> </mat-tab-group> </mat-card-content> </mat-card> </div>
import { Router } from "@angular/router"; @Component() export class SidenavComponent implements OnInit, OnDestroy { users: User[] = []; constructor(private userService: UserService, private router: Router) { } ngOnInit() { this.userService.getAllUsersIncludeNotes() .subscribe(data => { this.users = data; if (data && data.length > 0 && !this.router.navigated) { this.router.navigate(["/contactmanager", data[0].id]); } }); } }
let id = params['id']; if (!id) id = 1;
<mat-sidenav #sidenav
import { MatSidenav } from "@angular/material"; @Component() export class SidenavComponent implements OnInit, OnDestroy { @ViewChild(MatSidenav) sidenav: MatSidenav;
ngOnInit() { this.router.events.subscribe(() => { if (this.isScreenSmall) { this.sidenav.close(); } }); }
var student=context.Students.FirstOrDefault();
var student=context.Students.LastOrDefault();
var student=context.Students.ToList().LastOrDefault();
var student=context.Students.OrderByDescending(s=>s.Id).FirstOrDefault();
private static string GetScript() { string path = AppDomain.CurrentDomain.BaseDirectory + @"Scripts\script.sql"; var file = new FileInfo(path); string script = file.OpenText().ReadToEnd(); return script; } private static void ExecuteScript() { string script = GetScript(); //split the script on "GO" commands var splitter = new[] {"\r\nGO\r\n"}; string[] commandTexts = script.Split(splitter, StringSplitOptions.RemoveEmptyEntries); foreach (string commandText in commandTexts) { using (var ctx = new DbContext()) { if (!string.IsNullOrEmpty(commandText)) { ctx.Database.ExecuteSqlCommand(commandText); } } } }
GenericArguments[0], 'DataLayer.TestContext', on 'System.Data.Entity.IDatabaseInitializer`1[TContext]' violates the constraint of type parameter 'TContext'.
<contexts> <context type=" DataLayer.TestContext, DataLayer"> <databaseInitializer type="System.Data.Entity.DropCreateDatabaseAlways`1[ [DataLayer.TestContext, DataLayer]], EntityFramework" /> </context> </contexts>
An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately
PM> Install-Package Microsoft.AspNet.SignalR
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace SignalR02 { [HubName("chat")] public class ChatHub : Hub { public void SendMessage(string message) { Clients.All.hello(message); } } }
using System; using System.Web; using System.Web.Routing; namespace SignalR02 { public class Global : HttpApplication { protected void Application_Start(object sender, EventArgs e) { // Register the default hubs route: ~/signalr RouteTable.Routes.MapHubs(); } } }
http://localhost/signalr/hubs
proxies.chat = this.createHubProxy('chat');
Web socket -> SSE -> Forever frame -> long polling
C: cursor M: Messages H: Hub name M: Method name A: Method args T: Time out D: Disconnect
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace SignalR02 { [HubName("chat")] public class ChatHub : Hub { public void SendMessage(string message) { var msg = string.Format("{0}:{1}", Context.ConnectionId, message); Clients.All.hello(msg); } } }
//این دو عبارت هر دو یکی هستند Clients.Caller.hello(msg); Clients.Client(Context.ConnectionId).hello(msg);
Clients.Others.hello(msg);
public void JoinRoom(string room) { this.Groups.Add(Context.ConnectionId, room); } public void SendMessageToRoom(string room, string msg) { this.Clients.Group(room).hello(msg); }
public abstract class Hub : IHub, IDisposable { protected Hub(); public HubConnectionContext Clients { get; set; } public HubCallerContext Context { get; set; } public IGroupManager Groups { get; set; } public void Dispose(); protected virtual void Dispose(bool disposing); public virtual Task OnConnected(); public virtual Task OnDisconnected(); public virtual Task OnReconnected(); }
public override System.Threading.Tasks.Task OnDisconnected() { sendMonitorData("OnDisconnected", Context.ConnectionId); return base.OnDisconnected(); } private void sendMonitorData(string type, string connection) { var ctx = GlobalHost.ConnectionManager.GetHubContext<MonitorHub>(); ctx.Clients.All.newEvenet(type, connection); }
• Which API are you using? – Google+ API • Where will you be calling the API from? – Web server (e.g. node.js, Tomcat) • What data will you be accessing? – User data
https://localhost:6001/signin-google
{ "Authentication": { "Google": { "ClientId": "xxxx", "ClientSecret": "xxxx" } } }
namespace DNT.IDP { public class Startup { public void ConfigureServices(IServiceCollection services) { // ... services.AddAuthentication() .AddGoogle(authenticationScheme: "Google", configureOptions: options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = Configuration["Authentication:Google:ClientId"]; options.ClientSecret = Configuration["Authentication:Google:ClientSecret"]; }); }
public const string ExternalCookieAuthenticationScheme = "idsrv.external";
namespace DNT.IDP.Controllers.Account { [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { public async Task<IActionResult> Callback() { var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; var (user, provider, providerUserId, claims) = await FindUserFromExternalProvider(result); if (user == null) { // user = AutoProvisionUser(provider, providerUserId, claims); var returnUrlAfterRegistration = Url.Action("Callback", new { returnUrl = returnUrl }); var continueWithUrl = Url.Action("RegisterUser", "UserRegistration" , new { returnUrl = returnUrlAfterRegistration, provider = provider, providerUserId = providerUserId }); return Redirect(continueWithUrl); }
var (user, provider, providerUserId, claims) = await FindUserFromExternalProvider(result); foreach (var claim in claims) { _logger.LogInformation($"External provider[{provider}] info-> claim:{claim.Type}, value:{claim.Value}"); }
External provider[Google] info-> claim:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, value:Vahid N. External provider[Google] info-> claim:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname, value:Vahid External provider[Google] info-> claim:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname, value:N. External provider[Google] info-> claim:urn:google:profile, value:https://plus.google.com/105013528531611201860 External provider[Google] info-> claim:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, value:my.name@gmail.com
[HttpGet] public async Task<IActionResult> Callback() { // ... var (user, provider, providerUserId, claims) = await FindUserFromExternalProvider(result); if (user == null) { // user wasn't found by provider, but maybe one exists with the same email address? if (provider == "Google") { // email claim from Google var email = claims.FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"); if (email != null) { var userByEmail = await _usersService.GetUserByEmailAsync(email.Value); if (userByEmail != null) { // add Google as a provider for this user await _usersService.AddUserLoginAsync(userByEmail.SubjectId, provider, providerUserId); // redirect to ExternalLoginCallback var continueWithUrlAfterAddingUserLogin = Url.Action("Callback", new {returnUrl = returnUrl}); return Redirect(continueWithUrlAfterAddingUserLogin); } } } var returnUrlAfterRegistration = Url.Action("Callback", new {returnUrl = returnUrl}); var continueWithUrl = Url.Action("RegisterUser", "UserRegistration", new {returnUrl = returnUrlAfterRegistration, provider = provider, providerUserId = providerUserId}); return Redirect(continueWithUrl); }
dotnet new web
dotnet add package IdentityServer4
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddIdentityServer() .AddDeveloperSigningCredential(); }
using System.Collections.Generic; using System.Security.Claims; using IdentityServer4.Models; using IdentityServer4.Test; namespace DNT.IDP { public static class Config { // test users public static List<TestUser> GetUsers() { return new List<TestUser> { new TestUser { SubjectId = "d860efca-22d9-47fd-8249-791ba61b07c7", Username = "User 1", Password = "password", Claims = new List<Claim> { new Claim("given_name", "Vahid"), new Claim("family_name", "N"), } }, new TestUser { SubjectId = "b7539694-97e7-4dfe-84da-b4256e1ff5c7", Username = "User 2", Password = "password", Claims = new List<Claim> { new Claim("given_name", "User 2"), new Claim("family_name", "Test"), } } }; } // identity-related resources (scopes) public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } public static IEnumerable<Client> GetClients() { return new List<Client>(); } } }
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddTestUsers(Config.GetUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients()); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
https://localhost:6001/.well-known/openid-configuration
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))
\curl -L https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.sh | bash