I am writing a component (form) that acts as a container for form fields that are passed from the parent component.
I pass the form fields to the child as a prop:
formFields: [
{ name: 'email', type: 'email' },
{
name: 'sub_group',
type: 'group',
contents: [
{ name: 'sub_field_01', type: 'text' },
{ name: 'sub_field_02', type: 'text' },
]
}
]
That is all displayed fine and works well in my child component.
I used a computed property to build up data to post to a route to update the resource. I group the sub_group so that when I am posting, the controller understands that the sub_group is an array.
However, the sub_group array is always empty until I console log it, so it only evaluates when logged. The top level (email in this case) always shows up.
I have tried getting rid of the computed property and use a method to build up the object to post but I have the same issue. I really have no idea what is happening. How can 'force' the computed property to evaluate?
EDIT Adding form example
Code that renders the form:
<div v-for="field in formFields">
<!-- Individual Field -->
<div v-if="field.type !== 'group'">
<label v-if="field.label">{{ field.label }}</label>
<input :type="field.type" :name="field.name" v-model="field.value">
</div>
<!-- Group -->
<div v-else>
<div v-for="child in field.group">
<label v-if="child.label">{{ child.label }}</label>
<input :type="child.type" :name="child.name" v-model="child.value">
</div>
</div>
</div>
Computed property for post:
<div v-for="field in formFields">
<!-- Individual Field -->
<div v-if="field.type !== 'group'">
<label v-if="field.label">{{ field.label }}</label>
<input :type="field.type" :name="field.name" v-model="field.value">
</div>
<!-- Group -->
<div v-else>
<div v-for="child in field.group">
<label v-if="child.label">{{ child.label }}</label>
<input :type="child.type" :name="child.name" v-model="child.value">
</div>
</div>
</div>
I realise this needs refactoring but I can't actually get it working like this. I have tried adding cache:false and a getter to the computed property but that didn't work.
Related
I am using v-form. and I am trying to take some input field in v-for loop dynamically. here is my code
<div class="row attribute-fields">
<div class="col-md-4 col-sm-12 col-lg-4" v-for="(singleAttribute,index) in attributes"
:key="index">
<div class="form-group">
<label for="attribute_name">{{ singleAttribute.attribute_name }}</label>
<input type="text" class="form-control" id="attribute_name"
:placeholder="singleAttribute.placeholder" v-model="form.attribute[index]">
</div>
</div>
</div>
and this attribute is comming after on Change a selectLIst ok thats give me the response. but i got a error in v-model. like this
and in data property in initialized my variable like this
form: new Form({
id: '',
category_id: '',
subcategory_id: '',
child_category_id:
attribute: [],
})
now i want to save all inputs value in attribute varibale using their index number.like
form.attribute[1] = 'value';
PLease help me to solve this problem
I am working an edit form, it happens that I have several options to choose, these options are obtained by ajax with axios and I assign them to the variable permisos of the component that later renders through a v-for, the checked elements I have them in a array selected that is assigned to the vue-model as follows
<div class="row">
<div v-for="permiso in permisos" class="col-md-5 col-12 col-sm-5" >
<input type="checkbox" :value="permiso.id"
class="form-control" :id=permiso.id
v-model="selected" :checked=selected.filter(e => e.id === permiso.id).length > 0 > {{ permiso.name}}
</div>
</div>
later I make another ajax call to know what options I had before editing the item to know what options or checkbox I will assign the checked attribute, this is where I have problems do not check correctly.
axios.get('api/allpermisos')
.then(response =>{
this.permisos = response.data; //dataok
})
if(this.action===2){
axios.get('api/allpermisos/'+ this.dataobject.id)
.then(response =>{
this.selected = response.data;//data ok
})
}
How can I do so that when I get the ajax call from the options already selected, the attribute checked is assigned automatically and those that are not, are not assigned. try with includes but I do not have the desired result?
The code works correctly if I remove the v-model. Why is this happening?
<input type="checkbox" :value="permiso.id" class="form-control"
:id=permiso.id :checked=selected.filter(e => e.id === permiso.id).length > 0 > {{ permiso.name}}
You don't need both v-model and :checked. v-model is a two way binding.
https://jsfiddle.net/bbsimonbb/eywraw8t/15613/
<div v-for="permiso in permisos" class="col-md-5 col-12 col-sm-5" >
<input type="checkbox" :value="permiso.id"
class="form-control" :id=permiso.id
v-model="selected"> {{ permiso.name}}
</div>
Consider creating a component for your input. Form inputs inside a v-for rapidly gets complicated.
You need to keep their ids in "selected" array, you are probably keeping whole objects which didn't work from what I checked.
HTML:
<div id="app">
<div class="row">
<div v-for="permiso in permisos" class="col-md-5 col-12 col-sm-5" >
<input type="checkbox" :value="permiso.id"
class="form-control" :id=permiso.id
v-model="selected" :checked=selected.includes(permiso.id)> {{ permiso.name}}
</div>
</div>
</div>
Vue:
new Vue({
el: '#app',
data() {
return {
selected: [2, 4],
permisos: [{id: 1, name: "test1"}, {id: 2, name: "test2"}, {id: 3, name: "test3"}, {id: 4, name: "test4"}]
}
}
})
https://jsfiddle.net/eywraw8t/15555/
This works.
If you are getting object array as response, you could do this:
this.selected = response.data.map(obj => obj.id);
I am trying display checkboxes for my user roles:
For eg. I have two user roles : 1.Admin 2.Employee
I have an array of roles in userObject:
user={
"name":"Bhushan",
"email":"bhushan#yaho.com",
"roles":['Admin','Employee']
}
I want to use reactive form to populate this model into form. I want to populate the roles array into read-only checkboxes i.e. when form loads, user can edit name & email but checkboxes will show admin toggeled if user has admin role or untoggled if he is not an admin. same can be said for employee role.
So far I have tried following:
<form [formGroup]="userForm" (ngSubmit)="onSubmit()" novalidate>
<div style="margin-bottom: 1em">
<button type="submit" [disabled]="userForm.pristine" class="btn btn-success">Save</button>
<button type="reset" (click)="revert()" [disabled]="userForm.pristine" class="btn btn-danger">Revert</button>
</div>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div class="form-group">
<label class="center-block">Email:
<input class="form-control" formControlName="email">
</label>
</div>
<div class="form-group" *ngFor="let role of roles;let i=index">
<label>
// I tried this, but it doesn't work
<!--<input type="checkbox" [name]="role" [(ngModel)]="role">-->
{{role}}
</label>
</div>
</form>
<p>userForm value: {{ userForm.value | json}}</p>`
Any Inputs?
Perhaps do something like the following. Build your form, and stick the roles in a form array:
this.userForm = this.fb.group({
name: [this.user.name],
roles: this.fb.array(this.user.roles || [])
});
// OPTIONAL: put the different controls in variables
this.nameCtrl = this.userForm.controls.name;
this.rolesCtrl = this.userForm.controls.roles.controls;
and the roles array you are iterating in the template could look like this:
roles = ['Admin', 'Employee','Some role 1', 'Some role 2']
and in your iteration just compare and set the role in roles array as checked in case it matches a value in the form array. Use safe navigation operator, as we know that the roles array is probably longer that the form array, so that an error won't be thrown trying to read an index that doesn't exist:
<div class="form-group" *ngFor="let role of roles;let i=index">
<input [checked]="role == rolesCtrl[i]?.value"
[disabled]="true" type="checkbox">{{role}}
</div>
DEMO
I'm trying to get ng-repeat to repeat a list of items, spiffs, in a receipt. I have the edit function of this working but I can't get the add page to work because it reads:
TypeError: Cannot read property 'spiffs' of undefined
Here is the code causing it:
$scope.model.spiffs = [];
$scope.model.spiffs = [{ spiff_id: 0, receipt_id: 0, product_id: 0, spiff_amt:0, quantity: 1, active: true, note: null }];
At first it was the second line but I thought I'd try making the $scope.model an empty array to try and declare the array ahead of time, I tried different variations of this and I think I'm just missing something simple. What do I need to do to setup my form to ng-repeat a set of fields when I don't have any initial input to populate it with? And after that, will I be able to grab all the form data with a simple $scope.model call and send it to my API?
Here is the hope-to-be effected HTML:
<div class="col-xs-10 col-xs-offset-2" ng-repeat="item in model.spiffs">
<div ng-hide="item.active === false">
<div class="col-xs-4 form-group">
<select name="productID" ng-model="item.product_id" required="" ng-change="changeSpiffProduct($index)" class="form-control"
ng-options="product.product_id as product.model_number + ' ' + product.name for product in products">
</select>
</div>
<div class="col-xs-2 form-group">
<input name="spiff_sale_price" type="text" placeholder="" ng-model="item.quantity" class="form-control input-md">
</div>
<div class="col-xs-2 form-group">
<p name="spiff_amt" class="form-control-static">{{item.spiff_amt | currency: "$"}}</p>
</div>
<div class="col-xs-2 form-group">
<p name="spiff_total" class="form-control-static">{{item.spiff_amt * item.quantity | currency: "$"}}</p>
</div>
<div class="col-xs-2 form-group">
<button class="btn btn-danger" novalidate ng-click="removeSpiff($index)"><i class="fa fa-minus"></i></button>
</div>
</div>
</div>
Thank you very much in advance for the help!
You need to define the property "model" of your scope first before you can assign "spiff" to it. You can do that in two ways:
$scope.model = {};
//OR:
$scope.model = {
spiffs: []
};
You are writing $scope.model.spiffs. Here model is undefined, hence the error.
TypeError: Cannot read property 'spiffs' of undefined
You need to initialize $scope.model first, like this
$scope.model = {}
Now since $scope.model is not undefined, you can add a property.
Hope it helps.
Note: I'm quite new to angularjs
What is the best solution/practice for problem:
I have an array or typed values, for each type there should be different input(template and input validation)?
E.g. and simplified
var vars = [
{
type: 'int',
value: 42,
min: 0,
max: 42
},
{
type: 'text',
value: 'foobar'
},
]
for 'int' template will be
<input type="range" max="{{max}}" min="{{min}}" value="{{value}}" />
and for 'text'
<textarea>{{value}}</textarea>
In real case there will be quite many inputs with weird interfaces
An ng-switch (docs) can help you out here; something like this:
<div ng-repeat="item in items">
<div ng-switch on="item.type">
<div ng-switch-when="int">
<input type="range" max="{{item.max}}" min="{{item.min}}"
ng-model="item.value" />
</div>
<div ng-switch-when="text">
<textarea ng-model="item.value"></textarea>
</div>
</div>
</div>
[Update]
Since you mentioned you want to dynamically include a template based on the type, take a look at ng-include (docs) which takes an Angular expression evaluating to a URL:
<div ng-repeat="item in items">
<div ng-include="'input-' + item.type + '-template.htm'"></div>
</div>
If you don't like the inline string concatenation, you can use a controller method to generate the URL:
<div ng-repeat="item in items">
<div ng-include="templatePathForItem(item)"></div>
</div>
The example on the ngInclude documentation page is pretty good.
Note that the included template will be given a prototypal child scope of the current scope.