AngujarJs - ng-repeat + instance.$remove does not update view - angularjs

I have two functions: One for deleting data and another one for updating data. The data comes from a Resource.query().
I'm using ng-repeat to go over every element in the collection. Since every element is a Resource object, I guess that I should be able to apply $save, $remove, ... operations.
The Html could be as simple as:
<tr ng-repeat="data in data_collection">
<td>
<a class="btn btn-info" ng-click="togglePublish(data,'country')">RELEASE</a>
</td>
<td>
<div>
<button class='btn btn-danger' ng-click="remove(data)">Delete</button>
</div>
</td>
</tr>
why if I do:
// Arg: data is a Resource object from the collection
$scope.remove = function(data){
data.$remove(); // <----- THIS DOES NOT MODIFY THE VIEW
}
But this is correctly updating the data:
// Arg: original_data is a Resource object from collection
$scope.togglePublish = function(original_data, country){
// Deep copy
var copy = angular.copy(original_data);
// Modifications over the copy
copy = toogleRegionPublication(copy, country);
Resource().update(copy).$promise.then(function(){
// If update was taken, then update view value
original_data.regions = copy.regions; // <--- THIS MODIFIES THE VIEW
});
}
Why one of them is having some effect on the view but not the one that involves the $remove operation? Why?

As far as I understand
$remove
is just a HTTP DELETE method call over to your restful resource.

To remove the data object from the view I think you can just remove it from the data_collection array. See how to remove object from array.
However, if you still need to call http DELETE to remove that data object from your database or something, you will need to do that as well.

Related

Check if a row already exists before pushing it to table

I have a html table
<table>
<tr ng-repeat="row in rowsproductrequests">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
a second table for search products :
<table>
<tr ng-repeat="row in searchproductsList">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
On click I am pushing the item from the search table (Second table) to the main table (first table) :
JS:
$scope.addItemAsIng = function(row){
$scope.rowsproductrequests.push(row);
}
The items are pushed. My problem is how can I check if an item exists in the first table so I can stop pushing the same item twice.
Use includes, for example:
$scope.addItemAsIng = function(row){
if(!$scope.rowsproductrequests.includes(row)) $scope.rowsproductrequests.push(row);
}
An alternative approach
Rather than populate the second table by pushing selected items to a second array, I'd suggest using the same array but send it through a custom filter that identifies which items are selected.
How to do it
1) Create a function in your controller that sets the selected property of a given object to true:
$scope.onAvailableRowClicked = function(row){
row.selected = true;
}
2) Then wire that up to the first table (of available objects) using the ng-click directive:
<h4>Available</h4>
<table>
<tr ng-repeat="row in rowsproductrequests"
ng-click="onAvailableRowClicked(row)">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
3) Now create a custom filter that identifies all of the objects within a given array that have the selected property set to true:
app.filter("isSelectedFilter", function() {
return function(input){
return input.filter(function(obj){
return (obj.selected === true);
});
}
});
4) Finally, use the filter in the ng-repeat of the second table to identify selected records:
<h4>Selected</h4>
<table>
<tr ng-repeat="row in rowsproductrequests | isSelectedFilter">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
Benefits of this approach
You only have to maintain one array. Less variables means that your code becomes easier to read, you can see more easily what is driving the view. It's also easier to debug if things go wrong.
There's only ever one instance of each object. One source of the truth means that there's no danger of objects falling out of sync ("should I use this or that!?!?"). It also means that it's not physically possible to select an object more than once.
You can easily reuse the selected property in other parts of the view. If you wanted to highlight in the available list which objects had been selected, you could do so easily using ng-class for example. (The thought of using array comparison logic here is already giving me a headache!)
Demo
CodePen: A custom filter to identify selected records
Approach One: One solution will be once you add the row from the second table, remove that item from the collection and add to the first table that,s how you will never get duplicate items.
Second Approach: you will have to maintain an identity column in both tables based on these identity columns you need to check before adding whether this item exists.
Also you can use "indexOf()", it will return array index of your current item, if not exit it will return -1
Like
if($scope.rowsproductrequests.indexOf(row) != -1)

add table data <td> only to one row on ng-repeat

in a table with ng-repeat is it possible to add a cell only to one row?
in my code:
<tbody>
<tr ng-repeat="user in users ng-click="selectUser(user)">
<td>{{user.username}}</td>
<td><input type="text"....></td>
<td><input type="checkbox"...></td>
<td><input type="submit" ... ng-show="user==selectedUser" /></td>
</tr>
</tbody>
in this code I want the last td appears only on the selected row and does not affect other rows, is it possible? or it is JS or CSS thing ?
First off you should be using the controller as syntax, it automatically puts everything in the controller under 1 object, which can cause issues with Angular. But I don't think that's the issue here.
The user you select could be equal to the selectedUser, but if they aren't pointing to the same reference, they won't be able. If usernames are distinct I'd change the ng-show="user.username == selectedUser.username"
and that should work fine.
It is possible, it seems like your code is mostly correct, but you're using selectedUser as a function and as an object representation of user. Maybe your function would be called selectUser which would set $scope.selectedUser. ng-show="user == selectedUser" would make since then.
I'm personally not a big fan of having conditions in the view, so I'd have a function in the controller which does the comparison and returns true or false.
function isSelectedUser(user) {
return user == $scope.selectedUser;
}
then you can just use ng-show="isSelectedUser(user)"
Use JQuery to append the <td> on the selected row <tr>. The :nth-child() is an easy way for you to select a row.
var selectedRow = 2;
$('tbody tr:nth-child('+ selectedRow +')').append('<td><input type="submit" /></td>');

Knockout foreach accessing 2D json array

Hope someone can help me out with this:
I'm working with knockout and have the following json array:
[[174302,"BUSINESS - APPLICATION TO CONDUCT A BUSINESS FROM HOME.pdf",".pdf","DK89639"],[120183,"Glovent-Brochure.pdf",".pdf","DK472894"]]
inside my "consumerData" variable.
As you can see there are 2 arrays with 4 elements inside each.
Here is how I am trying to access it:
<div data-bind="foreach: consumerData" style="margin-bottom:100px;">
<table>
<tr>
<td colspan="2">
<p style="font-size:larger; margin-bottom:5px;"><a data-bind="attr: { href: 'http://someaddress/address/'+consumerData[0]+''+consumerData[2]+'?key='+consumerData[3]+'' }"><div data-bind="text: consumerData[1]"></div></a></p>
</td></tr>
</table>
</div>
So this is looping twice which is correct but how do I access my data inside each array?
PLease help!
Thanks!
Regards
Francois
You can access unnamed data within a loop by accessing the $data object (instead of consumerData again), which represents the current context
See this fiddle:
https://jsfiddle.net/5c6y46bo/
Also, you don't need to put a div inside your link to hold the text of the current object, just put the text binding within the <a> element's binding alongside the attr binding.

Update data from table to mongodb

Is there any framework or technique to simply update data edited in table and stored in mongoDB
I am using angularjs, node.js and mongoDB.
My 'by hand' idea is to:
track edits in angular table that will return json with updated fields
send REST request for update edited fields with proper document id
Using the Angular JS, we can do all CRUD operation. For your question i have answer,
In HTML
<tr ng-repeat="list in getList">
<td>{{$index+1}}</td>
<td>{{list.firstname}}</td>
<td>{{list.lastname}}</td>
<td class=" last"><button type="button" class="fa fa-edit btn btn-primary" ng-click="edit($index,list)"></button>
</td>
</tr>
In controller,
$scope.edit=function(idx,list){
$scope.<name of ng-model>=angular.copy(list)
flag=idx;
}
and now in controller where to saving, check the data in save() function, where the list is existing in the table. If yes the call update(), else call add().

How to set a boolean flag to collapse/expand a row with ng-repeat

I have this plunker code.
What I'm trying to do, is to display the gray box one time per row.
To achieve this, I thought to modify the partition filter in order to return a JSON to add it a new property by row to know if the gray box is expanded or not.
But, I could Not successfully return a JSON.
Do you know how to modify the filter to return a JSON or a better way to show the gray box by row?
Related questions:
Push down a series of divs when another div is shown
Update 1
The issue could be easily resolved by using the correct scope for the ng-repeat for the row without modifying the filter, thanks to #m59.
http://plnkr.co/edit/eEMfI1lv6z1MlG7sND6g?p=preview
Update 2
Live Demo
If I try to modify the item, it seems the ng-repeat would be called again losing the props values.
<div ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}">
<div ng-repeat="item in friendRow"
ng-click="collapse(item)"
ng-class="{myArrow: showArrow}">
{{item.name}} {{item.age}} years old.
<div>{{item.name}}</div>
</div>
<div collapse="!props.isExpanded">
some content
<br/>
<input type="text" ng-model="currentItem.name">
</div>
</div>
js
$scope.collapse = function(item){
this.props.isExpanded = !this.props.isExpanded;
this.showArrow = !this.showArrow;
$scope.currentItem = item;
};
This causes the gray box to collapse each time the item is modified. Any clue?
I've updated my code/answer regarding partitioning data. It's important to fully understand all of that before deciding on an approach to your project.
The problem you have in your plnkr demo is that you're modifying the parent $scope and not the scope of the ng-repeat for that row.
Just set a flag on the row and toggle it when clicked:
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="isExpanded = false"
ng-click="isExpanded = !isExpanded"
>
<div ng-repeat="item in friendRow">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!isExpanded">
some content
</div>
</div>
To access the correct scope within a function in the controller, you can use the this keyword instead of $scope. this will refer to the scope the function is called from, whereas $scope refers to the scope attached to the element with ng-controller (a parent of the ng-repeat scopes you want to target).
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-click="collapse()"
>
JS:
$scope.collapse = function() {
this.isExpanded = !this.isExpanded;
};
If you want to keep the ng-click directive on the item element instead of putting it on the row element as I have done, then you're dealing with another child scope because of that inner ng-repeat. Therefore, you will need to follow the "dot" rule so that the child scope can update the parent scope where the collapse directive is. This means you need to nest isExpanded in an object. In this example, I use ng-init="props = {}", and then use props.isExpanded. The dot rule works because the children share the same object reference to props, so the properties are shared rather than just copied, just like in normal JavaScript object references.
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}"
>
<div ng-repeat="item in friendRow" ng-click="collapse()">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!props.isExpanded">
some content
</div>
</div>
JS:
$scope.collapse = function(){
this.props.isExpanded = !this.props.isExpanded;
};
Update
We keep going through more and more issues with your project. You really just need to experiment/research and understand everything that's going on on a deeper level, or it will just be one question after another. I'll give it one last effort to get you on the right track, but you need to try in the basic concepts and go from there.
You could get past the issue of props reinitializing by putting $scope.expandedStates and then passing the $index of the current ng-repeat to your function (or just using it in the view) and setting a property of expandedStates like $scope.expandedStates[$index] = !$scope.expandedStates[$index]. With the nested ng-repeat as it is, you'll need to do $parent.$index so that you're associating the state with the row rather than the item.
However, you'll then have another problem with the filter: Using my old partition code, the inputs inside the partitions are going to lose focus every time you type a character. Using the new code, the view updates, but the underlying model will not. You could use the partition filter from this answer to solve this, but from my understanding of that code, it could have some unexpected behavior down the road and it also requires passing in this as an argument to the filter. I don't recommend you do this.
Filters are meant to be idempotent, so stabilizing them via some kind of memoization is technically a hack. Some argue you should never do this at all, but I think it's fine. However, you definitely should ONLY do this when it is for display purposes and not for user input! Because you are accepting user input within the partitioned view, I suggest partitioning the data in the controller, then joining it back together either with a watch (continuous) or when you need to submit it.
$scope.partitionedFriends = partitionFilter($scope.friends, 2);
$scope.$watch('partitionedFriends', function(val) {
$scope.friends = [].concat.apply([], val);
}, true); // deep watch

Resources