I am using angular-datatable ( http://l-lin.github.io/angular-datatables/#/basic/server-side-angular-way) .
Instead on assigning to this.dtOptions in ngOnInit, I want to do it in the response of another api, but the ajax call is not going through.
My code :-
ngOnInit(){
this.firstCall();
}
firstCall(){
this.api.serviceMethod().subscribe((data : model1) => {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: data.pageLength,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
that.http
.post<DataTablesResponse>(
'api.com/api',
dataTablesParameters, data.req_body, {}
).subscribe(resp => {
that.persons = resp.data;
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [{ data: 'id' }, { data: 'firstName' }, { data: 'lastName' }]
};
})
}
When I am putting the dtOptions assignment in ngOnInit, it is getting called, what am I missing here.
Thank you in advance.
You must set a check in super tag (like : tbody or table when you loop in tr tag).
component.html
<table *ngIf="!loading" ...>
...
...
component.ts
loading = true;
... .subscribe(resp => {
that.persons = resp.data;
loading = false;
Related
Trying to load datatable with few records initially and once datatable loaded, trying to click pagination buttons, here trying like if we click on pagination button need to reload the API with latest with updated params and load set of records instead of all records at a time. Developed below code for pagination for huge data,
loadTable() {
this.dtOptions = {
processing: true,
destroy: true,
pagingType: 'full_numbers',
pageLength: 10,
columns: [
{ title: '<input type="checkbox" />' },
{ data: 'index' },
{ data: 'firstname' },
{ data: 'lastname' }
],
infoCallback: (oSettings, iStart, iEnd, iMax, iTotal, sPre) => {
pageStartNo = iStart;
pageEndNo = iEnd;
console.log(pageStartNo, pageEndNo);
params = new HttpParams()
.set('param1', '111')
.set('param2', '222')
.set('minNumber', pageStartNo)
.set('maxNumber', pageEndNo);
console.log('params >>>>>>>>>>>>>' + params.toString());
}
};
this.http
.get<any[]>(
'https://angular-datatables-demo-server.herokuapp.com/',
{
params
}
)
.subscribe(response => {
this.persons = response.data;
this.dtTrigger.next();
});
}
Stackblitz
Trying to implement pagination, Initially I'm trying to load datatable with few records, once page loaded trying to click pagination buttons like next or any pagination buttons to update the new set of records. I'm able to get the iStart, iEnd records but unable to update the url for every pagination click. Trying to print the console but function is not calling and console.log is not updated with new params. Could you please suggest me how to do the update the params for API. Here is the sample code,
Sample Demo datatatble is not work with pagination, for verification printing the console for the updated querystring.
ngOnInit(): void {
this.dtOptions = {
processing: true,
destroy: true,
columns: [
{ title: '<input type="checkbox" />' },
{ data: 'index' },
{ data: 'firstname' },
{ data: 'lastname' }
],
infoCallback: (oSettings, iStart, iEnd, iMax, iTotal, sPre) => {
pageStartNo = iStart;
pageEndNo = iEnd;
console.log(pageStartNo, pageEndNo);
// this.loadTable();
}
};
}
loadTable(){
let params = new HttpParams()
.set('param1', '123')
.set('param2', '456')
.set('minNumber', pageStartNo)
.set('maxNumber', pageEndNo);
console.log('params >>>>>>>>>>>>>' + params.toString());
this.http
.get<any[]>(
'https://raw.githubusercontent.com/l-lin/angular-datatables/master/demo/src/data/data.json',
{
params
}
)
.subscribe(response => {
this.persons = response.data;
this.dtTrigger.next();
});
}
HTML code:
<button (click)="loadTable()">Load Table</button>
Sample Demo Stackblitz
If I understand your question correctly, you wanted to apply server-side pagination right?
Here is an official documentation for this.
Add ajax method in dtOptions.
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 10,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
console.log('Params', dataTablesParameters);
//If you have different key for page number/size change it
dataTablesParameters.minNumber = dataTablesParameters.start + 1;
dataTablesParameters.maxNumber =
dataTablesParameters.start + dataTablesParameters.length; this.http
.post<any[]>(
'YOUR_API_NAME_HERE',
dataTablesParameters,
{}
)
.subscribe(resp => {
this.persons = resp.data;
//Once API fetched data successfully inform datatable by invoking the callback
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [{ data: 'id' }, { data: 'firstName' }, { data: 'lastName' }]
};
Working Stackbliz Demo
I'm working on a Vue project which has a component for loading content into a modal via an ajax call:
<load-content target="foo"></load-content>
<load-content target="bar"></load-content>
<load-content target="oof"></load-content>
<load-content target="rab"></load-content>
Here's an example template:
<template>
<span class="load-content-wrapper" v-on:click="load">
Click
</span>
</template>
<script>
export default {
name: 'load content',
props: {
target: {
type: String,
required: true
}
},
methods: {
load() {
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
</script>
Which would trigger this example action:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
});
},
};
This all works well, except we cannot guarantee that the Ajax call will always return content. Depending on the target it could return 404.
Ideally I want to automatically disable individual load-content components if '/api/fetch-content/' + target isn't available to prevent users from trying to select unavailable content.
What is the correct/ most efficient way to do this?
You should make your "target" field not required and instead add a default value empty string.
And add an "if" condition to your load method. If "target" is empty, it will not proceed.
export default {
name: 'load content',
props: {
target: {
type: String,
default: ''
}
},
methods: {
load() {
if (!this.target) return;
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
Create a store variable loading and mutate it in your actions as follows:
loading: false
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
commit(setLoading)
});
},
};
Then in muatations ->
setLoading (state, loading) {
state.loading = true
}
Now in your vue file use this store variable and check if it is true then load the component.You may check this created or mounted events of the component.
Option 1
Preemptively load the content, and disable the ones that return an error.
This is what the parent component will look like
<template>
<load-content
v-for="(target, index) in loadedTargets"
:key="index"
target="target"
/>
</template>
<script>
export default {
name: 'load content parent',
data: function() {
return {
targets: [
{link: 'foo', data: null, loaded: false, error: null},
{link: 'bar', data: null, loaded: false, error: null},
{link: 'oof', data: null, loaded: false, error: null},
{link: 'rab', data: null, loaded: false, error: null},
]
}
},
computed: {
loadedTargets() {
return this.targets.filter(t => t.loaded)
}
},
methods: {
load(target) {
const self = this;
$.ajax({
url: '/api/fetch-content/' + target.link,
}).then((data) => {
self.targets[indexOf(target)].data = data
self.targets[indexOf(target)].loaded = true
}).catch((error) => {
self.targets[indexOf(target)].error = error
});
},
},
mounted() {
this.targets.forEach(target => this.load(target))
}
};
</script>
Option 2
Preemptive loading is expensive (and since I don't know how many targets you might have), you could also show success/error in the modal. Proper UX would dictate that an explicit action by the user should lead to a result (i.e. if the user clicks a link, he should either see data in the modal, or an error)
This is what your action will look like:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
}).catch((error) => {
// Load Modal Window, and show the error
});
},
};
I'm having troubles with my organizations in Laravel framework.
My store method is like this:
public function store(ProfileUpdateRequest $request, $id) {
$organizations = Input::only('organization');
foreach($organizations as $id) {
$data = array(
'organization_id' => $id,
'user_id' => Auth::user()->id
);
Organization_User::create($data);
}
}
And the HTML is like this:
<select class="select-icons" tabindex="-1" name="organization"></select>
I'm using select2 4.* to get the records of organizations to the field.
$('.select-icons').select2({
multiple: true,
minimumResultsForSearch: "-1",
ajax: {
url: '/api/v1/allorganizations',
dataType: "json",
type: "GET",
data: function (params) {
var queryParameters = {
term: params.term
}
return queryParameters;
},
processResults: function (data) {
return {
results: $.map(data, function (organization) {
return {
text: organization.name,
id: organization.id
}
})
};
}
},
formatResult: iconFormat,
minimumResultsForSearch: "-1",
width: '100%',
formatSelection: iconFormat,
escapeMarkup: function(m) { return m; }
});
The thing is, that no matter how much records I add, it'll add only one record (that one what I chose first). What should I do, I have been struggling since last night. :D
Here's a bit of refactoring to make this a bit more "Laravely"
collect($request->only('organization'))->each(function($id, $index) {
Organization_User::create([
'organization_id' => $id,
'user_id' => auth()->user()->id
]);
});
In this way, you can loop over the results from the request. We will then instantiate a collection from our $request->only('organization'), which will allow us to chain the ->each() method, returning the $id of the organization, and it's position in the array as represented by the $index.
We use auth() as opposed to Auth:: in the event that Auth:: ever changes to something else in the future, at least we can hope that the auth() helper doesn't.
This is what I have:
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
$scope.settings = {
colHeaders: ["Code", "Comments"],
contextMenu : ["row_above","row_below","remove_row"],
colWidths: [100, 100],
columns : [
{ type: 'dropdown',
source: ['Not Started', 'In Progress', 'Completed']
},
{},
{},
{ type: 'dropdown',
source: $scope.dataSource,
}
]
};
Problem is $scope.dataSource is undefined, it's not displaying the data. What should be the solution to this?
UPDATE:
This displays the data in my $http call. But in the settings source when I call source: $scope.dataSource is undefined
When you are making a request to the server, the rest of the controller will be compiled then you will enter the success function
you are declaring your array inside the success of your request, it's better be declared outside like this
$scope.dataSource = []
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
UPDATE
try this
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
getSetting();
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
var getSetting = function() {
$scope.settings = {
colHeaders: ["Code", "Comments"],
contextMenu: ["row_above", "row_below", "remove_row"],
colWidths: [100, 100],
columns: [{
type: 'dropdown',
source: ['Not Started', 'In Progress', 'Completed']
}, {}, {}, {
type: 'dropdown',
source: $scope.dataSource,
}]
};
}
So first you need to be aware that when you declare $scope.settings = {...} the $http call will not be over yet, so instead of writting
{
type: 'dropdown',
source: $scope.dataSource,
}
You may as well simply write
{
type: 'dropdown',
source: null,
}
Then when the $http call finishes, I assume you want to set this source property. However in your code you are overriding the whole $scope.settings.columns[3] instead of just its source property.
Try this instead:
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.settings.columns[3].source = $scope.dataSource;
});
Note that I have removed the safeApply which is an anti-pattern. Here the $http call will take care of the digest cycle.
Found the answer!
$scope.suppliers = [];
$http.get("http://localhost/i95/api/Suppliers").success(function(data, status, headers, config) {
for(var i = 0; i < data.length; i++) {
$scope.suppliers.push(data[i].company);
}
console.log($scope.suppliers);
});
So creating a global variable and adding push method fixed the issue.