Angular HttpClient datasource paginator does not work - pagination

I have the following implementation (Angular 6).
The data is pulled fine and shows in the table as expected. However the paginator doesn't work.
See below:
#ViewChild(MatPaginator) paginator: MatPaginator;
Then in the constructor:
this.dataSource = new MatTableDataSource([]);
this.requestHttpService.getStuff()
.subscribe(data => {
this.results = data;
this.dataSource = data;
});
Then
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
And finally in HTML:
<mat-paginator #paginator
[length]="100"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 25, 100]">
</mat-paginator>
I am not sure what is wrong with this code but none of those pagination features seem to be working. What am I missing.

Turned out the datasource load had to be initiated from
ngAfterViewInit() {
}
...and the actual paginator assignment has to be wrapped in SetTimeout() function as per suggestion here:
setTimeout(() => {
this.dataSource = new MatTableDataSource(this.results);
this.dataSource.paginator = this.paginator;
});

Related

IPageRetriever not working with Ajax calls

I have the following API call to retrieve page data
List<VillageNewsItem> newsList = pageRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
It works fine and return 2 records if I run the query on page load. Inside Index action of the controller.
public VillageNewsListController(IPageDataContextRetriever dataRetriever, VillageNewsListRepository villageNewsListRepository,
IPageRetriever pagesRetriever, IPageDataContextRetriever pageDataContextRetriever, IPageUrlRetriever pageUrlRetriever)
{
this._dataRetriever = dataRetriever;
this._villageNewsListRepository = villageNewsListRepository;
this._pagesRetriever = pagesRetriever;
this.pageDataContextRetriever = pageDataContextRetriever;
this.pageUrlRetriever = pageUrlRetriever;
}
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
newsItems.VillageNewsItems = newsList;
return View(newsItems);
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
return RedirectToAction("ErrorPage", "Error");
}
}
However, if I try to make the same API call via a client side AJAX call, it doesn't work and return 0 records. Why it's not working with Ajax calls?
Ajax call
function loadMoreNews() {
$.ajax({
url: '/VillageNewsList/VillageNewsItemList',
//data: { "term": request.term },
type: "POST",
success: function (data) {
response($.map(data,
function (item) {
console.log(data);
}));
},
error: function (response) {
//alert(response.responseText);
},
failure: function (response) {
// alert(response.responseText);
}
});
}
Server side method.
[HttpPost]
[Route("VillageNewsList/VillageNewsItemList")]
public VillageNewsListViewModel VillageNewsItemList(string NodeAliasPath = "", int villageId = 0, string state = "", int page = 1, int pageSize = 4)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
var model = new VillageNewsListViewModel
{
VillageNewsItems = newsList, // returns 0 records
};
return model;
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
//return RedirectToAction("ErrorPage", "Error");
}
return null;
}
Couple things I see.
You're calling IPageRetriever.RetrieveAsync, but you aren't putting an await before it. There may be some odd behavior due to this. Get rid of the ?.Result?.ToList() and instead just put await before it, it will return an IEnumerable of the specified type.
You don't need ".Published" nor "OnSite" with IPageRetriever, this API automatically uses the Current Site Context, the current culture, and either Published or not / Latest Version or not based on if it's in edit/preview mode or not.
See if those things fix the issue!
I also asume it is caused by async context here...
You can try to use a document query instead.
Would be something like this:
var items = new DocumentQuery<VillageNewsItem>(
.Path("/Home/Village-News", PathTypeEnum.Children)
.PublishedVersion()
.Published()
.OnCurrentSite()
.OrderByDescending(x => x.DocumentCreatedWhen))
?.Result
?.ToList();
If you have multiple cultures, add the culture to your query, too.
.Culture(LocalizationContext.CurrentCulture.CultureCode)

Hide payment method if specific shipping method selected in odoo eCommerce website

I want to hide specific payment method (cash on delivery) if specific shipping method selected before. For example, if I check shipping method A , then in the next step, payment methods I have only one method to check ( other methods disabled or unable to check). I try to edit and add many2many relational field of payment.acquirer model in shdelivery.carrier and use this filed in controller but it not work , so far I havent' find the solution.
this is my code snapshot:
my python code:
class ShippingMethod(models.Model):
_inherit = 'delivery.carrier'
payment_acquirer_ids = fields.Many2many('payment.acquirer',string='Payment Mathods')
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
my controller
class WebsiteSaleDeliveryExtraCost(WebsiteSale):
#http.route(['/shop/change_shipping_method'], type='json', auth='public', methods=['POST'], website=True, csrf=False)
def change_shipping_method(self, **post):
carrier_id = post.get('carrier_id')
print('******00******', request.session)
carrier = request.env['delivery.carrier'].browse(int(carrier_id))
acquirer_ids = carrier.payment_acquirer_ids.ids
acquirers = request.env['payment.acquirer'].search([('id','in',acquirer_ids)])
# if acquirers:
# return request.redirect("/shop/payment")
return acquirers.ids
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
her is JavaScript code to route controller:
odoo.define('website_sale_delivery_extra_cost.checkout', function (require) {
'use strict';
var core = require('web.core');
var publicWidget = require('web.public.widget');
require('website_sale_delivery.checkout')
var _t = core._t;
// hide payment method base on shipping method
publicWidget.registry.websiteSaleDelivery.include({
selector: '.oe_website_sale',
events: {
'click #delivery_carrier .o_delivery_carrier_select': '_change_shipping_method',
},
_change_shipping_method: function (ev) {
var self = this;
var $radio = $(ev.currentTarget).find('input[type="radio"]');
if ($radio.val()){
this._rpc({
route: '/shop/change_shipping_method',
params: {
carrier_id: $radio.val(),
}}).then(function (data) {
if (data.length >= 1) {
console.log(data[0]);
console.log('---------');
return { location.reload(); };
} else {
return false;
}
}); // end of then
} //end of if
},
/**
* #private
*/
_trackGA: function () {
var websiteGA = window.ga || function () {};
websiteGA.apply(this, arguments);
},
/**
* #private
*/
_vpv: function (page) { //virtual page view
this._trackGA('send', 'pageview', {
'page': page,
'title': document.title,
});
},
});
});
Any other ideas how to solve this issue ?

update page content

I have a morris chart that compares different students statistics. I also have a modal in which I can add a new student and the graph should update with new student statistics. After adding, the graph is getting updated but only when I refresh the whole page. How would I update the page without refreshing?
component.ts
ngOnInit() {
this.getData();
}
getData() {
this.http.get('url')
.subscribe(data => {
const graphData = data.stathistory;
const graphDataArr = [];
let currentChar = 'a';
for (const graphdata in graphData) {
const curDate = graphdata.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3');
const graphdataObj = { 'y': curDate };
for (const element in graphData[graphdata]) {
graphdataObj[currentChar] = Number(graphData[graphdata][element].rank);
currentChar = this.nextChar(currentChar);
}
graphDataArr.push(graphdataObj)
currentChar = 'a';
}
const yKeysArr = [];
for (let i = 0; i < data.domains.length; i++) {
yKeysArr.push(currentChar);
currentChar = this.nextChar(currentChar);
}
this.generateChart(graphDataArr, data.names, yKeysArr);
});
}
generateChart(graphRanks = [], names = [], yKeys = []) {
this.graphData = graphRanks;
this.graphOptions = {
xkey: 'y',
ykeys: yKeys,
labels: names,
resize: true,
parseTime: false,
pointSize: 0,
};
}
addStudent(name) {
this.http.post('url', {
name: name,
})
.subscribe(response => {
this.getData();
}
);
}
html
<div *ngIf = 'graphData' mk-morris-js [options]="graphOptions" [data]="graphData" type="Line" style="height: 500px; width: 100%;">
**code for modal dialog**
<button type="button" class="btn btn-primary" (click)="addStudent(name)">
Please let me know if more info is needed.
This looks fine. I would suggest you to add console.log(graphRanks); just before this.graphData = graphRanks; to ensure that the new data is loaded when expected. By the way your button calls the function addDomain(name) while in your script the function name is addStudent(name).
I would recommend that you make your graphData an observable and use the async pipe in your html. Something like this:
graphData$ = this.http.get('url').pipe(
map(x => // do stuff with x here)
)
Then, in your html you can make:
[graphData]="graphData$ | async"
Here is a good post by Todd Motto on the ng-if piece:
https://toddmotto.com/angular-ngif-async-pipe
EDIT:
If you don't want to make your graphData an observable - you could probably use a switchMap in you addStudent function.
addStudent(name) {
this.http.post('url', {
name: name,
})
.pipe(
switchMap(x => this.getData())
}
);
}
I finally got it working. I tried to clear the morris chart and generate the chart with new data. So, whenever there is a data change, it would clear the graph and redraw the graph with new data.
Clearing the chart
document.getElementById('idofthegraph').innerHTML = '';
This would draw the chart again
this.generateChart(graphDataArr, data.names, yKeysArr);

ES6, PreloadJS and scope

In PreloadJS' callback functions I'm losing scope. Have tried different methods such as an arrow function (which aren't valid on the highest level etc... Any tips? (sorry for the malformed code block (class defenition etc), still getting used to this editor, trust me, all is working code)...
import Navigation from './Navigation'
import ajax from "./Ajax";
import Helper from "./Helper";
let preload = null;
export default class Page{
constructor(){
this.preload = new createjs.LoadQueue();
this.preload.addEventListener("fileprogress", this.handleFileProgress);
this.preload.addEventListener("fileload", this.handleFileComplete);
}
initPage(_arg = []){
this.buildPage(this.thePage);
}
buildPage(_content){
this.loadImage(item.featured_image_thumbnail_url);
}
handleFileComplete(event){
console.log(event);
console.log(this); // undefined
let el = document.getElementById('heroImage');
let loader = el.getElementsByClassName('loader')[0];
this.showPage(); // cannot read property showPage of undefined
}
// small arrow test, doesn't seem valid
//handleFileComplete = (event) =>{
// this.showPage();
//}
handleFileProgress(event){
let hero = document.getElementById('heroImage')
let loader = hero.getElementsByClassName('loader')[0];
}
loadImage(_img){
let el = document.getElementById('heroImage');
let loader = el.getElementsByClassName('loader')[0];
let loadManifest = [
{
id: 'pat',
src: _img,
scope: this
}];
this.preload.loadManifest(loadManifest, true);
}
showPage(){
//Helper.removeClass(document.getElementById('mytest','hidden'));
}
}
Ok, update after only 5 min....
Addded an arrow function as a variable and passed that on to the event listener. This is not my preferred way but it does work though...
constructor(){
let handleFileCompleted = (event) => {
this.showPage(event)
}
this.preload = new createjs.LoadQueue();
this.preload.addEventListener("fileprogress", this.handleFileProgress);
this.preload.addEventListener("fileload", handleFileCompleted);
}
showPage(event){
console.log('I'm now reachable!');
console.log(event); // fileload event
}
Perhaps this can help anybody out.
Still interested to see if there's a better way though...

How should I get JSGridControl using Javascript in SharePoint 2013?

I’ve tried the ‘hook’ as shown below and found in other posts:
ExecuteOrDelayUntilScriptLoaded(function()
{
var oldGanttControl = SP.GanttControl;
SP.GanttControl = function()
{
oldGanttControl.call(this);
var oldInit = this.Init;
this.Init = function(jsGridControl, jsRawGridData, params)
{
oldInit.call(this, jsGridControl, jsRawGridData, params);
DoCustomizations(jsGridControl);
};
};
},"SPGantt.js");
Function DoCustomizations(grid)
{
//etc etc
}
However this seems to work only for SP2010. With SP2013 I get an error saying:
“SCRIPT438: Object doesn’t support property or method ‘WaitForGanttCreation’
sp.ui.timeline.debug.js, line 3335 character 13″
Is there a solution for this to work on a Gantt View in SP2013 ?
Any help would be greatly appreciated.
Try that:
ExecuteOrDelayUntilScriptLoaded(function()
{
var oldGanttControl = SP.GanttControl;
SP.GanttControl = function() {
oldGanttControl.call(this);
var oldInit = this.Init;
this.Init = function(jsGridControl) {
oldInit.apply(this, arguments);
DoCustomizations(jsGridControl);
};
for (prop in oldGanttControl) {
if (oldGanttControl.hasOwnProperty(prop)) {
SP.GanttControl[prop] = oldGanttControl[prop];
}
}
};
},"SPGantt.js");
Function DoCustomizations(grid)
{
//etc etc
}

Resources