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;
}
Related
I'm using a ngFor loop angular 7 I need to a hidden particular attribute(example userNo and UserType ) inside a ngFor loop.
emailId: "afsageg#dgvsf"
groupName: "dfsfgfdg"
mobileNo: "+91fyrtyrtyty"
userId: "ttrur"
userName: "srurttuidharan"
userNo: 2
userType: "rrhjty"
userTypeDisplay: "Operator Maker"
<tr *ngFor="let key of formData | keyvalue" >
<td [hidden]="key.userNo">{{key.key | titlecase}}</td>
<td>{{key.value}}</td>
</tr>
<td [hidden]="key.key==='userNo'">{{key.key | titlecase}}</td>
<td [hidden]="key.key==='userNo'">{{key.value}}</td> <!--if you also want to hide the value -->
Or you can use *ngIf instead of [hidden]
I would also suggest to rename your key to object (or something less generic even) as each iteration of the *ngFor holds an object and not just the key (hence why you can access key AND value)
So something like:
<tr *ngFor="let obj of formData | keyvalue" >
<td [hidden]="obj.key==='userNo'">{{obj.key | titlecase}}</td>
<td [hidden]="obj.key==='userNo'">{{obj.value}}</td>
</tr>
Small Stackblitz to illustrate
emailId: "afsageg#dgvsf"
groupName: "dfsfgfdg"
mobileNo: "+91fyrtyrtyty"
userId: "ttrur"
userName: "srurttuidharan"
userNo: 2
userType: "rrhjty"
userTypeDisplay: "Operator Maker"
<tr *ngFor="let key of formData | keyvalue" >
<td [hidden]="key.userNo === true">{{key.key | titlecase}}</td>
<td>{{key.value}}</td>
</tr>
If you want hide an attribute you can do some like
<td [attrib]="key.userNo?'value':null">
If you want not show a text, you can do
<td>{{key.userNo?'':key.key | titlecase}}</td>
Others is use style.display
<td [style.display]="key.userNo?'none':null">
of *ngIf
<td *ngIf="!key.userNo">
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
I'm having trouble displaying a <td> in my ng-repeat produced table.
The last <td> in the table should be displayed based on a condition, if vm.editDeleteButtonsVisibile (Using the Controller-As syntax) is true. By default this value is false but when I change it to true, the UI doesn't reflect the change.
This is how my <tbody> looks:
<tbody ng-controller="EditProductController as vm"> <!-- This ng-controller is somewhere else -->
<tr ng-repeat="product in vm.products">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
<td width="15%" ng-if="vm.editDeleteButtonsVisible"> <!-- Error Here -->
<button type="button" class="btn btn-default" ng-click="vm.editItem(product.productCode)">Edit</button>
<button type="button" class="btn btn-danger" ng-click="vm.deleteItem(product.productCode)">Delete</button>
</td>
</tr>
</tbody>
This is the controller where I'm updating the value vm.editDeleteButtonsVisible:
var EditProductController = function() {
vm.editDeleteButtonsVisible = false; // Default value
vm.showEditDeleteButtons = function() {
vm.editDeleteButtonsVisible = true;
console.log(vm.editDeleteButtonsVisible); // Prints true, so value was updated
}
}
This is how vm.showEditDeleteButtons() is being called, from another view:
<div id="editProductBtn" ng-controller="EditProductController as pvm">
<button type="button" class="btn btn-default" ng-click="pvm.showEditDeleteButtons()">Edit Products</button>
</div>
Could the problem be that I'm already declaring ng-controller="EditProductController"? I declare it once in it's main view and another time in a separate view.
Your problem is that you are defining the controller twice. Angular controllers are not singletons. So you are creating two instances of it. You are updating a variable in one of them and looking for it in the other. This won't work.
<div id="editProductBtn" ng-controller="EditProductController as pvm">
and
<tbody ng-controller="EditProductController as vm">
Creates two instances.
What you need to do is either use one controller or create a service (which IS a singleton) and hold the variable in the service.
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
I have a table where the last column in each row contains a little loading icon which I would like to display when a button inside the table is clicked.
When each table row is generated with ng-repeat, the loader shows up in every row rather than the individual one. How can I set ng-show to true or false for only the current index clicked?
Template:
<tr ng-repeat="record in records">
<td>{{ record.name }}</td>
<td><a ng-click="someAction(record.name)">Some Action</a></td>
<td ng-show="loading">Loading...</td>
</tr>
Controller:
$scope.someAction = function(recordName) {
$scope.loading = true;
};
You can pass in the $index parameter and set/use the corresponding index. $index is automatically available in the scope of an ng-repeat.
<td><a ng-click="someAction(record.name, $index)">Some Action</a></td>
<td ng-show="loading[$index]">Loading...</td>
$scope.someAction = function(recordName, $index) {
$scope.loading[$index] = true;
};
Here's a generic sample with all the logic in the view for convenience: Live demo (click).
<div ng-repeat="foo in ['a','b','c']" ng-init="loading=[]">
<p ng-click="loading[$index]=true">Click me! Item Value: {{foo}}<p>
<p ng-show="loading[$index]">Item {{$index}} loading...</p>
</div>
There are many ways to handle this.
The problem here is that your variable loading is sharing the scope between the rows.
One approach could be use $index
HTML
<tr ng-repeat="record in records">
<td>{{ record.name }}</td>
<td><a ng-click="someAction(record.name, $index)">Some Action</a></td>
<td ng-show="loading">Loading...</td>
</tr>
JS
$scope.someAction = function(recordName, $index) {
$scope.loading[$index] = true;
};
Using a property in your object record:
HTML
<tr ng-repeat="record in records">
<td>{{ record.name }}</td>
<td><a ng-click="someAction(record)">Some Action</a></td>
<td ng-show="record.loading">Loading...</td>
</tr>
JS
$scope.someAction = function(record) {
var name = record.name;
record.loading = true;
};
Best regards
The scope inside ng-repeat is different form the one outside. Actually the scope outside ng-repeat is the parent of the one inside. So the html code goes here
<tr ng-repeat="record in records">
<td>{{ record.name }}</td>
<td><a ng-click="someAction(record)">Some Action</a></td>
<td ng-show="$parent.loading">Loading...</td>
</tr>