Angularjs:How to remove dynamical table row, but no remove from array - javascript

This down is small from my html code.The code add/remove dynamical row.
<tr class="clone" ng-repeat="dataItem in dataInvoce.Invoicetoitem.items track by $index">
<td class="number">{{$index + 1}}</td>
<td><input type="text" id="codeto{{$index}}" size="11" ng-model="dataItem.item.code" ng-keyup="getCodeItem($index)" class="tagsItem">
<td><input type="hidden" id="hidden{{$index + 1}}" ng-model="dataItem.item.itemID" >
<input type="text" ng-model="dataItem.item.itemName" id="item{{$index}}" class="tagsItem" ng-keyup="getItemInvoice($index)" name="itemName" required></td>
<td><input type="text" ng-model="dataItem.item.solid" size="11" id="solid{{$index + 1}}" ng-disabled="invoice.itemName.$error.required" required></td>
<td><input type="text" ng-model="dataItem.quantity" id="quantity{{$index+1}}" ng-keyup="totals($index+1)" size="10" ng-disabled="invoice.itemName.$error.required" required></td>
<td><input type="text" ng-model="dataItem.price" ng-keyup="totals($index+1)" value="111" id="price{{$index + 1}}" ng-disabled="invoice.itemName.$error.required" required></td>
<td><input type="text" ng-model="dataItem.discount" id="discount{{$index+1}}" ng-keyup="totals($index+1)" size="11" ng-disabled="invoice.itemName.$error.required" required></td>
<td>{{dataItem.quantity * dataItem.price * (1 - dataItem.discount / 100) | number:2}}</td>
<td><a class="btn btn-default" ng-click="removeItem ($index)">-</a></td>
</tr>
This is my funciton for remove dynamical row.When click on "removeItem" button removen row, but and removen element from array on my all items.
I want to remove dynamicla row, but do not delete element from my array with all items.
$scope.removeItem = function (index)
{
console.log($scope.dataInvoce.Invoicetoitem.items);
$scope.dataInvoce.Invoicetoitem.items.splice (index, 1);
console.log($scope.dataInvoce.Invoicetoitem.items);
$scope.total ($scope.dataInvoce.Invoicetoitem.items);
};

You need to add ngIf directive to the row:
<tr class="clone" ng-repeat="dataItem in dataInvoce.Invoicetoitem.items track by $index" ng-if="!dataItem.deleted">
And you can pass the object to the removeItem function in the controller:
ng-click="removeItem (dataItem)" <!-- Instead of '$index' -->
And change the controller function:
$scope.removeItem = function (dataItem) {
console.log($scope.dataInvoce.Invoicetoitem.items);
dataItem.deleted = true;
var total = $scope.dataInvoce.Invoicetoitem.items.filter(function(item) { return typeof(item.deleted) === 'undefined' || !item.deleted; }); // get only the undeleted items
$scope.total (total);
};

I think what you want is a soft delete in angularjs.
you can achieve this by using a Filter on your ng-repeat:
<tr class="clone" ng-repeat="dataItem in dataInvoce.Invoicetoitem.items track by $index | filter: { 'isDeleted': 'false'}">...</tr>
By setting the isDeleted property of dataItem (any rows of dataInvoce.Invoicetoitem.items) to true, the filter will remove all of the rows that contain the property isDeleted : true.
Doing this will have the correct display behaviour while keeping the rows that are removed from display.
reference for ng-repeat filter

Use ng-if for better performance. Not a issue of extra watcher.
<tr class="clone" ng-repeat="dataItem in dataInvoce.Invoicetoitem.items track by $index" ng-if="dataItem.isDeleted">
inside controller $scope.removeItem method set dataItem.isDeleted = true; also pass dataItem rather than $index

Related

Get data from dynamically added elements and pass to Django request.POST

I have a restaurant_form which allows user to input menu items. Each menu item represent a table row which is added dynamically. Each table row has input for: menu item, price, halal(Boolean), and notes.
Here is a sample entry. data-ids and names of input elements are incremented.
<tbody class="menu-entry ">
<tr id='menu0' data-id="0" class="hidden">
<td data-name="name" class="name">
<input type="text" name='name0' placeholder='Menu Item' class="form-control"/>
</td>
<td data-name="price">
<input type="text" name='price0' placeholder='0.00' class="form-control"/>
</td>
<td data-name="halal">
<input type="checkbox" name="veg0" value="halal" class="form-control">
</td>
<td data-name="notes">
<textarea name="notes0" placeholder="Contains soy." class="form-control"></textarea>
</td>
<td data-name="del">
<button name="del0" class='btn btn-danger glyphicon glyphicon-remove row-remove'></button>
</td>
</tr>
</tbody>
What is the best way to retrieve data from the table? I have tried this:
$('#restaurant_form').submit(function () {
$('.menu-entry').each(function()
{
$(this).find('td').each(function(){
$(this).find("input").each(function() {
alert(this.value)
});
$(this).find("textarea").each(function() {
alert(this.value)
});
})
})
})
The alert() shows the correct values however I am wondering if there is a better way to do this?
Edit I simplified it using:
$('.menu-entry .form-control').each()
Also, after retrieving the values, how can I pass it to the view with a POST request? I would like to save the values to model
RestaurantMenu with columns name, price, halal and notes.
Thank you.
Here's what I did:
For every menu entry, I converted the values to a string separated by comma and added a hidden input to hold the value. I added class 'menu-row' to tr.
$('#restaurant_form').submit(function () {
$('.menu-row').each(function(){
var menu_entry = ""
$(this).find('.form-control').each(function(){
menu_entry = menu_entry + this.value + ","
})
$('#restaurant_form').append('<input type="hidden" name="menu_entries" value="'+ menu_entry +'" />')
alert(menu_entry)
})
})
In views.py :
menu_entries = request.POST.getlist('menu_entries')
for menu_entry in menu_entries:
entry = menu_entry.split(",")
if entry[1]:
price = round(float(entry[1]), 2)
else:
price = 0.00
if not entry[2]:
entry[2] = False
MenuItems.objects.create(name=entry[0], price=price, halal=entry[2], notes=entry[3], restaurant_id=r.id)
If anyone knows a better approach, I would love to know. Thank you!

Model in dynamic repeat

I have a weird problem with dynamic binding of model in Angular JS.
<tr ng-repeat="i in [].constructor( 5 ) track by $index">
<td ng-repeat="column in columns">
<input type="text" ng-model="column.defaults[i]" class="form-control">
</td>
</tr>
I define columns using:
$scope.addColumn = function() {
if(!$scope.field_column_name) return;
if(!$scope.columns) {
$scope.columns = []
}
$scope.columns.push( {
name: $scope.field_column_name,
defaults: $scope.field_column_defaults
});
$scope.field_column_name = $scope.field_column_default = undefined;
};
My goal is create inputs for all of it to store data added by user. The problem is, because all inputs looks like if they were the same model (value added in one of them shows also in other inputs). Why?
JSFiddle: http://jsfiddle.net/tz6fsz1o/5/
I think it should be like this:
<tr ng-repeat="i in [].constructor(5)" ng-init="outerIndex = $index">
<td ng-repeat="column in columns">
<input type="text" ng-model="columns[$index].defaults[outerIndex]" class="form-control">
</td>
</tr>
Note how you need to store outer loop $index into helper variable in order to access it in inner loop.
Demo: http://jsfiddle.net/tz6fsz1o/7/
You have mistake in creating rows_array
Try http://jsfiddle.net/tz6fsz1o/6/
$scope.$watch('rows', function(){
$scope.rows_array = [];
for(var i=0;i<$scope.rows;i++){
$scope.rows_array.push(i);
}
}, true);
You are binding each field on ng-model="column.defaults[i]", it means that each field present in the columns array will be binded on the same model property... ex: both column.foo and column.bar bind to columnt.default[i]...
You can fix it binding the text field on the column variable, for example:
<td ng-repeat="column in columns">
<input type="text" ng-model="column.foo" class="form-control">
</td>

Number the rows in ng-repeat

I have a table which has a ng-repeat directive in it. It uses a filter to filter out objects not having property qty>0. I need to give a serial no. to each of the rows. Note that the rows are not fixed. If a user changes the qty of a product to make it 0, it is removed from the table.
<table class="table table-striped">
<tr>
<th>S.no</th><th>Product Name</th><th>Qty</th><th>Sub-Total</th>
</tr>
<tr class="orders" ng-repeat="product in products" ng-if="product.qty">
<td>{{$index}}</td><td>{{product.name}}</td><td> <input type="number" min="0" ng-blur="product.qty = qty" ng-model="qty" value="{{product.qty}}"> </td> <td>{{product.qty*product.price | currency:"₹"}}</td>
</tr>
</table>
Now, the problem with this code is that, the $index gives the actual index of the product in the products array. I need it to give the index in the filtered array. How do I do that?
You can filter the items beforehand as follows:
<tr class="orders" ng-repeat="product in filterItems(products)">
<td>{{$index}}</td><td>{{product.name}}</td><td> <input type="number" min="0" ng-blur="product.qty = qty" ng-model="qty" value="{{product.qty}}"> </td> <td>{{product.qty*product.price | currency:"₹"}}</td>
</tr>
Controller:
$scope.filterItems = function(items) {
var filtered_items = [];
angular.forEach(items, function(item) {
if(item.qty > 0) {
filtered_items.push(item)
}
})
return filtered_items;
}
Use filter in ng-repeat:
HTML
<tr class="orders" ng-repeat="product in products | filter: hasQty">
<td>{{$index}}</td><td>{{product.name}}</td><td> <input type="number" min="0" ng-blur="product.qty = qty" ng-model="qty" value="{{product.qty}}"> </td> <td>{{product.qty*product.price | currency:"₹"}}</td>
</tr>
JS:
$scope.hasQty = function (value) {
return value.qty > 0
}
Untested.

Adding to a value declared in another function

It is hard to explain, you can see a DEMO HERE
I have a products table that dynamically creates/deletes new lines of products. I also have a totals table that totals up the totals of each line together.
In that totals box, I have a travel box I want to add to the grand total, but the issue I am having is the travel input is outside the table that is totaling all the values. I can replace the total with a new total, but I can not seem to call the sub total, add the travel and output a grand total.
HTML
<table class="order-details">
<tbody>
<tr>
<td><input type="text" value="" name="" placeholder="Work Description" class="wei-add-field description 1"/></td>
<td><input type="text" value="" name="" placeholder="QTY" class="wei-add-field quantity 1" /></td>
<td><input type="text" value="" name="" placeholder="$0.00" class="wei-add-field unit-price 1"/></td>
<td><input type="text" value="" name="" placeholder="$0.00" class="wei-add-field price-total 1" id=""/></td>
<td> </td>
</tr>
</tbody>
</table>
<div class="wei-add-service">Add Item</div>
<table class="wei-add-totals">
<tr>
<td width="50%">Sub Total</td>
<td width="50%" class="wie-add-subtotal"> </td>
</tr>
<tr class="alternate travel">
<td>Travel</td>
<td><input type="text" value="" placeholder="0.00" class="wei-add-field travel" /></td>
</tr>
<tr>
<td>Taxes</td>
<td><input type="text" value="" placeholder="0.00" class="wei-add-field wie-total-taxes" id="wei-disabled" disabled/> </td>
</tr>
<tr class="alternate total">
<td>Total</td>
<td><input type="text" value="" placeholder="0.00" class="wei-add-field wie-grand-total" id="wei-disabled" disabled/></td>
</tr>
</table>
Javascript
var counter = 1;
var testArray = [ 2,3,4,5];
jQuery('a.wei-add-service-button').click(function(event){
event.preventDefault();
counter++;
var newRow = jQuery('<tr><td><input type="text" class="wei-add-field description ' + counter + '"/></td><td><input type="text" class="wei-add-field quantity ' + counter + '" /></td><td><input type="text" class="wei-add-field unit-price ' + counter + '"/></td><td><input type="text" value="" name="" placeholder="$0.00" class="wei-add-field price-total ' + counter + '" id=""/></td><td>X</td></tr>');
jQuery('table.order-details').append(newRow);
});
jQuery('table.order-details').on('click','tr a',function(e){
e.preventDefault();
var table = $(this).closest('table');
jQuery(this).parents('tr').remove();
reCalculate.call( table );
});
jQuery('table.order-details').on("keyup", "tr", reCalculate);
function reCalculate() {
var grandTotal = 0;
jQuery(this).closest('table').find('tr').each(function() {
var row = jQuery(this);
var value = +jQuery( ".unit-price", row ).val();
var value2 = +jQuery( ".quantity", row ).val();
var total = value * value2;
grandTotal += total;
jQuery( ".wei-add-field.price-total", row ).val( '$' + total.toFixed(2) );
});
jQuery(".wie-add-subtotal").text( '$' + grandTotal.toFixed(2));
}
I don't think, given the task of creating this, I would have chosen to do it in the way you did.
However, using your existing code you can bind the Travel value on change, paste, or keyup and run a function on any of those actions. Within that function I have removed the special character ($) from ".wie-grand-total" using a regex and converted the value of ".wie-grand-total" to a float using parseFloat. I also converted the Travel value to a float using parseFloat. I then added them together and made the sum your new value for "wie-grand-total".
/* NEW SINCE COMMENTS */
//Add to your HTML New table
<table class="order-details">
<tbody>
<tr>
<td><input type="text" value="" name="" placeholder="Work Description" class="wei-add-field description 1"/></td>
<td><input type="text" value="" name="" placeholder="QTY" class="wei-add-field quantity 1" /></td>
<td><input type="text" value="" name="" placeholder="$0.00" class="wei-add-field unit-price 1"/></td>
<td><input type="text" value="" name="" placeholder="$0.00" class="wei-add-field price-total 1" id=""/></td>
/* NEW SINCE COMMENTS*/
<td><input type="text" id="travelHid" value=""></td>
<td> </td>
</tr>
</tbody>
</table>
/* NEW SINCE COMMENTS */
$('#travelHid').hide();
var travelVal = 0;
function updateTravelVal(travelVal){
var travelVal = travelVal;
$('#travelHid').val(travelVal);
};
updateTravelVal();
$("#travelVis").bind("change paste keyup", function() {
var noChars = jQuery(".wie-grand-total").val().replace(/[^0-9.]/g, "");
var newTot = parseFloat(noChars) + parseFloat($(this).val());
jQuery(".wie-grand-total").val( '$' + newTot.toFixed(2));
//added error checking
var checkError = jQuery(".wie-grand-total").val( '$' + newTot.toFixed(2));
//if the value that would go in input is NaN then use travelVal
//since there is no value in .wie-grand-total yet
if (typeof checkError !== "string") {
jQuery(".wie-grand-total").val( '$' + travelVal.toFixed(2))
} else if (typeof checkError === "string") {
jQuery(".wie-grand-total").val( '$' + newTot.toFixed(2))
}
/* NEW SINCE COMMENTS */
updateTravelVal(travelVal);
});
A fiddle for demonstration (now with hiddenVal per comment)
http://jsfiddle.net/chrislewispac/wed6eog0/3/
Only potential problems here are it only runs when you change, paste, or key up the value in #TravelVis.
/EXPLAINED SINCE COMMENTS/
It the html I added a td with input. Input id="travelHid". I then make that invisible by applying jQuery method .hide(). I then exposed travelVal to global scope an initiated it with a value of zero then created a function to update that value.
Within that function I set the value to the argument travelVal or to 0 if there are no args. I then immediately call it.
Then, I added a call to that function with the arg travelVal from our bind function to update it if a value is present.
And finally:
Just add a row to the table with preset value of Travel and Quant 1.
http://jsfiddle.net/chrislewispac/xntn7p5p/5/

Accessing Element From ngRepeat

How can I access a given record inside an ngRepeat loop from the controller, ultimately for form validate?
I have the following form which can have records dynamically added/removed by the user:
<table class="table table-condensed span12" id="tableParticipants">
<!-- snip -->
<tbody>
<tr ng-repeat="person in people">
<td><input type="text" pattern="[0-9]*" data-provide="typeahead" placeholder="8675309" ng-model="person.empid"></td>
<td><input type="text" data-provide="typeahead" placeholder="Firstname Lastname" ng-model="person.name"></td>
<td><input type="text" placeholder="Title" ng-model="person.title"></td>
<td style="text-align: center"><i class="icon-remove" ng-click="removeParticipant()"></i></td>
</tr>
</tbody>
</table>
On submitting the form I need to make sure that the employee IDs are valid, else I will get a database error. So, when I work through the loop:
$scope.people.forEach(function(element, index, array)
{
if ($element['empid'] == BAD)
{
// set corredponding ngRepeat row to error
}
}
How can I access that given row for the particular record?
You should be able to do something like the following. Add a css class name to the array item and/or an error message. The rest should be handled by Angular which will update. You have options of show/hiding a message, adding a class, etc.
<tr ng-repeat="person in people" ng-class="person.error">
<td><input type="text" pattern="[0-9]*" data-provide="typeahead" placeholder="8675309" ng-model="person.empid"></td>
<td><input type="text" data-provide="typeahead" placeholder="Firstname Lastname" ng-model="person.name"></td>
<td><input type="text" placeholder="Title" ng-model="person.title"></td>
<td style="text-align: center"><i class="icon-remove" ng-click="removeParticipant()"></i> <span>{{person.errorMessage}}</td>
</tr>
if ($element['empid'] == BAD)
{
$element['errorMessage'] = 'Invalid Id'; // could bind or show a span/depends.
$element['error'] = 'error'; // set to whatever class you want to use.
}

Categories

Resources