Angular 7 view initialization triggers calls multiple times - angular7

In ngAfterViewInit(), I'm calling an async method (tried with and without await) to initialize a table of meetings:
public async getMeetings() {
this.pendingOperation = false;
try {
var { meetingsUrl, bearerToken } = await this.getMeetingsUrl();
var authAndJsonContentHeaderOption = {
headers: {
"Authorization": `Bearer ${bearerToken}`,
"Content-Type": "application/json"
}
};
var httpResult = await this.http.get<any>(meetingsUrl, authAndJsonContentHeaderOption).toPromise();
this.meetings = new Array();
httpResult._embedded.myOnlineMeeting.forEach(element => {
this.meetings.push(element);
});
this.pagedMeetings = this.meetings.slice(0, this.pageSize);
setTimeout(() => {
this.observablePagedMeetings.next(this.pagedMeetings);
}, 100);
}
catch (ex) {
this.errors = `Error: ${ex.message}`;
console.log(`Error: ${ex.message}`);
}
this.pendingOperation = false;
}
As visible in the code above, the retrieved list of meetings is sent to a Subject so that the view can display it in a paginated material table.
ngAfterViewInit() is rather simple:
ngAfterViewInit() {
this.filteredUsers.subscribe((filteredUsers) => {
this.displayedUsers = filteredUsers;
});
this.matAutocomplete._keyManager.change.subscribe((index) => {
if (index >= 0) {
this.selectedUser = index;
}
});
this.getMeetings();
}
HTML is:
<table mat-table *ngIf="pagedMeetings" [dataSource]="observablePagedMeetings" style="width: 100%">
<!-- table markup -->
</table>
The problem is that this getMeetings() is called multiple times instead of once.
So far I've tried:
using setTimeout to postpone this call
specifying ChangeDetectionStrategy.OnPush on this component (as suggested here) to break out of a possible change-detection cycle.
Edit:
Indeed, ngAfterViewInit() itself is called multiple times

Related

How to respond with a stream in a Sveltekit server load function

Below I try to respond with a stream when I receive ticker updates.
+page.server.js:
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(ticker.price);
});
}
});
export async function load() {
return response????
};
Note: The YahooFinanceTicker can't run in the browser.
How to handle / set the response in the Sveltekit load function.
To my knowledge, the load functions cannot be used for this as their responses are JS/JSON serialized. You can use an endpoint in +server to return a Response object which can be constructed from a ReadableStream.
Solution: H.B. comment showed me the right direction to push unsollicited price ticker updates the client.
api route: yahoo-finance-ticker +server.js
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
/** #type {import('./$types').RequestHandler} */
export function GET({ request }) {
const ac = new AbortController();
console.log("GET api: yahoo-finance-ticker")
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(String(ticker.price));
}, { signal: ac.signal });
},
cancel() {
console.log("cancel and abort");
ac.abort();
},
})
return new Response(stream, {
headers: {
'content-type': 'text/event-stream',
}
});
}
page route: +page.svelte
<script>
let result = "";
async function getStream() {
const response = await fetch("/api/yahoo-finance-ticker");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
console.log("resp", done, value);
if (done) break;
result += `${value}<br>`;
}
}
getStream();
</script>
<section>
<p>{#html result}</p>
</section>

Unable to access Work Item Tracking services Azure DevOps Extensions

I am rendering extension on the Work item page using
<WebpageControlOptions AllowScript="true" ReloadOnParamChange="true">
<Link UrlRoot="http://.../extension/Validate-extension/1.0.69/assetbyname/workItemNotifications.html"/>
</WebpageControlOptions>
Following is the html/js code:
var workItemID = 0;
VSS.init({
explicitNotifyLoaded: true,
usePlatformScripts: true
});
VSS.ready(function () {
var currentContext = VSS.getWebContext();
VSS.register(VSS.getContribution().id, function (context) {
return {
// event handlers, called when the active work item is loaded/unloaded/modified/saved
onFieldChanged: function (args) {
if (!changedFields[args.id]) {
changedFields[args.id] = [];
changedFieldCount[args.id] = 0;
}
$.each(args.changedFields, function (key, value) {
if (!changedFields[args.id][key]) {
changedFields[args.id][key] = value;
changedFieldCount[args.id]++;
}
});
},
onLoaded: function (args) {
console.log("OnloadNotification");
VSS.require(["TFS/WorkItemTracking/Services"], function (workItemServices) {
workItemServices.WorkItemFormService.getService().then(function (workItemFormSvc) {
if (workItemFormSvc.hasActiveWorkItem()) {
console.log("Active work item is available.");
workItemFormSvc.getFieldValues(["System.Id"]).then(
function (value) {
var val = JSON.stringify(value);
$.each(value, function (key, values) {
if(key == "System.Id"){
workItemID = values;
}
});
});
}
else {
console.log("Active work item is NOT available.");
}
});
});
},
onUnloaded: function (args) {
},
onSaved: function (args) {
changedFieldCount[args.id] = 0;
changedFields[args.id] = [];
},
onReset: function (args) {
changedFieldCount[args.id] = 0;
changedFields[args.id] = [];
},
onRefreshed: function (args) {
changedFieldCount[args.id] = 0;
changedFields[args.id] = [];
}
};
});
VSS.notifyLoadSucceeded();
});
$(document).ready(function () {
$("#btnValidate").click(function () {
var getResponse = ValidateUser();
VSS.require(["TFS/WorkItemTracking/Services"], function (_WorkItemServices) {
var wiServiceNew = _WorkItemServices.WorkItemFormService.getService();
wiServiceNew.setFieldValue("System.Title", "Title set from your group extension!");
});
});
});
2 things which I am trying to achieve
After button click event to validate user, I have to access the Work Item fields after successful validation. Unable to access _WorkItemServices.
Not able to to get the Work Item fields.
When I set workItemID variable OnLoad event, it resets to 0 when a tab is clicked, value is not getting retained which is set OnLoad.
You may try to interact with the IWorkItemFormService service. For example:
import {
IWorkItemChangedArgs,
IWorkItemFieldChangedArgs,
IWorkItemFormService,
IWorkItemLoadedArgs,
WorkItemTrackingServiceIds
} from "azure-devops-extension-api/WorkItemTracking";
Check the sample here:
https://github.com/microsoft/azure-devops-extension-sample/blob/master/src/Samples/WorkItemFormGroup/WorkItemFormGroup.tsx

Why is `doesFilterPass` not being called in this custom filter

It's my understanding that when filterChangedCallback is called that the grid filters the current data and calls doesFilterPass and isFilterActive as it does so.
Here I have column that uses a custom filter, and a floating filter. The floating filter (a checkbox) gets displayed ok, and when I click on it onFloatingFilterChanged in the custom filter is called, the green filter active icon appears, and the grid refreshes, but doesFilterPass does not get called at all and I can't figure out why.
There's something fundamental I'm not getting with custom filters so if anyone can shed any light on this I'd be grateful.
(Edit: it may be the interplay between the custom/floating filters that's at issue. There was a similar ag-grid-angular issue posted to github last year but nothing came of that. So perhaps it's a bug?)
Custom filter
export default class BooleanFilter {
init(params) {
this.params = params;
this.valueGetter = params.valueGetter;
this.filterChangedCallback = params.filterChangedCallback;
this.status = false;
}
onFloatingFilterChanged(status) {
this.status = status;
this.filterChangedCallback();
}
getGui() {
return '<div />';
}
getModel() {
return this.isFilterActive() ? { filterType: 'boolean', filter: this.status } : undefined;
}
setModel(model) {
this.state.status = model ? model.value : '';
}
isFilterActive() {
return this.status === true;
}
doesFilterPass(params) {
console.log(this.status, params);
}
}
Floating filter
export default class FloatingCheckboxFilter extends Component {
state = { status: false };
handleToggle = (e) => {
const { target: { checked } } = e;
const { parentFilterInstance } = this.props;
this.setState({ status: checked });
parentFilterInstance((instance) => {
instance.onFloatingFilterChanged(checked);
});
}
onParentModelChanged = (parentModel) => {
this.setState({
status: !parentModel ? false : parentModel.filter
});
}
render() {
const { status } = this.state;
return (
<input
style={{ marginTop: '0.75em' }}
type="checkbox"
checked={status}
onChange={this.handleToggle}
/>
);
}
}

Model Id passed through to Angular Ctrl

I have a MVV view that used the Controller below, I need to get the product Id, ie Model.Id through to the controller somehow.
I have tried the $scope.init but it is coming through as null when I am making the first Ajax call, I suspect that this ajax get is kicking off before the init is fired and setting the product id, so the ajax fails as the productId is null when the call is made. I am new to angular so if its a schoolboy error I apologise !.
Controller and HTML are shown below.
angular.module('askQuestions', [])
.controller('questionController', function ($scope, $http) {
$scope.loading = true;
$scope.addMode = false;
$scope.replyMode = false;
$scope.parentClicked = 0;
$scope.init = function (productId) {
//This function is sort of private constructor for controller
$scope.productId = productId;
$scope.getUrl = '/Api/GetProductQuestions/' + $scope.productId;
};
//Used to display the data
//$http.get('/Api/GetAllManufacturers?apiToken=6a5ce02e-0506-0a41-2f50-37327080662f').success(function (data) {
$http.get($scope.getUrl).success(function (data) {
$scope.questions = data;
$scope.loading = false;
})
.error(function () {
$scope.error = "An Error has occured while loading questions!";
$scope.loading = false;
// alert($scope.getUrl);
});
});
<div data-ng-app data-ng-controller="questionController" ng-init="init('#Model.Id')" class="container">
Your $http.get is evaluated in the instantiation of the controller. The instantiation is before your init, so the ajax call is already being made. You can easily fix this by wrapping your $http.get also in a function:
$scope.init = function (productId) {
//This function is sort of private constructor for controller
$scope.productId = productId;
$scope.getUrl = '/Api/GetProductQuestions/' + $scope.productId;
getData();
};
var getData = function() {
$http.get($scope.getUrl)
.success(function (data) {
// success
$scope.questions = data;
})
.error(function () {
// error
$scope.error = "An Error has occured while loading questions!";
})
.finally(function () {
// always
$scope.loading = false;
});
}

Posting form data from Partial view to controller using ajax

I have a jquery grid that has a list of Employee Records.To edit the records there is a link on each row.On click of that A jquery model popup opens and loads the partial view to show and edit the data.But on click of the button(custom button not the jquery model button) on the popup(that loads a partial view),the client side validation using dataAnnotation does not work. If I make a submit form like:-
$("#btnUpdate).submit(function (e) {
e.preventDefault();
var $form = $(this);
if ($form.valid()) {
//Add ajax call
}
});
Then after submit it redirects to:- { ../Employee/Edit/EmployeeId } where it shows a blank screen as I dont have any view for this.
I want that after the form post it should just refresh the jquery grid.
public PartialViewResult _CreateSupplier()
{
return PartialView(new Supplier());
}
[HttpPost]
public JsonResult _CreateSupplier(Supplier model)
{
//Validation
return Json(new
{
status = transactionStatus,
modelState = ModelState.GetModelErorrs()
}, JsonRequestBehavior.AllowGet);
}
Form post jquery method
$('#create-supplier').submit(function (e) {
e.preventDefault();
var $form = $(this);
if (!ModelIsValid($form))
return;
AjaxPost($form.serialize(), '#Url.Action("_CreateSupplier")', function (result) {
if (result.status == 0) {
$form[0].reset();
//Success
var grid = $("#gridSupplier").data("kendoGrid");
grid.dataSource.read();
} else if (result.status == 1)
AddFormErrors($form, result);
else if (result.status == 2)
//error;
});
});
Checking model method is valid and if invalid adding errors to form
function ModelIsValid(form) {
var validator = $(form).validate(); // obtain validator
var anyError = false;
form.find("input").each(function () {
if (!validator.element(this)) { // validate every input element inside this step
anyError = true;
}
});
if (anyError)
return false; // exit if any error found
return true;
}
function AddFormErrors(form, errors) {
for (var i = 0; i < errors.modelState.length; i++) {
for (var j = 0; j < errors.modelState[i].errors.length; j++) {
var val = $(form).find("[data-valmsg-for='" + errors.modelState[i].key + "']");
if (val.html().length > 0) {
$(form).find("[for='" + errors.modelState[i].key + "']").html(errors.modelState[i].errors[j]);
} else {
val.html('<span for="' + errors.modelState[i].key + '" generated="true" class="" style="">' + errors.modelState[i].errors[j] + '</span>');
}
}
}
}
Ajax post method:
function AjaxPost(postData, url, callback) {
$.ajax({
url: url,
type: 'POST',
data: postData,
dataType: 'json',
success: function (result) {
if (callback) callback(result);
}
});
}
And last c# generic method which checks returns model state errors
public static IEnumerable<object> GetModelErorrs(this ModelStateDictionary modelState)
{
return modelState.Keys.Where(x => modelState[x].Errors.Count > 0)
.Select(x => new {
key = x,
errors = modelState[x].Errors.Select(y => y.ErrorMessage).ToArray()
});
}
Hope answer is useful...

Resources