i'm having problems with checkbox in vue.js component.
My idea is to update parent model on child event(change).
<parent-component>
<checkbox-component name="This is name"
:id="'This is Id'"
:val="'test'"
v-model="details"
>
<checkbox-component name="This is name 2"
:id="'This is Id 2'"
:val="'test2'"
v-model="details"
>
</checkbox-component>
</parent-component>
Here is component code:
<template>
<div>
<input type="checkbox"
:name="name"
:id="id"
:value="val"
v-on:change="update($event)"
autocomplete="off"
/>
<div class="btn-group">
<label :for="id" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-ok"></span>
<span> </span>
</label>
<label :for="id"
class="btn btn-default active btn-sm">
{{ name }}
</label>
</div>
</div>
</template>
<script>
export default {
model: {
event: 'change'
},
props: {
id: {
required: true,
type: String
},
name: {
required: true,
type: String
},
val: {
required: true,
type: String
},
},
methods: {
update(event){
this.$emit('change',event.target.value);
}
},
}
</script>
I would like to store checkbox values in details array, and to update it same as in https://v2.vuejs.org/v2/guide/forms.html#Checkbox
What is happening now is that details[] is becoming string details with value of selected checkbox.
Is there some clean solution for this, or i need to write method to check if value is in array and slice/push it ?
Related
I have a products object as mentioned below from which I have to create a complex form
component.ts
import { Component, OnInit } from "#angular/core";
import { FormBuilder, FormGroup, FormArray, Validators } from "#angular/forms";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
products = [
{
Id: 1,
Name: "productA",
referralCode: "codeA",
webUrl: "https://productA.com",
allowReferralCode: true
},
{
Id: 2,
Name: "productb",
referralCode: null,
webUrl: "https://productb.com",
allowReferralCode: true
},
{
Id: 3,
Name: "productC",
referralCode: null,
webUrl: "https://productC.com",
allowReferralCode: false
}
];
productForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.productForm = this.fb.group({
products: new FormArray([])
});
this.products.forEach(item => {
if (item.referralCode !== null && item.allowReferralCode) {
this.t.push(
this.fb.group({
productId: [""],
referralCode: [""],
link: [""]
})
);
} else if (item.allowReferralCode) {
this.t.push(
this.fb.group({
productId: [""],
referralCode: [""]
})
);
} else if (!item.allowReferralCode) {
this.t.push(
this.fb.group({
productId: [""],
link: [""]
})
);
}
});
}
get f() {
return this.productForm.controls;
}
get t() {
return this.f.products as FormArray;
}
get productsFormGroups() {
return this.t.controls as FormGroup[];
}
onSubmit(form) {
alert(
"SUCCESS!! :-)\n\n" + JSON.stringify(this.productForm.value, null, 4)
);
}
}
Below is the HTML:
component.html
<form [formGroup]="productForm" (submit)="onSubmit(productForm)">
<div *ngFor="let product of products">
<div
*ngIf="product.referralCode !== null && product.allowReferralCode; else showWithReferalCode"
>
<div class="card ml-3 mt-3 mb-3" style="width: 18rem;">
<h4 class="mb-0 ml-3">{{product.Name | titlecase}}</h4>
<div class="card-body">
<div class="new-custom-radio ml-2 mb-3">
<div class="custom-control custom-radio">
<input
type="radio"
id="customRadio1"
name="customRadio"
class="custom-control-input"
/>
<label class="custom-control-label" for="customRadio1"
>I have a Referral Code</label
>
</div>
<div class="referral-code-input">
<input type="text" class="form-control form-control-lg" />
</div>
</div>
<div class="new-custom-radio ml-2 mb-3">
<div class="custom-control custom-radio">
<input
type="radio"
id="customRadio2"
name="customRadio"
class="custom-control-input"
/>
<label class="custom-control-label" for="customRadio2"
>I have a Custom Web Link</label
>
</div>
<div class="referral-code-input">
<input type="text" class="form-control form-control-lg" />
</div>
</div>
</div>
</div>
</div>
<ng-template #showWithReferalCode>
<div *ngIf="product.allowReferralCode; else showWebUrl">
<div class="card ml-3 mt-3 mb-3" style="width: 18rem;">
<h4 class="mb-0 ml-3">{{product.Name | titlecase}}</h4>
<div class="m-3 form-group">
<label for="exampleInputEmail1">I have Referral code</label>
<input
type="text"
class="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"
/>
</div>
</div>
</div>
<ng-template #showWebUrl>
<div class="card ml-3 mt-3 mb-3" style="width: 18rem;">
<h4 class="mb-0 ml-3">{{product.Name | titlecase}}</h4>
<div class="m-3 form-group">
<label for="exampleInputEmail1">I have WebUrl</label>
<input
type="text"
class="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"
/>
</div>
</div>
</ng-template>
</ng-template>
</div>
<div class="mt-2 mb-4 text-center">
<button class="btn btn-primary">Save</button>
</div>
</form>
I want to create a dynamic form for some conditions as shown in the screenshot below.
Now let me break it down whenever I have a referral code and allowReferralCode=true within my product I need to allow users to choose either referral code or custom code. At the same time if any product doesn't have a referral code and allowReferralCode=true then they will have to ask for a referral code and also if allowReferralCode=false then enter a direct URL.
I'm trying to build a dynamic form with form builder and FormArrays and it seems like as I have conditions in my HTML I'm unable to set form controls of the FormArray to form.
Below is the stack blitz version of the same.
stackblitz code
Whenever I'm trying to form control name to the inputs I'm getting this kind of error
**ERROR Error: Cannot find control with name: 'referralCode'**
Any help would be greatly appreciated.
Thanks in advance.
I am developing product upload page.
Few fields are fixed & few fields will be generated based on user selection, and then entire data will be posted to mongoDb.
Model
var productsSchema = new Schema({
merchantId: {type: String, required: true},
productName: {type: String, required: true},
productDescription: {type: String, required: true},
productStock: [
{
price: {type: Number, required: true},
color: {type: String, required: true},
}
],
offerFromMerchant: {
offerStartDate: {type: Date, required: false},
offerEndDate: {type: Date, required: false},
discountPercent: {type: Number, required: false}
}
});
Dynamically field generator controller method-Angular js
$scope.choices = [];
$scope.addNewChoice = function() {
var newItemNo = $scope.choices.length+1;
$scope.choices.push({});
};
$scope.removeChoice = function() {
var lastItem = $scope.choices.length-1;
if(lastItem > 0){
$scope.choices.splice(lastItem);
}
};
HTML
<div class="col-12">
<form ng-submit="addProducts()">
<fieldset data-ng-repeat="choice in choices">
<div class="form-group">
<hr>
<h3>Attributes</h3>
<div class="form-group">
<label>Price</label>
<input type="text" class="form-control" ng-model="product.productStock.price" placeholder="Enter price">
</div>
<div class="form-group">
<label>Color</label>
<input type="text" class="form-control" ng-model="product.productStock.color" placeholder="Enter color">
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Add Product</button>
</form>
<button class="addfields" ng-click="addNewChoice()">Add fields</button>
</div>
</form><br/>
POST method-Angular js
$scope.addProducts = function () {
$http.post('/products/createProduct', $scope.product).then(function (res) {
console.log($scope.product);
});
}
I am trying to POST data with multiple attributes, but in database only single entry is present .
So the problem is, I don't know how to send $scope.choices with $scope.product, because some of the fields will be binding to $scope.product, and attributes field will be binding to $scope.choices.So how can I Post this kind of data to web-service ?
Please help..
You can directly bind the choice of color and and size to $scope.product by structuring it the way you want. So initialise productStock key of $scope.product to an array by $scope.product.productStock= [];. That way your object will be created in the desired way because of two way data binding.
HTML:
<div class="col-12">
<form ng-submit="addProducts()">
<fieldset data-ng-repeat="val in product.productStock">
<div class="form-group">
<hr>
<h3>Attributes</h3>
<div class="form-group">
<label>Price</label>
<input type="text" class="form-control" ng-model="val.price" placeholder="Enter price">
</div>
<div class="form-group">
<label>Color</label>
<input type="text" class="form-control" ng-model="val.color" placeholder="Enter color">
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Add Product</button>
</form>
<button class="addfields" ng-click="addNewChoice()">Add fields</button>
</div>
</form><br/>
JS:
$scope.product.productStock= [];
$scope.addNewChoice = function() {
var newItemNo = $scope.product.productStock.length+1;
$scope.product.productStock.push({price:'',color:''});
};
$scope.removeChoice = function() {
var lastItem = $scope.product.productStock.length-1;
if(lastItem > 0){
$scope.product.productStock.splice(lastItem);
}
};
$scope.addProducts = function () {
console.log($scope.product)
$http.post('/products/createProduct', $scope.product).then(function (res) {
});
}
You can initialize the object in the following way
$scope.product={
productName:YourProductValue,
productDescription:YourDescriptionValue,
productStock:[]
};
I have the following structure:
// JS
$scope.fields = [
{ text: 'Email', type: 'text', value: '' },
{ text: 'Password', type: 'password', value: '' }
]
// HTML
<div class="user">
<h2 class="header">Log in</h2>
<form name="login">
<input class="form-control" type="{{ field.type }}" placeholder="{{ field.text }}" ng-model="field.value" ng-repeat="field in fields" required />
<a class="btn primary" href="javascript:;" ng-click="login()">Log in</a>
</form>
</div>
How can I implement $error.required like shown here? http://plnkr.co/edit/VLOEfp?p=preview
I don't know where to place:
<div class="" ng-show="myform.routingNumber.$error.required">
<span class="help-block">Please enter routing number</span>
</div>
Maybe I have to re-structure that ng-repeat?
Try something like below. You are almost there. Just missed a basic point, all controls need name. Angular uses name for validations.
<div class="user">
<h2 class="header">Log in</h2>
<form name="login" novalidate="">
<div ng-repeat="field in fields" >
<input class="form-control" type="{{ field.type }}" placeholder="{{ field.text }}" ng-model="field.value" required name="{{field.name}}"/>
<div ng-show="login.{{field.name}}.$invalid">
<span class="help-block">Please enter</span>
</div>
</div>
<a class="btn primary" href="javascript:;" ng-click="login()">Log in</a>
</form>
</div>
Accordingly add name in your controller like below
$scope.fields = [
{ text: 'Email', type: 'text', value: '', name:'idname'},
{ text: 'Password', type: 'password', value: '', name:'password' }
]
In the example code, myform.routingNumber.$error.required myform is the name (html attribute) of your form and routingNumber is the name of your input element which should be validate.
So, like this,
<form name="myform">
<input name="routingNumber" ng-model="myRoute" />
<div class="" ng-show="myform.routingNumber.$error.required">
<span class="help-block">Please enter routing number</span>
</div>
</form>
Implement this on your code
So, in your actual code (with ng-repeat) you have to create a unique name for each input element.
<div class="user">
<h2 class="header">Log in</h2>
<form name="login">
<input class="form-control" name="{{field.text}}" type="{{ field.type }}" placeholder="{{ field.text }}" ng-model="field.value" ng-repeat-start="field in fields" required />
<div ng-repeat-end class="" ng-show="myform[field.text].$error.required">
<span class="help-block">Please enter {{field.text}}</span>
</div>
<a class="btn primary" href="javascript:;" ng-click="login()">Log in</a>
</form>
</div>
Even though #Naga Sandeep has answered the question. I'm just adding another condition to wrap the error message else the error message will be shown even if the user has not touched the form.
<div ng-app="myApp", ng-controller="myCtrl">
<form name="myForm" novalidate="">
<div ng-repeat="info in loginInfo">
<input type="{{info.type}}" ng-model="info.value" name="{{info.name}}" required novalidate>
<span ng-show="myForm.{{info.name}}.$touched || myForm.$submitted">
<span ng-show="myForm.{{info.name}}.$invalid">Please enter this field</span>
</span>
</div>
<button type="submit">
Login
</button>
</form>
</div>
angular.module("myApp", [])
.controller("myCtrl", myCtrl);
function myCtrl($scope){
$scope.loginInfo = [
{"name": "user_email", "value": "", "type":"email"},
{"name": "user_pass", "value": "", "type":"password"}
]
}
I was making a small "Reading List" application following guides of Soup to Bits: Shaping up with Angular.js video
I didn't like the way they put "Cancel" button at the top and just for practice decided to move it inside the form. I figured out how to make it work (click on 'Cancel' hid the form using expression ng-click="reviewFormCtrl.showForm = false"). It worked until guys from CodeSchool made an isolated scope
directive('reviewForm', function(){
return {
restrict: "E",
templateUrl: "partials/review-form.html",
replace: true,
controller: function() {
this.showForm = false;
this.book = {genres:{}};
this.addReview = function(form) {
books.push(this.book);
this.book = {genres:{}};
form.$setPristine();
}
},
controllerAs: 'reviewFormCtrl',
scope: {
books: '=',
genres: '='
}
}
})
From that point my 'Cancel' button stopped working. Console says that ReviewFormCtrl and ReviewFormCtrl.showForm are undefined. I can't figure out how to change that value from inside the scope I'm in.
Below is my app.html and app.js
(function() {
'use strict';
// Declare app level module which depends on views, and components
angular.module('readingList', []).
controller('ReadingListController', function(){
this.books = books;
this.genres = genres;
}).
directive('bookGenres', function(){
return {
restrict: "E",
templateUrl: "partials/book-genres.html",
scope: {
genres: "="
}
}
}).
directive('bookCover', function(){
return {
restrict: "E",
templateUrl: "partials/book-cover.html",
replace: true
}
}).
directive('reviewForm', function(){
return {
restrict: "E",
templateUrl: "partials/review-form.html",
replace: true,
controller: function() {
this.showForm = false;
this.book = {genres:{}};
this.addReview = function(form) {
books.push(this.book);
this.book = {genres:{}};
form.$setPristine();
}
},
controllerAs: 'reviewFormCtrl',
scope: {
books: '=',
genres: '='
}
}
})
;
var genres = [ 'fable', 'fantasy', 'fiction', 'folklore', 'horror', 'humor', 'legend', 'metafiction', 'mystery', 'mythology', 'non-fiction', 'poetry' ];
var books = [
{
title: 'A Game of Thrones: A Song of Ice and Fire',
author: 'George R.R. Martin',
isbn: '0553593714',
review: 'The most inventive and entertaining fantasy saga of our time—warrants one hell of an introduction. I loved this book!',
rating: 4,
genres: { 'non-fiction': true, fantasy: true }
},{
title: 'HTML for Babies',
author: 'John C Vanden-Heuvel Sr',
isbn: '0615487661',
review: "It's never too early to be standards compliant! I taught my little one mark-up in under one hour!",
rating: 5,
genres: { fiction: true }
},{
title: 'A is for Array',
author: 'Brandon J Hansen',
isbn: '1489522212',
review: 'A is for Array is the ABC book for future programmers. Filled with fun illustrations and simple real-world examples, my children loved seeing my world intertwined with theirs!',
rating: 4,
genres: { fiction: true }
},{
title: 'The Dragon Reborn',
author: 'Robert Jordan',
isbn: '0812513711',
review: 'The Wheel weaves as the Wheel wills, and we are only the thread of the Pattern. Moiraine',
rating: 4,
genres: { 'non-fiction': true, fantasy: true }
}
];
})();
<h1>Angular Reading List of Awesome</h1>
<div class="row" ng-controller="ReadingListController as readingListCtrl">
<button class="btn btn-default" ng-click="reviewFormCtrl.showForm = !reviewFormCtrl.showForm" ng-hide="reviewFormCtrl.showForm">{{reviewFormCtrl.showForm ? "Cancel" : "Create a Review"}}</button>
<hr />
<review-form books="readingListCtrl.books" genres="readingListCtrl.genres" ng-show="reviewFormCtrl.showForm"></review-form>
<hr ng-show="reviewFormCtrl.showForm"/>
<ul class="list-unstyled col-sm-8">
<li class="book row" ng-repeat="book in readingListCtrl.books">
<!-- Book cover goes here -->
<book-cover></book-cover>
<div class="col-sm-9">
<h3>{{book.title}}</h3>
<cite class="text-muted">Written by {{book.author}}</cite>
<p>{{book.review}}</p>
<!-- Put genre here -->
<book-genres genres="book.genres"></book-genres>
</div>
</li>
</ul>
</div>
Here's a review-form.html
<form name="reviewForm" class="form-horizontal" ng-submit="reviewFormCtrl.addReview(reviewForm)">
<section class="row well live-preview" ng-show="reviewFormCtrl.book.isbn">
<aside class="col-sm-3">
<a href="http://www.amazon.com/gp/product/{{reviewFormCtrl.book.isbn}}">
<img src="http://images.amazon.com/images/P/{{reviewFormCtrl.book.isbn}}.01.ZTZZZZZZ.jpg" alt="Cover of Book" class="full">
</a>
<p>{{reviewFormCtrl.book.rating}}/5</p>
</aside>
<div class="col-sm-9">
<h3>
<a href="http://www.amazon.com/gp/product/{{reviewFormCtrl.book.isbn}}">
{{reviewFormCtrl.book.title}}
</a>
</h3>
<cite class="text-muted">Written by {{reviewFormCtrl.book.author}}</cite>
<p>{{reviewFormCtrl.book.review}}</p>
<book-genres genres="reviewFormCtrl.book.genres"></book-genres>
</div>
</section>
<div class="input-container">
<fieldset class="form-group">
<label for="title" class="col-sm-2 control-label">Title:</label>
<span class="col-sm-9">
<input type="text" class="form-control" id="title"
placeholder="Book Title" ng-model="reviewFormCtrl.book.title">
</span>
</fieldset>
<fieldset class="form-group">
<label for="isbn" class="control-label col-sm-2">ISBN:</label>
<span class="col-sm-9">
<input type="text" id="isbn" class="form-control"
maxLength="10" placeholder="ISBN-10" ng-model="reviewFormCtrl.book.isbn">
</span>
</fieldset>
<fieldset class="form-group">
<label class="control-label col-sm-2" for="author">Author</label>
<span class="col-sm-9">
<input type="text" id="author" class="form-control"
placeholder="Name of the Author" ng-model="reviewFormCtrl.book.author"></span>
</fieldset>
<fieldset class="form-group">
<label class="control-label col-sm-2" for="review">Review</label>
<span class="col-sm-9">
<textarea id="review" class="form-control" cols="30" rows="3"
placeholder="Book Review" ng-model="reviewFormCtrl.book.review"></textarea>
</span>
</fieldset>
<fieldset class="form-group">
<label for="rating" class="control-label col-sm-2">Rating:</label>
<span class="col-sm-9">
<select class="form-control" id="rating" value="5" ng-model="reviewFormCtrl.book.rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</span>
</fieldset>
<fieldset class="form-group">
<label class="control-label col-sm-2" >Genre:</label>
<div class="genre">
<label for="{{genre}}" class="genre-label form-control" ng-repeat="genre in genres">
<input type="checkbox" name="genre" id="{{genre}}" ng-model="reviewFormCtrl.book.genres[genre]"/>
{{genre}}
</label>
</div>
</fieldset>
<fieldset class="form-group">
<span class="col-sm-9 col-sm-offset-2 button-from-hell">
<button class="btn btn-primary">Save Review</button>
<button class="btn btn-default" ng-click="reviewFormCtrl.showForm = false" style="margin-right: 15px;">Cancel</button>
</span>
</fieldset>
</div>
</form>
You are referencing the reviewFormCtrl outside of the review-form directive on multiple occasions. Both here:
<button class="btn btn-default" ng-click="reviewFormCtrl.showForm = !reviewFormCtrl.showForm" ng-hide="reviewFormCtrl.showForm">{{reviewFormCtrl.showForm ? "Cancel" : "Create a Review"}}</button>
and here:
<hr ng-show="reviewFormCtrl.showForm"/>
You don't have access to it in those places. If you wish to control the directives visibility then perhaps create the showForm property on the readingListCtrl controller instead, double bind it into the directive like so:
<review-form books="readingListCtrl.books" genres="readingListCtrl.genres" visibility="readingListCtrl.showForm"></review-form>
Then add it to the scope property on the directive:
scope: {
books: '=',
genres: '=',
visibility: '='
}
And finally applying it directly to the form element within your template:
<form ng-show="reviewFormCtrl.visibility" name="reviewForm" class="form-horizontal" ng-submit="reviewFormCtrl.addReview(reviewForm)">
And of course updating your Cancel button to change visibility now instead.
HTML
<form role="form" ng-submit="addStore(store)" name="AddStoreForm" novalidate>
<label>Store Category</label>
<label class="checkbox-inline" ng-repeat="store_category in store_cat">
<input type="checkbox" name="store_category" ng-model="store.store_category">{{store_category.name}}
</label>
</label>
</form>
<button type="submit" class="btn btn-primary campwidth">SUBMIT</button>
AngularJS
$scope.store_cat = [
{ name: 'A', selected: false },
{ name: 'B', selected: false },
{ name: 'C', selected: false }
];
$scope.addStore = function(store){
console.log("responsestore", store);
alert(store.store_category);
};
Here I put store category as array. After submit the form. I got alertbox category undefined. I want to sent the result using API. how to fix this problem. PLUNKER
It will work with button tag not with form as in bootstrap <button type="submit" class="btn btn-primary campwidth" ng-click="addStore(store_cat)">SUBMIT</button> here no need of using the form tag b'coz it may leads to conflict in angular.js so, it will work definately for checkbox.
http://plnkr.co/edit/qSFnxTNZ4n63pw3JXZA5?p=preview
You can, do something like that: <button type="submit" class="btn btn-primary campwidth" ng-click="addStore(store_cat)">SUBMIT</button> here
http://plnkr.co/edit/KtBzqeMF9FZt6bnI0MBF?p=preview
You shouldn't use form and take on that function nope, that way is wrong when you use angular.
Assuming your issue is with data format not being pushed into a new store object, here is a solution for that:
<form role="form" name="AddStoreForm" novalidate ng-init="store={}">
<label>Store Category</label>
<label class="checkbox-inline" ng-repeat="store_category in store_cat">
<input type="checkbox" name="store_category" ng-init="store[$index]={name: store_category.name, selected: false}" ng-model="store[$index].selected">{{store_category.name}}
</label>
</label>
<button type="submit" ng-click="addStore(store)" class="btn btn-primary campwidth">SUBMIT</button>
</form>
And the plunker with working example.