angularjs change ng-repeat variable and call function - angularjs

I am trying to toggle a class when clicking on the Save button but also call a function. I am using coffeescript. The function gets called but the variable never gets set to false.
div(ng-class="{'someclass':setListFocus}, ng-repeat="item in items")
a(ng-click="setListFocus=false;someFunction();")
span(class="gs-desktop") Save
// Delete user data.
a(ng-click="setListFocus=true")
span Edit

I assume that your small example is missing all kinds of data.
I would recommend either using controllerAs, for example:
<div ng-controller="myController as vm">
and then changing your bindings to vm.setListFocus.
Or another option would be to change your controller code like so:
function myController($scope) {
$scope.list = { focus: false };
}
And change your binding to list.focus instead. This will solve your scope inheritance problems.

You could begin someFunction() with toggling the value of setListFocus
$scope.someFunction = function(){
$scope.setListFocus = !$scope.setListFocus;
// ... rest of someFunction()
}
And then the angular template would be just this:
a(ng-click="someFunction()")
Another thing you could do is set up a toggle ListFocus function, that can take an optional callback parameter. This means you can toggle the listfocus between true or false, and optionally execute another function. I'm not going to continue answering in coffeescript because I'm not that familiar, and not everyone is.
$scope.toggleListFocus = function(andThen = false){
$scope.listFocus = !$scope.listFocus;
if(andThen==false) andThen();
}
//usage..
//toggle $scope.listFocus
enter code here
a(ng-click="toggleListFocus()")
//toggle then someFunction()
a(ng-click="toggleListFocus(someFunction())")

Related

Boolean in view is true but in controller false angularJS

In parent scope (external one which wrap entire webapp) I define boolean variable if you're logged in.
$localForage.getItem('authorization')
.then(function(authData) {
if(authData) {
$scope.authentication.isAuth = true;
//token is added in http interceptor
} else {
$scope.authentication.isAuth = false;
}
}, function(){
console.log("error with getting authorization localForage after refresh");
}
);
It is basically working correct. Now I can build UI basing on this boolean with ng-if="$parent.authentication.isAuth". I also can display true/false like so <p>{{$parent.authentication.isAuth}}</p> in view and it is also working correct.
In one controller I want to use this boolean inside controller not in view. So I do if($scope.authentication.isAuth){ or if($scope.$parent.authentication.isAuth){ (I tried both) and this condition go to else even when <p>{{$parent.authentication.isAuth}}</p> in view of controller where I'm using this condition display true.
So it
<div ng-controller="ctrl">
<p>{{$parent.authentication.isAuth}}</p>
</div>
display true in paragraph and within controller named ctrl this condition if($scope.authentication.isAuth){ go to else... Why such weird behaviour?
if($scope.authentication.isAuth){
console.log($scope.authentication.isAuth);
console.log('true');
} else {
console.log($scope.authentication.isAuth);
console.log('false');
}
And it console.log false and "false" string.
As you set the value async ($localForage.getItem('authorization').then ...), it will be first false, then it might be set to true after the async operation has finished. Your view updates the value accordingly - everytime it changes - that's why you say it shows true in the view. It will be first false there, and after a few milliseconds will change to true. You don't neccessarily see that, but it still happens. The console.log is run before that async change happens, and as the if > else only runs once, it will only log the variable's state before it has been set to true.

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

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

Using ng-init to call controller functions and passing vars between controllers

The below method works...however, it only does so with $timeout added to the tabList() function. The ng-init is executing before the DOM renders thus the document.getElementById('')'s is coming back as undefined. I must force a delayed timer of 1 to 2 seconds until the DOM loads before appending the elements. This is not optimal but it does work. I am looking for another method that is cleaner and not dependent on delayed execution.
angular.module('starter.controllers', [])
.constant('constants', {
tabColors: {
curID:0,
},
})
.controller('TabsCtrl', function($scope,Tabs,constants) {
$scope.constants = constants;
$scope.tabList = function() {
var tID = $scope.constants.tabColors ;
console.log(tID.curID) ;
if (tID.curID) {
$timeout(function() {
document.getElementById('bike_tabItem_'+tID.curID).style.color = 'green' ;
document.getElementById('bike_tabItem_'+tID.curID).style.color = 'black' ;
},1000) ;
}
}
})
.controller('TabDetailCtrl', function($state,$scope,$stateParams,Tabs,constants) {
$scope.constants = constants; //make it available constants on html
$scope.itemSelect = function(thisID) {
$scope.constants.tabColors.oldID = $scope.constants.tabColors.curID ;
delete $scope.constants.tabColors['tabID_'+$scope.constants.tabColors.curID] ;
$scope.constants.tabColors.curID = thisID ;
$scope.constants.tabColors['tabID_'+thisID] = 'green' ;
}
})
// In HTML on Tab.html :
<ion-item cache-view="false" id="tab_tabItem_{{tab.tabID}}" ng-init="tabList()">
// In HTML on Tab-Detail.html
<button id="tab_button" class="button button-small button-outline button-positive" ng-click="itemSelect({{tab.tabID}});">
Select this item
</button>
On a side note, another way to call tabList() is like:
ng-init="tabList('{{tab.tabID}}')"
This gives you a way of passing values through the ng-init which, unlike my above call, gives you better control without having to define globals. Though you would still need a global for the above to track which element was turned green so you could then set it back to black before setting the new element green.
As said in the AngularJS documentation you should avoid the use of ng-init.
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
To passes variables between controllers you can use a provider.

Firebase $save doesn't work inside an event handler function

I must be missing something really basic. I have an input box where the list name is entered. The name is then saved to the Firebase.
When using $watch, it works just fine. However, if done through ng-keyup event, it returns the following error
TypeError: undefined is not a function.
What am I missing?
HTML:
<input id="which_list" ng-keyup="enterThis($event)" ng-model="which_list.name" >{{which_list.name}}</span>
Controller:
$scope.which_list = sync.$asObject();
$scope.$watch('which_list.name', function() {
gDataService.which_list.name= $scope.which_list.name;
$scope.which_list.$save() // THIS WORKS
// $scope.which_list => d {$$conf: Object, $id: "id", $priority: null, name: "to1_list", $save: function…}
.then(function(){
console.log($scope.which_list.name);
});
});
$scope.enterThis = function(event){
if (event.keyCode === 13) {
gDataService.which_list.name= $scope.which_list.name;
$scope.which_list.$save(); // THIS DOESN't WORK
// $scope.which_list = Object {name:"list_name"}
}
};
EDIT: In the comment, I included the value of $scope.which_list shown at the breakpoint.
Currently as you are changing in scope which_list converting to plain old JavaScript objects (POJO), I believe you need to unable 3 way binding between scope variable and $asObject().
Code
var which_list = sync.$asObject();
// set up 3-way data-binding
which_list.$bindTo($scope, "which_list");
Update
Also as you are using $scope.which_list object which contains name and other property,So do initialize it on starting of your controller like
$scope.which_list = {}
Hope this could help you, Thanks.

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