Im trying to search if a value exits in an array. Im using ui-select to capture the values, and i create function to verify if the value exists and all works great but the console show me many times Cannot read property 'indexOf' of undefined.
here the select codes used to capture the data
<ui-select multiple ng-model="entrevistainicialModel.personalidad"
ng-disabled="false"
search-enabled="true"
append-to-body="true"
class="form-control ">
<ui-select-match placeholder="Comportamientos">
{{$item.comportamiento}}
</ui-select-match>
<ui-select-choices repeat="multipleItem.idcomportamiento as multipleItem in datosJson[0].comportamientos | filter: $select.search">
{{multipleItem.comportamiento}}
</ui-select-choices>
</ui-select>
here is the checkbox code with the function
<td class="text-center"><input type="checkbox" ng-checked="check(entrevistainicialModel.personalidad, 1)" value="1"></td>
and here it's the function
$scope.check = function (data, n) {
if (data.indexOf(n) !== -1) {
return true;
} else {
return false;
}
}
all works great! only the console send me many times the error of indexOf
If you can not read indexOf of data it is because data is undefined.
I wanted to say that data have no value in it so it can not have methods like indexOf. You can check like this:
console.log (typeof data);
Here, you try to read the type of value in data. If the result is undefined it's mean that data is empty.
So, you have to check if data is not empty (undefined). You can check it like that:
$scope.check = function (data, n) {
if (!data) {return;} // If data is undefined return.
if (data.indexOf(n) !== -1) {
return true;
} else {
return false;
}
}
It's because data contains undefined, its because of what you are passing is undefined or from somewhere else you are passing nothing to this method. As per your code, I can say entrevistainicialModel.personalidad should be initialized with an array or string or something like tha, which have the indexOf method, then you will not get this error Cannot read property 'indexOf' of undefined
Otherwise you should handle the scenario like (data || []).indexOf(...)
Related
I am gathering data from an API to show the Pokemon typing. Some Pokemon have one type, while others have two.
The variables to gather the typing data are as such.
function createPokemonCard(pokemon) {
const type = pokemon.types[0].type.name;
const second_type = pokemon.types[1].type.name;
...}
And then I call them via InnerHTML in the same function with the following code.
<small class="type"><span>${type}/${second_type}</span></small>
Predictably, when it hits undefined for a Pokemon, it breaks and doesn't display the card. However I am not sure how to get it to not print the second type when it's undefined.
I thought about doing an if statement, but what should I call if there is an undefined variable?
function undefined {
if(second_type === 'undefined') {
???
}}
Or would a for loop work better? I am not sure how to get it to bypass the undefined and print nothing.
const second_type = pokemon.types[1] ? pokemon.types[1].type.name: undefined;
`<small class="type"><span>${type}${second_type!=undefined ? `/${second_type}`: ''}</span></small>`
The ? : syntax is a ternary operator (mdn)
It's a less verbose way of writing out the following:
if (second_type!=undefined) { // check if second_type is not undefined
return `/${second_type}` // if it's not return / and the second type
} else { //otherwise
return '' // return an empty string
}
If you do not want to display the trailing / when second_type is not defined one way to go could be
const type = pokemon.types.map(({ type }) => type.name).join("/")
and then
<small class="type"><span>${type}</span></small>
I am trying to filter a column of a ngx-table I follow the the example but it keeps giving that error "Cannot read property 'toLowerCase' of undefined"
here's the template part
<ngx-datatable-column name="Nom" class="name">
<ng-template ngx-datatable-cell-template let-value="value">
{{value}}
</ng-template>
</ngx-datatable-column>
and the function attached to it
updateFilter(event) {
const val = event.target.value.toLowerCase();
// filter the data
const temp = this.temp.filter(function(d) {
return d.name.toLowerCase().indexOf(val) !== -1 || !val;
});
// update the rows
this.rows = temp;
// Whenever the filter changes, always go back to the first page
this.table.offset = 0;}
Any idea of how to solve this?
It would appear that the the items being filtered through do not all contain a value 'name' within them. Try console.log(d) before your return within that function to verify you are receiving the data you expect.
It appears that your name property must be undefined in some cases. This could be that it is not the correct property name (is it lastName instead of name?) or that for some objects in the temp array that the value is undefined. In either case, you can check for a null first before returning the value.
if (d.name) {
return d.name.toLowerCase().indexOf(val) !== -1 || !val;
} else {
return null;
}
I have a dropdown with values in it. They're accessed through the .value property. I have an ng-repeat on a div that is repeating a lot of data. That data has statuses. When a user selects to filter by a status in the dropdown, I want to filter the ng-repeat by whatever status they chose and the status in the ng-repeat. Here's a better example of what I mean:
data-ng-repeat="stackoverflow in overflows| filter:stackoverflow.property.status===status.value"
In my case, I need to access stackoverflow.property.status and compare it to whatever status is in the drop down.
You'll want to do something like the following:
<span ng-repeat="stackoverflow in overflows | filter: { status : selectedStatus }"></span>
selectedStatus will be the value of ng-model on your dropdown.
You could have something like this
ng-repeat="stackoverflow in overflows | filter: { locations: [{ status: status.value }] }"
Under the circumstances, it ended up just being easier to write my own function and filter by it. In this case, I have a function search that I pass to filter. Works great for me.
I followed an answer that I found on another question. You can view it here: http://jsfiddle.net/suCWn/
$scope.search = function (shop) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
var found = false;
angular.forEach(shop.locations, function (location) {
if (location.city_id === parseInt($scope.selectedCityId)) {
found = true;
}
});
return found;
};`
Here's the question: Angularjs filter nested object
VIEW:
I have a rows repeating , with a save button on each row to save each object individually. I want this button to be disabled if no changes have been made.
<tr ng-repeat="option in options | filter:search">
<a ng-click="save(option)" ng-disabled="isUnchanged(option)">Save</a>
</tr>
CONTOLLER:
So I pass the option object to the function, I get its index position in the array. Then compare this 'option' object to its original self in apiKeyOptions[index] which is injected as a service.
angular.module('PartOfApp')
.controller('PartOfAppCtrl', function( $scope, ... apiKeyOptions) {
$scope.options = apiKeyOptions;
$scope.isUnchanged = function(option) {
var index = $scope.options.indexOf(option);
//compare object to the original
if(option.value == apiKeyOptions[index].value && apiKeyOptions[index].setting == option.setting){
//then no changes have been made to this
return true;
}else{
return false;
}
For some reason I get a console of 100's of errors when any data is changed, saying that the apiKeyOptions[index].value and apiKeyOptions[index].setting are undefind.
The app works perfectly as it should returning true if they are the same but still throws a
TypeError: Cannot read property 'value' of undefined
on apiKeyOptions[index]
if I console.log(apiKeyOptions[index].value) I get no undefined values and all log correctly.
Im guessing Im breaking some angular rules, if anyone could help that would be great.
apiKeyOptions overview:
apiKeyOptions is an array of up to 50 objects
each object is in the form
{
defaultValue: boolean,
description: null,
name: String,
setting: "Default" or Boolean,
value: Boolean
}
Added after comment below:
If I add-
console.log(index);
console.log(apiKeyOptions[index]);
to the function $scope.isUnchanged, I get the expected results
example :
13
Object {name: "LOREM IPSUM", description: null, defaultValue: false, setting: "default", value: falseā¦}
So index is not always -1. The reason I pass the object to the function and not $index is because of the filter | search so the index will change depending on the search.
FIXED
As shown in the answer below . I was getting a index = -1 error but its was buried in 100's of CORRECT log outputs.
Oddly this did not stop the app from working and I will need to have a deeper look into how ng-disabled is bound to a value. To fix I simply replaced the indexOf with
for (var i = 0; i< $scope.options.length; i++ ){
if($scope.options[i].name == option.name){
var index = i;
}
}
The problem seems to be with the parameter passed to $scope.isUnchanged = function(option) {
Since ng-repeat creates a new scope for each loop, i suspect that the 'option' available to each loop would be a new object and will not have a reference to 'options' array.
<tr ng-repeat="option in options | filter:search">
Therefore your isUnchanged function will receive parameter as a new object and hence below code always returns -1. Because indexOf matches the given argument in the array and since the argument 'option' is an object and doesn't refer(reference comparison) the same element of array hence no match will found. i.e var a = {id:1};var b = [a]; b.indexOf({id:1}) === -1; b.indexOf(a) === 0;
var index = $scope.options.indexOf(option);//always be -1 in your case
// therefore apiKeyOptions[index] will always be undefined
As a workaround you should pass $index to isUnchanged from the view.
Please, consider the following example:
Template:
<body ng-controller="MainCtrl">
Search: <input ng-model="search.$" ><br>
Search by tag: <input ng-model="search.tag" >
<p ng-repeat="item in items | filter:search">
<span>{{item.content}}</span> <span>{{item.tag}}</span>
</p>
</body>
Script:
app.controller('MainCtrl', function ($scope, $filter, filterFilter) {
$scope.items = [
{content:'3333113', tag:['a','b','c']},
{content:'111111g', tag:['b','c','d']},
{content:'345345', tag:[]},
{content:'2221122', tag:['c','d','e']},
{content:'2221122', tag:[]},
{content:'333', tag:[]}
];
});
When searching via the first input ng-model="search.$" with any data everything is Ok.
When searching via the seond input ng-model="search.tag" search does work by tags like a, b, but when it is cleared the restored array lacks the items which had empty search value, e.g. {content:'2221122', tag:[]} in this example.
jsBin example
Why does it happen ? Is there an easy way to avoid it ?
Short answer:
here is a working code: http://jsbin.com/AwunOyAt/2
You need a directive to make search.tag undefined when it's empty:
Directive:
app.directive('modelUndefined', function(){
return {
require: 'ngModel',
link: function(scope,elm,attrs,ngModel){
ngModel.$parsers.push(function(val){
return val === "" ? undefined : val;
});
}
}
});
html:
<input ng-model="search.tag" model-undefined>
Long answer:
From the docs of filter:filter:
In HTML Template Binding
{{ filter_expression | filter:expression:comparator }}
Parameters#expression
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above.
As you can see, the filter expression can be an object with more then one predicates.
how to trace it?
Initially ngModel won't set search.tag until there is an input so it's still undefined.
First I pass an input into search.$, the search object looks like so:
$scope.search = {
'$' : 'something'
}
Then I'll pass something into search.tag, the search object:
$scope.search = {
'$' : 'something',
'tag': 'anything'
}
But when I clear it then the search object still have the tag property
$scope.search = {
'$' : 'something',
'tag': ''
}
filter:filter filters based on both predicates, this is the source code:
case "object":
// jshint +W086
for (var key in expression) {
(function(path) {
if (typeof expression[path] == 'undefined') return;
predicates.push(function(value) {
return search(path == '$' ? value : (value && value[path]), expression[path]);
});
})(key);
In our case the expression is the search object , and the paths are $ and tag.
See this line: if (typeof expression[path] == 'undefined') return;
If we set search.tag = undefined , the filter ignores it.
But If we set search.tag = '' this tag path is added to the predicates check array.
How to enforce ngModel to make search.tag undefined when it's empty?
See the directive above, you need to use ngModelController#$parsers to change the way the view value is converted when it updates the model.
I do not know if it helps but I put in the following:
<p>{{search.tag == undefined}}</p>
It then showed that initially it is undefined und later on it is an empty string (if you empty the input). The search results then kind of make sense.
Looks like there is a change in AngularJS version 1.2.10. Using https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js does not eliminate empty entries.