How to hide element if object in array is missing field - angularjs

So I have an html element like this:
<div ng-hide="!myarr.length">
Im showing
</div>
Somewhere else an array is populated.
Before it is being populated it will not have a length, so the div above will not show up.
However, when the array will be filled with data (json objects) I need to understand if all the objects in the array contain a specific field (and it cannot be empty).
So my json could eventually look like this:
myarr[0] = {name: 'jack', type: ''}
myarr[1] = {name: 'jack'}
myarr[2] = {name: 'jack', type: 'mytype'}
In the case above, the div should continue to be hiding as not all the json objects contain a "type"-field that is not empty.
How do I accomplish this?
I was thinking to have an OR statement in the ng-hide. Maybe I could use the "some"-function. But when doing this it seems I cannot run this kind of function inside the ng-hide (at least my editor marks the code with red). Is there an alterntive way to do this without needing to call a function in my controller?

When you retrieve the data, you can iterate over each object in the array and examine the individual objects. If any individual object DOES NOT contain the necessary data, set the display flag to false and break out of the loop.
$scope.showElement = true;
$scope.array = [
{name: 'jack', type: ''},
{name: 'jack'}
{name: 'jack', type: 'mytype'}
];
for(let i=0; i<$scope.array.length; i++){
if($scope.array[i].type === undefined || $scope.array[i].type === ''){
$scope.showElement = false;
break;
}
}

Related

Can't access nested stateful objects in ReactJS

I am trying to access a nested object in ReactJS. This is what the object looks like:
const characteristics = [
{ id: "geo", content: 'Geopolitical' },
{ id: "dog", content: 'Dog Loving' },
];
const starterColumns = {
"1": {
name: 'I am',
items: characteristics
},
"2": {
name: 'fullstack developer',
items: []
}
}
const [columns, setColumns] = useState(starterColumns);
This is the error I get when I try to console.log(columns['2']['items']['0']['id']):
TypeError: Cannot read property 'id' of undefined
Does this have to do with the fact that I am working with a stateful variable? Is something funky going on with the nested objects? Thanks for the help!
EDIT
The problem was that there was no object in the column so I had no object to access. Now the problem outstanding is how do I fill that void without displaying a new drag and drop piece. Thanks for helping!
EDIT
I used a try/catch statement to check the object so if it is empty, nothing happens.
Use try catch only for errors that you can't handle
To access an element use dot notation whenever it's possible instead of using bracket notation []
When there is an empty array in the items you can't access to the id you will get an error so the solution is to check if the array is not empty
columns['2'].items.length > 0
To access the first element of an array you have to use [0] instead of ['0']
try this solution
if (columns['2'].items.length > 0) {
console.log(columns['2'].items[0].id)
}

How to correctly update redux state in ReactJS reducer?

I have the following structure in my Redux data store:
{
filterData: {
22421: {
filterId: 22421,
selectedFilters: [
{
filterName: 'gender',
text: 'Male',
value: 'male'
},
{
filterName: 'gender',
text: 'female',
value: 'female'
}
]
}
22422: {
filterId: 22422,
selectedFilters: [
{
filterName: 'colour',
text: 'Blue',
value: 'blue'
},
{
filterName: 'animal',
text: 'sheep',
value: 'Sheep'
}
]
}
Can someone point me towards using the correct way to update the selectedFilters array without mutating the state directly? i.e. How can I add/remove elements in the selectedFilters array for a given filterId?
Generally it's done by using non mutating (ie. returning a new object, rather than modifying the existing one) operators and function:
spread operator (...) for objects and arrays (for additions and edits),
filtering, mapping and reduction for arrays (for edits and removals),
assigning for object (for edits and additions).
You have to do this on each level leading to the final one—where your change happens. In your case, if you want to change the selectedFilters on one of those objects you'll have to do something like that:
// Assuming you're inside a reducer function.
case SOME_ACTION:
// Returning the new state object, since there's a change inside.
return {
// Prepend old values of the state to this new object.
...state,
// Create a new value for the filters property,
// since—again—there's a change inside.
filterData: {
// Once again, copy all the old values of the filters property…
...state.filters,
// … and create a new value for the filter you want to edit.
// This one will be about removal of the filter.
22421: {
// Here we go again with the copy of the previous value.
...state.filters[22421],
// Since it's an array and we want to remove a value,
// the filter method will work the best.
selectedFilters:
state.filters[22421].selectedFilters.filter(
// Let's say you're removing a filter by its name and the name
// that needs to be removed comes from the action's payload.
selectedFilter => selectedFilter.name !== action.payload
)
},
// This one could be about addition of a new filter.
22422: {
...state.filters[22422],
// Spread works best for additions. It returns a new array
// with the old values being placed inside a new one.
selectedFilters: [
// You know the drill.
...state.filters[22422].selectedFilters,
// Add this new filter object to the new array of filters.
{
filterName: 'SomeName',
text: 'some text',
value: action.value // Let's say the value comes form the action.
}
]
},
}
}
This constant "copy old values" is required to make sure the values from nested objects are preserved, since the spread operator copies properties in a shallow manner.
const someObj = {a: {b: 10}, c: 20}
const modifiedObj = {...someObj, a: {d: 30}}
// modifiedObj is {a: {d: 30}, c: 20}, instead of
// {a: {b: 10, d: 30}, c: 20} if spread created a deep copy.
As you can see, this is a bit mundane to do. One solution to that problem would be to create some kind of nested reducers functions that will work on separate trees of the state. However, sometimes it's better not to reinvent the wheal and use tools that are already available that were made to solve those kind of problems. Like Immutable.js.
If you want to use a dedicated library for managing the immutable state (like suggested in another answer) take a look at Immer.
I find that this library is simpler to be used than Immutable.js (and the bundle size will be smaller too)

Angularjs : How to retrieve data from ng-options in angularjs using single string or only one loop

good day, i would like to ask question about retrieving selected data from ng options using string as comparator.
in my controller i have this data
$scope.myList = [
{ id: 0, name: "foo" },
{ id: 1, name: "foo1" },
{ id: 2, name: "foo2" }
];
for my index.html
<select
ng-options="list.name for listin myList"
ng-model="selectedList"
/select>
so the scenario is this, i have my own way of retrieving data but it includes nested looping and my question is, is there any way's to do this just using one loop or a one liner code? because basically i already have the value from database, is there any way so that i don't need to iterate the whole list just to show the data in HTML select input or if not possible at least i wont use nested loops, thanks
this is what i tried so far
// assume the value retrieved from database is "foo1"
var retrievedData = "foo1";
for(var i=0; i<myList.length; i++) {
if(myList[i]['name'] == retrievedData ) {
$scope.selected = myList[i];
}
}
You can do this in one line like bellow
var retrievedData = "foo1";
$scope.selected = myList.find((ele) => ele['name'] == retrievedData);
find() will return the first match.
You can do this in one line like bellow
myList.filter(function(listitem,listitemid){return listitem.name==retrievedData});

Reactjs: What is the right way to modify a object in a large array in state?

I have thousands of objects in an array stored in state, like this:
state: {
data: [{name: 'a', status: true}, {name: 'b', status:false}, ...]
}
this.state.data.length > 10000
I want to modify some status in the array, like set status from this.state.data[1000] to this.state.data[3000] to true;
I used to clone the data into a new array first, but I met some performance issue for this. Since all we have clone are the object references, when we modify the cloned array, we are still modifying the actual object. So I don't know if it is still meaningful to clone the array.
And what is the right way to do this?
React got an update helper to deal with this kind of situations
import update from 'react-addons-update'
this.setState(
{
data: update(this.state.data,{
[indexToChange] : {
status: {$set: true}
}
})
}

In AngularJS, is it possible to filter inside of a specific element of an array using only a view?

I've got some data like this:
people = [
{
names: [
{first: "Joe", last: "Smith"},
{first: "Joseph", last: "Smith"},
...
]
},
...
]
In other words, an array of objects with an array of names. For example, a person could be called "Joe Smith" or "Joseph Smith". How can I use a filter to only search the first element of names? IE: If I typed in "Jo" or "Smith" it would find the first person. But, if I typed in "seph" it wouldn't.
I've been looking at the examples on this page, but there isn't really an example of filtering inside arrays. Here's what I've tried but it gives me an error:
<input ng-model="search.names[0].$">
TypeError: Cannot set property '$' of undefined
Working Code
Input HTML
<input ng-model="searchTerm">
Results
<tr ng-repeat="p in people | filter:searchFunc">...</tr>
Controller
$scope.searchFunc = function(person) {
var firstPersonsName = [person.names[0]]; // Wrapping in array since the 'filter' $filter expects an array
var matches = $filter('filter')(firstPersonsName, $scope.searchTerm); // Running firstPersonsName through filter searching for $scope.searchTerm
return matches.length > 0;
}
Plunker Demo
Answer to the question in your title
I played around with filter and it doesn't seem like you can go beyond one level deep when specifying a pattern object for it e.g. ng-model="search.names" works but ng-model="search.names.otherVal" doesn't.
Also, even if filter supported going beyond one level deep, you still wouldn't be able to do ng-model="search.names[0]". This is because filter expects the input to be an array, but the elements of your names array are all objects e.g. people[0].names[0] == {first: "Joe", last: "Smith"} so filtering will never work.
The only way to do what you are asking purely through the view and no extra code in your controller is to just create your own custom filter that handles your case.
Would this do the trick?
Say that your input is
<input ng-model="search.name" type="text">
Then, you can display results like this:
<div ng-repeat="person in people[0].names | filter: alias">...</div>
Controller:
$scope.alias = function (object) {
if (object.first.match(new RegExp($scope.search.name))) {
return true;
}
return false;
};

Resources