Adding additional table rows using Angular after using ng-repeat - javascript

New to angular, still working out the many kinks that I hit. I've got a question to pose to you all.
I currently have a table that is being populated on load with ng-repeat. The data itself is a bunch of sums of money. One particular entry, is a collection of those sums. I have an ng-onclick on it to run my function getLadders() when clicked. I would like it to basically pan out below wherever that element is in the chart, and create new rows of the data that is in that particular element.
A mini - drawing of the data it, as I can not paste the exact data.
ID Name Total
1 Single $20
2 Single $35
3 Combination $60***
4 Single $10
That is the current chart, with combination being the data with the many breakdowns within it. On click of that row, I want it to do something like this.
ID Name Total
1 Single $20
2 Single $35
3 Combination $60***
3.1 Single $20
3.2 Single $20
3.3 Single $20
4 Single $10
Basically, break out into everything inside that one piece. I have the function written with the extracted data, I have, right now, plain old jQuery to select that row, and append a tablerow after it, but it ends up attaching it to the end of the whole table instead of at that line.
Any help?
Also, for added benefits, I also have an icon on that row that I am looking to alter as well. It's a plus sign, that I want to be a minus sign when the data is being shown, and then back to a plus sign afterwards.
<tbody ng-controller="myCDsController">
<tr ng-repeat="holding in holdings" ng-if="holding.type !== 'Ladder'" data-type="{{holding.typeClass}}" data-id="{{holding.id}}">
<td>{{holding.type}}</td>
<td contenteditable="true">{{holding.name}}</td>
<td>{{holding.maturityDate}}</td>
<td>{{holding.amount | currency:"$"}}</td>
<td>{{holding.rate | percentage:2}}</td>
</tr>
<tr ng-repeat="holding in holdings" ng-if="holding.type == 'Ladder'" ng-controller="openLaddersController" data-type="{{holding.typeClass}}" data-id="{{holding.id}}">
<td><i ng-class="{'icon-dislike': !icon, 'icon-plus':icon}" ng-click="openLadders(holding.id)" >dfgh</i> {{holding.type}}</td>
<td contenteditable="true">{{holding.name}}</td>
<td>{{holding.maturityDate}}</td>
<td>{{holding.amount | currency:"$"}}</td>
<td>{{holding.rate | percentage:2}}</td>
</tr>
</tbody>

Here's some guidance based on what you have above. You should probably simplify the table structure into something like this:
<tbody ng-controller="myCDsController">
<tr ng-repeat="holding in holdings" data-type="{{holding.typeClass}}" data-id="{{holding.id}}">
<td><i ng-if="holding.type === 'Ladder'" ng-class="{'icon-dislike': !icon, 'icon-plus':icon}" ng-click="openLadders(holding)" >dfgh</i>{{holding.type}}</td>
<td contenteditable="true">{{holding.name}}</td>
<td>{{holding.maturityDate}}</td>
<td>{{holding.amount | currency:"$"}}</td>
<td>{{holding.rate | percentage:2}}</td>
</tr>
<tr ng-show="!icon" ng-if="holding.type === 'Ladder'>
<td colspan="5">
<table>
<tbody>
<tr ng-repeat="child in holding.children">
<td><!-- insert child content here --></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
There is probably a better way to handle the data-id / data-type attributes above, but I need more context.
Also, notice that I removed one of your controllers; I somehow doubt you need two controllers for this use case. Your controller should look something like this, probably:
app.controller('myCDsController', function(holding) {
$scope.openLadders = function() {
holding.children = getChildRows(holding)
}
})

Related

.each() function on affect current object

having some issues with my code below, first here is the HTML:
<table class="finance-table">
<tbody><tr>
<th></th>
<th>Deposit</th>
<th>Balance</th>
<th>Fees</th>
<th>Total Payable</th>
<th>Term</th>
<th>Fixed Rate</th>
<th>Representative APR</th>
<th>Monthly Pmt</th>
</tr>
<tr class="hp">
<td><strong>HP</strong></td>
<td id="td_finance_deposit">£11700.00</td>
<td id="td_finance_balance">£105300.00</td>
<td id="td_finance_fees">£298.00</td>
<td id="td_finance_total_inc_deposit">£146255.50</td>
<td id="td_finance_term">60 mths</td>
<td id="td_finance_rate">5.50%</td>
<td id="td_finance_apr">10.1%</td>
<td id="td_finance_monthly_payments">£2242.59 p/m* x 60 mths</td>
</tr>
</tbody></table>
There is about 10 of these tables [within the same document], all with the same id's and class's. I'm using an each loop to execute some code against each table found, however it only seems to be working on the first table and disregards the others.
Below is the jQuery, like I said works find on the first table, but ignores the rest!
<!-- Remove First and Final Payment from Showroom Finance Examples -->
<script>
$(".finance-table").each(function(key, value) {
// Display loading
var html = $(this);
// Remove the First Payment and Final Payment Column
$(this).find("#td_finance_first_payment, #td_finance_final_payment").remove();
$(this).find("th:contains('1st Pmt')").remove(); $(this).find("th:contains('Final Pmt')").remove();
// Get the Term and update the monthly payment
var term = $(this).find("#td_finance_term").html(); // .replace(/\D/g,'')
var payments = ($(this).find("#td_finance_monthly_payments").html()).split('x')[0];
($(this).find("#td_finance_monthly_payments")).html(payments + " x " + term);
})
</script>
Edit:
Please note, I can't change the HTML at all
You should first give a unique ID to each <td>, perhaps with your DB identifier for that record. You don't need it now but this will allow you to do other thing later if you need it.
Then change all the <td> ids to classes:
<td class="td_finance_fees">£298.00</td>
Finally change all your javascript accordingly to use class instead of IDs:
$(this).find(".td_finance_first_payment, .td_finance_final_payment").remove();
Using Attribute Equals Selector
Change your code from:
$(this).find("#td_finance_first_payment, #td_finance_final_payment").remove();
to:
$(this).find('td[id="td_finance_first_payment"], td[id="td_finance_final_payment"]').remove();
Do this type of change for all areas of #xxx to id="xxx"
What this does is find all tds with attribute id="xxx", rather than using #id identifier, this is forces jQuery to do a tree search.
Also your HTML does not match your code, (theres no td_finance_first_payment in your html, I assume you removed it?)
Edit: This solution is useful if you 100% cannot edit the html (comes from a source you have no control over, such as an API or internal software). Best solution would be to fix the ids!

angular-translate multiple keys in one element

In my application I am pulling translations keys from a single cell in a database table and displaying them dynamically in a setting page. Most will only have one key in the display object but there will be a number of entries that would have multiple.
Is there a way in angular-translate to have one element host multiple keys and translate them?
en.json={"title1":"balance","title2":"other stuff"}
ctrl.list=[{display:"title1 title2"}]
<tbody class="validation-table-body">
<tr ng-repeat="entry in ctrl.list">
<td class="validation-name" >
{{entry.display | translate}}
</td>
</tr>
</tbody>
displayed:
<td>title1 title2</td>
expected:
<td>balance other stuff</td>
I am just trying to get this to work before trying to add a comma or something to display.
This is possible in the translate directive at least:
<ANY translate="{{toBeInterpolated}}"></ANY>
toBeInterpolated is the name of the key to be looked up.
Ref:https://angular-translate.github.io/docs/#/guide/05_using-translate-directive

ng-if only deletes innerHTML in my table

I am making a custom directive which will paginate and make my table sortable. I am trying to make it so that multiple data types will work with the sorting with no effort from the user. Here is my problematic html in my templateUrl:
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index" ng-if="!isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]]}}
</td>
<td ng-repeat="data in row track by $index" ng-if="isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]] | date: cndDateFormat}}
</td>
</tr>
I would prefer to use ng-if as opposed to ng-show/hide because it would unnecessarily duplicate DOM elements with ng-show/hide and just not show them. I have tried both ways, but I get the same result either way. The content of columns with dates in them don't show up, but the td itself is still there taking up space. So, I end up having three headers for columns and more than three columns. As far as I was aware, ng-if is supposed to remove the entire element from the DOM.
here is my other code of significance:
scope.isValidDate = (data) => {
var timestamp = Date.parse(data);
return !(isNaN(timestamp));
}
As for cndPaginatedObject, it is just an array of objects with multiple strings contained within. Thank you for any help in advance!
UPDATE:
Just thought I would also add that the cndTableHeaders is literally the table headers. cndPaginatedObject uses the table headers as the keys to each of the values. This way they show up in the proper order in the table.
Two ways to do what you're doing better without the need to produce extra doms and looping a ng-repeat twice
A better way to do what you want to do is
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index">
{{(!isValidDate(row[cndTableHeaders[$index]])) ? row[cndTableHeaders[$index]] : row[cndTableHeaders[$index]] | date: cndDateFormat}}
</td>
</tr>
Or add a span inside the with the ng-if logic
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index"">
<span ng-if="!isValidDate(row[cndTableHeaders[$index]])>
{{row[cndTableHeaders[$index]]}}
</span>
<span ng-if="isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]] | date: cndDateFormat}}
</span>
</td>
</tr>
Alternatively you could create your own filter which checks if date is valid and then using the date filter to filter it.

using ng-repeat filter without hiding filtered out rows

I'm trying to do the following:
1. use ng-repeat to create an html table.
2. use an input field for the user to enter text
3. when the text is contained in a set of specific cells, the relevant rows will perform some kind of animation (lets say change their background color).
I have used till now ng-repeat with the filter option, but it hides the rows which doesn't match the criteria while I wish them to be presented.
This is my current code:
<input id="filterPositions" ng-change="showFiltered()" type="search" ng-model="q.secret" placeholder="filter position..." />
<table>
<tr ng-repeat="record in (filteredItems = ( body | filter:q:containsComparator ) ) track by record.positionId" ng-class="rowClass(record)">
<td>....</td>
<td>....</td>
<td>....</td>
<td>....</td>
</tr>
</table>
Thanks
I don't think that filter is necessary in this case. You can use simple ng-repeat and ng-class directive for example
<tr ng-repeat="record in allrecords" ng-class="{'containscmp': isContainsComparator(record)}">
<td>....</td>
<td>....</td>
<td>....</td>
</tr>
where $scope.isContainsCoparator(record) function returns true if the record contains user's input

jsRender template for table containing variable-length elements

I have a simple View Model that has the traditional one-to-many relationship: Customers have Orders.
I'm creating a table such that each line has a Customer name with a line of Orders following it. It's all working great, but unfortunately few Customers have the same amount of orders, so building the table ends up looking like a sideways bar chart.
I know there is a standard solution to this, but I can't for the life of me figure it out. What I'm going for is to have every row contain the same amount of cells. I've considered putting dummy data into my model, but that just smells bad. I can get the maximum amount of orders possible, but I can't seem to figure out how to design a template that can use any of that information to create uniform tables. Here's what I have so far:
//#data is my ViewModel containing a few other things
<table id="ordersTable">
<!--header stuff-->
<tbody>
{{for #data.Customers tmpl='tmplRow' /}}
</tbody>
// OrderRow.tmpl.html
{{if #index % 2 == 0}}
<tr class="tableRowAlt1">
{{else}}
<tr class="tableRowAlt2">
{{/if}}
<td>{{>Name}}</td>
<td></td>
{{for Orders }}
<td>
{{>OrderId}}
</td>
{{/for}}
</tr>
EDIT: So I've decided to just force the numbe of columns, but now I'm getting some strange behavior on my new template:
// OrderRow.tmpl.html
{{if #index % 2 == 0}}
<tr class="tableRowAlt1">
{{else}}
<tr class="tableRowAlt2">
{{/if}}
<td>{{>Name}}</td>
<td>
{{>Order[0].Id}}
</td>
<td>
{{>Order[1].Id}}
</td>
<!--...-->
</tr>
It's rendering "Order.0 is null or not an object." I've verified that every row has data in it, but yet the template thinks it does not. I imagine this has something to do with the fact that I'm looking for array elements, but I'm not sure how to go about accessing the elements by index in a way that jsRender will accept.
This may not qualify as an answer but its too long as a comment.
I don't 100% follow what you want. I'm guessing that if you have 5 customers and one customer has 10 orders and the 4 other customers have 7 orders, then you want 3 empty td tags for the other 4 customers each.
The first thing you need to know is the length of the longest order list. Lets say its 10. You then need to pass that into the OrderRow template. Actually, you need the difference between the max and the number of orders of the current customer. Lets call that delta.
Now, after the {{for Orders}} loop, you have two choices. You can either add a single td with a span of delta or you can add delta number of empty td tags. You want to do either of these additions only if delta is greater than 0. Assuming ~max is passed in as the maximum number of orders than something like:
{{if (~max - Orders.lengh) > 0 }}
<td colspan='{{ ~max - Orders.length }}'></td>
{{/if}}
What I'm confused about is the end result is going to look the same. It will still have the orders as a sideways bar chart on the left hand side of the table.
You mentioned "a standard solution to this". Can you point to a rendered example somewhere?

Categories