I have an array with a few items in it. Every x seconds, I receive a new array with the latest data. I check if the data has changed, and if it has, I replace the old one with the new one:
if (currentList != responseFromHttpCall) {
currentList = responseFromHttpCall;
}
This messes up the classes provided by ng-animate, as it acts like I replaced all of the items -- well, I do actually, but I don't know how to not.
These changes can occur in the list:
There's one (or more) new item(s) in the list - not necessaryly at the end of the list though.
One (or more) items in the list might be gone (deleted).
One (or more) items might be changed.
Two (or more) items might have been swapped.
Can anyone help me in getting ng-animate to understand what classes to show? I made a small "illustation" of my problem, found here: http://plnkr.co/edit/TS401ra58dgJS18ydsG1?p=preview
Thanks a lot!
To achieve what you want, you will need to modify existing list on controller (vm.list) on every action. I have one solution that may work for your particular example.
you would need to compare 2 lists (loop through first) similar to:
vm.list.forEach((val, index)=>{
// some code to check against array that's coming from ajax call
});
in case of adding you would need to loop against other list (in your case newList):
newList.forEach((val, index)=>{
// some code to check array on controller
});
I'm not saying this is the best solution but it works and will work in your case. Keep in mind - to properly test you will need to click reset after each action since you are looking at same global original list which will persist same data throughout the app cycle since we don't change it - if you want to change it just add before end of each function:
original = angular.copy(vm.list);
You could also make this more generic and put everything on one function, but for example, here's plnkr:
http://plnkr.co/edit/sr5CHji6DbiiknlgFdNm?p=preview
Hope it helps.
Related
I have this small app I created using a REST Countries API.
https://rest-country-react.netlify.app/
If you click on one country card then it is displayed under the "Recently Viewed" header. So far it works fine, but I wanna tune it a little bit. What I thought I'd do:
#1 Add a limit of three recently viewed countries, so basically if the user clicks on 4,5,6 countries, only the three most recent clicked countries are displayed.
#2 Visited countries are currently sorted in an "oldest" to "newest" order. I wanna reverse that so the newest gets the first spot, then the second newest, then the third and so on.
I am stuck because I am not sure how to implement these tweaks. For the first one I thought I'd filter the state array before mapping it in the component, saying something like... if index > 2, filter it out the element.
But for the second, I haven't found a solution yet. Maybe instead of using concat() method, I should use unshift()? From what I read in the React documentation, it's not advised to directly edit the state or its array, so I don't know what to do.
onCountryClick(country) {
const uniqueRecent = [
...new Set(this.state.recentlyViewed.concat(country)),
];
this.setState({
// ... other state updates here
recentlyViewed: uniqueRecent
});
}
There are actually multiple solutions, but let's take the one which is very clean and understandable:
onCountryClick(country) {
const { recentlyViewed } = this.state;
const newState = {}; // add your other updates here
if (!recentlyViewed.includes(country)) {
// firstly take first two items as a new array
newState.recentlyViewed = recentlyViewed.slice(1);
// add country into beginning of new array
newState.recentlyViewed.unshift(country);
}
this.setState(newState);
}
Firstly we check if country already exists in the recentlyViewed array and if no - then continue.
We must use new array when updating the state, so simply calling unshift() method will not work for us as it modifies original array and doesn't return new one. Instead, we firstly call .slice() method which solves two main things for us: it takes a portion of original array (in our case items with index 0 and 1) and return new array with them. Great! Now we can easily use unshift to add country into beginning of new array. Then simply update the state.
With this solution you always get a new array with max of 3 countries, where the first item is the newest.
Actually i want to read emails one by one in junk folder of "outlook:live" and mark emails "Not spam".
emails = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH,"//div[#class = 'xoCOIP8PzdTVy0T6q_uG6']")))
This xpath matches 400 instances. I want to make a loop to select one email at a time like select first email, click on the div and perform action and then 2nd email and so on. I'm trying this
emails = WebDriverWait(driver,
5).until(EC.element_to_be_clickable((By.XPATH,"//div[#class =
'xoCOIP8PzdTVy0T6q_uG6']")))
for count in range(0,len(emails)):
(emails)[count+1].click()
Please help me know where im doing wrong. Thanks in advance
It appears that the function you're using to return the clickable elements is only returning a single element, so you'll have to use a different function, make a change in your logic, etc.
For instance, you could use Selenium's find_elements_by_xpath("//div[#class = 'xoCOIP8PzdTVy0T6q_uG6']") which will return a list of WebElement object(s) if the element(s) are found, or an empty list if the element(s) is not found. This will, of course, not take into consideration the possibility of the elements not being completely loaded on the page. In my experience, just slapping a time.sleep(10) after you open the page is "'good enough".
I recommend making sure your elements can be discovered and interacted with first to make sure this isn't all in vain, if you haven't already.
Another option is to add another function, something like a elements_to_be_clickable() function, to the Expected Conditions source code.
From the Expected Condition documentation, I've done some research and it looks like the element_to_be_clickable() function only returns a single element. Moreover, from the source code, said function mainly makes use of the visibility_of_element_located() function. I believe you could follow similar logic to the element_to_be_clickable() function, but instead use the visibility_of_all_elements_located() function, in order to return multiple WebElements (since visibiilty_of_all_elements_located() returns a list of WebElements).
I'm using angular-ui-grid 3.0.5 with the treeview extension to display a tree. The data loads normally, everything works as expected, except that expandRow fails silently.
My use case is this: suppose we have a path like a > b > c and I need c shown to the user as preselected. I know the selection is correctly done because when I manually expand the parent rows, the child row is indeed selected.
Should I call expandAllRows, all rows would be expanded. However, calling expandRow with references on rows a and b taken from gridOptions.data leads to nothing happening: all rows will remain collapsed.
Is there any precaution to be taken that I have maybe overlooked, or is this a bug?
There's one mention in a closed issue that may be related to this but problem I'm having, but I'm not even sure it's related, given how dry the comment/solution was.
There's no example of using expandRow in the documentation but it's in both the API and the source code.
The gridRow objects mentioned in the documentation http://ui-grid.info/docs/#/api/ui.grid.treeBase.api:PublicApi are not the elements you put into the data array (though this seems to be not explained anywhere).
What the function expects is an object that the grid creates when building the tree, you can access them by looping through the grids treeBase.tree array. This will only be valid when the grid has built the tree, it seems, so it is not directly available when filling in the data, that's why registering a DataChangeCallback helps here https://github.com/angular-ui/ui-grid/issues/3051
// expand the top-level rows
// https://github.com/angular-ui/ui-grid/issues/3051
ctrl.gridApi.grid.registerDataChangeCallback(function() {
if (ctrl.gridApi.grid.treeBase.tree instanceof Array) {
angular.forEach(ctrl.gridApi.grid.treeBase.tree, function(node) {
if (node.row.treeLevel == 0) {
ctrl.gridApi.treeBase.expandRow(node.row);
}
});
}
});
self.onContentReady = function (e) {
e.component.expandRow(e.component.getKeyByRowIndex(0));
e.component.expandRow(e.component.getKeyByRowIndex(1));
};
selec which row you wanna expand
I'm have a List<Ref<Entity>>. I add new entries to the list like this:
entities.add(Ref.create(new_entry));
modified.add(new_entry);
When I store the entity that contains the list, I store the list itself and all the entities that are in the modified list. This works fine.
The problem is, that I have to work with the entities-list, while I add new entries to it. This requires iterating the list multiple times. The problem here is, that the refs in the list point to old entries (which are already in the datastore) and new entries (which are not yet in the datastore).
This causes the Ref.get()-method to return null for all the yet unstored entries in the list (the ones that are still in the modified-list).
I worked around this by doing this when inserting:
Ref<T> ref = new DeadRef<>(
Key.create(data),
data
);
this.entities.add(ref);
this.modified.add(data);
This way, I can mix stored and unstored entries in one list and Ref.get() always returns a value.
This works, but I have noticed that the refs in the entities-list stay DeadRefs when I store them to the datastore and load them in again.
Will this be a problem? Is there maybe even a better way to accomplish this?
This seems like a bad idea, although I don't know what specific problems you will run into.
The "right answer" is to save your entities first.
Edit: Also look at the documentation for ofy().defer().save(), which can prevent you from issuing a lot of unnecessary save operations.
For reference, here is the fiddle: http://jsfiddle.net/6u3Gn/1/
I am playing around with angular and ran into a behavior that I can understand, but am not sure how to stop. I created a simple form for places and things, where you define a place, and then you can add things at that place. When the button to add a thing is clicked, it successfully adds the thing to the place:
$scope.addThing = function() {
if ('things' in $scope.place) {
$scope.place.things.push($scope.thing);
} else {
var things = [$scope.thing]
$scope.place['things'] = thing;
}
};
However, when I try to add another thing, the first one is still bound to $scope.thing, so the first one updates to be the exact same as the second thing I add.
How can I stop the 2 way data binding once the object in the array has been added? Is there a way to do so, or am I going about adding it to the array all wrong?
Well that was easy. Not exactly sure how I missed it but the right way to do this is to use angular.copy($scope.variable). Whoops!