How To Update Database Using Angular Material Reactive Form And Restful API - asp.net

Let Me take famous Book Example To explain My question.
I have a angular material reactive form build based on Book Model in my BookService.ts. When I change some field In this form and Submit it to my back end to update according record using Angular HttpClient PUT method It is not updating my database and comes back with and error. When I debug it, It shows ID is not defined, When I console.log(BookForm.value) I get This out put: {$key: 1234, Title: "Book Title1", Topic: "Topic1"} , No need to say my Angular HttpClient PUT Restful API needs that ID in order to be able to update that particular record in My Database table. Bellow Is My Simplified Mocked Code To Explain It.
BookModel.ts File, My Model
export interface Book{
ID: number;
Title: string;
Topic: string;
}
BookService.ts File, My Service
BookForm: FormGroup = new FormGroup({
$key: new FormControl(null),
Title: new FormControl('', Validators.required),
Topic: new FormControl('', Validators.required),
});
UpdateBook(bookObj: Book): Observable<Book>
{
return this.http.put<Book>(`...api/book/${bookObj.ID}`, bookObj,{
headers: new HttpHeaders({
'Content-Type: 'application/json'
})
})
}
Note: This Throw Error, ID Undefined
Book-form.component.html File
<form [formGroup] = "BookService.BookForm" class="FormCls">
<mat-grid-list rowHeight="200px">
<mat-grid-tile>
<div class="form-controles-container">
<input type="hidden" formControlName="$key" />
<mat-form-field>
<input formControlName="Title" matInput placeholder="Title*" />
<mat-error>Title Needed</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="Topic" matInput placeholder="Topic*" />
<mat-error>Topic Needed</mat-error>
</mat-form-field>
<div class="button-row">
<button mat-raised-button color="primary" type="submit" (click)="onSubmit()">Submit</button>
</div>
</div>
</mat-grid-tile>
</mat-grid-list>
</form>
Book-form.component.ts File
onSubmit(): void
{
BookService.UpdateBook(BookService.BookForm.value).subscribe(
b => alert(`Book Updated Successfully`),
err => alert(`Exception While Updating: ${err}`)
);
}
For sure I know I need to some how convert my form value to my Book model and make sure I have that ID inside that before I pass it to my http put service. but I dont know how to do that, Im fairly new in both Angular and Typescript world and I am learning. I love reading before I ask, so went thru lots of Articles but none worked for me. For Example I tried below Article on stackoverfelow but did not work for me
Reactive Forms correctly convert Form Value to Model Object
I really Appreciate you professionals and Thank For Your Time And Help.

when you call any AbstractControl's .value method, you are gettin an object with prop: value pairs for each input in your form. As you can see, you will get an IDless object, maybe the reason is because the form has no 'ID' property, or maybe it is because the value is null (when you transform an object to JSON, all properties that have null value get deleted).
If you know the id from the HTML, you can pass it in the function
<button mat-raised-button color="primary" type="submit" (click)="onSubmit(sth.id)">
if that's not the case (I think it isn't), you could add the ID before calling your service with the .patchValue({ id: idInfo }) method that any AbstractControl has (in this case, you won't need to add a hidden input, unless you add it for another reason).
Do a patch before calling the service and it should work.
I'm not sure if this is enough info to help you, feel free to ask any questions.
edit (to add a code example):
onSubmit(): void
{
// the lines I added
const bf = BookService.BookForm;
bf.patchValue({ ID: 'anyIdYouLike' });
// what stays the same (except the bf.value shortened version)
BookService.UpdateBook(bf.value).subscribe(
b => alert(`Book Updated Successfully`),
err => alert(`Exception While Updating: ${err}`)
);
}

Related

Angular Reactive form asynchronous operation

I am getting this error when I try to build a reactive form for creating a new password form.I have mentioned the source code below and when I remove the source code part then there is no error but without that my operation is not working as well. I think I have to add or delete something in my source code to get the desired output
main.ts:12 TypeError: Cannot read property 'value' of null
at FormGroup.passwordShouldMatch [as validator] (password.validators.ts:18)
at FormGroup._runValidator (forms.js:4089)
at FormGroup.updateValueAndValidity (forms.js:4050)
at new FormGroup (forms.js:4927)
at FormBuilder.group (forms.js:8924)
at new ChangePasswordComponent (change-password.component.ts:15)
at createClass (core.js:31987)
at createDirectiveInstance (core.js:31807)
at createViewNodes (core.js:44210)
at callViewAction (core.js:44660)
static passwordShouldMatch(control : AbstractControl) {
let newPassword = control.get('newPassowrd');
let confirmPassword = control.get('confirmPassowrd');
if (newPassword.value !== confirmPassword.value){
return { passwordShouldMatch:true };
return null;
}
}
As you didn't add any code snippet I am considering your form structure is something like this.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
});
here, you include an custom validation function called passwordShouldMatch and this function looks fine. So, I assume that you did something wrong when you set the validator to that form group.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
}, { validator: this.passwordShouldMatch});
this is how you should set the validation function for the form group. And in html your form should be something like this.
<form [formGroup]="form" novalidate (ngSubmit)="onSubmit(survey)">
<input type="text" placeholder="Untitled form" formControlName="newPassowrd">
<input type="text" placeholder="Untitled form" formControlName="confirmPassowrd">
<span *ngIf="form.hasError('passwordShouldMatch')">not match</span>
</form>
everything should work this way. Here is the working version of stackblitz

use the same form for create and read operation

I have a master and a child component. The child component persists the data for the create mode as well as the edit mode. The child has a data section as follows which is being used when the component is in create mode
data() {
return {
title: '',
description: '',
organizer: '',
startdate: '',
enddate: '',
email: '',
phone: ''
}
},
and my inputs in create mode are as follows
<input type="text" placeholder="enter event title here" class="form-control" v-model="title">
In the edit mode, I am updating a prop value on the client as follows, which is
props:['currentevent']
The value of the currentevent is being passed from the master component to the child component and is also the value that is currently being edited.
so, the complete code for handling an input value looks like as follows
<input type="text" placeholder="enter event title here" class="form-control" v-if="currentevent" :value="currentevent.title">
<input type="text" placeholder="enter event title here" class="form-control" v-else v-model="title">
and in my save method (in the child component), I am checking if currentevent is empty or not. If it is empty then I trigger the add code otherwise, I trigger the update code.
Question : This works , but I have a large form and having to do this for each and every component is not a clean design . Can you please let me know what should I be doing ?
I totally appreciate your predicament. The best way to handle form data is to make it create/update agnostic. Here's what I'd recommend you try:
Instead of maintaining all the data fields as disparate properties, contain them in a single object, in this case I'm calling it eventObj for clarity:
data () {
return {
eventObj: {}
}
}
Then in your markup you'd reference them via the object:
<input type="text" placeholder="..." class="form-control" v-model="eventObj.title">
You'd then need to define a prop for passing in the data (as an object) from the parent component if you are editing:
props: {
currentevent: Object
}
And then you'd just need to map the incoming prop to the child component's data:
created() {
Object.assign(this.eventObj, this.currentevent || {})
}
Now when your inputs like <input v-model="eventObj.title"> are processed, if there is a saved title (that was passed in with currentevent) the field will be prepopulated with it, otherwise it will be blank.
I think this should help you in the right direction toward solving the complexity you're trying to figure out. There are other logistical issues involved with this kind of stuff in general, but I won't drone on. :)
The issue I see is you want to remove the v-if/else in the form. I will recommend here is keep your local data of child to be in sync with the props passed and only use local variable in the form.
One way to do this can be put a watcher on props and whenever props changes, update local variables and only use those variables in form.
watch: {
currentevent: function(newVal){
title = newVal.title,\
description = newVal.description
...
}
}

creating element on click in Meteor

I'm using Meteor with React. I have a really simple goal, but i have tried a lot and can't solve it for myself. I will show you my attemps below.
I want to create a form for the Ingredients. At the first moment there is only one input (for only one ingredient) and 2 buttons: Add Ingredient and Submit.
class IngredientForm extends Component {
render() {
return(
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text"/>
{ this.renderOtherInputs() }
<input type="button" value="Add Ingredient" onClick={this.addIngredient.bind(this)}>
<input type="submit" value="Submit Form">
</form>
);
}
}
So when I click Submit then all the data goes to the collection. When I click Add Ingredient then the another text input appears (in the place where renderOtherInputs() ).
I know, that Meteor is reactive - so no need to render something directly. I should underlie on the reactive data storage.
And I know from the tutorials the only one way to render something - I should have an array (that was based on collection, which is always reactive) and then render something for each element of that array.
So I should have an array with number of elements = number of additional inputs. that is local, so I can't use Collection, let's use Reactive Var instead of it.
numOfIngredients = new ReactiveVar([]);
And when I click Add button - the new element should be pushed to this array:
addIngredient(e) {
e.preventDefault();
let newNumOfIngredients = numOfIngredients.get();
newNumOfIngredients.push('lalal');
numOfIngredients.set(newNumOfIngredients);
}
And after all I should render additional inputs (on the assumption of how many elements I have in the array):
renderOtherInputs() {
return numOfIngredients.get().map((elem) => {
return(
<input type="text"/>
);
}
}
The idea is: when I click Add button then new element is pushed to the ReactiveVar (newNumOfIngredients). In the html code I call this.renderOtherInputs(), which return html for the as many inputs as elements I have in my ReactiveVar (newNumOfIngredients). newNumOfIngredients is a reactive storage of data - so when I push element to it, all things that depends on it should re-render. I have no idea why that is not working and how to do this.
Thank you for your help.
Finally I got the solution. But why you guys don't help newbie in web? It is really simple question for experienced developers. I read that meteor and especially react have powerful communities, but...
the answer is: we should use state!
first let's define our state object in the constructor of react component:
constructor(props) {
super(props);
this.state = {
inputs: [],
}
}
then we need a function to render inputs underlying our state.inputs:
renderOtherInputs() {
return this.state.inputs.map( (each, index) => {
return (
<input key={ index } type="text" />
);
});
}
and to add an input:
addInput(e) {
e.preventDefault();
var temp = this.state.inputs;
temp.push('no matter');
this.setState({
inputs: temp,
});
}
p.s. and to delete each input:
deleteIngredient(e) {
e.preventDefault();
let index = e.target.getAttribute('id');
let temp = this.state.inputs;
delete temp[index];
this.setState({
inputs: temp,
});
}

Meteor: resetPassword email link behavior

I'm trying to implement the "reset password" functionality in my Meteor app. I have a very simple implementation of it based on this tutorial: Julien's tutorial on gentlenode
There are several examples floating around that use this same basic approach. I did mine almost exactly like Julien's but I used only one template; I use an {{#if}} in my template that displays the 'reset password' form, if my session variable sResetPassword is not falsey. (I don't know how the correct template is supposed to get displayed in Julien's example and it doesn't work for me as it is written -- the template doesn't change.)
Here's the critical piece of code. Two different methods that both work on my local app, but neither one works on my hosted (modulus) app.
/* method one
if (Accounts._resetPasswordToken) {
Session.set('sResetPassword', Accounts._resetPasswordToken);
}
/* method two
Accounts.onResetPasswordLink( function(token) {
Session.set('sResetPassword', token);
});
On my deployed version (Modulus), the link opens up my app and just goes straight to the start screen. When I check the value of my sResetPassword session var, it's undefined, so somehow the value of the token never gets put into the var.
While we're on the subject, does anyone know how you are supposed to get the correct template to load when you use a separate template for the reset password form?
Here is how it works for us. Code:
var token, done;
Accounts.onResetPasswordLink(function (t, d)
{
token = t;
done = d;
setTimeout(()=>Router.go("reset_password"), 0);
});
Template["reset_password"].events({
"click #resetBtn": function (event:Event, instance:Blaze.TemplateInstance)
{
var password1: string = instance.$("#input_password1").val();
var password2: string = instance.$("#input_password2").val();
console.log(password1, password2);
if (password1 != password2)
{
return;
}
Accounts.resetPassword(token, password1, ()=>
{
done();
Router.go("somewhere");
});
}
});
Template:
<template name="reset_password">
<form data-parsley-validate>
<div class="input-field">
<input id="input_password1" type="password" class="validate" data-parsley-trigger="keyup" data-parsley-minlength="6" data-parsley-minlength-message = "Please provide a password that is at least a 6 characters long." required>
<label for="input_password1">New Password</label>
</div>
<div class="input-field">
<input id="input_password2" type="password" class="validate" data-parsley-trigger="keyup" data-parsley-minlength="6" data-parsley-minlength-message = "Please provide a password that is at least a 6 characters long." required>
<label for="input_password2">Again</label>
</div>
<button id="resetBtn" class="waves-effect btn">Reset Password</button>
</form>
OK, for whatever reason, replacing iron-router with flow-router fixed this issue for me. I created a new app with only the login and reset password functionality and it worked fine. I added iron-router and again it worked, but only dev mode. When I ran it in production mode, the problem returned. Replaced iron-router with flow-router (in both the test app and my full app) and now the problem is gone. The email link works as expected in both modes.

Ember.js : how to bind a model attribute on a custom component

I would like to integrate a date picker in a form. So I created a custom DateTimePickerView like this :
App.DateTimePickerView = Em.View.extend
templateName: 'datetimepicker'
didInsertElement: ->
self = this
onChangeDate = (ev) ->
self.set "value", moment.utc(ev.date).format("dd/MM/yyyy hh:mm")
#$('.datetimepicker').datetimepicker(language: 'fr', format: 'dd/mm/yyyy hh:ii').on "changeDate", onChangeDate
Here is the template :
<script type="text/x-handlebars" data-template-name="datetimepicker" >
<input type="text" class="datetimepicker" readonly>
</script>
In my form I want to bind this component to an attribute of my model (I am using the RestAdapter) :
<script type="text/x-handlebars" id="post/_edit">
<p>{{view Ember.TextField valueBinding='title'}}</p>
<p>{{view App.DateTimePickerView valueBinding='date'}}</p>
</script>
Everything works fine in apparence : the DateTimePicker is well displayed and the value is set inside the input.
But there is a problem in the effective binding : when I send the form, the post param "date" (corresponding to the attribute) is null.
When I look inside the generated html code I can see the following :
<p>
<input id="ember393" class="ember-view ember-text-field" type="text" value="Event 1">
</p>
<div id="ember403" class="ember-view">
<input type="text" class="datetimepicker" readonly="">
</div>
I am not an expert in the global ember structure, but I guess that the id element is important for the binding. In that case, for my component, the ember id is put to the container of my component and not to the input containing the value. So I guess the problem is here.
So what could be the correct way to make it work ?
I just created a working jsfiddle here ; we can see that the modifications in the title field are taken into account but not the modifications in the DateTimePickerView component.
I guess the problem lies in the fact that you where trying to listen on an event fired from the datetimepicker which is not captured, and thus the model value not set.
To make things more solid you should get the datetimepicker current date value in your doneEditing function, just before saving the model back to the store.
Let me show in code what I mean:
window.App.EventController = Ember.ObjectController.extend(
...
doneEditing: ->
// relevant code line
#get("model").set("date_begin", $('.datetimepicker').data('date'))
#set "isEditing", false
#get("store").commit()
...
)
And here your (working) jsfiddle.
Hope it helps
Edit
After reading your comment I've modified the input field inside your datetimepicker template. Please see here an updated jsfiddle that also initializes the input field of the datetimepicker on edit begin when calling edit.
...
edit: ->
#set "isEditing", true
startDate = #get("model").get("date_begin")
#$(".datetimepicker").data({date: startDate}).datetimepicker("update")
...
You are now safe to remove the onDateChange function and do init and save inside your edit and doneEditing respectively, applying format or whatever.
Edit 2
Reading your last comment, this is how you register customEvents for example in your App.DateTimePickerView:
...
customEvents: {
changedate: "changeDate"
}
...
this way Ember will be aware of your custom events. You can register whatever events you want but notice that the keyname is lowercased and the value must have the event name to listen to camelcased. For more infos on custom events see here.
Please see here for another update jsfiddle with the changeDate custom event registered.
I have finally resolved this problem making some controls when using the moment.js library.
Everything was working fine with the binding process of the custom datetimepickerview.
Here is a working jsfiddle : here
The relevant code is here :
window.App.DateTimePickerView = Ember.View.extend(
templateName: 'datetimepicker'
didInsertElement: ->
#this test is important and was the problem
if #.get 'value'
#$('input').val moment.utc(#.get 'value').format('LLL')
onChangeDate = (ev) =>
date = moment.utc(ev.date).format('LLL')
#.set "value", date
#$('.datetimepicker').datetimepicker(format: 'dd/MM/yyyy', pickTime: false).on "changeDate", onChangeDate
)

Resources