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

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)
}

Related

Angular how do I use *ngFor with the Async pipe?

Hi I'm having problems with using the asynchronous ngFor, I've got the simplest example of this thing, an array of objects that is obtained from a server onInit, and I want to iterate on int once it arrives,this is how I've written it on the template:
<p *ngFor="let msg of messages | async">test</p>
I mean it looks ok to me but apparently not, here's the ts part:
export class ChatComponent implements OnInit {
url = 'http://localhost:8080';
otherUser?: User;
thisUser: User = JSON.parse(sessionStorage.getItem('user')!);
channelName?: string;
socket?: WebSocket;
stompClient?: Stomp.Client;
newMessage = new FormControl('');
messages?: Observable<Array<Messaggio>>;
constructor(
private route: ActivatedRoute,
private userService: UserService,
private http:HttpClient
) {}
ngOnInit(): void {
this.userService
.getUserByNickname(this.route.snapshot.paramMap.get('user')!)
.subscribe((data) => {
this.otherUser = data;
this.otherUser.propic = "data:image/jpeg;base64,"+ this.otherUser.propic;
this.connectToChat();
});
}
connectToChat() {
const id1 = this.thisUser.id!;
const nick1 = this.thisUser.nickname;
const id2 = this.otherUser?.id!;
const nick2 = this.otherUser?.nickname!;
if (id1 > id2) {
this.channelName = nick1 + '&' + nick2;
} else {
this.channelName = nick2 + '&' + nick1;
}
this.loadChat();
console.log('connecting to chat...');
this.socket = new SockJS(this.url + '/chat');
this.stompClient = Stomp.over(this.socket);
this.stompClient.connect({}, (frame) => {
//func = what to do when connection is established
console.log('connected to: ' + frame);
this.stompClient!.subscribe(
'/topic/messages/' + this.channelName,
(response) => {
//func = what to do when client receives data (messages)
let data:Messaggio = JSON.parse(response.body);
console.log(data);
//this.messages.push(data);
//this.messages = this.messages.slice();
}
);
});
}
loadChat(){
let messages: Array<Messaggio>;
this.http.post<Array<Messaggio>>(this.url+'/getMessages' , this.channelName).subscribe(data =>{
messages = data;
console.log(messages);
})
}
the section regarding the question is the loadChat method which is called in a method called in the onInit, so basically it is called in the on init, and the declaration of the array
point is the array gets defined I even print it on the console but the html page doesn't do jack
Make sure your message object is of type Observable.
and
Add a null check before looping over it with a ngIf
once you messages observable has some data this below code will work fine
<div *ngIf="(messages | async)">
<p *ngFor="let msg of messages | async">test</p>
</div>
Thanks to those who are still answering this but I solved it from the first comment and the problem was: I'm stupid and I assigned the data from the server to an array local to the method instead of the property of the component, if I did that it would have worked from the begininng
lmao

CKEditor Blazor integration

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>

Angular Detect change on html input element or trigger it automatically

I want to get the value of an input element once it changes. The value is not typed by the user. I got it from a source and bind it with [(ngModel)] or just [value]. I have tried everything. But I still got nothing. I even search on how to trigger an event manually. if some of you can help me, I would be grateful
My code: I have tried various things. But this is the one that seems to work. But this instructions in the subscribe method was executed twice
this.maxGroup$ = this.reponseService.getMaxGroupe();
this.maxGroup$.subscribe((rep: MaxGroup) => {
if (rep) {
this.maxGroupe = rep;
this.max = rep.valeur;
this.questions.forEach(q => {
let r = new Reponse();
r.QuestionId = q.Id.toString();
r.Question = q;
r.Groupe = rep.valeur;
this.reponses.push(r);
});
}
this.max++;
this.maxGroupe.valeur = this.max;
let ref = this.reponseService.createMax(this.maxGroupe);
this.reponseService.deleteMax(this.idMax);
});
In my service:
return this.db.list('/maxGroup').snapshotChanges().map(snapshots => {
return snapshots.map(c => ({ key: c.payload.key, ...(c.payload.val()) as {} }));
});
In my template:
<div *ngFor="let m of maxGroup$ | async">
<input #KeyMax type="text" id="keyMax" [(ngModel)]="m.key" [value]="m.key" (change)="keyChange()"/>

Angular (keyup) event not detecting only # symbol

Im trying to implement search bar using angular (keyup) event.And i have file name like
base #,
base $,
base 1,
base #,
base 2,
when i search base # or base $ or base 1 in the search bar it filters fine. but when i search base # it dont filter base # it filter all file name with base.
here is the code below which i have coded
My html:
<input type="search" class="form-control" placeholder="Search file" (keyup)="onSearch($event)" [(ngModel)]='searchKeywords'>
my js code:
onSearch(event: any) {
const keywords = event.target.value;
if (keywords && keywords.length > 2) {
const apiURL =`abc/thg/hjy?filter[file.name]=${keywords}`;
this.api.get(apiURL).subscribe((data: any) => {
console.log(data);
this.topics = data.list;
if (this.trigger) {
this.trigger.openMenu();
}
});
} else {
this.topics = [];
this.trigger.closeMenu();
}
}
Now I'm able to pass # .
onSearch(event: any) {
const keywords = event.target.value;
const params: any = {};
if (keywords && keywords.length > 2) {
params['filter[translations.title]'] = keywords;
const options = {
search: params
};
const apiURL = `abc/thg/hjy`;
this.api.get(apiURL, options).subscribe((data: any) => {
console.log(data);
this.topics = data.list;
if (this.trigger) {
this.trigger.openMenu();
}
});
} else {
this.topics = [];
this.trigger.closeMenu();
}
}
I notice that you have an missing in the HTML markup.
<input type="search" class="form-control" placeholder="Search file" (keyup)="onSearch($event)" [(ngModel)]="searchKeywords">
Then, in the .ts file:
onSearch(event: any) {
...
}
I think the value is getting set ok in apiURL in the line:
const apiURL =`abc/thg/hjy?filter[file.name]=${keywords}`;
I think the problem is the following line, where you are passing the # (hashtag) without URL encoding it.
Try swapping out the hashtag with %23 before you use it in the next line - your get request.
See: How to escape hash character in URL

SPFx uploading and adding attachment to a list

I am having some difficulty upload and attachment to a list item in sharepoint using the PNP/SP package. I dont have much experience with the input file component so I think I may be missing a step between the file upload html element and submitting the file to the SharePoint web service.
So far I've tried to follewing the PNP example with a few changes https://pnp.github.io/pnpjs/sp/docs/attachments/ and tried a few different arguments but they all tend to result in 409 or 500 errors, one error mentions that it's attempting a GET request instead of post.
My code is below and i'll post full error messages when i get into the office tomorrow but any help would be greatly appreciated.
private setButtonsEventHandlers(): void {
let fileUpload = document.getElementById("fileUploadInput")
if(fileUpload) {
fileUpload.addEventListener('change', () => {
this.uploadFiles(fileUpload);
});
}
}
private async uploadFiles(fileUpload) {
let file = fileUpload.files[0];
let attachmentsArray = this.state.attachmentsToUpload;
let _web = new Web(this.props.wpContext.pageContext.site.absoluteUrl);
let _listItem;
let listUrlSplit: string[] = this.props.listUrl.split("/");
let listName: string = listUrlSplit[listUrlSplit.length-1];
_listItem = await _web.lists.getByTitle(listName).items.getById(this.props.id);
let attachmentUpload = await _listItem.attachmentFiles.add(file.name,file)
}
I tested the code (below) by replacing my file upload with strings and it does work so I think my error is in misunderstanding the input file element
let attachmentUpload = await _listItem.attachmentFiles.add("Testfile.txt","This is test content")
Thanks in advance all and enjoy whats left of sunday ;)
Andy
Here is my simple test demo which works(React framework).
Component .tsx
<div className={ styles.column }>
<input type='file' id='fileUploadInput' name='myfile'/>
<span className={ styles.title }>Welcome to SharePoint!</span>
<p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
<p className={ styles.description }>{escape(this.props.description)}</p>
<a href="https://aka.ms/spfx" className={ styles.button }>
<span className={ styles.label }>Learn more</span>
</a>
</div>
webpart.ts
public render(): void {
const element: React.ReactElement<IPnpspUploadAttachementProps > = React.createElement(
PnpspUploadAttachement,
{
description: this.properties.description
}
);
ReactDom.render(element, this.domElement);
this.setButtonsEventHandlers();
}
private setButtonsEventHandlers(): void {
let fileUpload = document.getElementById("fileUploadInput")
if(fileUpload) {
fileUpload.addEventListener('change', () => {
this.uploadFiles(fileUpload);
});
}
}
private async uploadFiles(fileUpload) {
let file = fileUpload.files[0];
//let attachmentsArray = this.state.attachmentsToUpload;
let item = sp.web.lists.getByTitle("MyList").items.getById(15);
item.attachmentFiles.add(file.name,file).then(v => {
console.log(v);
});
//let attachmentUpload = await _listItem.attachmentFiles.add(file.name,file)
}