angular material dialog pass dynamic function when clicked on save - javascript

dialog.ts
export class DialogComponent {
constructor(private service: RestService, private dialogRef: MatDialogRef<DialogComponent>) {
}
no() {
this.dialogRef.close();
}
save() {
const url = 'https://gorest.co.in/public-api/users';
const options = {headers: HttpHeaders, params: HttpParams};
const getResp: any = this.service.Get(url, options);
this.dialogRef.close();
///THIS save() FUNCTION HARDCODED, BUT I WANT TO MAKE RESTCALL DYNAMICALLY WHEN ANYONE CLICKS SAVE BUTTON IN DIALOG
}
}
dialog.html
<mat-dialog-actions>
<button class="mat-raised-button"
(click)="no()">
no
</button>
<button class="mat-raised-button mat-primary"
(click)="save()">
save
</button>
</mat-dialog-actions>
component1.ts
getUsers() {
const dialogConfig = new MatDialogConfig();
const dialogRef = this.dialog.open(DialogComponent,
dialogConfig, passSomeRestCall-1-FunctionSomehow());
}
component2.ts
getEmployees() {
const dialogConfig = new MatDialogConfig();
const dialogRef = this.dialog.open(DialogComponent,
dialogConfig, passSomeRestCall-2-FunctionSomehow());
}
Above 2 components have to make use of Dialog Component dynamically, currently above save() hardcoded with some restcall, but actually rest call needed when clicked on save() for both above components. So, in short, save() button should happen dynamic restcall based on the component.
Could anyone please help me on above, I am very new to angular.
EDIT:
for (let i = 0; i < this.items.length; i++) {
this.service.makeRestCall(this.items[i]);
}
How do I pass above for loop logic in dialog component? I should be able to do some business logic dynamically inside save() based on component like below
save(){
dynamicMethod() // this should be passed somehow from a component
this.dialogRef.close();
}

You do not need to pass the callback method. It should be part of modal's parent.
Only you have need is to set #Output into the modal with type EventEmitter. Like:
#Output() save = new EventEmitter<boolean>();
On Save button just implement:
handleSave: void {
this.save.emit(true);
}
Parent will listen this #Output and handle it properly.
dialogRef.componentInstance.save$.subscribe((res) => {
// Here is your business logic
}
);
So:
If after processing is everything OK, you can use dialogRef in your parent and close the modal (dialogRef.close()).
If something is wrong, modal will not be closed.
Using this way, our modal will be free of business logic. Especially important for generic modals, like confirmation, user inputs, ...

You can pass your url , options etc as data in your dialog component and pass it in save function like below.
UserComponent
openDialog(): void {
let ApiDetails = {
url: 'https://gorest.co.in/public-api/users'
}
const dialogRef = this.dialog.open(DialogComponent,
{
data: ApiDetails
});
}
DialogComponent
export class DialogComponent implements OnInit {
constructor(
private dialogRef: MatDialogRef<DialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
}
no(){
this.dialogRef.close()
}
save(){
console.log(this.data) // this will have url and other details whatever you send from calling parent
}
}

Related

load data on change of dropdown in other component in Angular

I have component A which is navbar and component B which is list view
Navbar has dropdown where all the users are listed, onInit method of component B all the data is loaded in the Component B Table that selected user is in local storage, now i want when the user changes the users from dropdown then that users data should automatically be refreshed in the Component B selected users are saved in localstorage. Is there anyway i can call method of second component from first component.
Navbar Component
<select class="form-control"(change)="selectUsers($event)" [(ngModel)]="UserId">
<option *ngFor="let opt of userlist" value={{opt.UserId}}>{{opt?.userName}}
</option>
</select>
selectUsers(event) {
this.Service.SelectedUserId = event.target.value;
}
In Service File
set SelectedUserId(value: string) {
if (value) localStorage.setItem('ActiveUserId', value);
}
Component B:
fetch() {
this.Userslist = [];
this.param.userId = localStorage.getItem('ActiveUserId');
this.service.getUsers(this.param).subscribe((data => {
this.Userslist = data.response;
}
}
In View:
<tbody>
<tr *ngFor="let data of Userslist | paginate:{itemsPerPage: 10, currentPage:p} ; let i=index"
..
..
</tbody>
Any solution to resolve this issue, Thanks
setup a common service which is shared by the two components. Lets assume its called testService.
so service will be like.
export class testService {
testEmitter: Subject<void> = new Subject<void>()
testObserver$: Observable<void>;
constructor () {
this.testObserver$ = this.testEmitter.asObservable();
}
}
Now the power of this pattern is that we can listen for next of the subject from any component in the application and perform action!
so in the navbar we start the next which will trigger action on all the observables!
selectUsers(event) {
this.Service.SelectedUserId = event.target.value;
this.testService.next();
}
So we subscribe on the component where the refresh should happen, and then reload the grid.
child component.
export class ChildComponent implements OnInit, OnDestroy {
subscription: Subscription = new Subscription();
ngOnInit() {
this.subscription.add(
this.testService.testObserver$.subscribe(() => {
this.fetch();
})
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
You can also emit objects like {eventType: 'test', data: {}} inside the next and then add multiple if conditions to validate the eventType and perform multiple actions on the same subject!

httpClient.get is undefined when using dynamic function/observable references

So I asked a question a few days ago and got some headway on a solution, however now I'm stuck at another wall I'm unsure how to get over.
I have two parent components, a shared view/component with an extended base component, and a service all hooked together. The objective is to use the two parent components to drive what data is shown within the shared component. The two parent components use references to service methods passed into the shared component to get the data.
I've reached an issue where my http.get is always undefined no matter what I try. I've instantiated it like I do in my other services but I've had no luck. I suspect this is caused by how i pass in my service references. Code below:
Parent Component Code:
// PARENT COMPONENT
myData$: Observable<myType>;
searchMethod: Function;
constructor(private myService){
this.myData$ = this.myService.myData$;
this.searchMethod = this.myService.searchData;
}
// PARENT COMPONENT HTML
<app-shared-component
[myData$] = "myData$"
[searchMethod]="searchMethod">
</app-shared-component>
Shared Component Code:
export class MySharedComponent extends BaseComponent<MyType> implements OnInit {
#Input() myData$: Observable<myType>;
#Input() searchMethod: Function;
constructor() { super(); }
ngOnInit(): void {
this.data$ = this.myData$;
}
search(): void {
this.searchMethod().subscribe(//do something);
}
Base Component Code:
#Input data$: Observable<T>;
ngOnInit(): void {
this.data$.subscribe((response: T) => //do something);
super.ngOnInit();
}
Service Code:
private myDataSubject = new BehaviorSubject<MyType>(new MyType());
get myData$(): Observable<MyType> {
return this.myDataSubject.asObservable();
}
constructor(private http: HttpClient) { }
searchData(): Observable<void> {
return new Observable<void>(observer => {
this.http.get<MyType>(
'http://myuri'
).subscribe(
response => {
// do something
},
() => observer.error(),
() => observer.complete()
);
});
}
It looks like you're losing the context of your service when you set this.searchMethod = this.myService.searchData in your parent component. It should work if you change searchData() { to an arrow function: searchData = (): Observable<void> => {.

Angular 7 Communication through service subscribe method called twice

I'm using angular, trying to communicate to a component that is not parent-child. So I'm communicating it through service
service.ts
Istoggle=false;
#Output() change: EventEmitter < boolean > = new EventEmitter();
toggle() {
this.Istoggle= !this.Istoggle;
this.toggle.emit(this.Istoggle);
}
search.ts
submit():void{
this.service.toggle();
}
Home.ts
ngOnInit() {
this.service.change.subscribe(_toggle=> {
//some code here
}
}
so when I click on submit in Home component toggle subscribe get hit twice
as I see you have 3 fields with exact the same name in your service. 2 of them are missed. you could do
value=false;
search: EventEmitter < boolean > = new EventEmitter();
toggle() {
this.value = !this.value;
this.search.emit(this.value);
}
in your service and in component:
ngOnInit() {
this.service.search.subscribe(value => {
//some code here
}
}
note that I removed #Output() decorator. it is used only inside components just for parent-child communication.

Unable to get variable outside subscribe in Angular

Well, maybe my issue sounds similar to the few questions here about Angular2-EventEmitter-Subject-Observable-Subscribe-stuff... but it's quite different.
My goal (in short):
Be able to go from route ItemsList to route AddNewItem and then push on the custom 'back' button and find myself in ItemsList WITH ALL SETTINGS THAT I HAD HERE. For a example - search params. So... the only way (afaik) in Angular2+ to store that params between non-related components is to use shared service. So... My code:
ItemsList ts\html (very briefly):
export class ListComponent implements OnInit {
public addItem(allSettings: ISettingsTable) {
this._shService.returnToList = true;
this._shService.allCurrentSettings = allSettings;
this._router.navigate(['someRoute', 'add']);
}
ngOnInit() {
this._settings = {
search: /* .. */,
/* .. */
}
this._shService.getSettings().subscribe((settings: ISettingsTable) => {
this._settings = settings;
}
}
}
<button (click)='addItem(_settings)'>
<mat-icon svgIcon='add'></mat-icon>
</button>
Service (in short):
export class SettingsService {
private _returnToList: boolean;
private _settingsChanged$: Subject<ISettingsTable> = new Subject();
public emitSettings(val: ISettingsTable) {
this._settingsChanged$.next(val);
}
public getSettings(): Subject<ISettingsTable> {
return this._settingsChanged$;
}
}
AddEditItems ts\html (very briefly as well):
export class EditComponent implements OnInit {
public back(par: ISettingsTable): void {
if (this.returnToList) {
this._router.navigate(['someRoute', 'list']).then(() => {
this._shService.emitSettings(par);
});
ngOnInit() {
if (this._shService.returnToList) {
this.returnToList = true;
this._p = this._testS.allCurrentSettings;
}
}
}
<button mat-raised-button (click)='back(_p)'>Go back</button>
So! When I go to the Add route I store the current params in service. Fine. Then on the Add route I get them from service. Fine. Then when I wanna go back I pass that params to the emit method of Subject\Event Emitter... Fine! But on the List route this._settings exists only INSIDE subscribe... It's undefined outside. Yeah, I know about asynchronous behavior and stuff... But how to resolve my issue properly??? Anyone! Please. Hope I made myself clear.
EDIT...
Obviously I always on all routes have some settings. The issue is I dunno how to code If I've got some settings from observable - use them, if not - use default this._settings ...

ngFor doesn't fires after update depending variable in Angular2

I have 2 components: CommandListComponent and CommandLineComponent. Inside of a CommandListComponent template i handle a click event on a text string:
CommandListComponent template:
<li *ngFor="#command of commandList" class="b-command-list__command"><span (click)="checkCommand(command)" class="b-command-list__text">{{command}}</span></li>
commandlist.component.ts
import {CommandLineComponent} from "./commandline.component";
...
export class CommandListComponent {
commandLineComponent: any;
constructor(private _commandLine: CommandLineComponent) {
this.commandLineComponent = _commandLine;
}
checkCommand(command: string): void {
this.commandLineComponent.add(command);
}
}
When click is fired i pass choosen command to add method of a CommandLineComponent:
export class CommandLineComponent {
commands: string[] = [];
add(command: string): void {
if (command) this.commands.push(command);
console.log(this.commands);
}
}
And within a template of a CommandLineComponent i print a list of a commands with *ngFor:
<li *ngFor="#command of commands" class="b-command-textarea__command">{{command}}</li>
But *ngFor doesn't fires when i choose a command and commands array of a CommandLineComponent updated. So, data binding is not working. commands array updates successfully:
Thank you for help.
The problem is the way you reference the commandLineComponent component. If there is a relation between them you could use the ViewChild decorator
class CommandListComponent {
#ViewChild(CommandLineComponent)
commandLineComponent: any;
(...)
}
If not, you need to use a shared service to share the commands list between these two components. Something like that:
export class CommandService {
commands:string[] = [];
commandAdded:Subject<string> = new Subject();
add(command: string): void {
if (command) {
this.commands.push(command);
this.commandAdded.next(command);
}
console.log(this.commands);
}
}
You need to define the service when bootstrapping your application and both components can inject it.
class CommandListComponent {
constructor(private commandService:CommandService) {
}
}
checkCommand(command: string): void {
this.commandService.add(command);
}
The CommandLineComponent component will be notified of a new command like this and can update the view accordingly:
class CommandLineComponent {
constructor(private commandService:CommandService) {
this.commandService.commandAdded.subscribe(command => {
// Update the list displayed in the component...
});
}
}

Categories

Resources