routerlink = "functionName()" invoked immediately upon page load - javascript

My component's html is this:
<div id="summary">
<div *ngFor="let question of thisSurvey">
<div>
<span class="badge">#{{question.questionNumber}}</span>
<span>{{question.questionText}}</span>
</div>
<p>Your answer: {{question.questionAnswer}}</p>
</div>
</div>
<br/>
<button class="btn btn-danger yes-no-btn" routerLink="/survey">Go Back</button>
<button class="btn btn-primary" [routerLink]="submitSurvey()" routerLinkActive="active">Finish</button> <!-- Issue here -->
When the page loads, submitSurvey is invoked immediately, and is then constantly invoked. This is submitSurvey:
// Send the answers back to the api for processing
submitSurvey() {
// Make sure everything is answered
const allOKClientSide: boolean = this.surveyService.checkEntireForm(this.thisSurvey);
if (allOKClientSide) {
if (this.surveyService.checkFormOnline(this.thisSurvey).subscribe()) {
return '/placeOne';
}
}
return '/placeTwo';
}
The method begins to hit the service immediately and continues until I kill the server. How do I keep the function from being invoked until the button is clicked? I'm new to Angular and am probably just making a rookie mistake, if so you may point that out as well. Thanks in advance.

[routerLink] is an Input, note the []. So Angular will resolve that immediately and every change detection cycle, to satisfy the template. You want to use (click) which is an Output, note the () and will only call when the button is clicked. Then instead of returning the url on the submitSurvey() function call router.navigate() (inject the router first.)
html
<button class="btn btn-primary" (click)="submitSurvey()" routerLinkActive="active">Finish</button>
ts
constructor(private router: Router) { }
public submitSurvey(): void {
// Make sure everything is answered
const allOKClientSide: boolean = this.surveyService.checkEntireForm(this.thisSurvey);
if (allOKClientSide) {
if (this.surveyService.checkFormOnline(this.thisSurvey).subscribe()) {
this.router.navigateByUrl('/placeOne');
return;
}
}
this.router.navigateByUrl('/placeTwo');
}

You want your method to be called when the button is clicked. You can do this by using (clicK):
Instead of
[routerLink]="submitSurvey()"
do
(click)="submitSurvey()"
Then you use the router in your class to do the navigation:
constructor(private router: Router) {}
submitSurvey() {
// ...
this.router.navigate(['/placeOne']);
}

Related

Angular method understanding

example code below:
<div id=A>
<div id=B>
<input type="text" onkeyup="some.doSomething()">
</div>
<div id=C>
<button id=btn onclick=some.next('A')>NEXT</button>
</div>
</div>
public doSomething(): void {
//do something
}
public next(): void {
//go next
}
I don't understand the relation between some.next('A') and next() method.
The next method itself isn't taking any parameter, yet div id is included inside html call.
For that Javascript code, you should call in your HTML the method doSomething() directly, the same for next() without params, otherwise won't work.

Make button to wait for mehtod to resolve

I have a couple of buttons that I want to display only if user is logged in. My problem is that simple *ngIf is not working like I want it to.
For example I have this button Add Recipe:
<div class="background">
<button type="button" *ngIf="isUserAuthenticated" class="btn btn-primary float-right m-2" data-bs-toggle="modal" data-bs-target="#exampleModal"
(click)="addClick()"
data-backdrop="static"
data-keyboard="false">
Add Coffee
</button>
It is button for showing modal. I want that button to be displayed only if user is authenticated. This is how my .ts looks like:
export class RecipeDetailsComponent implements OnInit {
...
public isUserAuthenticated: boolean = false;
ngOnInit(): void {
this.userService.authChanged
.subscribe(res => {
this.isUserAuthenticated = res;
})
}
and my userService methods for checking if authenticated:
export class UserService {
private _authChangeSub = new Subject<boolean>()
public authChanged = this._authChangeSub.asObservable();
public sendAuthStateChangeNotification = (isAuthenticated: boolean) => {
this._authChangeSub.next(isAuthenticated);
}
public isUserAuthenticated = (): boolean => {
const token = localStorage.getItem("token");
return token != null && this._jwtHelper.isTokenExpired(token) == false;
}
Button appears only If i set isUserAuthenticated = true when declaring and hides if it false.
I guess button reads isUserAthenticated property and show it selfs before ngOnInit.
How can I delay button display till ngOnInit is resolved or just refresh after that?
If I understand your question, You're having trouble because of timing. Your code doesn't include when or how the value changes but I am guessing that the value is changed before your component initializes and therefore you are subscribing late?
If so, I recommend changing
private _authChangeSub = new Subject<boolean>()
to
private _authChangeSub = new ReplaySubject<boolean>(1);
This way, even when subscribing after the value has been emitted, the component will still receive the value. See https://www.learnrxjs.io/learn-rxjs/subjects/replaysubject

Vue, basing link in href off of the method completing first

I'm trying to figure out how to reconcile this, but I have a button in Vue calling a function, which works, but it's taking more than a few seconds to complete and the href link to the next page happens first about half the time.
Is there a way to make this so that the button called method has to get a 200 success back in order for the href link to be triggered?
<button #click="changeStatus(dateEvent)" type="button" class=" taskButton btn-default">
<a v-bind:href="'/task/' + dateEvent.taskt_id + '/progress'" style="color:white;">Accept Task</a>
</button>
methods: {
changeStatus: function(dateEvent) {
//console.log(dateEvent.status, dateEvent.taskt_id)
let data = {
id: dateEvent.taskt_id,
status: 'P'
};
}
You need programmatic navigation (vue router if you want) and async/await:
You could do it without the router in vanilla js as well:
methods: {
async changeStatus(dateEvent) {
await this.myAsyncFunction(); // your function that takes two seconds to complete
let data = {
id: dateEvent.task_id,
status: 'P'
};
var route = '/task/' + dateEvent.task_id + '/progress'"
this.$router.push(route);
}
}
<button #click="changeStatus(dateEvent)" type="button" class=" taskButton btn-default">
<a style="color:white;">Accept Task</a>
</button>

Prevent javascript firing on load page

I have MVC application with JavaScript in the body of the cshtml page. In Model, I have a method that returns a string, and I want that string to add in some div on a page on click of a button. It works, but, the method is triggered every time I load the page (and I want it to be triggered only on click.
Here is code:
Model:
public class TestJS
{
public string Tekst1 { get; set; }
public string Tekst2 { get; set; }
public TestJS()
{
Tekst1 = "one";
Tekst2 = "two";
}
public string AddTekst()
{
return "three (additional text from method)";
}
}
Controller:
public class TestJSController : Controller
{
// GET: TestJS
public ActionResult Index()
{
Models.TestJS tjs = new Models.TestJS();
return View(tjs);
}
}
View:
#model TestJavaScript.Models.TestJS
#{
ViewBag.Title = "Index";
}
<script type="text/javascript">
function faddtekst() {
whr = document.getElementById("div3");
var t = '#Model.AddTekst()';
whr.innerHTML += t;
}
</script>
<h2>Testing JavaScript Firing</h2>
<p>
First to fields:
#Model.Tekst1;
<br />
#Model.Tekst2;
</p>
<form>
<input type="button" value="Click to show Tekst3" onclick="faddtekst()" />
</form>
<br />
<hr />
<div id="div3">
</div>
I tried to wrap JS in $(document).ready() with same result.
Somebody may think of this as a strange approach, but, a model method that I'm trying to execute takes over 10 seconds in real code, so, I want to prevent waiting every time page loads (waiting should be only if the user clicks button).
The strangest thing is that Model.AddTekst() is executed EVEN if I comment it in javascript function with '//'.
Anyone knows how to avoid unwanted execution of Model.Method?
The behavior you are experiencing is not strange at all. #Model.AddText() executes on the backend once the view is compiled which is normal behaviour.
A comment in razor would look like this
#* Comment goes here *#
But this is not what you want to achieve.
I'm afraid your approach wont work since you can't execute a method on a model asynchronously.
I suggest you take a look at Ajax.BeginForm - more info here
You could implement a controller action on the backend which would return the text you want to display on the submitting of the form.
Try to use e.preventDefault() for button click.
<form>
<input type="button" value="Click to show Tekst3" id="Show" />
</form>
Try with jQuery
$(document).on("click", "#Show", function (e) {
e.preventDefault();
faddtekst();
});

Render partial view with form in Modal on Html.ActionLink click

I have a page with table where each row has a link "Move".
On clicking of the link I am trying to get the controller GET method called which will in turn render the _MoveReportPartial view in a modal.
Once the user makes the selections in the modal the submit button should post to the Post method of the controller.
If I remove the class attribute (move-modal) from Html.ActionLink(...), it in effect disengages the js file and ignores it. Then it works by opening the _MoveReportPartial in a new window and then consequently posting to the correct method if user clicks submit.
I am trying to get it to open in the modal, but the js I have doesn't work and routes to the POST method instead on "Move" click.
EDIT
Why does the .load call the POST method instead of the GET? How can I change the js? (added event.preventDefault();, still the same behavior is observed)
The move link on the originating view looks like this:
<div class="d20 actionLink">
#Html.ActionLink("Move", "MoveReport", "ReportsWeb", new {id = item.ReportDefinitionId, newReport = false}, new {#class = "move-modal"})
</div>
I have a js file:
$(function () {
$('.move-modal').click(function () {
event.preventDefault();
$('<div/>').appendTo('body').dialog({
close: function (event, ui) {
dialog.remove();
},
modal: true
}).load(this.href, {});
});
});
My ReportsWebController looks like this:
[HttpGet]
public ActionResult MoveReport(Guid id, bool newReport)
{
//some code
return PartialView("_MoveReportPartial", model);
}
[HttpPost]
public ActionResult MoveReport(MoveReportModel Model)
{
try
{
//some code
}
catch (Exception exc)
{
InternetReportingTrace.Source.WriteError(exc);
throw;
}
return RedirectToAction("ListReports");
}
and my _MoveReportPartial looks like this:
<div id="dialog-confirm">
<div align="center">
<h2>Please Confirm</h2>
</div>
#using (Html.BeginForm("MoveReport", "ReportsWeb", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div tabindex="-1" role="dialog">
<div class="modal-content">
<p>Report #Model.Report.Name with ID #Model.Report.ReportDefinitionId </p>
<p>Will be moved to:</p>
#for (int i = 0; i < Model.MoveOptions.Count; i++)
{
<div class="radio">
<label><input type="radio" name="optradio">#Model.MoveOptions[i]</label>
</div>
}
<div>
<input type="submit" value="Move Report" />
</div>
</div>
</div>
}
I don't think you're preventing the default behaviour of the ActionLink properly.
Try:
$('.move-modal').click(function (event) {
event.preventDefault();
$('<div/>').appendTo('body').dialog({
close: function (event, ui) {
dialog.remove();
},
modal: true
}).load(this.href, {});
});
Because it's a jQuery handler, you can use event.preventDefault() instead of return false;. If your click handler uses return false to prevent browser navigation, it opens the possibility that the interpreter will not reach the return statement and the browser will proceed to execute the anchor tag's default behavior before that point. Using event.preventDefault() as the first line in the handler means you can guarantee that the default navigation behavior will not fire.
Secondly, your .load method call is wrong.
Change
.load(this.href, {});
to
.load(this.href);
The documentation at api.jquery.com/load states "The POST method is used if data is provided as an object; otherwise, GET is assumed." Because you're sending it an empty object, it assumes you want to use POST. There's no need to do this.

Categories