Code Asp Core 5 mit React: Speichern in API mit
Beispiel Code für Asp Core und React.
Asp Core Server Code
Der Code enthält den Asp Core Code für die Startup.cs und den Controller. Wichtig ist mir dabei, dass der Code für die Authorization und ValitdateAntiforgery in den API übernommen werden.
React Code
Der React Code zeigt wie die Daten vom Server über eine API abgeholt werden und wieder gespeichert werden.
React Edit Page
React Edit Page
Asp.Net Core Net5
Wichtig ist hierbei, dass die Schreibvorgänge im Server durch Authorize und ValidateAntiForgery geschützt sind.
[HttpPut("Save/{id}")] //*ok [Authorize] [ValidateAntiForgeryToken] public async Task<ActionResult<ApiArticleModel>> Save(int id, ApiArticleModel apiArticle) { .. |
React Speichern
Gesendet werden die Daten von einer React Seite über ein Api. Hier wird ebenfalls der Sicherheitstoken angebunden
async send_Data_to_Api() { //------< send_Data_to_Api() >------ console.log(this.state); //< get text > let element = document.getElementById('ctleditor_html'); let editor_innerhtml = element.innerHTML; let text_of_htmleditor = element.innerText; let xsrfToken = this.getCookie('XSRF-TOKEN'); //*get AntiforgeryToken from Cookies if (xsrfToken == null) { alert("AntiforgeryToken is missing")}; //</ get text >
//--< prepare send >-- const token = await authService.getAccessToken(); const requestOptions = { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, 'X-XSRF-TOKEN': xsrfToken //*add antiforgerytoken in from Cookies into Header }, body: JSON.stringify( { //< data to send > idarticle: this.state.idarticle, iduser: this.state.iduser, title: this.state.title, htmlcontent: editor_innerhtml, textcontent: text_of_htmleditor, folder: this.state.folder, keywords: this.state.keywords, //</ data to send > }) }; //--</ prepare send >-- //< send > const response = await fetch('api/articles/save/' + this.state.idarticle, requestOptions); //*SEND DATA to save-API //</ send > |
Validation Token in Startup.cs Asp Core
ValidationToken Platzhalter.
Mit services.AddAntiforgery wird ein Header vorbereitet, wenn eine Seite geöffnet wird
//*AntiforgeryToken // Angular's default header name for sending the XSRF token. services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; }); |
Validation Token erzeugen
Der AntiforgeryToken wird bei allen Seiten als Cookie erzeugt, welche in notwendig sind zur Sicherung beim Server.
//--< AntiForgery >-- //*add in Configure(.. , IAntiforgery antiforgery) app.Use(next => context => { string path = context.Request.Path.Value; string[] urlAreas = { "/api", "/swagger", "articles" }; if ( string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) || string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase) || urlAreas.Any(urlAreas=>path.StartsWith(urlAreas)) ) { // The request token can be sent as a JavaScript-readable cookie, // and Angular uses it by default. var tokens = antiforgery.GetAndStoreTokens(context); context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false , Secure=false, IsEssential=true, SameSite=SameSiteMode.Strict }); }
return next(context); }); //--</ AntiForgery >-- |
Routing
Dabei wird das Asp Core Routing mit Endpoint Pattern verwendet.
Wenn der ValidationToken falsch ist, kommt die Seite 400 zurück
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "🏠", pattern: "🏠", defaults: new { controller = "Articles", action = "List" } ); endpoints.MapControllerRoute( name: "📜", pattern: "📜", defaults: new { controller = "Articles", action = "List" } );
endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action}/{id?}" //Important! Add your [httpget("action/{id?}")] in controller ); endpoints.MapRazorPages(); });
app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } }); |
Example Code
Asp Core .Net5 und React
Asp Core Startup.cs in .Net5
using CodeDocu.Data; using CodeDocu.Models; using CodeDocu.Services; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Linq; //*any
namespace CodeDocu { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } //Interface
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //--------< ConfigureServices() >-------- services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); //Server=.\\sqlexpress;Database=codedocu_de;Trusted_Connection=True;MultipleActiveResultSets=true
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
//*AntiforgeryToken // Angular's default header name for sending the XSRF token. services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; //options.Cookie.Name = "X-XSRF-TOKEN"; //"X-CSRF-TOKEN-OurAppName"; // Set Cookie properties using CookieBuilder properties†. //options.FormFieldName = "AntiforgeryFieldname"; //options.HeaderName = "X-CSRF-TOKEN-HEADERNAME"; //options.SuppressXFrameOptionsHeader = false; });
services.AddAuthentication() .AddIdentityServerJwt();
services.AddControllersWithViews();
//-< Email Registration > services.AddTransient<IEmailSender, EmailSender>(); services.Configure<AuthMessageSenderOptions>(Configuration); //-</ Email Registration >
services.AddRazorPages();
// In production, the React files will be served from this directory services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; });
//--------</ ConfigureServices() >-------- }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. //*Added Antiforgery: , IAntiforgery antiforgery public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery) { //--------< Configure() >-------- if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); }
app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication(); app.UseIdentityServer(); app.UseAuthorization();
//--< AntiForgery >-- //*add in Configure(.. , IAntiforgery antiforgery) app.Use(next => context => { string path = context.Request.Path.Value; string[] urlAreas = { "/api", "/swagger", "articles" }; if ( string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) || string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase) || urlAreas.Any(urlAreas=>path.StartsWith(urlAreas)) ) { // The request token can be sent as a JavaScript-readable cookie, // and Angular uses it by default. var tokens = antiforgery.GetAndStoreTokens(context); context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false , Secure=false, IsEssential=true, SameSite=SameSiteMode.Strict }); }
return next(context); }); //--</ AntiForgery >--
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "🏠", pattern: "🏠", defaults: new { controller = "Articles", action = "List" } ); endpoints.MapControllerRoute( name: "📜", pattern: "📜", defaults: new { controller = "Articles", action = "List" } );
endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action}/{id?}" //Important! Add your [httpget("action/{id?}")] in controller ); endpoints.MapRazorPages(); });
app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } });
//--------</ Configure() >-------- } } }
|
Asp Core ArticlesController.cs .Net5
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using CodeDocu.Models;
namespace CodeDocu.Controllers { //------------< Namespace >------------
[Route("api/articles")] [ApiController] public class ArticlesController : ControllerBase { //--------< Controller: ArticlesController >-------- #region init private readonly Data.ApplicationDbContext _dbContext;
public ArticlesController(Data.ApplicationDbContext context) { _dbContext = context; //*get database context } #endregion /init
// GET: api/Articles ============ [Route("List")] [HttpGet] public async Task<ActionResult<IEnumerable<ApiArticleListModel>>> List() { //-------------< Liste: GetArticles >------------- //< get Data from SQL-Server > var query = new List<ArticleModel>(); try { var data = _dbContext.tbl_Articles.Take(10); query = await data.ToListAsync(); } catch (Exception err) { return BadRequest(err.Message); }
//</ get Data from SQL-Server >
//----< fill Data_to_Output >----📜📜📜 List<ApiArticleListModel> dataList = new List<ApiArticleListModel>(); //---< @Loop: Rows >--- foreach (var row in query) { //--< Row to Data >-- //< correct > string sShort = row.TextContent; if (sShort.Length > 255) { sShort = sShort.Substring(0, 255); }
row.TextContent = sShort; //</ correct >
//< Data > ApiArticleListModel item = new ApiArticleListModel(); item.idarticle = row.IDArticle; item.title = row.Title; item.textcontent = sShort; item.folder = row.Folder; item.imagepath = "/User_Files/Articles/Images/Image_144_0_pad.jpg"; //*144 " + item.IDArticle + " //</ Data >
//< add > dataList.Add(item); //</ add > //--</ Row to Data >-- } //---</ @Loop: Rows >--- //----</ fill Data_to_Output >----
return dataList;
//-------------</ Liste: GetArticles >------------- }
// GET: api/Articles/5 ============👁👁👁 // EventsController [HttpGet("Read/{id}")] public async Task<ActionResult<ApiArticleModel>> Read(long? id) { //-------------< Liste: GetArticles >------------- if (!(id > 0)) { return NotFound(); }
long IDArticle = System.Convert.ToInt64(id);
//--< Get User ID >-- //internal referenz-Number for tracking in tables String sIDUser = Request.Headers["sub"]; //*ID in aspUsers wie: 1428ca0b-186c.. //--</ Get User ID >--
//--< Get Data >-- var article = await _dbContext.tbl_Articles.SingleOrDefaultAsync(article => article.IDArticle == IDArticle); if (article == null) { return NotFound(); //:break } //--</ Get Data >--
//----< fill Data_to_Output >---- //< Data > ApiArticleModel apiArticle = new ApiArticleModel(); apiArticle.idarticle = article.IDArticle; if (sIDUser == article.IDUser) { apiArticle.isowner = true; } else { apiArticle.isowner = false; } apiArticle.iduser = article.IDUser; apiArticle.title = article.Title; apiArticle.htmlcontent = article.HtmlContent; apiArticle.folder = article.Folder; apiArticle.keywords = article.Keywords; apiArticle.imagepath = "/User_Files/Articles/Images/Image_144_0_pad.jpg"; //*144 " + item.IDArticle + " //</ Data > //----</ fill Data_to_Output >----
return apiArticle; //-------------</ Liste: GetArticles >------------- }
// PUT: api/tbl_Articles/5 ============== ✍✍✍ [HttpPut("Save/{id}")] //*ok //[HttpPut("{id}")] //*ok [Authorize] [ValidateAntiForgeryToken] public async Task<ActionResult<ApiArticleModel>> Save(int id, ApiArticleModel apiArticle) { //int IDArticle = id; var test = Request.Path; // -------------< Edit_Postback() > ------------- if (id != apiArticle.idarticle) { return BadRequest("Bad"); }
//--< Get User ID >-- //internal referenz-Number for tracking in tables String sIDUser = User.getUserId(); //*ID in aspUsers wie: 1428ca0b-186c.. if (sIDUser == "") return BadRequest(); //--</ Get User ID >--
//< get_database > ArticleModel article; if (id > 0) { article = await _dbContext.tbl_Articles.SingleOrDefaultAsync(a => a.IDArticle == id); //#changed to async 09.02.2021 if (article== null) { return Content("No Record found for ID=" + id); }
//< check Owner > String IDOwner = article.IDUser; if (IDOwner != sIDUser) { return StatusCode(StatusCodes.Status403Forbidden,"user is not owner."); } //</ check Owner > } else { article = new ArticleModel(); article.IDUser = sIDUser; } //</ get_database >
//----< Save Note-Data >---- //< get > string sHTML = apiArticle.htmlcontent; string sTitle = apiArticle.title; string sFolder = apiArticle.folder; string sKeywords = apiArticle.keywords; //</ get >
//< correct > //*SQL injection, script blocks.. sTitle = Correct_Methods.correct_In_String_Strong(sTitle); sFolder = Correct_Methods.correct_In_String_Strong(sFolder); sKeywords = Correct_Methods.correct_In_String_Strong(sKeywords); sHTML = Correct_Methods.correct_In_String_Minimal(sHTML); //</ correct >
//< Convert > sHTML = Correct_Methods.replace_Text_To_Links(sHTML); sHTML = Correct_Methods.replace_Youtube(sHTML);
string sText = Html_Methods.HTML_to_Text(sHTML); //</ Convert >
//--< data >-- //article.IDUser = *oben gesetzt article.Title = sTitle; article.HtmlContent = sHTML; article.TextContent = sText; article.Folder = sFolder; article.Keywords = sKeywords; apiArticle.isowner = true; article.DtEdit = DateTime.Now; //note.IsDraft = false; //try allways on //--</ data >--
try { if (id > 0) { //< update Server > _dbContext.Update(article); //</ update Server > } else { //< Add on Server > _dbContext.tbl_Articles.Add(article); _dbContext.SaveChanges(); id = article.IDArticle; //</ Add on Server > } //await _dbContext.SaveChangesAsync(true); } catch (DbUpdateConcurrencyException) { return Content("Error in saving Note with ID=" + id); } //----</ Save Note-Data >----
//----< Delete Images not in HTML >---- //List<Note_Image_Model> list_Images = _dbContext.tbl_Notes_Images.Where(img => img.IDNote == IDNote).ToList(); //foreach (Note_Image_Model image in list_Images) //{ // string sImage_BaseTag = "Image_" + IDNote + "_" + image.ImageNr; // if (sHTML.IndexOf(sImage_BaseTag) < 0) // {
// //< delete Image_sized > // //--< Image >-- // string folder_Path_Images = _hostingEnvironment.WebRootPath + "\\User_Files\\Notes\\Images\\"; // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_mini.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_pad.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_blog.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + ".jpg"); // //</ delete Image_sized >
// _dbContext.tbl_Notes_Images.Remove(image); // } //} //----</ Delete Images not in HTML >----
//< save note+images > await _dbContext.SaveChangesAsync(true); //</ save note+images >
//< load Output-Data > //ApiArticleModel apiData = new ApiArticleModel(); apiArticle.idarticle = article.IDArticle; apiArticle.iduser = sIDUser; //#todo: check apiArticle.title = article.Title; apiArticle.htmlcontent = article.HtmlContent; apiArticle.folder = article.Folder; apiArticle.keywords = article.Keywords; apiArticle.dtcreated = article.DtCreated; apiArticle.dtedit = article.DtEdit; //</ load Output-Data > // -------------</ Edit_Postback() > -------------
return apiArticle; }
[HttpGet("images/{id}")] public async Task<ActionResult<ApiArticleModel>> Images(long id) { Console.WriteLine("Images " + Request.Path); return new JsonResult(new { route = "Images" }); }
//----< Delete Images not in HTML >---- //List<Note_Image_Model> list_Images = _dbContext.tbl_Notes_Images.Where(img => img.IDNote == IDNote).ToList(); //foreach (Note_Image_Model image in list_Images) //{ // string sImage_BaseTag = "Image_" + IDNote + "_" + image.ImageNr; // if (sHTML.IndexOf(sImage_BaseTag) < 0) // {
// //< delete Image_sized > // //--< Image >-- // string folder_Path_Images = _hostingEnvironment.WebRootPath + "\\User_Files\\Notes\\Images\\"; // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_mini.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_pad.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + "_blog.jpg"); // File_Methods.Delete_File(folder_Path_Images + "\\" + sImage_BaseTag + ".jpg"); // //</ delete Image_sized >
// _dbContext.tbl_Notes_Images.Remove(image); // } //} //----</ Delete Images not in HTML >----
//< save note+images > await _dbContext.SaveChangesAsync(true); //</ save note+images >
//< load Output-Data > //ApiArticleModel apiData = new ApiArticleModel(); apiArticle.idarticle = article.IDArticle; apiArticle.iduser = sIDUser; //#todo: check apiArticle.title = article.Title; apiArticle.htmlcontent = article.HtmlContent; apiArticle.folder = article.Folder; apiArticle.keywords = article.Keywords; apiArticle.dtcreated = article.DtCreated; apiArticle.dtedit = article.DtEdit; //</ load Output-Data > // -------------</ Edit_Postback() > -------------
return apiArticle; }
[HttpGet("images/{id}")] public async Task<ActionResult<ApiArticleModel>> Images(long id) { Console.WriteLine("Images " + Request.Path); return new JsonResult(new { route = "Images" }); }
// DELETE: api/tbl_Articles/5 [HttpDelete] //[HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { var tblContent = await _dbContext.tbl_Articles.FindAsync(id); if (tblContent == null) { return NotFound(); }
_dbContext.tbl_Articles.Remove(tblContent); await _dbContext.SaveChangesAsync();
return NoContent(); }
private bool TblContentExists(int id) { return _dbContext.tbl_Articles.Any(e => e.IDArticle == id); } //----------</ Controller: ArticlesController >-------- }
//------------</ Namespace >------------ }
|
React Page: Edit.js
import React, { Component } from 'react'; import authService from './api-authorization/AuthorizeService' import './Edit.css'; import "bootstrap/dist/css/bootstrap.min.css"; import Fab from '@material-ui/core/Fab'; import RemoveRedEye from '@material-ui/icons/RemoveRedEye'; import TextField from '@material-ui/core/TextField'; import { Link } from 'react-router-dom';
export class Edit extends Component { //--------< component: Article >--------
//----< compontent >---- baseURL = "/"; id = 0;
constructor(props) { //-< react: parameter > super(props); //-</ react: parameter > //--< react: variables >-- this.state = { idarticle: 0, iduser: "", isowner: false, title: "", textcontent: "", htmlcontent: "", folder: "", keywords: "", nimages: "", nvideos: "", nfiles: "", dtcreated: "", dtedit: "", loading: true, status: "", ok: true,
token: "", }
//this.onChangeEditor = this.onChangeEditor.bind(this); this.onClickSubmit = this.onClickSubmit.bind(this); //for using this.function() in component }
//--< component.events >-- async componentDidMount() { this.id = this.props.match.params.id; if (this.id > 0) { this.get_Data_from_Api(this.id); } else { this.setState({ loading: false }); } } //--</ component.events >--
//onChangeEditor() { // var editor_innerHtml = document.getElementById("ctleditor_html").innerHTML; // this.setState(state => ({ // editor_innerhtml: editor_innerHtml // })); //}
//----</ compontent >----
//====< functions:Api >==== onClickSubmit(event) { this.send_Data_to_Api(); //window.location.href = '...'; //with history window.location.replace('/👁/' + this.state.idarticle); //*no back history } // using jQuery getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; }
//*load data from web api async get_Data_from_Api(id) { //--------< populateData() >-------- const user = await authService.getUser(); const token = await authService.getAccessToken(); const response = await fetch('api/articles/read/' + id, { method: 'get', headers: !token ? {} : { 'Authorization': `Bearer ${token}`, 'sub': user.sub } }); const data = await response.json(); //*convert json to data //console.log("this.state=" + this.state); //console.log(this.state); if (data.isowner !== true) { this.props.history.goBack(); };
this.setState(state => ({ idarticle: data.idarticle, iduser: data.iduser, isowner: data.isowner, title: data.title, htmlcontent: data.htmlcontent, folder: data.folder, keywords: data.keywords, nimages: data.nimages, nvideos: data.nvideos, nfiles: data.nfiles, dtcreated: data.dtcreated, dtedit: data.dtedit,
loading: false, })); //*refresh state of arcticles //--------</ populateData() >-------- }
async send_Data_to_Api() { //------< send_Data_to_Api() >------ console.log(this.state); //< get text > let element = document.getElementById('ctleditor_html'); let editor_innerhtml = element.innerHTML; let text_of_htmleditor = element.innerText; let xsrfToken = this.getCookie('XSRF-TOKEN'); //*get AntiforgeryToken from Cookies if (xsrfToken == null) { alert("AntiforgeryToken is missing")}; //</ get text >
//--< prepare send >-- const token = await authService.getAccessToken(); const requestOptions = { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, 'X-XSRF-TOKEN': xsrfToken //*add antiforgerytoken in from Cookies into Header }, body: JSON.stringify( { //< data to send > idarticle: this.state.idarticle, iduser: this.state.iduser, title: this.state.title, htmlcontent: editor_innerhtml, textcontent: text_of_htmleditor, folder: this.state.folder, keywords: this.state.keywords, //</ data to send > }) }; //--</ prepare send >-- //< send > const response = await fetch('api/articles/save/' + this.state.idarticle, requestOptions); //*SEND DATA to save-API //</ send > if (response.status !== 200) { //-< error: sending >- if (response.status === 400) alert("400 Bad Data Request"); else if (response.status === 401) alert("401 Unauthorized"); else if (response.status === 403) alert("403 User is not Owner"); else alert('send error ' + response.status); //-</ error: sending >- } else { //-< ok: sending result >- const data = await response.json(); //*refresh return values this.setState({ idarticle: data.idArticle, iduser: data.idUser, title: data.title, htmlcontent: data.htmlcontent, folder: data.folder, keywords: data.keywords,
loading: false, status: "data ok" }); //-</ ok: sending result >- } //------</ send_Data_to_Api() >------ } //====</ functions:Api >====
//====< HTML >==== //----< render >---- render() { //--------< render(HTML) >--------
document.title = "✍ " + this.state.title;
return (
//----< return >---- <div className="submit-form"> { //--< IsLoaded >-- <form className="submit-form"> <div>
{ this.state.loading===false && ( //--< Buttons >-- //#to={"/👁/" + this.state.idarticle} <Link onClick={this.onClickSubmit}> <Fab color="secondary" aria-label="save and show" style={{ float: 'right' }} > <RemoveRedEye /> </Fab> </Link> //--</ Buttons >-- ) }
<div id="divHeadFields"> <div style={{ flexFlow: "row" }}> <TextField id="ctlfolder" value={this.state.folder} label="Folder" type="text" placeholder="software/subject/.." autoComplete="true" color="primary" InputLabelProps={{ shrink: true, }} onChange={(e) => { this.setState({ folder: e.target.value }) }} style={{ width: '45%', marginRight: '8px' }} /> <TextField id="ctlkeywords" value={this.state.keywords} label="Keywords" type="text" placeholder="keyword;part1 part2.." autoComplete="true" color="primary" InputLabelProps={{ shrink: true, }} onChange={(e) => { this.setState({ keywords: e.target.value }) }} style={{ width: '45%' }} /> </div>
<TextField id="ctltitle" label="Title" type="text" placeholder="Title" autoComplete="true" required fullWidth color="primary" InputLabelProps={{ shrink: true, }} value={this.state.title} onChange={(e) => { console.log(e.target.value); this.setState({ title: e.target.value }) }} style={{ width: '100%', marginBotton: '10px', }} /> </div>
<div id="ctleditor_html" className="edit_texteditor" contentEditable={true} suppressContentEditableWarning={true} dangerouslySetInnerHTML={{ __html: this.state.htmlcontent }} />
</div>
<input type="hidden" value={this.state.idarticle} /> </form> //--</ IsLoaded >-- } </div> //----</ return >---- ); //--------</ render(HTML) >-------- } //----</ render >----
//====</ HTML >====
//--------</ component: Articles >-------- }
|