CKEditor Blazor integration - html

I am trying to use CKeditor with Blazor.
I used Online builder to create a custom build, with ImageUpload and Base64UploadAdapter, and it is integrated in BlazorApp.
I can successfully show it on the page, and put / get HTML content from it.
Source of the working version for Blazor app is here https://gitlab.com/dn-misc/BlazorCKEditor1/
But as I would like to inser image as Base64 encoded string directly in HTML content, when I try to upload image I get following error:
Assertion Failed: Input argument is not an HTMLInputElement (from content-script.js)

I have successfully implemented Chris Pratt implementation. Check this out:
IMPORTANT: this works with ClassicEditor ONLY.
Blazor component, I called mine InputCKEditor.razor. Yeah I know, no very original.
#namespace SmartApp.Components
#inherits InputTextArea
#inject IJSRuntime JSRuntime
<textarea #attributes="AdditionalAttributes"
id="#Id"
class="#CssClass"
value="#CurrentValue"></textarea>
#code {
string _id;
[Parameter]
public string Id
{
get => _id ?? $"CKEditor_{_uid}";
set => _id = value;
}
readonly string _uid = Guid.NewGuid().ToString().ToLower().Replace("-", "");
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await JSRuntime.InvokeVoidAsync("CKEditorInterop.init", Id, DotNetObjectReference.Create(this));
await base.OnAfterRenderAsync(firstRender);
}
[JSInvokable]
public Task EditorDataChanged(string data)
{
CurrentValue = data;
StateHasChanged();
return Task.CompletedTask;
}
protected override void Dispose(bool disposing)
{
JSRuntime.InvokeVoidAsync("CKEditorInterop.destroy", Id);
base.Dispose(disposing);
}
}
Then, you have to put this in your interop.js
CKEditorInterop = (() => {
var editors = {};
return {
init(id, dotNetReference) {
window.ClassicEditor
.create(document.getElementById(id))
.then(editor => {
editors[id] = editor;
editor.model.document.on('change:data', () => {
var data = editor.getData();
var el = document.createElement('div');
el.innerHTML = data;
if (el.innerText.trim() === '')
data = null;
dotNetReference.invokeMethodAsync('EditorDataChanged', data);
});
})
.catch(error => console.error(error));
},
destroy(id) {
editors[id].destroy()
.then(() => delete editors[id])
.catch(error => console.log(error));
}
};
})();
Now time to use it:
<form>
<label class="col-xl-3 col-lg-3 col-form-label text-sm-left text-lg-right">Description</label>
<div class="col-lg-9 col-xl-6">
<InputCKEditor #bind-Value="_model.Description" class="form-control form-control-solid form-control-lg"></InputCKEditor>
<ValidationMessage For="#(() => _model.Description)" />
</div>
</form>

Related

Upload image in angular, Required request part '' is not present

I can't find any solution for the Upload image in angular .. Note: the Spring back-end works.
Required request part 'condidat' is not present
Controller
#PutMapping(
path="/{id}",
consumes={MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE},
produces={MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE}
)
public ResponseEntity<CondidatResponse> updateCondidat(
#PathVariable String id , #RequestPart(value = "condidat") CondidatRequest condidatRequest,
#RequestPart(value = "photo", required = false) MultipartFile file) {
Postman
Service
updateC(id: string, photo: FormData){
return this.http.put(${environment.baseUrl}/condidat/ + id, photo);
}
Ts
updateCond() {
this.con.userId = this.id;
const formData = new FormData();
formData.append('photo', this.photo);
this.con.photo = this.photo.name;
console.log(this.con);
formData.append('condidat', JSON.stringify(this.con));
this.sign.updateC(this.id, formData)
.subscribe(res => {
console.log(res);
this.router.navigate(['/profil']);
});
this.toastr.warning('Data update successfully !!', 'UPDATE', {
timeOut: 3000,
positionClass: 'toast-bottom-left'
});
}
onSelectFile(event: any) {
if (event.target.files.length > 0) {
this.photo = event.target.files[0];
this.message = this.photo;
console.log(this.message);
}
}
html
<input type="file" accept="image/*" (change)="onSelectFile($event)" />
....
Problem
message: "Required request part 'condidat' is not present"

A circular reference was detected while serializing an object of type when performing ajax call

On my view, I am using a Viewmodel, and I have a form that has only one textbox that accepts dates (not a part of the viewmodel) and 3 tables. By default on page load.. the tables are populated with data based on today's date (you can see that in the controller code below), but if a user selects a date and clicks the search button then I want the tables data to be changed without a page refresh based on the date they selected.
#using (Html.BeginForm())
{
<div class="form-group mb-3 mt-3" style="margin-right: -1.3%;">
<div class="input-group col-md-3 offset-md-9">
#Html.TextBox("detailsDate", null, new { id = "Details-Date", #class = "form-control datetimepicker" })
<div class="input-group-append">
<button id="Details-Date-Btn" type="submit" class="btn btn-outline-primary"><span class="fa fa-search"></span></button>
</div>
</div>
</div>
}
What I am trying to do is if a user selects and date and hits the search button.. I would like the page to not refresh and the tables data have been changed based on the date. As of right now I am getting:
A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.tbl_WeighAssc_8AA7AB5F9DAB261D5142F1D5F5BA6705A588A5AAD2D369FBD4B4BC1BBE0487D4'.
Viewmodel
public class PersonnelDetailsVm
{
private static ConnectionString db = new ConnectionString();
public PersonnelDetailsVm()
{
CurrentWeekDates = new List<DateTime>();
WeighAssociations = new List<tbl_WeighAssc>();
ArrestAssociations = new List<tbl_TEUArrestAssc>();
InspectionAssociations = new List<tblTEUInspectionAssc>();
}
public string IBM { get; set; }
[Display(Name = "Name")]
public string UserName { get; set; }
public bool Active { get; set; }
public List<DateTime> CurrentWeekDates { get; set; }
public List<tbl_WeighAssc> WeighAssociations { get; set; }
public List<tbl_TEUArrestAssc> ArrestAssociations { get; set; }
public List<tblTEUInspectionAssc> InspectionAssociations { get; set; }
public List<code_WeighLocation> WeighLocations => db.code_WeighLocation.ToList();
public List<code_ArrestType> ArrestTypes => db.code_ArrestType.ToList();
public List<code_InspectionLevel> InspectionLevels => db.code_InspectionLevel.ToList();
}
Ajax:
// Submission
//var redirectUrl = '#Url.Action("Index", "Personnels")';
var settings = {};
settings.baseUri = '#Request.ApplicationPath';
var infoGetUrl = "";
if (settings.baseUri === "/AppName") {
infoGetUrl = settings.baseUri + "/Personnels/Details/";
} else {
infoGetUrl = settings.baseUri + "Personnels/Details/";
}
$("#Details-Date-Btn").click(function() {
$.ajax({
url: infoGetUrl,
method: "POST",
data: $("form").serialize(),
success: function(response) {
console.log("success");
$("body").html(response);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
}
});
});
Controller:
public ActionResult Details(string id, string detailsDate)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
tblPersonnel tblPersonnel = db.tblPersonnels.Find(id);
if (tblPersonnel == null)
{
return HttpNotFound();
}
Mapper.Initialize(config => config.CreateMap<tblPersonnel, PersonnelDetailsVm>());
PersonnelDetailsVm person = Mapper.Map<tblPersonnel, PersonnelDetailsVm>(tblPersonnel);
var employeeData = EmployeeData.GetEmployee(person.IBM);
person.UserName =
$"{ConvertRankAbbr.Conversion(employeeData.Rank_Position)} {employeeData.FirstName} {employeeData.LastName}";
if (string.IsNullOrWhiteSpace(detailsDate))
{
var startOfWeek = DateTime.Today.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)DateTime.Today.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
return View(person);
}
else
{
var paramDate = DateTime.ParseExact(detailsDate, "MM/dd/yyyy", CultureInfo.CurrentCulture);
var startOfWeek = paramDate.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)paramDate.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
return Json(person, JsonRequestBehavior.AllowGet);
}
}
So, if the actionresult's paramets detailsDate is not null, then it goes into the else statement which returns a JSON object. When debugging this goes through and when the view is returned I am receiving the error I posted above.
Is there a way to replace the model in the view with what I'm returning from the ajax call so the tables can be based on the right date without a page refresh?
Any help is greatly appreciated.
UPDATE
Based on answer's below I have edited the else statement in my controller method to:
Controller
else
{
var paramDate = DateTime.ParseExact(detailsDate, "MM/dd/yyyy", CultureInfo.CurrentCulture);
var startOfWeek = paramDate.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)paramDate.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var jsonStr = JsonConvert.SerializeObject(person);
return Json(jsonStr, "text/plain");
}
My jQuery/Ajax is still the same:
$("#Details-Date-Btn").click(function() {
$.ajax({
url: infoGetUrl,
data: $("form").serialize(),
success: function(response) {
console.log("success");
console.log(response);
$("body").html(response);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
}
});
});
But now, when the date is selected I am being returned to a page that shows the Json like a plain text file and losing the HTML and CSS like a normal view.
Here is what I am being returned when a date is selected and the button is clicked.
Also, when I check the console when I select a date and click the button for that date to be sent to the controller I am seeing this:
UPDATE 2
Here is one of my tables.. the other ones are the same setup:
<table class="table table-bordered">
<thead>
<tr>
<th></th>
#foreach (var date in Model.CurrentWeekDates)
{
<th class="text-center">#date.ToString("ddd") <br /> #date.ToShortDateString()</th>
}
<th class="text-center table-success">Total For Week</th>
</tr>
</thead>
<tbody>
#foreach (var weighLocation in Model.WeighLocations)
{
<tr class="text-center">
<td class="table-dark">#weighLocation.Weigh_Location</td>
#foreach (var date in Model.CurrentWeekDates)
{
if (Model.WeighAssociations.Any(x => x.tbl_TEUForm.EventDate == date && x.WeighLocationId == weighLocation.ID))
{
<td>#Model.WeighAssociations.Single(x => x.tbl_TEUForm.EventDate == date && x.WeighLocationId == weighLocation.ID).OccurenceCount</td>
}
else
{
<td>0</td>
}
}
<td class="table-success font-weight-bold">#Model.WeighAssociations.Where(x => x.WeighLocationId == weighLocation.ID).Sum(x => x.OccurenceCount)</td>
</tr>
}
</tbody>
</table>
As far as I can see from your problem, to fix it you can do following steps:
1- View
Add a partial view (_Detail.cshtml)
You need a partial view like _Detail that includes your table like this:
#model PersonnelDetailsVm
<table class="table table-bordered">
<thead>
<tr>
<th></th>
#foreach (var date in Model.CurrentWeekDates)
{
<th class="text-center">#date.ToString("ddd") <br /> #date.ToShortDateString()</th>
}
<th class="text-center table-success">Total For Week</th>
</tr>
</thead>
<tbody>
#foreach (var weighLocation in Model.WeighLocations)
{
<tr class="text-center">
<td class="table-dark">#weighLocation.Weigh_Location</td>
#foreach (var date in Model.CurrentWeekDates)
{
if (Model.WeighAssociations.Any(x => x.tbl_TEUForm.EventDate == date && x.WeighLocationId == weighLocation.ID))
{
<td>#Model.WeighAssociations.Single(x => x.tbl_TEUForm.EventDate == date && x.WeighLocationId == weighLocation.ID).OccurenceCount</td>
}
else
{
<td>0</td>
}
}
<td class="table-success font-weight-bold">#Model.WeighAssociations.Where(x => x.WeighLocationId == weighLocation.ID).Sum(x => x.OccurenceCount)</td>
</tr>
}
</tbody>
</table>
2- Controller
Return the partial view
You should fill the model in your controller and pass it to the partial view.
public ActionResult Details(string id, string detailsDate)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
tblPersonnel tblPersonnel = db.tblPersonnels.Find(id);
if (tblPersonnel == null)
{
return HttpNotFound();
}
Mapper.Initialize(config => config.CreateMap<tblPersonnel, PersonnelDetailsVm>());
PersonnelDetailsVm person = Mapper.Map<tblPersonnel, PersonnelDetailsVm>(tblPersonnel);
var employeeData = EmployeeData.GetEmployee(person.IBM);
person.UserName =
$"{ConvertRankAbbr.Conversion(employeeData.Rank_Position)} {employeeData.FirstName} {employeeData.LastName}";
if (string.IsNullOrWhiteSpace(detailsDate))
{
var startOfWeek = DateTime.Today.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)DateTime.Today.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
// return View(person);
}
else
{
var paramDate = DateTime.ParseExact(detailsDate, "MM/dd/yyyy", CultureInfo.CurrentCulture);
var startOfWeek = paramDate.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)paramDate.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
// return Json(person, JsonRequestBehavior.AllowGet);
}
// return PartialView with the person model
return PartialView("_Detail", person);
}
As you can see in the above code, you should comment the two lines in your code:
// return View(person);
// return Json(person, JsonRequestBehavior.AllowGet);
3- Ajax call
Get the partial view and fill form by it
You don't any changes in ajax call and you can do it like this:
$("#Details-Date-Btn").click(function() {
$.ajax({
url: infoGetUrl,
method: "POST",
data: $("form").serialize(),
success: function(response) {
console.log("success");
$("body").html(response);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
}
});
});
response in this way is a html that comes from partial view and it has all classes as you designed it.
That error message means that one of your child properties refers back to the parent and JSON serialization causes a circular loop.
To fix, replace this:
return Json(person, JsonRequestBehavior.AllowGet);
with this:
return Content(JsonConvert.SerializeObject(person,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}), "application/json");
You will have to install NewtonSoft.Json:
using Newtonsoft.Json;
A circular reference was detected while serializing an object of type occurred because JSON serializer doesn't support circular references inside your object hierarchy (i.e. passing PersonnelDetailsVm that contains references to data models). To resolve this issue, use JSON.NET's JsonConvert.SerializeObject() with default settings set like this:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
Afterwards, you can return JsonResult from viewmodel:
string jsonStr = JsonConvert.SerializeObject(person);
return Json(jsonStr);
If you're using IE and encountering save dialog because of friendly JSON errors configuration, you may need to add text/html or text/plain when returning JSON data:
return Json(jsonStr, "text/html");
Or hide the Json() method inside controller class like this:
protected new JsonResult Json(object data)
{
if (!Request.AcceptTypes.Contains("application/json"))
return base.Json(data, "text/plain");
else
return base.Json(data);
}
Additionally, instead of return View(person); you may consider return PartialView("Details", person); because AJAX call intended to stay on same page.
Setting ReferenceLoopHandling = ReferenceLoopHandling.Ignore will handle your issue related to circular reference exception.
Now to about issue(s) you presented in your updates, I think you might have some misunderstanding about how Ajax request work. I think this because when you make an ajax request to server it 'll respond with JSON data which will be representation of your view model and will be agnostic to your view (cshtml) code, so when you call $("body").html(response); you are replacing content of your page with stringified representation of your JSON view model. Take away here is that when you make an ajax request only back-end code will get executed and your view code(cshtml) will not be executed, period.
To solve your you will have to replace content of the table itself and not the body of the page, so you Ajax success callback should be something like:
var tempData = {
"IBM": "IBM",
"UserName": "UserName",
"Active": false,
"CurrentWeekDates": [],
"WeighAssociations": [],
"ArrestAssociations": [],
"InspectionAssociations": [],
"WeighLocations": [],
"ArrestTypes": [],
"InspectionLevels": []
};
function onSuccess(response){
var table = $("#tblData");
table.html("");
// code to create your head same code as your cshtml
table.append("<thead><th>New Column</th></thead>");
table.append("<tr><td>Column 1</td></tr>");
$("#btnLoad").text("Loaded");
}
$("#btnLoad").click(function(){
$("#btnLoad").attr("disabled", "");
$("#btnLoad").text("Loading...");
// call onSuccess function after 5 second to replicate server call
setTimeout(function(){ onSuccess(tempData) }, 5000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btnLoad">Load Data</button>
<table id="tblData" class="table table-bordered">
<thead>
<tr>
<th></th>
<th class="text-center">Mon<br /> 01-01-1990</th>
<th class="text-center table-success">Total For Week</th>
</tr>
</thead>
<tbody>
<tr class="text-center">
<td class="table-dark">Column 1</td>
<td>Columns 2</td>
<td>0</td>
<td class="table-success font-weight-bold">0</td>
</tr>
</tbody>
</table>
I hope this helps you!
You have 2 different return methods and you're setting the body content to the response of the request, which if the else statement is run will be JSON and not html.
I would suggest to either always return JSON or always return a View/Partial.
else
{
var paramDate = DateTime.ParseExact(detailsDate, "MM/dd/yyyy", CultureInfo.CurrentCulture);
var startOfWeek = paramDate.AddDays((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek -
(int)paramDate.DayOfWeek);
person.CurrentWeekDates = Enumerable.Range(0, 7).Select(i => startOfWeek.AddDays(i)).ToList();
var teuFormIds = db.tbl_TEUForm
.Where(x => person.CurrentWeekDates.Contains(x.EventDate) && x.PersonnelIBM == person.IBM).Select(t => t.Id).ToList();
person.WeighAssociations = db.tbl_WeighAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.ArrestAssociations = db.tbl_TEUArrestAssc.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
person.InspectionAssociations =
db.tblTEUInspectionAsscs.Where(x => teuFormIds.Contains(x.TEUId)).ToList();
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var jsonStr = JsonConvert.SerializeObject(person);
//return Json(jsonStr, "text/plain");
return Partial(person);
}
I would suggest the below steps
One partial view for filters which include DateTime and Submit button(FilterPartial)
One Partial view for tables to be rendered(ResultsPartial)
When the body is loaded then load the first partial view(let's call FilterPartial) which will set the values in the ResultsPartial view.
function GetData(params) {
$.ajax({
type: "POST",
url: '/controller/Action',
data: {
//Parameters
},
dataType: 'html',
success: function (data) {
//You can either load the html directly or render the control here
}
});
}
public PartialViewResult Action()
{
return PartialView("");
}
The ViewModel logic can be kept as it is, you have to segregate the views.
Let me know if this helps...
You need to create two methods, first one to return the view with ActionResult return type:
public ActionResult Details(string id, string detailsDate)
{
....
return View(person);
}
And a second method which will be called via Ajax to return the Json data with JsonResult type
public JsonResult GetData(string id, string detailsDate)
{
....
return Json(person, JsonRequestBehavior.AllowGet);
}
To prevent repeating same getting data logic twice, you can move all data logic to the Json method and keep View method return with no data, then just trigger Ajax call when page load is completed to get initial data from Json method.

Angular Firebase Storage, Assigning User Input Properties to Real Time Database

I want to upload a file especially an image to my firebase storage. I found a tutorial from this link. I added the more properties like url and file to my existing class and i followed the function template on that link. But apparently i did something wrong. The file uploaded to my storage and the console log didn't return any error. I need help with assigning another properties like prdName, prdCategory, and prdSup with user input correctly. Can someone help me with this please?
//product.ts
export class Product {
$prdKey: string;
prdName: string;
prdCategory: string; //Category
prdSup: string; //supplier
prdDescription: string;
prdImage: string; //name
prdUrl: string; //url
file: File;
constructor(file: File) {
this.file = file;
}
}
//service.ts
variable: any;
selectedProduct: Product = new Product(this.variable); //-->there was an error here that said expected 1 argument but got 0 so i add variable:any;
private basePath = '/product';
pushFileToStorage(Product: Product, progress: {
percentage: number
}) {
const storageRef = firebase.storage().ref();
const uploadTask = storageRef.child(`${this.basePath}/${Product.file.name}`).put(Product.file);
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
(snapshot) => {
// in progress
const snap = snapshot as firebase.storage.UploadTaskSnapshot
progress.percentage = Math.round((snap.bytesTransferred / snap.totalBytes) * 100)
},
(error) => {
// fail
console.log(error)
},
() => {
// success
/*--What should i assign here?--*/
Product.prdName = Product.file.name,
Product.prdCategory = Product.file.name,
Product.prdSup = Product.file.name,
Product.prdDescription = Product.file.name,
/*------------------------------------------*/
Product.prdUrl = uploadTask.snapshot.downloadURL,
Product.prdImage = Product.file.name,
this.saveFileData(Product)
}
);
}
private saveFileData(Product: Product) {
this.firebase.list(`${this.basePath}/`).push(Product);
}
//component.ts
upload() {
const file = this.selectedFiles.item(0);
this.currentFileUpload = new Product(file);
this.ProductService.pushFileToStorage(this.currentFileUpload, this.progress);
}
<!--component.html-->
<!--form snippet-->
<form #productForm="ngForm" (ngSubmit)="upload()">
<div class="form-group">
<label>Product Name</label>
<input class="form-control" name="prdName" #prdName="ngModel" [(ngModel)]="ProductService.selectedProduct.prdName">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Please let me know if more snippets are needed. Thank you in advance.
(Update)
I put the push function inside //success condition, however i'm not sure what to assign for each class properties. Product.prdName = Product.file.name, will give me prdName equal to the file name. I tried Product.prdName = selectedProduct.prdName, but looks like it is not correct.
I figured it out, it should looks like this, works for me :D
() => {
// success
this.productList.push({
prdName: this.selectedProduct.prdName,
prdCategory: this.selectedProduct.prdCategory,
prdSup: this.selectedProduct.prdSup,
prdDescription: this.selectedProduct.prdDescription,
prdUrl: this.selectedProduct.prdUrl = uploadTask.snapshot.downloadURL,
prdImage: this.selectedProduct.prdImage = Product.file.name,
})
this.saveFileData(Product)
}

Keep non form related data (like dropdown options) in controller's post action

Controller:
public ActionResult New()
{
var viewModel = new ViewModel();
viewModel.AllUsers = **Run a Database Query to get all users**
return View(viewModel);
}
View:
#using (Html.BeginForm("New", "Card", FormMethod.Post, new Dictionary<string, object>() { { "id", "cardForm" } }))
{
**Among other form fields***
<div class="control-group">
label class="control-label LabelStyle">
Assigned To
</label>
<div>
#Html.DropDownListFor(model => model.AssignedToIds, Model.AllProjectUsersList.ToSelectList(u => u.Id.ToString(), u => u.Text, u => u.Selected ), new { #class = "chosen", multiple = "multiple"})
</div>
</div>
}
<script>
var cardForm = $('#cardForm');
cardForm.submit();
</script>
Controller (Post Action):
[HttpPost]
public ActionResult New(CardFormViewModel viewModel)
{
if (ModelState.IsValid)
{
//Do Something
}
return View(viewModel);
}
The problem is, if the ModelState is Invalid, and I return the View with the viewModel, AllProjectUsersList is null. Hence, an exception occurs when building the view. I can, of course, fix this by running the database query again and populating AllProjectUsersList.
Is there any way that I can keep the previously populated AllProjectUsersList or avoid the database query some other way?

need signalR polling for asp.net mvc

Any kind soul can guide me how to use signalR on an existing mvc project to poll data in real time i'd be greatly appreciate.
example code:
[controller]
private ApplicationDbContext db = new ApplicationDbContext();
public PartialViewResult Chat(string people) // <---need to send real time data to partial
{
var model = new MessageVM()
{
sender = User.Identity.Name;,
messageList = db.messages.Where(x => x.receiver == people).ToList().Take(30)
};
return PartialView("_chat", model);
}
[view]
#Ajax.ActionLink(item.name, "Chat", new { people = item.name }, new AjaxOptions()
{ HttpMethod = "GET", UpdateTargetId = "divChat", InsertionMode = InsertionMode.Replace })
<div id="divChat"></div> // <---this area need real-time messages data from controller.
First create your signalr connection in js in client side. something like:
function signalrconnection() {
$.connection.hub.url = "http://localhost:54321/signalr";
chat = $.connection.myHub;
if (chat != undefined) {
$.connection.hub.start()
.done(function () {
chat.server.send("client", "Status\tasking for status");
chat = $.connection.myHub;
})
.fail(function () { NoLTConnectionAlert(); });
}
else {
///do something.
}
}
return chat;
}
Then add signalr call to your $(document).ready(function ()) in your js something like:
$(document).ready(function () {
chat = signalrconnection();
intervalstatus = setInterval(checkstatus, 1000);
// Create a function that the hub can call to broadcast messages.
chat.client.addMessage = function (name, message) {}
}
In your controller you should have a class for hub and method inside like:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.Caller.addMessage("parameter", reply);
}
}
Then again you should handle Clients.Caller.addMessage in you js to update <div id="divChat"></div>