AngularJS v2 is not a function? - angularjs

I have a very simple piece of code to run a dynamic sortBy over my array. I am using a select with ng-model to return the correct key by which to sort. However, I can change the select once, and the orderBy works. But once I do it again, I get a very strange error
Controller
//change task sort
$scope.changeOrder = 'task_date';
$scope.changeOrder = (filterTask) => {
if (filterTask == "due") {
$scope.changeOrder = 'task_date';
} else if (filterTask == "imp") {
$scope.changeOrder = 'task_importence';
}
};
Template
<select ng-change=changeOrder(filterTask) ng-model="filterTask">
<option value="due">Due First</option>
<option value="imp">Importance</option>
</select>
<task-item ng-repeat="task in $ctrl.user.task | orderBy : changeOrder"></task-item>
Here is the error - There is nothing called "v2" in my system

Welcome to the untyped world that is JavaScript.
Your error is actually quite apparent: $scope.changeOrder becomes a function and a standard variable. Once you select a value in your select drop-down, it ceases to be a function and reverts to a standard variable. Then, you can no longer call it.
You would be wise to split this up into two variables instead. I'd recommend using $scope.orderState and $scope.changeOrder, where orderState just holds the strings and changeOrder is your function.

I think the problem is that both of your $scope variables have the same name. You try to assign a function and a value to $scope.changeOrder. Try splitting it up into two variables

Related

Angular ng repeat filter inside ng repeat

I've been trying to make a list of geozones, with a select of taxes each (not all taxes apply to all geozones).
So I did a ng-repeat for the geozones, and inside each of them a ng-repeat with all taxes. Problem is I don't know how to send the id of the geozone being filtered at the moment. This is the code right now:
<md-option ng-repeat="tax in taxClasses | filter: filterTax" value="{{ '{{ tax.id }}' }}">{{ '{{ tax.name }}' }}</md-option>
and the JS:
$scope.filterTax = function(tax, n){
angular.forEach(tax.geoZone , function(geo){
if(geo === $scope.prices[n].geozoneId){
return true;
}
});
return false;
};
Need n to be the index of the geozone, or something of the sort. Thanks in advance!
Your idea is not that far off, but using filter: is not even necessary, as the pipe | is already a filter command:
ng-repeat="<var> in <array> | <filterFunction>:<...arguments>"
Thus you can create a filter (see https://docs.angularjs.org/guide/filter for details on that)
ng-repeat="tax in taxClasses | filterTax: <geozoneIndex>"
The value form the collection will be passed as the first argument of your filterTax function. Any further argument is passed separated by a colon :.
When you use this, you have to propagate a filter method like this:
app.module(...).filter('filterTax', function(/* injected services */) {
return function (/* arguments */ input, geozoneIndex) {
// function body
}
});
Alternatively use a filter function from your scope:
// template
ng-repeat="tax in filterTaxes(taxClasses, <geozoneIndex>)"
// script
$scope.filterTaxes = function(taxClasses, geozoneIndex) {
// do the whole filtering, but return an array of matches
return taxClasses.filter(function(taxClass) {
return /* in or out code */;
});
};
This means your geozoneIndex may be either a fixed value or being pulled from a variable, that's available in that scope.
Just be aware that your filterTax function will be called a lot, so if your page is getting slow you might want to consider optimizing that filtering.

Creating orderBy expression that runs a function in ng-repeat

I have an array of people with firstNames and lastNames. I'm want to ng-repeat through the them and then orderBy: 'lastName'. This works fine.
However, some persons in my people array only have a first name. I want to be able to order by the last name, and whenever it finds someone with a only a first name, to treat that first name as a last name, ie. the initial of the first name is alphabetised with the rest of the last names (hope that makes sense...)
Is it a case of writing a function in my controller and then calling that variable in the orderBy: expression? I've tried that and it didn't work:
Controller:
self.nameOrder = function(thisOne) {
if (thisOne.lastName == null) {
thisOne.firstName == thisOne.lastName;
}
}
View:
<p ng-repeat="name in people | orderBy: 'lastName.nameOrder()' ">{{lastName}}</p>
I'm aware the above is probably totally wrong, but I thought it better to at least show what I've been attempting so it's clearer what my intentions are :/
Any help would be appreciated. Thanks in advance.
The function is supposed to be a getter, i.e. to return a value that orderBy will use to compare all the users with each other.
Your function doesn't return anything. Moreover, you're not passing the function as argument to orderBy. The code should be:
self.nameOrder = function(thisOne) {
// return lastName, or firstName if lastName is null
return thisOne.lastName || thisOne.firstName;
}
and the html should use
ng-repeat="name in people | orderBy:vm.nameOrder"
(assuming vm is the alias used for your controller)
Maybe the problem is with:
thisOne.firstName == thisOne.lastName;
That line should be:
thisOne.firstName = thisOne.lastName;

What is the criteria for md-select to check duplicate options in md-options

I have been using Angular Material for a while in my project. While using md-select, I am stuck to a problem wherein I am getting Duplicate md-option values error.
I am aware that md-options takes unique values and I am assigning an array to md-options. This is however, an array of objects. So I would like to know what is the criteria that is used to differentiate objects. The API do not say much about it.
My use case demands to change md-options of an md-select, based on selection from another md-select. So I am watching the selection of first md-select and firing a watch on its change and updating md-options of second md-select.
Below is the approach I am using to assign array to md-options:
$scope.$watch('search.selectedTrades', function(newTrades, oldTrades) {
if ((newTrades.length === 0)) {
$rootScope.search.selectedTrades = oldTrades;
return;
}
if ($rootScope.search.selectedTrades && $rootScope.search.selectedTrades.length > 0) {
if (!$rootScope.identity.isClusterManager) {
$rootScope.search.selectedTrades = newTrades;
SearchFilterData.setSelectedTrades(newTrades);
$rootScope.search.selectedClusters = [];
$scope.clusters = [];
$scope.subareas = [];
var clusterKeys = [];
$rootScope.search.selectedTrades.forEach(function(t) {
t.lstClusters.forEach(function(c) {
if (clusterKeys.indexOf(c.ClusterKey) == -1) {
clusterKeys.push(c.ClusterKey);
$scope.clusters.push(c);
}
})
})
}
} else {
$scope.clusters = [];
$scope.subareas = [];
$rootScope.search.selectedClusters = [];
$rootScope.search.selectedSubAreas = [];
SearchFilterData.setSelectedTrades($rootScope.search.selectedTrades);
}
});
In above code, clusterKey is a unique entity for each object. So I am using it to push unique values into array.
This however happens on few random scenarios, after I have selected and de-selected various options. Please advise what I am doing wrong and what is the criteria for marking two objects duplicate
You did not provide your markup, so I cannot be sure, but in my case the problem was caused by omitting the double curleys on the 'value' attribute in the md-option tag.
This is bad: Notice the missing curly braces
<md-option ng-repeat="item in vm.list" value="item.id">{{item.text}}</md-option>
This is not:
<md-option ng-repeat="item in vm.itemlist" value="{{item.id}}">{{item.text}}</md-option>
I believe the reason that this fails is that each item will be placed into the option list will be given a value of 'item.id' (literally). It will fail on the second iteration of the repeat.
Using the curly braces causes the value in 'item.id' to be used.
Hope this helps.
Try using ng-value instead of just value attribute.
<md-option ng-repeat="item in vm.list" ng-value="item.id">{{item.text}}</md-option>

rootScope is upating on scope variable update

I have created a rootScope variable like
$rootScope.globalData = data;
$rootScope.globalData.chillerConditions.HeatSource.Value = "ST"; //Default Value
$scope.chillerConditions.HeatSource.Value = 1; //Default Value
where data is my returning value from api. Also create a scope variable which is a object contains a list of items.
$scope.chillerAttributes = data.ObjCandidateListChillerAttributes;
$scope.chillerConditions = data.ObjCandidateListConditions;
On HTML I have:
<select ng-model="chillerConditions.HeatSource.Value" style="width:53%;" ng-options="item.Id as item.Description for item in ValidRatingHeatSource" ng-change="heatSourceChanged()" id="ddRatingHeatSource" class="form-control search-select designComboboxHeight" data-container="body"></select>
Here ValidRatingHeatSource is
$scope.ValidRatingHeatSource = \*list of items*\
On change of Drop Down I have written an function. In that
if($scope.chillerConditions.HeatSource.Value == 2)
{
$rootScope.globalData.chillerConditions.HeatSource.Value = "HW";
}
else
{
$rootScope.globalData.chillerConditions.HeatSource.Value = "ST";
}
Till now was the my current code.
Issue is :
When the above function is called then whenever current $rootScope varible i.e. $rootScope.globalData.chillerConditions.HeatSource.Value is changed to "HW" or "ST" it also changing $scope.chillerConditions.HeatSource.Value to "HW" or "ST".
Why so?
Is there any inbuilt functionality in angularjs?
Please suggest if I am making any mistake? New suggestion are also welcome.
This behavior is the way JavaScript works and has nothing to do with AngularJS. JavaScript is an object-oriented (prototype-based) language where objects are addressed by reference and not by value. E.g. assign car2 to car1 and both of them will reference the same object (JSFiddle)
var car1 = {make: "Audi"}
var car2 = car1;
car2.make = "Toyota";
So in your case, $rootScope.globalData.chillerConditions.HeatSource and $scope.chillerConditions.HeatSource are the same object.
Rather, it seems like you want to create a copy. You can do so with angular.Copy
$scope.chillerAttributes = angular.copy(data.ObjCandidateListChillerAttributes);
$scope.chillerConditions = angular.copy(data.ObjCandidateListConditions);
In your example u have both ng-model and ng-change, so:
1. User change value in select.
2. $scope.chillerConditions.HeatSource.Value changes (ng-model)
3. heatSourceChanged starts (ng-change) -> $rootScope.globalData.chillerConditions.HeatSource.Value changes
So everything works as should...

order by of angularjs for sorting based on three different parameters

I am using order by of Angular for sorting but I want to sort data based on three different fields, i.e success, in-progress and failed, without using any constant and variable directly from in-built function. Is there any way?
If you want you can call a function to sort your data as you want.
you can call following custom 'orderBy'
$scope.sort = function(column,reverse) {
$scope.persons = $filter('orderBy')($scope.persons,column,reverse);
};
'$scope.persons' - is your collection of data you need to order
'column' - name of the column you want to sort data
'reverse'- bool value to reverse the order
and you can call this 'sort' function from your View(HTML) as below.
data-ng-click="sort('name',nameReverse)"
you can pass array of fields to orderBy
<div ng-repeat="row in list | orderBy:['param1','param2']">
....
</div>
EDIT
to do it in javascript
$scope.sortedList = $filter('orderBy')(list,['param1', 'param2']);
EDIT
in smart table
function customSortAlgorithm(arrayRef, sortPredicate, reverse) {
//do some stuff
return sortedArray;
}
scope.globalConfig = {
sortAlgorithm: customSortAlgorithm
};

Resources