How can I access filteredArray in my .ts component? Because right now it is accessible only inside ng-container.
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<tr *ngFor="let user of filteredArray">
<td>{{user.name}}</td>
<td>{{user.group}}</td>
</tr>
<div>Count: {{ filteredArray.length }}</div>
</ng-container>
How can I modify the code in order to obtain what I want? Thank you for your time!
To answer your question directly: it's not possible the way you describe it. But read on.
Pipes (sometimes still called "filters") should be used only to format data, i.e. prepare it in a human-readable form. For example, the build-in date pipe can be used to transform an ISO string to a string such as "March 21st, 1995", which is how a human from the USA might expect to read the date.
The way you're using pipes is not recommended, precisely because of the question you have. You've essentially put application logic inside a template, which is an anti-pattern and beats the purpose of having easy-to-read declarative templates, which Angular uses in order to figure out how to update DOM.
You should move the filtering logic back to the class. For example, instead of setting this.userList = xxx, you could have a function which you call every time, such as this.changeUserList(xxx).
changeUserList (list) {
this.userList = list
this.filteredArray = list.filter(...)
}
You can put this logic in a setter as well, which allows you to run custom code when you write the usual this.userList = list, but you'll need a separate (usually prefixed private) property on the class where you'd actually store the value. It's not really a limitation since you can also have a trivial getter, so you can still us this.userList normally as a getter without having to remember to use this._userList, essentially tucking this away as the get/set pair's implementation detail.
private _userList
public set userList (list) {
this._userList = list
this.filteredArray = list.filter(...)
}
public get userList (list) { return this._userList }
Observables could really come in handy here as well, since you could just rx.map the userList$ to filteredArray$ with an Array#filter.
public userList$
public filteredArray$ = this.userList$.pipe(map(arr => arr.filter(...))
Then in the template, you can use the async pipe.
*ngIf="filteredArray$ | async as filteredArray"
Avoid doing the following.... but it works for demo purposes 😃
Create a component (e.g. demo-element.component.ts) that takes a single #Input() value:any
Add this new component as the first child of the <ng-container>, and give it a template reference #containerRef e.g.:
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<demo-element #containerRef [value]="filteredArray"></demo-element>
In your main component, add
#ViewChild('containerRef') ref;
ngAfterViewInit() {
this.filteredArray = this.ref.value; // Terrible pattern, but answers the question:-)
}
I hope this below code will help you.
<div class="rsrvtn_blk" *ngIf="(items | fSearch:firstname) as filteredItems">
<div class="col-md-3 pl-0" *ngFor="let item of filteredItems">
// you can display the filtered content here
</div>
</div>
Related
I've been learning jhipster for the past week now and I'm trying to implement a sortable table. I wanna ask how does the jhiSort and jhiSortBy work? I don't quite understand what does the predicate, ascending and callback function in jhiSort do. Also, which files do the id, date and amount in jhiSortBy come from and how does jhiSortBy work?
<tr jhiSort [(predicate)]="predicate" [(ascending)]="reverse" [callback]="transition.bind(this)">
<th jhiSortBy="id"><span>ID</span> <span class="fa fa-sort"></span></th>
<th jhiSortBy="date"><span>Date</span> <span class="fa fa-sort"></span></th>
<th jhiSortBy="amount"><span>Amount</span> <span class="fa fa-sort"></span></th>
</tr>
jhiSort and jhiSortBy are two directives provided by JHipster that help you implement sorting in data tables. You can see exactly what they do in the links I've provided.
jhiSortBy
The call you see in the header of each column just tells the jhiSortBy directive the name of the attribute it will sort the table by. In your case this means id, date and amount are all attributes of the entities being listed in your table.
This directive handles the user interaction and calls jhiSort to do the actual sorting, plus some extra steps to manage icon changes.
jhiSort
This directive has three properties:
#Input() predicate: string;
#Input() ascending: boolean;
#Input() callback: Function;
When you do [(predicate)]="predicate" you are telling angular that the predicate property of the jhiSort directive (left hand side of the assignment) will bind to a property in your component of the same name (right hand side). So in your *.component.ts you must have a property named predicate that will store the sorting information.
The directive has also a boolean property named ascending, when you do [(ascending)]="reverse" you are telling angular to bind the ascending directive property to a component property named reverse, just like before but this time the names don't match. This property controls only the sorting direction (ascending or descending).
Finally, callback is just the method that will be called on user interaction (click on a table header) to put everything in motion. transition is the name of such method in your *.component.ts, and it looks something like this:
transition(): void {
this.router.navigate(['./'], {
relativeTo: this.activatedRoute.parent,
queryParams: {
page: this.page,
sort: this.predicate + ',' + (this.ascending ? 'asc' : 'desc'),
},
});
}
This will force a navigation change so that the new order and page information persist on the URL, allowing future navigation events to get back without resetting the table.
This navigation change eventually calls loadAll() sending a query to your server-side with the new ordering (and paging) parameters.
The result of this query (a properly ordered list of entities) will be later sent back to the client-side to be displayed.
Very roughly this is how they work.
I is not working correctly:
I found the solution,
it is related to the method
fillComponentAttributesFromResponseBody
change the content of that method to be like this::
protected fillComponentAttributesFromResponseBody(data: IReferences[] | null): IReferences[] {
const referencesNew = [];
if (data) {
for (const d of data) {
if (referencesNew.map(op => op.id).indexOf(d.id) === -1) {
referencesNew.push(d);
}
}
}
return referencesNew;
}
and it will work.
enjoy
I need to implement a download feature. It will read the data in the react-data-grid (adazzle), respecting the current columns, filters and sort, and create an array json (or comma separated strings) I can then pass to the react-csv module.
I have a data structure populated from the backend but it is not filtered nor sorted. I need to be able to ask the grid for it's data on a row-by-row basis. Can anyone point me in the right direction?
Without code or some context, I can't answer with certainty...
You supply the rowGetter prop with the collection to display, or the method to get the rows to display...I'm thinking if you filtering, then most likely you've got some sort of mechanism supporting that... Either way, you can use this property's value somehow to get exactly what you see in the grid.
If you literally want to interrogate the grid, you could try adding a reference to the grid, and then see if you can ask it for the row data. I can't remember with certainty that I saw a rows prop in the grids available props via the ref, but I imagine you should be able to (**,)
...
handleExport = async => {
const exportRows = rows;
// const exportRows = getRows(initialRows, filters);
// const exportRows = this.state.gridref.CurrentRows DISCLAIMER:CurrentRows is just for giving the idea... check out the ref yourself to see if it's possible to get the rows via the grid refs props.
downloadCSV( exportRows )
}
...
<ReactDataGrid
ref={input => {this.state.gridref = input}}
columns={columns}
rowGetter={i => rows[i]} // or maybe rowGetter={i => getRows(initialRows, filters)[i]}
rowsCount={rows.length}
onGridSort={(sortColumn, sortDirection) =>
setRows(sortRows(initialRows, sortColumn, sortDirection))
}
/>
I've only ever [set / initialised] the this.state.gridRef prop in my constructor, but I guess you could also [set / initialise] it in your componentDidMount as well...
initialise like this:
this.state.gridRef = React.createRef()
I want to allow a user to provide a list of one-word attributes without parameter values. For example,
<container row crosscenter wrap spacearound ...>
which results in something like this in container.html
<div [ngClass]="{ 'flexDisplay': true, 'directionRow': isRow, 'directionCol': isCol, 'contentSpaceAround': isSpaceAround}" ...>
What I'm missing is how to set
#Input('row') isRow = false;
to true if 'row' was present in the container line.
Any ideas?
Thanks in advance.
Yogi
This can be handled in ngOnChanges. The value can be assigned either back to input property or to some object that will be passed to ngClass
ngOnChanges(changes: SimpleChanges) {
if ('foo' in changes) {
this.options.foo = true;
}
}
Since there's no way how inputs can become unassigned, there's no reason to provide bindings for them. #Attribute can be used instead:
constructor(#Attribute('foo') public foo: boolean|null) {
this.foo = (foo != null);
}
Using attributes for regular options isn't a good decision, design-wise. This prevents from setting them dynamically. Instead, it is always preferable to accept options input. If all options are supposed to be flags, it can be a string input that will be split and processed in ngOnChanges, like:
<container options="row crosscenter wrap spacearound">
or
<container [options]="'row crosscenter wrap spacearound'">
I think the answer to my question is to create directives for each of the "one-word" tags (attributes) I want to use.
:-)
I'd like to define a new mapping annotation. For instance, wouldn't it be nice if #NgAttr added the mustache (the braces {{ and }}) automatically if the attribute looks like a controller value? So that it doesn't matter whether I write
<pui-input pattern="{{ctrl.pattern}}" ng-model="ctrl.value">
or
<pui-input pattern="ctrl.pattern" ng-model="ctrl.value">
Is it possible to add a custom mapping to AngularDart? My first approach was to simply derive from NgAttr, but it doesn't work because annotations must not contain methods in Dart:
/**
* When applied as an annotation on a directive field specifies that
* the field is to be mapped to DOM attribute with the provided [attrName].
* The value of the attribute to be treated as a string, equivalent
* to `#` specification.
* If the value of the attribute looks like a property of a controller, it
* is surrounded by a mustache ({{ }}) if it is missing.
*/
class PuiAttr extends AttrFieldAnnotation {
final mappingSpec = '#';
const PuiAttr(String attrName) : super(attrName);
String get attrName => addMustache(super.attrName);
String addMustache(String attrName)
{
if (attrName.indexOf("{{") == 0) // Todo: find a nice regexp
if (attrName.indexOf("\.")>0)
return "{{$attrName}}";
return attrName;
}
}
Please note the mustaches are only an example. My question is whether it's possible to add custom mappings to the standard set #NgAttr, #NgOneWay, #NgTwoWay, #NgOneWayOneTime and #NgCallback.
Any ideas or suggestions?
Let's say I have a JSON array of data, something like:
[ {"name":"parijat","age":28},
{"name":"paul","age":28},
{"name":"steven","age"27},
...
]
that is part of my model, and this model is setup like this:
App.UserRoute = Ember.Route.extend({
model:function(){
return App.User.FIXTURES ; // defined above
}
});
I want to get the unique ages from this JSON array and display them in my template, so I reference the computed properties article and read up a little on how to enumerate Ember Enumerables, to eventually get this:
App.UserController = Ember.ArrayController.extend({
ages:function(){
var data = this.get('content');
var ages = data.filter(function(item){
return item.age;
});
}.property('content');
});
Now the above piece of code for controller is not correct but the problem is that it doesn't even go into data.filter() method if I add a console.log statements inside. IMO, it should typically be console logging as many times as there exist a App.Users.FIXTURE. I tried using property('content.#each') which did not work either. Nor did changing this.get('content') to this.get('content.#each') or this.get('content.#each').toArray() {spat an error}.
Not sure what to do here or what I am completely missing.
Filter is used for reducing the number of items, not for mapping.
You can use data.mapBy('age') to get an array of your ages:
ages:function(){
return this.get('content').mapBy('age');
}.property('content.#each')
or in your handlebars function you can just use the each helper:
{{#each item in controller}}
{{item.age}}
{{/each}}