Get values of dynamically named inputs in Angular - javascript

In my fees-list.component.html template, I have something like the following:
<div class="container">
<div class="row" *ngFor="let meta of fees">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Amount owing (AUD)</th>
<th>Fee type</th>
<th>Title</th>
<th>Amount to pay (AUD)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let fee of meta.fee">
<td>{{ fee.balance }}</td>
<td>{{ fee.type.desc }}</td>
<td>{{ fee.title }}</td>
<td><input id="{{ fee.id }}" type="text" name="toPay" value="{{ fee.balance }}"></td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<div class="col-md-4"><strong>Total to pay: </strong><input type="text" name="total" value="{{ meta.total_sum }}"> </div>
</div>
</div>
</div>
The requirement for my interface is twofold. If users change the value of an individual fee (fee.balance), the value in the total text input field should be updated.
Secondly, if the total input field is updated, I need to update the value(s) in the individual fees accordingly (reducing those by the appropriate amount from oldest fee to newest fee).
My question is, how do I do binding for these input fields which are dynamically generated, though they do have unique ids (id="{{ fee.id }})? I cannot work out how to target an individual fee field in my typescript file.

Use two way binding with ngModel
<input [name]="fee.id" type="text" [(ngModel)]="fee.balance">
Now when the user types in the textbox it will update the value in the array directly.
Also you should not be using {{}} binding on your attributes like that, use attribute binding with the [box] syntax.
<input type="text" name="total" [value]="total_sum">
You can calculate the total in your TypeScript like
get total_sum() {
return meta.fees.reduce((total, fee) => total + fee.balance, 0);
}

One approach ( preferred) is to create the controls dynamically.
your typescript file :
constructor(){
this.formGroup = new FormGroup();
const controls = meta.fee.map((fee)=>{
this.formGroup.addControl(fee.id, new FormControl(fee.balance))
});
// now you have a formGroup with all the controls and their value are initialized, you just need to use it in your template
}
your template :
<tbody>
<tr *ngFor="let fee of meta.fee">
<td>{{ fee.balance }}</td>
<td>{{ fee.type.desc }}</td>
<td>{{ fee.title }}</td>
<td><input id="{{ fee.id }}" [formControl]="formGroup.get(fee.id)" type="text" name="toPay"></td>
</tr>
</tbody>
So obviously, after this, you have the full power to do anything, for example, if you want to update a specific one of them:
updateValue(){
this.formGroup.get('oneOfThoseFeeIds').setValue('new value')
}
NOTE :
I haven't done Angular for a while and don't remember the exact syntaxes, but I hope this gives you a path

Can't you just use a service for that? I guess this is a way to go here, since you should try keeping your components as small as possible and communicate them by services.
Please, have a look at this chapter in angular documentation: https://angular.io/tutorial/toh-pt4

Related

Show text depending of property

I want to show the text online or offline, depending on the value of a property. So if the property camera.key is null there the text offline has to be shown. Otherwise, the text online has to be shown.
So I have this template:
<h3>Camera sensoren</h3>
<table>
<th>Name</th>
<th>Last update</th>
<th>Status sensor</th>
<tr *ngFor="let camera of sensorStatusCollection.cameraSensors">
<td>{{ camera.key }}</td>
<td>{{ camera.latestTimestamp }}</td>
<td *ngIf ="camera.key === null ? online : offline "></td>
</tr>
</table>
But what I have to declare in the typescript part?
Thank you
just modify your code, put it in td body
<td>{{camera.key === null ? "online" : "offline" }}</td>
for better understanding to *ngIF if you want to use it, it an angular structural directive here you can find more about it, what is it and how to use it NgIf, here is an example for you:
<td *ngIf ="camera.key === null">online</td>
<td *ngIf ="camera.key !== null">offline</td>

Object parent key JavaScript

I'm using AngularJS to display some data on a table, for that I'm making use of ng-repeat which takes an object and displays the values of the properties of it in the table cells.
The object structure is like this:
{
...
...
"containerKey" : {
"parentKey" : {
"childKey1" : "value",
"childKey2" : "value",
"childKey3" : "value
}
}
}
The ng-repeat shows a table
<table ng-repeat="key in containerKey">
<tr>
<!-- This should display the value of the key (in my case it is the name of a day in the week, but it displayed the entire object inside key.parentKey, which is expected -->
<th id="day">{{ key.parentKey }}</th> <!-- I would like for this to show the value of the key, in my case that would be the name of the day, like "monday" -->
</tr>
<tr>
<td>{{ parentKey.childKey1 }}</td>
</tr>
<tr>
<td>{{ parentKey.childKey2 }}</td>
</tr>
<tr>
<td>{{ parentKey.childKey3 }}</td>
</tr>
</table>
How would I go about showing only the key value of the parentKey in the table cell? Consider that I'm using ng-repeat to show multiple rows and each of this rows contain the ng-repeat that contains the days (parentKey).
To clarify:
I would like the <th> element with id="day" to show the text of the parentKey key. The th element's content would be parentKey (the string parentKey), literally, instead of the value of parentKey.
Have you tried altering your ng-repeat to use the object notation:
<table ng-repeat="(key, value) in containerKey">
You should have access to the key then.
Firstly, in your code you are trying to use ng-repeat over an object. ng-repeat should iterate over arrays.
Considerating containerKey is an array of parentKey, this may works:
<table>
<tbody ng-repeat="(key, value) in containerKey">
<th>{{key}}</th>
<tr>{{value.childKey1}}</tr>
<tr>{{value.childKey2}}</tr>
<tr>{{value.childKey3}}</tr>
</tbody>
</table>

Pass a filter with a form in AngularJS?

I'm trying to teach myself AngularJS, and I've been staring at this piece of code for so long, my eyes are starting to cross.
I have a JSON file of cats containing the properties name, sex, color, pattern, and picture for each cat object. (sex in this case is a Boolean; 0 for female and 1 for male. This will come back up soon.)
I use the following code to loop through the JSON file and print out a table of all cat objects, and it works correctly (and even pretties up the formatting a bit):
<table>
<tr>
<th>Name</th>
<th>Sex</th>
<th>Color</th>
<th>Pattern</th>
<th>Picture</th>
</tr>
<tr ng-repeat="kitty in cats">
<td>{{kitty.name|capitalize}}</td>
<td ng-if="kitty.sex == 0">Female</td>
<td ng-if="kitty.sex == 1">Male</td>
<td>{{kitty.color|capitalize}}</td>
<td>{{kitty.pattern|capitalize}}</td>
<td ng-if="kitty.picture"><img ng-src="{{kitty.picture}}" alt="{{kitty.name|capitalize}}"></td>
<td ng-if="!kitty.picture">NO IMAGE</td>
</tr>
</table>
What I would like now is to allow a user to click a checkbox, e.g. "Male", and have the view change to display all cat objects where sex is 1. I can achieve this by replacing:
<tr ng-repeat="kitty in cats">
...with...
<tr ng-repeat="kitty in cats | filter:{'sex': 1}">
...but for obvious reasons, I would much prefer to have this functionality available dynamically, rather than hard-coded.
I've tried various ng-models as well as names, ids, and values on a given checkbox, but I have yet to figure out the correct syntax with which to pass the argument 1 to the repeat function, to have it filter the cats as necessary.
Does anyone have any ideas on how these two should be bound?
you probably want this:
HTML
<table>
<tr>
<th>Name</th>
<th>Sex</th>
<th>Color</th>
<th>Pattern</th>
<th>Picture</th>
</tr>
<tr ng-repeat="kitty in cats | filter : {sex: genderFilter}">
<td>{{ kitty.name }}</td>
<td>{{ kitty.sex ? 'Male' : 'Female' }}</td>
<td>{{ kitty.color }}</td>
<td>{{ kitty.pattern }}</td>
<td ng-if="kitty.picture">
<img ng-src="{{kitty.picture}}" alt="{{kitty.name|capitalize}}">
</td>
<td ng-if="!kitty.picture">NO IMAGE</td>
</tr>
</table>
<label>
<input type="checkbox"
ng-true-value="1"
ng-false-value
ng-model="genderFilter">Male
</label><br>
<label>
<input type="checkbox"
ng-true-value="0"
ng-false-value
ng-model="genderFilter">Female
</label>
PLUNKER: http://plnkr.co/edit/av2cyzJmwxJLkqhSFnOv?p=preview
You can send filter value dynamically based on selected checkbox value.
<table ng-repeat="cat in cats | filter : { sex: selectedGender }" style="width:300px">
verify this sample http://fiddle.jshell.net/w9darn8a/2/

AngularJS - cannot reference $index inside ng-model using a directive

I am currently facing an issue whereby I would like to generate a dynamic number of input forms and also name them dynamically (as defined by a JSON) so that I can reference them separately.
For example, if my JSON object had three items in it, I would generate three input boxes with ng-model="1", ng-model="2" and ng-model="3" respectively. In real life the ID's will come from the JSON itself.
I'm currently using ng-repeat to generate the forms in a table;
<tr ng-repeat="x in names track by $index">
<td>{{ $index }}</td> <!-- this outputs fine -->
<td>{{ x.Description }}</td>
<td>{{ x.Metric }}</td>
</tr>
And using a directive & the $compile function to dynamically generate an input form with a unique ng-model name.
app.directive("outDynamic", function($compile){
return{
link: function(scope, element, attrs){
var template = "<input type='number' ng-model='" + scope.ray[attrs.element1] + "'>";
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
}
} });
However: none of the following works (when nested in the ng-repeat)
<!-- None of these work -->
<td out-dynamic element1=$index></td>
<td out-dynamic element1="$index"></td>
<td> <input type='number' ng-model="array[$index]"> </td>
<td> <input type='number' ng-model="array['$index']"> </td>
Summary of issue;
Every time I try and reference the $index tracker, I get an
undefined error in my directive code.
You should use {{}} to assign $index value for attribute. so use
element1={{$index}} instead of element1=$index
<tr ng-repeat="x in names track by $index">
<td out-dynamic element1={{$index}}></td>
<td out-dynamic element1="{{$index}}"></td>
</tr>
I guess your scope.arr is perfect.
PLUNKER DEMO

AngularJS on ng-dblclick change input[text]

I have a table that is populated with ngRepeat and I have a input[text] where you can filter the table.
This works fine but now I came up with the idea to have the possibility to double-click on an element in the table and add the text to the search input[text] so the filter is applied straight when you double-click on the text.
Unfortunately it does not work as expected.
I have done this:
<input type="text" placeholder="Search..." data-ng-model="userinput" />
<p data-ng-dblclick="userinput='query'">Double click to use query to search</p>
And in the ngRepeat I use the ng-model "userinput" to filter but the value of the text input is not changing.
I also tried to specify the model "userinput" as variable in the controller and then change it per function but it is not working.
Is there something I'm missing?
Normally I would change the variable in the controller and it should automatically change the text input since it uses this variable as model. Then with this it should change the filter too but nothing happens.
WORKING
Code ngRepeat
<tr data-ng-repeat="dat in data | filter: userInput | filter: tsSelect | filter: advSelect | filter: checkedFilter | orderBy: ['client', 'ssrstatus'] | limitTo: totalDisplay" id="{{ dat.bannerid }}"> <!-- | unique: 'bannerid' | filter: errorSelect| -->
<td>
<input type="checkbox" id="checked" data-ng-model="dat.checked" data-ng-change="updateCheckedStatus(dat._id['$id'], dat.checked)">
<label for="checked">Checked</label>
</td>
<td data-ng-dblclick="search(dat.clientid)">{{ dat.clientid }}</td>
<td data-ng-dblclick="search(dat.client)" class="txtleft">{{ dat.client }}</td>
<td data-ng-dblclick="search(dat.tsengineer)">{{ dat.tsengineer }}</td>
<td data-ng-dblclick="search(dat.bannerid)">{{ dat.bannerid }}</td>
<td data-ng-dblclick="search(dat.bannertype)" class="txtleft">{{ dat.bannertype }}</td>
<td data-ng-dblclick="search(dat.width + 'x' + dat.height)">{{ dat.width == 0 ? 0 : dat.width - 50 }}x{{ dat.height == 0 ? 0 : dat.height - 50 }}</td>
<td data-ng-dblclick="search(dat.ssrstatus)" class="txtleft">{{ dat.ssrstatus }}</td>
<td data-ng-dblclick="search(dat.datebegin)">{{ dat.datebegin }}</td>
<td data-ng-dblclick="search(dat.dateupdated)">{{ dat.dateupdated }}</td>
<td>
<button class="preview {{ dat.bannerid }}" data-ng-click="showPreview(dat.bannerid, dat.clicktotestbanner, dat.width, dat.height)"></button>
</td>
<!-- <td id="{{ dat.bannerid }}" class="banner-preview"></td> -->
Controller
$scope.userInput = "";
$scope.search = function(query){
$scope.userInput = query;
}
I think it's because of your userinput='query' evaluated inside ng-repeat.
Let's name your outer scope "scopeA". The ng-model="userinput" of the search input would be referencing scopeA.userinput.
As we know, a new scope is created for every ng-repeat items. If you run userinput='query' in one of these scopes (name it scopeB), you would be assigning 'query' to scopeB.userinput instead of scopeA.userinput.
In this situation, scopeB is likely to be a child of scopeA. If you use angular-batarang Chrome extension to have a look at the scope tree, you would find both scopes to have userinput field.
One solution would be to use a function to assigning value to userinput instead of ng-dblclick expression. Like:
<p data-ng-dblclick="setUserinput('query')">Double click to use query to search</p>
And add a function setUserinput to your scope:
$scope.setUserinput = function(newValue) {
$scope.userinput = newValue;
}

Categories

Resources