Parent to Child communication not working as expected - javascript

I have two components, a parent and a child.
Every time I click on a certain button in the parent component, an object gets populated and sent to the child component via an #Input decorator.
The issue here is that even though the changes to the object are detected in the child component, the data that I'm trying to populate will only appear on even clicks.
This is what my code currently looks like:
parent.component.ts
private toSend = {};
public sendToChild() {
var objectToSend = {
headerMessage:`Title`,
bodyMessage:"Body"
};
this.toSend = { ...objectToSend };
$('#childComponent').appendTo("body").modal('toggle');
}
parent.component.html
<child-component #childComponent [data]="toSend">
</child-component>
child.component.ts
public headerMessage: string = "";
public bodyMessage: string = "";
#Input('data')
set data(data: any) {
if (data !== undefined && data.length !== 0) {
this.setData(data);
}
}
private setData(el): void {
for (let key in el) {
switch (key) {
case "headerMessage":
this.headerMessage = el[key];
break;
case "bodyMessage":
this.bodyMessage = el[key];
break;
}
}
}
child.component.html
<div class="modal" tabindex="-1" role="dialog" id="childComponent">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header" style="padding: 1rem;">
<h5 class="modal-title">{{headerMessage}}</h5>
<button type="button" class="close" id="btn-close-id" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="modal-body-id">
<div class="container-fluid">
<div class="col-12">
{{bodyMessage}}
</div>
</div>
</div>
<div class="modal-footer" style="padding: 1rem;">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
I also tried with ngOnChanges and I was able to print the object I was sending every time but I had the same problem.

You could force the detection by using the ChangeDetectorRef
import { ..., ChangeDetectorRef } from '#angular/core';
Add it on your constructor
constructor(private _cdr: ChangeDetectorRef) {...}
And in your child component
this._cdr.markForCheck();
// or if not working
this._cdr.detectChanges()
Another thing, JQuery is not recommanded with Angular. If you want to use modal in your project, you should check Angular material modal or make your own modal component.

Related

Passing value in Component Template - knockout JS

I have a component where I'm handling all the errors / error message. I debug it and successfully passed the error string in variable, but I can't display in template
Here is my code
export class ErrorHandlerComponent {
get ComponentName() {
return "error-handler";
}
constructor() {
this.errorMessage = ko.observable();
if (!ko.components.isRegistered(this.ComponentName)) {
ko.components.register(this.ComponentName, {
viewModel: ErrorHandlerComponent,
template:
`
<div class="modal fade" id="modalError" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" data-backdrop='static' data-keyboard='false'>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel"><i class="fa fa-exclamation-circle"></i> Error occurred</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body" data-bind="text: errorMessage">
</div>
<div class="modal-footer modal-btn">
<button type="button" class="btn btn-warning btn-lg" data-bind="click: ClickRefreshPage">Refresh</button>
</div>
</div>
</div>
</div>
`
});
}
}
Show(msg) {
this.errorMessage(msg);
$('#modalError').modal('show');
}
Close() {
$('#modalError').modal('hide');
$(".modal-backdrop").remove();
$("body").css({ 'padding-right': '0px' });
}
ClickRefreshPage() {
alert('refreshing...');
}
}
when I call Show function, the parameter is passed and this.errorMessage() had a value, but in text: errorMessage I can't display it.
john is right though, this refers to the ErrorHandlerComponent at the time of components.register and that is obviously always empty.
What I'm missing is an observable parameter passed through from the parent via the components' param property. if you put that in your constructor, ko will link everything through and passing a validationmessage to the component will be displayed.
https://jsfiddle.net/ezw9q02x/1/

How to change click event of button according to different situations on Angular 9

I use Angular 9 to develop a web application. So I need to use Bootstrap Modals
like as
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{MODAL_TITLE}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>{{MODAL_BODY}}</p>
</div>
<div class="modal-footer">
<button type="button" (click)="function_1()" class="btn btn-primary">SUBMIT</button>
</div>
</div>
</div>
</div>
I called a function when I clicked the Submit button. Well, I just want to change the function that submit function called in different cases.
So the thing that I just want to do in ts file:
MODAL_BODY = "BODY STRING";
MODAL_TITLE: FUNCTION ;
modal.show();
function1(){
console.log("function 1 works"}
}
function2(){
console.log("function 2 works")
}
I want to use function2() instead of function1() sometimes.
Then I want to change function that submit button called in different situations just like as:
<button type="button" (click)="function_1()" class="btn btn-primary">SUBMIT</button>
//some times i need to use this button below. so I need to change it from ts file
<button type="button" (click)="function_2()" class="btn btn-primary">SUBMIT</button>
Is there anyway to do that? from ts file or dynamically?
Thanks in advance.
If we want to change dynamically the button itself in the template the *ngIf structural directive can be used. (As chrnx writes in the question comment.)
To have an example for this take a look at the example below.
In Angular template code:
<p>
Used button
<br />
<button *ngIf="displayedButton === 1" (click)="onButtonOneClick()">
ButtonOne
</button>
<button *ngIf="displayedButton === 2" (click)="onButtonTwoClick()">
ButtonTwo
</button>
</p>
<p>
Change button state
<br />
<button (click)="onChangeStateClick()">Change</button>
</p>
In component code:
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
displayedButton = 1;
onButtonOneClick() {
console.log("Button 1 click");
}
onButtonTwoClick() {
console.log("Button 2 click");
}
onChangeStateClick() {
this.displayedButton = this.displayedButton === 1 ? 2 : 1;
}
}
Stackblitz example:
https://stackblitz.com/edit/angular-ivy-irmbkk?file=src/app/app.component.ts

show particular data in pop up modal in vue.js

this is regarding Vue.js question
i'm trying to open bootstrap model form inside the Vue template
i use two vue template components,
this sub component call inside this competence and pass data from this to sub component
this component use for show particular (load one by one products) model data
so i need to show one by one products data on the model form (when product 1 show name 'Abc') like this
but i cant do this.. all implementation are done and working fine
but cant show the particular data on the model form
show it only first loop value (i have 3 products all load in the table,but when click edit button first product show correctly,but click 2nd product show first product data)
but when i call console.log function and view when open the model show particular data in the console, but not showing its on the model form
why it that
i put my code segment in the below
example-component
<tbody >
<tr div v-for="invoices in invoice">
<th class="invoice_name ">{{invoices.p_name}}</th>
<td class="unit">
<sub-com :pID=invoices.p_id :invoice=invoices :invoiceID=invoice_id></sub-com>
</td>
</tr>
</tbody>
sub-com
<template>
<div>
<div class="form-group">
Refund
</div>
<div class="col-md-6">
<div class="modal fade" id="refundModel" tabindex="-1" role="dialog" aria-labelledby="addNewLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<input v-model="form.name" type="text" name="name" placeholder="Name" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
this is sub.vue script segment
<script>
export default{
data(){
return{
form: {
name:''
}
}
},
props: {
pID: String,
invoiceID:String,
invoice:{},
}
methods: {
refundMethod(invoices){
this.form.name = invoices.p_name;
console.log(invoices.p_name);
$('#refundModel').modal('show');
}
}
There are a couple of issues that might clear things up.
First you need to add a key to your template v-for loop:
<tr v-for="invoices in invoice" :key="invoices.p_id">
Second you are using jquery to trigger the modal which could work but you will have to generate unique ids for each div:
<div :id="'refundModel_'+pID">
A more Vue way to do this is to use the bootstrap data-show attribute and link it to a Boolean modal property in your data:
<div :data-show="modal" :id="'refundModel_'+pID">
export default {
data(){
return{
modal : false,
form: {
name:''
}
}
},
props: {
pID: String,
invoiceID: String,
invoice: Object,
}
methods: {
refundMethod(invoices){
this.form.name = invoices.p_name;
console.log(invoices.p_name);
this.toggleModal()
}
toggleModal () {
this.modal = !this.modal
}
}
}

How to close bootstrap 4 model by calling a function in typescript

I am using bootstrap 4 modal followed by the example
Below is the template code:
<div class="container">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">
Open
</button>
<div class="modal" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Modal Heading</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
Modal body..
</div>
<button type="button" class="btn btn-danger" (click)="save()">Save</button>
</div>
</div>
</div>
</div>
Here on clicking the open button i am opening dialog window,On clicking save button i will call save() method.In .ts i have written some conditions inside save method like this:
save(){
if(condition){
......don't close the modal....
} else{
......close the modal....
}
}
How can i close the modal by calling the save() method in typescript?
Stackblitz DEMO
The proper way to do this in Angular/Typescript is to use ViewChild.
First import ViewChild :
import { ViewChild } from '#angular/core';
Add this line in your component, with the selector of your choice :
#ViewChild('myModalClose') modalClose;
Add the tag myModalClose in your html (here we target the close button) :
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" #myModalClose class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
...
In your save() method :
this.modalClose.nativeElement.click();
Working Stackblitz
I have another solution without trick on close button.
First step you need install jquery and bootstrap by npm command.
Second you need add declare var $ : any; in component (important step)
And use can use $('#myModal').modal('hide'); on onSave() method
Demo https://stackblitz.com/edit/angular-model-bootstrap-close?file=src/app/app.component.ts
You can use ng-bootstrap alternatively to control all this in your component class.
ng-bootstrap
In typescript component code you need to inject NgbActiveModal in the constructor like this:
constructor(public activeModal: NgbActiveModal){}
and then in save method, you can just close it:
save(){
if(condition){
......don't close the modal....
} else{
this.activeModal.close();
}
}
Use Ngx bootstrap https://valor-software.com/ngx-bootstrap/#/modals
here is the common component for ngx bootstap modal.
<!-- Contains insides of a host element e.g. button text or link text -->
<ng-content></ng-content>
<!-- Dialog box content -->
<ng-template #modalWrapper>
<!-- Header template -->
<div class="modal-header">
<h3 class="modal-title pull-left">{{ modalTitle }}</h3>
<button type="button" class="close pull-right" aria-label="Close" (click)="closeModal()">
<span aria-hidden="true">×</span>
</button>
</div>
<!-- Body template that consist of provided template reference content -->
<div class="modal-body">
<ng-container [ngTemplateOutlet]="modalContent"></ng-container>
</div>
</ng-template>
Component.ts
export class AppModalComponent implements OnInit, OnChanges {
/** Template Reference that is displayed as part of dialog box content */
#Input('app-modal') public modalContent: TemplateRef<any>;
/** Title of the dialog box */
#Input() public modalTitle: string = '';
/** Defines if modal is open */
#Input() public isOpenByDefault: boolean = false;
/** Modal reference */
public modalRef: BsModalRef;
/** Content config object */
#Input() public config: ModalOptions = {};
/** Event on modal open */
#Output() public open = new EventEmitter<void>();
/** Event on modal close */
#Output() public close = new EventEmitter<void>();
/** Wrapper template reference */
#ViewChild('modalWrapper') public content: TemplateRef<any>;
/** Injects modalService to manipulate modals */
constructor(private modalService: BsModalService) { }
public ngOnChanges(changes: SimpleChanges) {
if (changes.config) {
this.modalService.config = changes.config.currentValue;
}
}
public ngOnInit(): void {
this.isOpenByDefault ? this.openModal() : this.closeModal();
}
/** On click of host element opens a modal with provided modal content */
#HostListener('click')
public openModal(): void {
this.modalRef = this.modalService.show(this.content, this.config);
this.open.emit();
}
/** On click of close button closes the modal */
public closeModal(): void {
if (this.modalRef) {
this.modalRef.hide();
this.close.emit();
}
}
}
Usage :
<button [app-modal]="template" modalTitle="Modal Title" [isOpenByDefault]="false" #modalRef>Open modal</button>
<ng-template #template>
<h4>Hello world</h4>
</ng-template>

Knockout Component View Not Updating When Its ViewModel Observable Changes

I have a component setup to use AMD to get the html template and viewmodel code. Everything works fine. The component loads when it is supposed to and behaves fine with the params passed to it. The problem is I defined an observable in the viewModel whose value shows up in the template view, but when the observable's value changes the text on the view does NOT change. Can anyone explain what is going on here? The text I am trying to bind to is modalTitle. When the modal loads its title is 'TEMP' but if I go to the console and type 'window.modalTitle()' I get 'CREATE REPORT SCHEDULE'. It's like the view is getting the first value of the observable and then ignoring it after that. Is there anyway I can force it to look for updates?
ViewModel: (schedules.component.js)
define(['knockout'], function (ko) {
console.log('schedules hit');
loadCss('schedules');
function SchedulesViewModel(params) {
this.scheduledItems = params.scheduledItems;
this.itemName = params.itemName;
this.modalTitle = ko.observable("TEMP");
window.modalTitle = this.modalTitle;
}
SchedulesViewModel.prototype.initiateAddScheduledItem = function () {
this.modalTitle("CREATE " + this.itemName + " SCHEDULE");
$('#schedulesModal').modal('show');
};
SchedulesViewModel.prototype.removeSelectedScheduledItem = function () {
this.chosenValue('dislike');
};
window.ReportsApp.SchedulesViewModel = SchedulesViewModel;
return SchedulesViewModel;
});
View Template
<div id="schedulesModal" class="modal fade lcmsModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<!--<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>-->
<img src="/Content/images/modalASLogo.png" style="float: right;" />
<h4 class="modal-title" data-bind="text: modalTitle()">Test Title</h4>
</div>
<div class="modal-body">
<p>One fine body ...</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">CANCEL</button>
<button type="button" class="btn btn-primary">SAVE</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- /Bootstrap Modal -->
It does not get changed because this.itemName has not been defined as an observable. it is better to define a computed observable which will automatically update whenever any observables change.
Instead of using prototype to add methods, you can use knockout function which foes it for you.
Example :https://jsfiddle.net/kyr6w2x3/34/
function SchedulesViewModel(params) {
var self = this ;
self.scheduledItems = ko.observable(params.scheduledItems);
self.itemName = ko.observable(params.itemName);
self.modalTitle = ko.observable("TEMP");
self.chosenValue= ko.observable();
self.modalTitle = ko.computed(function() {
return "CREATE " + self.itemName() + " SCHEDULE" ;
}, self);
// you can change below to show your modal whenever you want
$('#schedulesModal').modal('show');
self.removeSelectedScheduledItem = function (){
self.chosenValue('dislike');
}
}
ko.applyBindings(new SchedulesViewModel({scheduledItems:"scheduledItems" ,itemName : "itemName" }));
Update : yes you can have multiple view models or better to say nested view models. Look at the new example and see how you can communicate between your models.https://jsfiddle.net/kyr6w2x3/35/

Categories