ES6 class calling a method within a class with bind vs call? - angularjs

I have a class written in ES6 and I have a directive "action" which needs to access a controller value called "selected". This controller value "selected" is updated by another directive "grid". ( 2 way binding)
I need to pass "selected" value from the controller that has been updated by Directive "grid" to Directive "actions" on-select . I have tried to pass by doing a "bind" but i get an type error as "cannot read actionHandler of undefined"
I am not sure what is the best way to handle this , such that when the "selected" value has been updated by the "grid" directive, the actionEvent is triggered with the updated value from the controller. The directives are working correctly and i am able to see that it breaks on breakpoints.
Here is what i have in HTML
<div class="col-xs-9">
<action options="ctrl.Actions" on-select="ctrl.actionEvent">
</action>
</div>
<div class="col-xs-12">
<grid config="ctrl.gridOptions" data="ctrl.data" selected="ctrl.selected"></grid>
</div>
In the Controller,
class CheckC {
constructor($scope) {
this.$scope = $scope;
this.selected = this.$scope.selected;
}
actionEvent() {
this.actionHandler.bind(this);
}
actionHandler(item, event) {
let selection;
let model = this.selected;
if(model) {
item.id = 1;
}
}
}

First of all, don't be confused between .bind() and .call().
First returns a new function instance, that can be called later, but with preserved this.
Second calls function immediately, but modifies context of this only for this call.
Read this answer for more information
You are passing a reference to actionEvent method. At the moment of call, the reference to original controller object is already lost.
To preserve the reference, you need to save it first in constructor
class CheckC {
constructor($scope) {
this.$scope = $scope;
this.selected = this.$scope.selected;
//bind method here
this.actionEvent = this.actionEvent.bind(this);
}
actionEvent(item, event) {
// this here will be always contain a referene to class instance
this.actionHandler(item, event);
}
actionHandler(item, event) {
let selection;
let model = this.selected;
if(model) {
item.id = 1;
}
}
}
Also in your code actionEvent method seems redundant. Consider to recfactor code and pass actionHandler directly. (Bu don't forget to update .bind() call, it should bind actionHandler after).

Related

Angular : bootstrap-modal value doesn't bind on first time

I am having an issue with a modal binding ( parent to child )
I have an array which I fill from a web service.
The Array is then passed to modal.
The array is empty on first call, but not on following calls and on click I get [n-1] index value everytime instead of [n] index value.
What can be the reason for this ?
Child Component
car-detail.html
<div class="modal-body">
<input [(ngModel)]="car.Id" type="number">
<input [(ngModel)]="car.make" type="string">
</div>
Car-detail-component.ts
export class CarDetailComponent implements OnInit {
#Input() public car;
constructor(private appService: AppService,
public activeModal: NgbActiveModal,
private route: ActivatedRoute,private location: Location) { }
ngOnInit() {
}
Parent Component
car-component.html
<label *ngFor="let car of carslist" (click)= "getCarDetailsById(car.id);open()">
{{car.make}}
<br/>
</label>
I get the id and pass it to a web service call and I open the modal.
**car.component.ts**
carDetailsList : any =[];
public getCarDetailsById(id:number):Car[]{
this.appService.getCarDetailsById(id).subscribe(
car =>{
this.carDetailsList=car;
for (let i = 0; i < this.carDetailsList.length; i++) {
console.log(car.make);
}
}
);
return this.carDetailsList;
}
On first call, array is empty, not on subsequent calls.
I get the previous car details each time when the modal opens.
Thanks for the help.
You are opening the modal before your service returns the value. You should open the model inside the subscribe method. Thats why you are not getting the value first time, on subsequent calls you always gets the old value. Remove the open() from your parent html template.
public getCarDetailsById(id:number):Car[]{
this.appService.getCarDetailsById(id).subscribe( car =>{
this.carDetailsList=car;
for (let i = 0; i < this.carDetailsList.length; i++) {
console.log(car.make);
}
this.open();
return this.carDetailsList;
});
}
Your problem is actually that you execute a method asynchronous (the subscription is only executed when the server return the data asked) into a function executed synchronously so your function will end and reach the return before the subscription execution is reached that's why when your modal will pop up the array will be empty on the first call.
public getCarDetailsById(id:number):Car[]{
this.appService.getCarDetailsById(id).subscribe(
// 1 when the server return data the code here is executed
car =>{
this.carDetailsList=car;
for (let i = 0; i < this.carDetailsList.length; i++) {
console.log(car.make);
}
}
);
// until server return data the execution continue so the return statement is reached before the code after point 1 is executed
// the variable returned will be null in that case.
return this.carDetailsList;
}
try instead to make like this:
async getCarDetailsById(id:number):Promise<Car[]> {
var asyncResult = await this.appService.getCarDetailsById(id).toPromise();
console.log('this console.log will be executed once the async call is fetched and the value of the returned promise is stored in asyncResult');
return asyncResult;
}
then in the returned promise in the .then function bind the returned value in one of your value of type Car[].
and you call the 'getCarsDetailsRecovered' function when you open your modal.
an usefull link about asynchronous calls in angular: link.

Angular 1.5 Components + ng-model $formatters and $parsers

I would like to know how to use $formatters and $parsers with angular 1.5 components. Can someone post an example.
Or is there something similar that I can use.
The following is an example of a component called example. This takes in a object that contains firstName and secondName. It then displays a combination of the firstName and secondName. If the object changes from the outside the formatter will fire followed by the render. If you want to trigger a change from the inside, you need to call this.ngModel.$setViewValue(newObject) and this would trigger the parser.
class example {
/*#ngInject*/
constructor() {}
// In the post link we need to add our formatter, parser and render to the ngmodel.
$postLink() {
this.ngModel.$formatters.push(this.$formatter.bind(this));
this.ngModel.$parsers.push(this.$parser.bind(this));
this.ngModel.$render = this.$render.bind(this);
}
// The formatter is used to intercept the model value coming in to the controller
$formatter(modelValue) {
const user = {
name: `${modelValue.firstName} ${modelValue.secondName}`
};
return user;
}
// The parser is used to intercept the view value before it is returned to the original source
// In this case we want to turn it back to it's original structure what ever that may be.
$parser(viewValue) {
// We know from out formatter that our view value will be an object with a name field
const namesParts = viewValue.name.split(' ');
const normalisedUser = {
firstName: namesParts[0],
secondName: namesParts[1],
};
return normalisedUser;
}
// This will fire when ever the model changes. This fires after the formatter.
$render() {
this.displayName = this.ngModel.$viewValue.name;
}
}
class ExampleComponent
{
bindings = {};
controller = Example;
require = {
ngModel: 'ngModel',
};
}
component('example', new ExampleComponent());
// Template for example component
<span>
{{ $ctrl.displayName }}
</span>
// Using the above component somewhere
<example ng-model="userModel"></example>

angular-dialog-service update parent scope data object

I've a template:
<p class="text-right">
<a ng-click="editTherapeuticProposal(meow.accepted_tp)" class="fa fa-pencil"></a>
</p>
which calls the editTherapeuticProposal function defined in its controller, passing it the meow.accepted_tp object (here I use angular-dialog-service: https://github.com/m-e-conroy/angular-dialog-service):
// here tp is equal to meow.accepted_tp
$scope.editTherapeuticProposal = function(tp) {
dialogs.create('surgeon/templates/create_edit_therapeutic_proposal.tpl.html', 'SurgeonCreateEditTherapeuticProposalCtrl', {scope: $scope, tp: tp}, { copy: false });
};
tp is an object.
Then in the dialog controller I display a form in order to let the user modify tp. I do some stuff, the relevant ones are:
// data is the object received by the dialog controller: {scope: $scope, tp: tp}
if(typeof data.tp != 'undefined') {
$scope.therapeuticProposal = angular.copy(data.tp);
}
I copy the object to work on a different object (I don't want data to be updated if not saved)
When pressing the save button in the dialog, the following function runs:
var complete = function(tp) {
data.tp = tp;
//...
}
Ok, the problem is that meow.accepted_tp in the parent scope doesn't get updated. If I do
var complete = function(tp) {
data.tp.title = 'meow';
//...
}
Its title gets updated. There is clearly something wrong with the prototypal inheritance, I know that in order to get variables updated they should be properties of an object, but tp is already passed as an object property (of the data object). Any ideas?
Edit
After re-reading the angular-dialog-service docs, you can pass a result back using modalInstance. It sounds like this is what you want to do.
The reason your binding isn't working is because you're changing the object reference from a child scope, rather than a property on the object bound (which is why data.tp.title = 'meow' works).
Anyway, for your case, try this:
// here tp is equal to meow.accepted_tp
$scope.editTherapeuticProposal = function(tp) {
var dlg = dialogs.create('surgeon/templates/create_edit_therapeutic_proposal.tpl.html', 'SurgeonCreateEditTherapeuticProposalCtrl', {scope: $scope, data: data}, { copy: false });
dlg.result.then(function(tp) {
// Get the result and update meow.accept_tp
$scope.meow.accepted_tp = tp;
});
};
Then in the dialog, when you complete, do:
var complete = function(tp) {
$modalInstance.close(tp);
}
For an example, see http://codepen.io/m-e-conroy/pen/rkIqv, in particular the customDialogCtrl (not customDialogCtrl2) is what you want.

Passing JSON object as parameter from View to Controller function?

Basically I've a panel called DummyPanel, Now on dummypanel initialize event I've called a controller function like as follows:
var me = component;
var fieldCollection =
{
"Order" : 'ordNumber',
"Ref": 'refNumber'
};
me.fireEvent('myControllerFunction','Param1', fieldCollection, 'Param3');
Now I want to get fieldCollection JSON object value within function myControllerFunction, to get value from fieldCollection I'm using following code:
myControllerFunction(param1, collection, param3)
{
Ext.Msg.alert(collection.Order);
}
But it does not return anything. So please let me know how to resolve this problem!!
Any comment will appreciated!!
I'm not quite sure what it means "But it does not return anything", but I'll try.
So, your "DummyPanel" view have a alias or itemId property. In yor controller (in init() function), you need "keep track" of your view. For example:
In your view:
me.fireEvent('myEventName','Param1', fieldCollection, 'Param3');
In your controller:
init:function(){
var me = this;
this.control({
'panel[itemId=your-view-itemId]': { // call your function after event
myEventName: me.myControllerFunction
}
});
...
},
...
myControllerFunction: function(...) {
...
}
Should it not be
Ext.Msg.alert(collection["Order"])?
Or if you want to keep Ext.Msg.alert the way it is fieldCollection should be defined this way
var fieldCollection =
{
Order : 'ordNumber',
Ref : 'refNumber'
};

How can I pass a parameter to an ng-click function?

I have a function in my controller that looks like the following:
AngularJS:
$scope.toggleClass = function(class){
$scope.class = !$scope.class;
}
I want to keep it general by passing the name of the class that I want to toggle:
<div class="myClass">stuff</div>
<div ng-click="toggleClass(myClass)"></div>
But myClass is not being passed to the angular function. How can I get this to work? The above code works if I write it like this:
$scope.toggleClass = function(){
$scope.myClass = !$scope.myClass;
}
But, this is obviously not general. I don't want to hard-code in the class named myClass.
In the function
$scope.toggleClass = function(class){
$scope.class = !$scope.class;
}
$scope.class doesn't have anything to do with the paramter class. It's literally a property on $scope called class. If you want to access the property on $scope that is identified by the variable class, you'll need to use the array-style accessor:
$scope.toggleClass = function(class){
$scope[class] = !$scope[class];
}
Note that this is not Angular specific; this is just how JavaScript works. Take the following example:
> var obj = { a: 1, b: 2 }
> var a = 'b'
> obj.a
1
> obj[a] // the same as saying: obj['b']
2
Also, the code
<div ng-click="toggleClass(myClass)"></div>
makes the assumption that there is a variable on your scope, e.g. $scope.myClass that evaluates to a string that has the name of the property you want to access. If you literally want to pass in the string myClass, you'd need
<div ng-click="toggleClass('myClass')"></div>
The example doesn't make it super clear which you're looking for (since there is a class named myClass on the top div).

Resources