Editing using Kendo UI angular grid external form(using a popup) - kendo-ui-angular2

I am editing using kendo ui angular grid in using rest api, that opens the edit component in the popup.
I am referring https://www.telerik.com/kendo-angular-ui/components/grid/editing/external-editing/
When I edit the record, the grid does not gets refreshed, only after if I F5 the page, the updated data is shown.
//Emp-List Component
private view: Observable<GridDataResult>;
constructor(private service: EmployeeService) {
this.view = this.service;
this.service.getEmpList();
}
public saveHandler(employee: Employee) {
this.service.updateEmployee(employee);
this.service.getEmpList();
this.editDataItem = undefined;
}
export class EmployeeService extends BehaviorSubject<GridDataResult> {
constructor(private http: HttpClient) {
super(null);
}
readonly APIUrl = 'http://localhost:12121/api/employee';
private getEmployee(): Observable<GridDataResult> {
return this.http.get(`${this.APIUrl}`).pipe(
map(
(response) =>
<GridDataResult>{
data: response,
total: 100,
}
)
);
}
public getEmpList(): void {
this.getEmployee().subscribe((x) => super.next(x));
}
updateEmployee(employee: Employee) {
return this.http
.put(`${this.APIUrl}/${employee.employeeID}`, employee)
.subscribe((data) => {
console.log('Employee updated ', data);
});
}
}

Related

Image not rendering without a refresh

In my Angular app I am trying to save image and text inside a table. Everything is working fine. I can add the the data, I can get the data. But the problem is if I click on save, data shows inside the table but only texts can be seen without refresh, image is showing the alt image value.
But if I refresh the page the page it works perfectly.
and in the table I can see something like this,
Here is my service file:
#Injectable({
providedIn: 'root'
})
export class CategoriesService {
private categories:Category[] = [];
private categoryUpdated = new Subject<Category[]>();
constructor(private http : HttpClient, private router: Router) { }
getUpdateListener(){
return this.categoryUpdated.asObservable();
}
/* posting request */
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData.category.id,
name : name,
image : responseData.category.image
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}
/* getting categories, data must be as backend i.e message and object */
getCategories(){
this.http.get<{message: string; categories: any}>(
"http://localhost:3000/api/v1.0/categories"
)
.pipe(map((cateData)=>{
return cateData.categories.map(category=>{
return {
id: category._id,
name : category.name,
image: category.image
}
})
}))
.subscribe(transformedCate =>{
this.categories = transformedCate;
this.categoryUpdated.next([...this.categories])
})
}
}
And my main component.ts file:
export class CategoriesComponent implements OnInit,OnDestroy{
togglePanel: any = {};
categoryPanel: any = {};
categories : Category[] = [];
private categorySub : Subscription;
constructor(private _categoriesService : CategoriesService, private dialog : MatDialog){}
ngOnInit(){
this._categoriesService.getCategories();
this.categorySub = this._categoriesService.getUpdateListener().subscribe((cate: Category[])=>{
this.categories = cate;
})
}
OnFormOpen(){
this.dialog.open(CategoryFormComponent)
}
ngOnDestroy(){
this.categorySub.unsubscribe();
}
}
And my form component:
export class CategoryFormComponent implements OnInit {
form : FormGroup;
imagePreview : string;
constructor(private dialogRef : MatDialogRef<CategoryFormComponent>,
#Inject (MAT_DIALOG_DATA) private data : any,
private _categoriesService : CategoriesService) {}
onCancel(){
this.dialogRef.close();
}
ngOnInit(): void {
this.form = new FormGroup({
name : new FormControl(null,{validators:[Validators.required, Validators.minLength(3)]}),
image : new FormControl(null,{validators: [Validators.required], asyncValidators : [mimeType]})
})
}
/*event for checking the image after load */
onImgPicked(event : Event){
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({image: file});
this.form.get('image').updateValueAndValidity();
// console.log(file);
// console.log(this.form)
const reader = new FileReader();
reader.onload = () =>{
this.imagePreview = reader.result as string;
};
reader.readAsDataURL(file);
}
/*On category added */
OnCategoryAdded(){
//is loading
this._categoriesService.addCategory(this.form.value.name, this.form.value.image);
this.form.reset();
this.dialogRef.close();
}
}
Setting a timeout on ngOnInit works but I want to make it without settiimeout
setTimeout(() => {
this.OnInit();
},10000)
}
It looks you are misunderstanding the properties of the http response in addCategory. Try modifying as below.
addCategory(name: string, image: File){
const categoryData = new FormData();
categoryData.append('name', name);
categoryData.append('image',image, name);
this.http.post<{message : string, category: Category}>(
'http://localhost:3000/api/v1.0/categories',categoryData
).subscribe(responseData=>{
const category : Category = {
id: responseData._doc.id, // here
name : name,
image : responseData._doc.image // here
}
this.categories.push(category);
this.categoryUpdated.next([...this.categories]);
})
}

Emit events between different tabs

I'm writing a web app where tab1 opens tab2 and needs to reload when tab2 is closed but nothing happens when I send the next() value (in debug I saw it get executed only once when the component is initialized). I assume it has something to do with the different browser tabs
the shared service that should allow communication between the two:
private tabClosedSource = new ReplaySubject<boolean>();
tabClosedEvent = this.tabClosedSource.asObservable();
toggleTabClosed() {
this.tabClosedSource.next(true);
}
on tab1 :
constructor(private service: ExampleService) {}
ngOnInit() {
this.service.tabClosedEvent.subscribe(
event => {
if (event) {
location.reload();
}
});
// had some issues with the path starting with '#' so I ended up replacing values
openTab2() {
window.open(window.location.href.replace('tab1', 'tab2'));
}
on tab2:
constructor(private service: ExampleService) {}
async onContinueClicked() {
api requests...
procces data...
this.service.toggleTabClosed();
window.close();
}
Your requirement is achievable with the help of Broadcast Channel API. Full credit goes to another blog, I've just updated it to suit your needs and by the way I learned some new things too.
First you need to create a service to communicate.
import { Injectable, NgZone } from '#angular/core';
import { MonoTypeOperatorFunction, Observable, Subject } from 'rxjs';
import { map } from 'rxjs/operators';
// helper method to notify zone
function runInZone<T>(zone: NgZone): MonoTypeOperatorFunction<T> {
return (source) => {
return new Observable(observer => {
const onNext = (value: T) => zone.run(() => observer.next(value));
const onError = (e:any) => zone.run(()=> observer.error(e));
const onComplete = () => zone.run(()=> observer.complete());
return source.subscribe(onNext, onError, onComplete);
});
};
}
#Injectable({
providedIn: 'root'
})
export class BroadcastHelperService {
private broadcastChannel: BroadcastChannel;
private onMessage = new Subject<any>();
constructor(
private ngZone: NgZone) {
this.broadcastChannel = new BroadcastChannel('testChannel');
this.broadcastChannel.onmessage = (message: any) => { this.onMessage.next(message) }
}
publish(message: string): void {
this.broadcastChannel.postMessage(message);
}
getMessage(): Observable<any> {
return this.onMessage.pipe(
runInZone(this.ngZone),
map((message: any) => message.data));
}
}
In tab1 component write code to reload page.
export class Tab1Component implements OnInit {
worker: any;
constructor(
public broadcastHelper: BroadcastHelperService
) { }
ngOnInit(): void {
this.broadcastHelper.getMessage().subscribe((message: any) => {
console.log(message);
if(message === 'close') {
window.location.reload();
}
})
}
}
In tab2 component, write code to broadcast message when the tab is closed.
export class Tab2Component {
constructor(
public broadcastHelper: BroadcastHelperService
) { }
#HostListener('window:beforeunload', ['$event'])
beforeUnloadHander(event: any) {
this.broadcastHelper.publish('close');
}
}

How can i asynchronouslycall this service function in another component? Angular 11

I have an async function getIdentByInfo and in the console i get the right output if i log it in this function. As soon as i call it in another component it doesnt work and i only get 'undefined'. I know it has something to do with beeing ssynchrone and Promises but i cant figure out how to solve my issue. I need the Model class filled with attributes coming from the http request in another component to send them to another service
import { EventEmitter, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IdentModel } from "../models/identmodel.model";
import { IdentteilComponent } from "../pages/identteil/identteil.component";
#Injectable({
providedIn: 'root',
})
export class InfoWebservice {
url = 'http://localhost:4201';
ident: IdentModel[];
constructor(private http: HttpClient) { }
// promise vom typ IdentModel zurückgeben
getIdentByInfo(id: string, vwk: string) {
this.http.get(this.url).toPromise().then(data => {
for (let i in data){
this.ident.push(data[i])
if ( this.ident[i].identNr == id && this.ident[i].vwk == vwk){
return this.ident[i];
}
}
});
}
}
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { InfoWebservice } from '../../webservices/info.webservice'
import { ImageWebservice } from '../../webservices/image.webservice'
import { IdentModel } from "../../models/identmodel.model";
#Component({
selector: 'app-identteil',
templateUrl: './identteil.component.html',
styleUrls: ['./identteil.component.scss']
})
export class IdentteilComponent implements OnInit {
ident = [];
identNr:string;
vwk:string;
imgFrontLink:string;
imgBackLink:string;
constructor(private router: Router, private service: InfoWebservice, private image: ImageWebservice) { }
getIdentNr() : string {
var split = this.router.url.split("/");
this.identNr = split[2];
return this.identNr;
}
//return type is STRING
getVwk() {
// output von window.location.host = repapp-maw.dbl.de
// var splitHost = window.location.host.split(".");
var splitHost = 'repapp-maw';
var splitV = splitHost.split("-");
this.vwk = splitV[1];
return this.vwk;
}
callInfoService = async () => {
return await this.service.getIdentByInfo(this.getIdentNr(), this.getVwk());
}
ngOnInit() {
console.log(this.callInfoService());
}
}
When you use angular, its always preferred not to use await/Promise. Angular has an in-built RX-JS library which has tonnes of super-awesome functionalities that you can use.
For Example, in your case, you can do something like this:
// Your Service File can make use of 'Behavior Subject'
// Please read more about it here: https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject
import { EventEmitter, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IdentModel } from "../models/identmodel.model";
import { IdentteilComponent } from "../pages/identteil/identteil.component";
#Injectable({
providedIn: 'root',
})
export class InfoWebservice {
url = 'http://localhost:4201';
ident: IdentModel[];
initialIdentValues: IdentModel = [];
private identSource: BehaviorSubject<IdentModel[]> = new BehaviorSubject<IdentModel[]>(this.initialIdentValues);
public identValuesObs$: Observable<IdentModel[]> = this.identSource.asObservable();
// Create a method to set the values in component-1
setIdentValues(identValues: IdentModel[]) {
this.identSource.next(identValues);
}
// Create a method to return values in component-2 or any component
returnIdentValues() {
return this.identValuesObs$;
}
constructor(private http: HttpClient) { }
// Change your service call to this:
getIdentByInfo(id: string, vwk: string): Observable<any> {
return this.http.get(this.url);
}
}
Now in your component-1 where you want to set the values of this identvalues:
// Component-1
constructor(private infoWebService: InfoWebService){}
// Create a method where you get the values
someMethod() {
// Call the API method here and subscribe and then set the values
this.infoWebService.getIdentInfoById(id, vwk).subscribe((data: any) => {
// Your logic goes here ANDD
if (data) {
for (let i in data){
this.ident.push(data[i])
let localIdentsWithRequiredLogic = [];
if ( this.ident[i].identNr == id && this.ident[i].vwk == vwk){
localIdentsWithRequiredLogic.push(this.ident[i]);
}
// THIS IS IMPORTANT
this.infoWebService.setIdentValues(localIdentsWithRequiredLogic);
}
}
})
}
Then in component-2 or whatever component you want, you can retrieve it using the returnIdentValues method like this:
// In component-2
inSomeMethodWhereYouRequireIdentValues() {
this.infoWebService.returnIdentValues().subscribe(data => {
console.log(data) // this is data that you set in component one
})
}

Angular 7 / Material DataTable not updating after any operation

I'm building an Angular 7 application using #angular/material. When I load the application for the first time, the Datatable renders correctly, but when I call any function, example - delUser, after deleting a user from the database, it's meant to render the table immediately, but it doesn't until I refresh the whole page. I've tried everything, but to no avail.
Here's my code:
import { Component, OnInit, ViewChild, TemplateRef } from '#angular/core';
import { UserService } from 'src/app/services/user.service';
import { MatTableDataSource } from '#angular/material/table';
import { MatSort } from '#angular/material/sort';
import { MatPaginator } from '#angular/material/paginator';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
pgtitle:string = "Manage Users";
dataSource:any;
displayedColumns:string[] = ['userName','email','roleId','userType','Actions'];
#ViewChild(MatSort, {static: true}) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
constructor(
private service:UserService
){}
ngOnInit(): void {
this.getAllUsers();
}
applyFilter(filterValue:String){
this.dataSource.filter = filterValue.trim().toLowerCase();
}
getAllUsers(){
this.service.getAllUsers().subscribe( result => {
this.dataSource = new MatTableDataSource(result);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});
}
delUser(id){
this.service.deleteUser(id).subscribe(result => {
this.getAllUsers();
});
}
Maybe you can try this:
this.service.getAllUsers().subscribe( result => {
this.dataSource = null; //add this
this.dataSource = new MatTableDataSource(result);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});

Why can I not call upon the property of this object? (Angular, MongoDB)

I am making an angular application which uses a MongoDB database and NodeJS server.
The idea is that I make an application which for now only has a list of posts and beside that the detailed-post. The components are nicely standing next to eachother and working but I have one problem. When I try to retrieve a single post I can see via console.dir(post) that all is good and the object has been transmitted to the angular app. The problem is that when I try to use post.content I get an undefined message.
I have searched for hours but can not seem to find the cause of this. I would greatly appreciate any help you can give me. Beneath here is all the information, if you need to see something else, please tell me.
Thanks in advance.
This is the post-detail.component.html where I want to display the data.
<div class="row">
<div class="col-xs-12">
<p>Content:</p>
<h1>{{ post.content }}</h1>
</div>
</div>
The detail.ts file (I left out the imports)
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post = new Post();
id: string;
constructor(private postService: PostService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = params['id'];
this.postService.getPost(this.id).then(res => {
console.dir(res);
console.dir(res.content);
this.post = res;
});
}
);
}
}
The post.service.ts which I am using to retrieve the actual data:
#Injectable()
export class PostService {
postChanged = new Subject<Post[]>();
private headers = new Headers({'Content-Type': 'application/json'});
private serverUrl = environment.serverUrl + '/blogPosts/'; // URL to web api
private posts: Post[];
constructor(private http: Http) {
}
//this one DOES work
getPosts() {
console.log('Fetching BlogPosts from database.')
return this.http.get(this.serverUrl, {headers: this.headers})
.toPromise()
.then(response => {
this.posts = response.json() as Post[];
return response.json() as Post[];
})
.catch(error => {
return error;
});
}
getPost(index: string) {
console.log('Fetching individual BlogPost from database.');
console.log('index' + index);
if (index == null) {
console.log('null');
return null;
}
return this.http.get(this.serverUrl + index, {headers: this.headers})
.toPromise()
.then(response => {
console.dir(response.json().content);
console.dir(response.json());
return response.json() as Post;
})
.catch(error => {
return this.handleError(error);
});
}
}
The Post model:
export class Post {
private id: string;
private _content: string;
constructor(values: Object = {}) {
Object.assign(this, values);
}
public get _id(): string {
return this.id;
}
public set _id(n: string) {
this.id = n;
}
public get content(): string {
return this._content;
}
public set content(n: string) {
this._content = n;
}
}
And I added in the Postman GET /blogPost/id and the console log as images.
Thanks!
Console log
Postman GET route
I might be wrong but can you please change the _content to content everywhere in the service ?
Edit: are you sure the this.id is correct when you call the service method ? cause if it is null or undefined then return null will be executed.
Another note is that in Postman i see the response is an array of objects (one object) at this example. Can you try this.post = res[0]; in the component ?
return response.json() as Post;
In post.service.ts should be:
return response.json()[0] as Post;
I did not see that the object was wrapped in an array, by accessing it I was able to get it out and use it.

Resources