I´m building up an Angular basic CRUD app for try new things, but wish to know why this is happening.
I´m trying to get the "post" data and I do from a service, this is working fine, the problem is when I´m setting up the form, show the rest of the values, but not the user ID.
This is the code:
HTML
<div class="container">
<h1 class="hidden__content">Modify register</h1>
<div class="row new__register__form__wrapper mt-5">
<h3 class="mb-5">Modify register</h3>
<form [formGroup]="editRegisterForm" (ngSubmit)="editRegister()">
<div class="form-group mb-3">
<input type="text" class="form-control" id="userIdInput" formControlName="user" readonly>
</div>
<div class="form-group mb-3">
<textarea class="form-control" id="titleInput" cols="30" rows="3" placeholder="Write here the title of your post" formControlName="title"></textarea>
</div>
<div class="form-group form-check mb-3">
<select class="form-select" name="completedSelect" id="comletedSelect" formControlName="completed">
<option value="default" hidden>Choose an option</option>
<option value="completed">Completed</option>
<option value="no completed">Not completed</option>
</select>
</div>
<button type="submit" class="btn btn-outline-primary new__register__form__submit">Update register</button>
</form>
</div>
</div>
TypeScript
ngOnInit(): void {
this.registerId = this.route.snapshot.paramMap.get('id');
this.todosService.getAllRegistersById(this.registerId).subscribe((res: any) => {
this.editRegisterForm = this.initEditForm(res);
});
}
initEditForm(response: any){
const { user, title, completed } = response;
let status;
completed === true? (status = 'completed'): (status = 'no completed');
return this.fb.group({
user: [user, [Validators.required]],
title: [title, [Validators.required, Validators.maxLength(199)]],
completed: [status, [Validators.required]]
});
}
Try injecting ChangeDetectorRef in constructor and run markforcheck() after form is initialized.
I would suggest a different approach.
Don't try to reassign a value to editRegisterForm, but instead inside initEditForm, you can do the following:
this.editRegisterForm.get('user').setValue(yourUserValue);
this.editRegisterForm.get('title').setValue(yourTitleValue);
this.editRegisterForm.get('completed').setValue(yourCompletedValue);
This way you're going to update your FormControl's values. Otherwise your approach is probably causing an issue with change detection and I don't see a point to reassign the value of your FormGroup, when you're just trying to update your field's values and there's an API defined for that.
Related
This code is a functional component that should render a user detail page(Show details view) OR a form to edit(Edit detail view) this user info depending on the state of isUpdate. This works alright, as the update and save buttons change the state of isUpdate to render the Edit View or Show View respectively.
When I select a user, the page gets the specific user by match.params.id which also works. My problem is when the isUpdate is set to true (the edit form with the original values of the user details is rendered) I expect that the input values change when I type on the input for, but instead the value remains the same as its original value(unable to type new values).
I dont know why I am facing this problem.
UserDetail Component code below:
import React, {Fragment, useEffect, useState} from 'react';
import { connect } from 'react-redux';
import { findUser } from '../../actions/user';
const UserDetails =({match, user, loading, findUser}) => {
const[isUpdate, setIsUpdate]=useState(false);
//fetching user info based on the params id
useEffect(() => {
findUser(match.params.id);
},[])
//setting the initial state of user to the values from the fetched data
const[userData, setUserData]=useState({
name: user.name,
email: user.email,
});
//on Change handler
const onChange = e =>{
setUserData({...userData, [e.target.name]:e.target.value})
}
//User detail rendered to be viewed or edited
return loading ?
(<Fragment>
<div className="loader-container" ></div>
<div className="loader"></div>
</Fragment>) :
(isUpdate ? (
<Fragment>
<div className="detail-container">
<form>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="name">Full Name:</label>
<input
type="name"
className="form-control"
id="name"
name="name"
value={user.name}
onChange={e=>onChange(e)}/>
</div>
<div className="form-group col-md-6">
<label htmlFor="email">Email:</label>
<input
type="email"
className="form-control"
id="email"
name="email"
value={user.email}
onChange={e=>onChange(e)}/>
</div>
</div>
<button type="submit" className="btn btn-primary" onClick={()=>setIsUpdate(false)}>SAVE</button>
</form>
</div>
</Fragment>):
(<Fragment>
<div className="detail-container">
<div className="d-flex justify-content-end mb-20"> <button className="btn btn-warning "
onClick={()=>setIsUpdate(true)}> UPDATE</button>
</div>
<div className="row mb-10">
<div className=" col-md-6">
<div className="detail-label">Full Name:</div>
<div className="detail">{`${user.name}`}</div>
</div>
<div className=" col-md-6">
<div className="detail-label">Email:</div>
<div className="detail">{`${user.email}`}</div>
</div>
</div>
</div>
</Fragment>))
const mapStateToProps = (state) => ({
user: state.user.a_user,
loading: state.user.loading
})
export default connect(mapStateToProps,{findUser})(UserDetails);
Please I would appreciate anyone pointing me in the right direction.
The user data remains constant during the editing session, so the input element cannot work properly. Try to change its declaration using the userData instead:
<input
type="name"
className="form-control"
id="name"
name="name"
value={userData.name}
onChange={e=>onChange(e)}/>
UPDATE: in order to update the userData state whenever the user changes, you could use this trick:
const[userData, setUserData]=useState();
useEffect(
() => {
setUserData({
name: user.name,
email: user.email,
});
},
[user]
);
Take care to handle the initial condition where userData is undefined.
Your form inputs values should be targetting userData instead of user i.e
<input
...name="name"
value={userData.name} />
You need to set your userData in the useEffect method as it seems that for each user, the userData value is using values fetched previously.
useEffect(() => {
findUser(match.params.id)
setUserData(user)
},[])
I'm guessing the findUser method updates the user in the parent component.
This should work.
I have my chat and I dont want people to send empty message so I would like that my input become required. Thanks for your help.
I tried to put "required='required'" in the input line, I also tried veeValidate but it broke my chat when I use it, I also tried to put "Required = true" in Props and data but without a good result
This is ChatForm.vue
<template>
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" #keyup.enter="sendMessage">
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendMessage">
✓
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
newMessage: '',
}
},
methods: {
sendMessage() {
this.$emit('messagesent', {
user: this.user,
message: this.newMessage
});
setTimeout(function() {
const messages = document.getElementById('mess_cont');
messages.scrollTop = messages.scrollHeight;
}, 200);
this.newMessage = '';
}
}
}
</script>
And this is my form in the app.blade.php
<div id="app" class="container-chat">
<div class="row">
<div class="col-md-12 col-md-offset-2">
<div class="col-md-12 col-md-offset-2">
<div class="panel-body panel-content" id="mess_cont">
<chat-messages id="mess" :messages="messages" :currentuserid="{{Auth::user()->id}}"></chat-messages>
</div>
<div class="panel-footer">
<chat-form
v-on:messagesent="addMessage"
:user="{{ Auth::user() }}"
></chat-form>
</div>
</div>
</div>
</div>
</div>
Try to change your ChatForm.vue like this:
<template>
<form #submit.prevent="sendMessage">
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" required>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" type="submit" id="btn-chat">
✓
</button>
</span>
</div>
</template>
You are not treating the input in the correct way, the input which is required needs to be inside a form and the required keyword will prevent the form submission if the input field is empty.
There are a few things I would do differently.
1/ Wrap your chat form in a tag, and execute the sendMessage() method on submit. This will give your users a nicer experience, as they can just to submit the message.
2/ Convert the button into a submit button so it triggers the form.submit event.
3/ You can easily disable the button by checking whether newMessage has contents. I don't think you need vee validate or anything else to achieve this; for something as simple as a chat form, your user doesn't need much more feedback than seeing a disabled button to realise (s)he needs to write something first.
4/ in the addMessage method you can just check the contents of newMessage and not do anything when it's empty. This is perfectly fine because you already hinted the user by disabling the button too.
I think this is a subtle way where you guide your user, but don't overdo it.
Please add name attributes to all of your form elements. Some of the element in my form had name attribute and some didn't. Element which had name attributes worked correctly but the one's which didn't had name failed.
I have two fields in the exported class. The template has a drop-down with its ngModel bound to the first field (selectedInterval) with two-way binding. When the dropdown option changes, the calculateReviewDate() event takes place and successfully updates the 2nd field (nextReviewDate), but the dropdown stays blank until I select the same option twice. In addition, the spinner never appears during the calculation. Does anyone know why?
<form #FormVar="ngForm" novalidate>
<div class="form-group">
<div class="row">
<div class="col col-md-2 col-sm-3">
<input type="text" [ngModel]="nextReviewDate | date:shortDate" name="nextReviewDate" id="nextReviewDate1" class="form-control" disabled/>
</div>
<div class="col col-md-1 com-sm-3" *ngIf="showSpinner">
<fa [name]="'spinner'" [size]=1 [spin]=true></fa>
</div>
<div class="col col-md-2 col-sm-3">
<select class="form-control" name="nextReviewDate" id="nextReviewDate2" [(ngModel)]="selectedInterval" (change)="calculateReviewDate()">
<option *ngFor="let r of reviewIntervals" [value]="r.interval">{{r.intervalDescription}}</option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary" [disabled]="!FormVar.valid" (click)="save(FormVar)">Review Note</button>
</form>
calculateReviewDate(): void {
this.showSpinner = true;
let calculator: calculateDate = new calculateDate();
let today: Date = new Date();
this.nextReviewDate = calculator.addMonth(today, this.selectedInterval);
this.showSpinner = this.nextReviewDate === undefined;
}
How you get reviewIntervals? And for the spinner, my thought it's because too fast, try to add a delay before this.showSpinner = this.nextReviewDate === undefined;like set time out.
Not sure about your issue with the select, but I know what is going on with your spinner. You have no asychronous code in your calculateReviewDate method so the spinner won't be shown. JS runs on a single thread and unless you break the synchronous code up into parts that allow the control to be given back to the browser to paint, your spinner will not be shown.
I think you have two issues here:
1. onChange, the selected value is not shown the first time.
2. Spinner is not shown on Select value change.
Why the Spinner is not shown?
On Change since the calculateReviewDate() method is being called directly (Synchronous behavior), and in this method the spinner is set to true in the starting and then state gets set to either true/false based on nextReviewDate variable, I guess nextReviewDate variable would never become undefined,so nextReviewDate always holds some valid value, so it sets to false again, so in the background the spinner will become rendered and immediately gets removed as you have used a structural directive and all logic in the method happens synchronous manner and will be in a very short span, so visually we are not able to see the rendered spinner getting on and off.
Why the Select controls selected value is not shown?
I have shared a modified example of your version in which things are fine,
Template:
<div>
<form #FormVar="ngForm" novalidate>
<div class="form-group">
<div class="row">
<div class="col col-md-2 col-sm-3">
<div class="form-group">
<input type="text" [ngModel]="nextReviewDate" name="nextReviewDate" id="nextReviewDate1" class="form-control" disabled/>
</div>
</div>
<div class="col col-md-1 com-sm-3" *ngIf="showSpinner">
<p>Spinner</p>
</div>
<div class="col col-md-2 col-sm-3">
<select class="form-control" name="nextReviewDate" id="nextReviewDate2" [(ngModel)]="selectedInterval" (change)="calculateReviewDate()">
<option *ngFor="let r of reviewIntervals" [value]="r">{{r}}</option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary" >Review Note</button>
</form>
</div>
TS:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
reviewIntervals = [1,2,3,4,5];
selectedInterval = 5;
showSpinner = false;
nextReviewDate;
calculateReviewDate(value): void {
this.nextReviewDate = this.selectedI`enter code here`nterval;
}
}
I think a more appropriate title is my private JavaScript Hell.
First off I am new to JavaScript. Never used it before this attempt.
Three or four weeks back when I first encountered this error I posted on ASP.Net forums and the good folks helped me out there. However after a full week of one hour a day concentrating on this issue I am no further advanced than I was three weeks ago even with their help. The issue seems to be either I am transcribing something incorrectly and I am blind (this wouldn't surprise me) or the error is specific to some aspect within my machine.
First the set up. I have a controller for jobs where I am wanting to cascade a drop down box content based on a preceeding selection. The two drop downs involved are Site and Waterbodys. The relevant part of the controller is:
public class JobsController : Controller
{
private readonly EVAContext _context;
public JobsController(EVAContext context)
{
_context = context;
}
// GET: Jobs/Create
public IActionResult Create()
{
ViewData["SiteID"] = new SelectList(_context.Sites, "SiteID", "SiteName");
ViewData["WaterBodyID"] = new SelectList(_context.WaterBodys, "WaterBodyID", "WBName");
ViewData["DepartmentID"] = new SelectList(_context.Departments, "DepartmentID", "iDepartment");
return View();
}
public IActionResult GetWaterBody(int siteID)
{
var waterBody = new List<WaterBody>();
waterBody = getWaterBodyFromDataBaseBySiteID(siteID);
return Json(waterBody);
}
public List<WaterBody> getWaterBodyFromDataBaseBySiteID(int siteID)
{
return _context.WaterBodys.ToList();
}
Within the associated view I have :
#model EVA.Models.Job
#{
ViewData["Title"] = "Add Job";
}
<h2>Add Job</h2>
<form asp-action="Create">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrderNumber" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OrderNumber" class="form-control" />
<span asp-validation-for="OrderNumber" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="SiteID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="SiteID" class="form-control" asp-items="ViewBag.SiteID" id="site-target">
<option>---Select Site First---</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="WaterBodyID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="WaterBodyID" class="form-control" asp-items="ViewBag.WaterBodyID" id="wb-target">
<option>---Select Site First---</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="DepartmentID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
<option>---Select Department---</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="JobNumber" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="JobNumber" class="form-control" value="1234" />
<span asp-validation-for="JobNumber" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="JobDescription" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="JobDescription" class="form-control" />
<span asp-validation-for="JobDescription" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script>
$(document).ready(function () {
$("#site-target").on("change", function () {
$list = $("#wb-target");
$.ajax({
url: "Jobs/GetWaterBody",//Core0309 is my controller name
type: "GET",
data: { id: $("#site-target").val() }, //id of the site which is used to extract waterbodies
traditional: true,
success: function (result) {
$list.empty();
$.each(result, function (i, item) {
$list.append('<option value="' + item["WaterBodyID"] + '"> ' + item["WBName"] + ' </option>'); //Must be lowercase ,if not ,you will get value 'Undefined'
});
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status);
alert(textStatus);
},
//error: function () {
// alert("Danger!! Will Robertson Danger!!!!!!!!!!!");
// }
});
});
});
</script>
<!--<script src="~/js/WaterBody.js"></script>-->
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I previously had Waterbody.js which was also returning the same 404 based error.
What is frustrating and has led me here is on the ASP.Net forum the people there have copied my code to their own machine and it is running without error I am told. Although they are using a different set of drop downs.
I am seriously feeling like I am bashing my head against the wall. I have tried every suggestion, spent hours looking at examples on line and making minor changes to try and find the culprit but nothing works.
I am left with these questions:
1: Is my JobController code correct? Have I inadvertently introduced a stuff up there?
2: Is the script correct? I added alerts at one stage and I keep getting the idea the error is in the url but don't know that.
3: Is there some reference or dependency I may be missing on my machine specifically?
4: Some other question I haven't considered as I am still too new to this?
Help please. I am at the point where I must get this working to be able to move on with the balance of the project. Thanks in advance. I honestly hope this is a silly thing I done.
Bit of an update...
I added some alerts and they are indicating the error is occurring on $.ajax line.
alert("Inside")
$list = $("#wb-target");
alert("aferlist")
$.ajax({
Afterlist is firing but the next alert "Ajax" doesn't fire.
I am used Reactive form Validation(Model driven validation) but cant set the value to form object on Dropdown change
This is my Formgroup
studentModel:StudenModel
AMform: FormGroup;
Name = new FormControl("", Validators.required);
Address = new FormControl("", Validators.maxLength(16));
constructor(fb: FormBuilder){
this.AMform = fb.group({
"Name": this.Code,
"Address": this.Abbrev,
});
}
onAccntChange(event: Event) {
// set the value from Class Model
//// this.studentModel
// how to set this.studentModel value to form
}
This is My html page
<form [formGroup]="AMform" (ngSubmit)="submit()">
<select (change)="onAccntChange($event)" class="form-control" [disabled]="ddlActivity" formControlName="AccountManagerID">
<option value="0">Select</option>
<option *ngFor="let item of allStudent" value={{item.StudentID}}>
{{item.Name}}
</option>
</select>
<div class="col-sm-9">
<input type="text" class="form-control" formControlName="Name">
</div>
<div [hidden]="Name.valid || Code.pristine" class="error"> Name is required </div>
<div class="col-sm-9">
<input type="text" class="form-control" formControlName="Address">
</div>
<div [hidden]="Address.valid || Address.pristine" class="error">Address is required </div>
<button type="submit" class="btn btn-warning "><i class="fa fa-check-square"></i> Save</button>
</form>
On change i need to set the formcontrol value
You can achievie that by invoking setValue method on your FormControl object:
(<FormControl> this.AMform.controls['Name']).setValue("new value");
or:
this.Name.setValue("new value");
Use patchValue method of your FormGroup object.
onAccntChange(event: Event) {
this.AMform.patchValue({yourControl: studentModelValue})
}
Using setValue you need to specify all the FormControls:
this.AMform.setValue({'Name':'val1', 'Address':'val2'})
Using patchValue you can specify just the one you need:
this.AMform.patchValue({'Name':'val1'})
Here you can read a little bit more.