How to sort table in jhipster? Not sure how it works - html

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

Related

How to show a particular URL to the user depending on JSON input using ngIf? (Angular)

In Angular, how to use *ngIf to check whether a JSON value includes a certain string, and then show them a certain URL ? In my case I have a object name called campaigns.description which has a value that includes a description. I want to see whether a given string, for example "one beam" is included in that description and show an URL based on that.
So not the way that the value equals a certain string, but the text that is held within the value includes a certain string.
You can use indexof() function to check the existence of some substring inside a string. This function returns '-1' if the substring is not present in the string.
<label *ngIf="campaigns.description.indexOf('One Beam') != -1 ? true : false">{{urlToShow}}</label>
You could generally use indexOf to check whether a string contains a sub-string.
console.log("Sample string".indexOf('string'));
console.log("Sample string".indexOf('not'));
The Angular part:
Trivial (not recommended)
Trivial solution is to check directly in the *ngIf condition
<div *ngIf="campaigns.description.indexOf('one beam') !== -1; else other">
<!-- contains the sub-string -->
</div>
<ng-template #other>
<!-- does not contain the sub-string -->
</ng-template>
However binding a function to *ngIf directive with default change detection strategy would trigger the function for each change detection cycle. It might lead to performance issues.
Additional property (recommended)
You could introduce additional property to hold the result of the condition in the controller and use it in the template.
Controller (*.ts)
// I assume `campaigns` is initialized in a subscription
ngOnInit() {
someObservable.subscription(
(res: any) => {
this.campaigns = {
...res,
subString: res.description.indexOf('one beam') !== -1
}
},
(error: any) => { }
);
}
Template (*.html)
<div *ngIf="campaigns?.subString; else other">
<!-- contains the sub-string -->
</div>
<ng-template #other>
<!-- does not contain the sub-string -->
</ng-template>

Implement CSV download using current filters and sort

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()

Access filter result Angular 6

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>

angular 2+ component with attribute name and no parameters

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.
:-)

ExtJS 5.0 Forms generated/driven by a store

I would like to create a Form in ExtJS 5.0 completely based on a Store. Every store item represents a "line in the form". A "line" consists three or more form widgets.
Basically this is a search panel, where you define search conditions. Every condition consits of: FieldName selector, an operator selector, and a widget to write/select a condition operand. For example search for people with:
name starting with Joe (FieldName:name, operator:starting with, widget:textfield)
birtday before 1980.01.01. (FieldName:birthday, operator:before, widget:datepicker)
I get the conditions in JSON, and load them in a Store. I would like to dynamically generate the form based on this store, make modifications in the form, and ask the Store for a new JSON with the modifications (new conditions, etc).
I have problems with the first step: simply generate form widgets based on store content.
How can this be done?
I'm going to assume here that the JSON data represents a variety of dynamic data, and you can't simply use a pre-canned control like a grid, or a fixed form.
What you need to do is to make your own container class, which dynamically creates widgets based on the JSON content. You'll have to write this yourself, of course.
One extreme is to make your JSON content in the store be valid arguments to, say, Ext.widget - but that's probably not feasible, or even desirable.
For a more middling position, use the JSON data to determine, based on conditions, what widgets to add.
As a rough outline, you want something like this:
Ext.define('MyFormContainer', {
extend: 'Ext.form.FormPanel',
config: {
// A store or MixedCollection of JSON data objects, keyable by id.
formData: null
},
layout: 'vbox',
initComponent: function() {
this.callParent(arguments);
this.getFormData().each(this.addField, this)
},
addField: function(fieldData) {
var widgetConfig = this.buildWidgetConfig(fieldData);
this.add(widgetConfig);
},
buildWidgetConfig: function(fieldData) {
// The heart of the factory. You need a way to determine what sort of widget to make for
// the field. For the example config, a fieldset with three fields would probably be
// appropriate:
var fieldSet = { xtype: 'fieldset', layout: 'hbox' };
var items = [];
items[0] = { xtype: 'textfield', name: fieldData['FieldName'] };
// this would be a link to a custom widget to handle the operator. Or maybe you could
// just spit out text, if it's not meant to be alterable.
items[1] = { xtype: 'myoperator_' + fieldData['operator'], name: 'operator' };
items[2] = { xtype: fieldData['widget'], name: 'value' }
fieldSet.items = items;
return fieldSet;
}
})
This is a simple and contrived example, but it should (after you fill in the blanks, such as missing requires and the custom operator widgets) render a form based on the JSON data.
(I personally use this approach - with a great deal more sophistication that I can show in a simple example - to generate dynamic forms based on server-supplied form descriptions)