please can someone help me with this little binding mess. I'm trying to generate a list of tasks
here is the model definition in my TaskController:
angular.module('yeomantestApp')
.controller 'TaskController', ($scope) ->
$scope.currentTask
$scope.tasks = [
{
id: 1
name: 'write test'
elapsedTime: 15
},
{
id: 2
name: 'run test'
elapsedTime: 32
},
{
id: 3
name: 'write code'
elapsedTime: 22
}
]
So, now I want to render the model with the following view. The view iterates over the task array and build a list of radio buttons for each task.
My problem is, that the model binding to currentTask is somehow not working. When I select any task the currentTask model entry is not updating. But according the tutorials and documentation it should.
<div class="hero-unit" ng-controller="TaskController">
<h1>Tasks</h1>
<h2>current {{currentTask}}</h2>
<form name="taskForm">
<div ng-repeat="task in tasks">
<input type="radio" name="taskGroup" ng-model="currentTask" value="{{task.id}}">{{task.name}} {{task.elapsedTime}}
</div>
</form>
</div>
Changing ng-model attribute to ng-model="$parent.currentTask" should solve your problem.
Here is the jsFiddle: http://jsfiddle.net/dp3xq/8/
Related
This is the sample form item inside my form for user to input while create new data :
<nz-form-item>
<nz-form-label>{{ 'label.bank.number' | translate }}</nz-form-label>
<nz-form-control [nzErrorTip]="'error.bank.number' | translate">
<input nz-input formControlName="bankAccNo"/>
</nz-form-control>
</nz-form-item>
This is the sample hard code data :
this.bank.push(
{
id: '0',
code: 'P0001',
bankName: 'MayBank',
bankNumber: '1231231233',
currency: 'RM',
status: 'Active'
},
{
id: '1',
code: 'P0002',
bankName: 'Public Bank',
bankNumber: '1231123145',
currency: 'RM',
status: 'Inactive'
});
What I try to achieve is when user press edit button, the input box with formControlName="bankAccNo" will retrieve the bankNumber data. Please help if you have any idea on how to do this, tried few solution but none of it works.
Solution found to this question : https://angular.io/api/forms/FormControlName
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}`)
);
}
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
...
}
}
I have created a property editor and I have set up a document to show the propery, like this:
I still can not figure out how to display the text to the frontpage. I manage to display the other property, called box, that I added to the document
Here is how my controller looks:
angular.module("umbraco").controller("mytext",
function ($scope) {
// do code here
});
Here is how my mytext.html looks:
<div ng-controller="mytext" class="mytext">
<h2>Hellooooooo!</h2>
<!--<input type="text" ng-model="model.value" />
ng-model="model.value" />-->
<div class="display"><h2>Hellooooooo!</h2></div>
</div>
And this is how my template looks:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.MyHelooo>
#using ContentModels = Umbraco.Web.PublishedContentModels;
#{
Layout = "Master.cshtml";
}
#CurrentPage.Name
#CurrentPage.Hejhej
#CurrentPage.box
<p>All your bases</p>
Finally this is how my package.manifest looks:
{
"propertyEditors": [
{
"alias": "MyText",
"name": "My text",
"hideLabel": false,
"valueType": "JSON",
"editor": {
"view": "~/App_Plugins/MyText/mytext.html"
}
}
],
"javascript": [
"~/App_Plugins/MyText/mytext.controller.js"
],
"css":[
"~/App_Plugins/MyText/mytext.css"
]
}
EDIT:
I used Joniff answer to make a controller like this:
angular.module("umbraco").controller("mytext",
function ($scope) {
$scope.value = 'Helloooooooooooo!';
});
To make it work I had to clear the cache of my browser with CTRL+SHIFT+DELETE.
I also had to make sure that I ran Visual Studio in debugging mode, minor things but frustrating when you don't know what is wrong.
From the exact code published, you have commented out the code that would actual set model.value in mytext.html to a value. This does need to be set to something, otherwise, your new property type has no value to output on the front end.
If you want to hardcode your property to always display 'Helloooooooooooo!' what about
angular.module("umbraco").controller("mytext",
function ($scope) {
$scope.value = 'Helloooooooooooo!';
});
or
<div ng-controller="mytext" class="mytext" ng-init="model.value='Hellooooooo'">
<h2>Hellooooooo!</h2>
<div class="display"><h2>Hellooooooo!</h2></div>
</div>
though, why not have the model.value editable using an input control, like
<div ng-controller="mytext" class="mytext">
<input type="text" ng-model="model.value" />
</div>
Ultimately, I'm not sure what you are trying to achieve, as the code you have posted is doing the same job as a label, which
A) Already exists in Umbraco as a property type and
B) has no input or output just like your version of it.
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,
});
}