I have running Angular 9 and I want to enable button only when checkbox is clicked. I am using bootstrap cards and each card has its own checkbox and button. The problem I am facing is that whenever I click on any card checkbox, every button of the card gets enabled and vice versa. I want only particular card button to gets enabled or disabled.
test.component.html
<div class="col-3" *ngFor="let data of nominationData">
<form [formGroup]="nominationForm" (ngSubmit)="acceptNomination($event, data)">
<div class="card">
<div class="card-header">
<div class="title">{{data.contributor.name}}</div>
</div>
<div class="card-body">
<div class="row" style="height: 190px;">
<div class="col-sm-12">
<span class="nomination-title">Project Name: </span><div class="desc">{{data.projectName}}</div>
<span class="nomination-title">Posted Date: </span><div class="desc">{{data.nominatedDateTime | date}}</div>
<span class="nomination-title">Technology: </span>
<div *ngFor="let data of data.technology; let i=index" class="tech">
<input type="checkbox" style="margin-right: 10px;" [value]="data"
(change)="onCheckboxChange($event)" />
<label for="chk" style="font-size: 15px;">{{data}}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="buttons">
<button class="btn btn-success" type="submit" [disabled]="!techStack.length">Accept</button>
<button class="btn btn-danger" (click)="rejectNomination(data)">Reject</button>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
test.component.ts
export class TestComponent implements OnInit, OnDestroy {
public search = '';
private nominationSubscription: SubscriptionLike;
nominationForm: FormGroup;
isLoading = false;
techStack: string[] = [];
nominationData: Nomination;
constructor(private fb: FormBuilder, private projectsService: ProjectsService) {
this.nominationForm = this.fb.group({
Technology: this.fb.array([], [Validators.required])
});
}
ngOnInit() {
this.getNominations();
}
getNominations() {
this.isLoading = true;
this.nominationSubscription = this.projectsService.getAllNominations().subscribe(
{
next: (nominations: Nomination) => {
this.nominationData = nominations;
this.isLoading = false;
},
error: (response) => {
this.isLoading = false;
}
}
);
}
onCheckboxChange(e) {
const Technology: FormArray = this.nominationForm.get('Technology') as FormArray;
if (e.target.checked) {
Technology.push(new FormControl(e.target.value));
} else {
let i = 0;
Technology.controls.forEach((item: FormControl) => {
if (item.value === e.target.value) {
Technology.removeAt(i);
return;
}
i++;
});
}
this.techStack = Technology.value;
}
async rejectNomination(data) {
const nomination: Nomination = {
projectId: data.projectId,
contributor: { name: data.contributor.name, emailId: data.contributor.emailId },
// technology: data.technology,
ProjectName: data.projectName
};
await this.projectsService.rejectNomination(nomination);
this.getNominations();
}
async acceptNomination(event, data) {
if (event.submitter.innerHTML === 'Accept') {
const nominatedData = {
ContributorMailId: data.contributor.emailId,
ProjectId: data.projectId,
UserType: 'Contributor',
TechnologiesOpting: this.nominationForm.value.Technology
};
await this.projectsService.acceptNomination(nominatedData);
this.getNominations();
}
}
ngOnDestroy() {
if (this.nominationSubscription) {
this.nominationSubscription.unsubscribe();
}
}
}
The problem in your code is that you have an array of data in nominationData. But the techStack variable is common to all elements of the array. You should have an array of techStack elements also.
// Initialize techStack to be an array of arrays
techStack: string[][] = [];
// In the getNominations function, once you receive the nomination data, insert empty arrays in techStack.
nominationData.forEach(_ => techStack.push([]))
// Modify the onCheckBoxChange function to take an index to indicate checkbox in which item in the list was clicked.
onCheckboxChange(e, index) {
...
this.techStack[index] = Technology.value;
...
}
// In the html, pass this index to the onCheckBoxChange function
<div class="col-3" *ngFor="let data of nominationData; let index=index">
.....
<input
type="checkbox"
style="margin-right: 10px;"
[value]="data"
(change)="onCheckboxChange($event, index)"
/>
....
....
<button
class="btn btn-success"
type="submit"
[disabled]="!techStack[index].length"
>
Accept
</button>
.....
</div>
Related
I have an input with ngfor, it appears several options
<article *ngIf="layout=='esalpet'" class="filter-group">
<header class="card-header">
<a href="#" data-toggle="collapse" data-target="#collapse_4" aria-expanded="true" class="">
<i class="icon-control fa fa-chevron-down"></i>
<h6 class="title">Marcas</h6>
</a>
</header>
<div class="filter-content collapse show" id="collapse_4">
<div *ngFor="let categ of filtro" class="card-body">
<label class="custom-control custom-radio">
<input (change)="filterMarcas($event)" class="custom-control-input" type="radio" id="5" name="marca"
value="{{categ.marcaJ.marca}}" id="flexCheckChecked" />
<div class="custom-control-label" id="5">{{categ.marcaJ.marca}}</div>
</label>
</div>
<!-- card-body.// -->
</div>
when selecting some of the inputs, I would like only the input selected by the user to appear
follow my method i tried to do
filterMarcas(event){
this.slicedItems = this.slicedItems.filter(item => item.marcaJ?.marca === event.target.value )
console.log(this.slicedItems)
this.filtro = this.slicedItems.filter((record, index) => this.filtro.findIndex(check => check.marcaJ?.marca === record.marcaJ?.marca) === index);
this.peso = this.peso.filter((record, index) => this.peso.findIndex(check => check.peso === record.peso) === index);
}
Is there a better method than this?
this.filtro = this.slicedItems.filter((record, index) => this.filtro.findIndex(check => check.marcaJ?.marca === record.marcaJ?.marca) === index);
Can anyone help me with this ?
Create a new PipeTransform
import { Pipe, PipeTransform } from '#angular/core';
interface Marca {
marcaJ: {
marca: string
}
}
#Pipe({
name: 'marcaFilter'
})
export class MarcaFilterPipe implements PipeTransform {
transform(marcas: Marca[], name: string): Marca[] {
if (!name) {
return marcas;
}
return marcas.filter(marca => marca.marcaJ?.marca === name);
}
}
Declare it within your module.
#NgModule({
...,
declarations: [
...
MarcaFilterPipe,
],
})
export class AppModule { }
And add the pipe to your *ngFor
<div *ngFor="let categ of filtro | marcaFilter: categ.marcaJ.marca">
...
</div>
I have a small task board like trello with tasks to be done, doing and done. All the tasks are stored on 3 separate arrays in one service called TaskService. To show the task and change task state I have implemented angular's cdk drag n drop.
My goal now is to subscribe to the the task arrays so when the array changes send the changes to an api. For now I'm just trying to console.log the events but I'm not understanding what's happening it seemly to work but I can't get the arrays updates.
This is my component controller:
doing: any[];
constructor(private taskService: TaskService) {}
ngOnInit(): void {
this.getTodoTasks();
// this.getDoingTasks();
this.getDoneTasks();
const obs$ = this.taskService.getDoing();
obs$.subscribe({
next: (data: any[]) => {
this.doing = data;
console.log(data);
},
});
}
todo: any[];
// doing: Subscriber<any[]>;
done: any[];
newTaskText: string = '';
isModalShown: boolean = false;
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer == event.container) {
moveItemInArray(
event.container.data,
event.previousIndex,
event.currentIndex
);
} else {
transferArrayItem(
event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex
);
}
}
newTask() {
console.log(`Click Modal!`);
this.isModalShown
? (this.isModalShown = false)
: (this.isModalShown = true);
}
getTodoTasks() {
this.taskService.getTodos().subscribe((data) => {
this.todo = data;
console.log(`Se ha añadido Tak a : Todo`);
});
}
This is my view:
<app-modal *ngIf="isModalShown" (close)="newTask()">
<div class="modalContent">
<textarea
name=""
id=""
cols="30"
rows="10"
class="newTask"
[(ngModel)]="newTaskText"
></textarea>
<div class="modalButtons">
<input
type="button"
value="Cancel"
class="btn btn-secondary"
(click)="cancelTask()"
/>
<input type="button" value="Save" class="btn btn-primary" (click)="saveTask()" />
</div>
</div>
</app-modal>
<div class="container">
<div class="list">
<h2>TO DO</h2>
<input type="button" value="Modal" (click)="newTask()" />
<div
class="tasks"
cdkDropList
#todoList="cdkDropList"
[cdkDropListData]="todo"
[cdkDropListConnectedTo]="[doingList, doneList]"
(cdkDropListDropped)="drop($event)"
>
<div class="task" *ngFor="let item of todo" cdkDrag>{{ item }}</div>
</div>
</div>
<div
class="list"
cdkDropList
#doingList="cdkDropList"
[cdkDropListData]="doing"
[cdkDropListConnectedTo]="[doneList, todoList]"
(cdkDropListDropped)="drop($event)"
>
<h2>DOING</h2>
<div class="tasks">
<div class="task" *ngFor="let item of doing" cdkDrag>{{ item }}</div>
</div>
</div>
<div
class="list"
cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="done"
[cdkDropListConnectedTo]="[doingList, todoList]"
(cdkDropListDropped)="drop($event)"
>
<h2>DONE</h2>
<div class="tasks">
<div class="task" *ngFor="let item of done" cdkDrag>{{ item }}</div>
</div>
</div>
</div>
And my service:
constructor() {}
todo = ['task 1', 'task 2', 'task 3'];
doing = [];
done = [];
getTodos(): Observable<any[]> {
return of(this.todo);
}
getDoing(): Observable<any[]> {
return new Observable((subscriber) => {
subscriber.next();
})
}
getDone(): Observable<any[]> {
return of(this.done);
}
I've been doing some tries and I couldn't manage to get a console.log on my terminal when a new element is added to any of the array through the drag n drop.
I couldn’t find any solution so instead I added an id parameter to the drop areas so I can identify the drop area destination and then implement my logic.
If anyone finds out what’s missing in my code I’ll update the question with the right solution.
Component.html
<div class="bootstrap-wrapper" *ngIf="!isSubmit">
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!-- instructions -->
<h2>Instructions</h2>
</div>
<div class="col-md-8">
<!-- questions -->
<ng-container *ngIf="questions">
<h2>{{questions[0].quiz.title}}</h2>
</ng-container>
<mat-card *ngFor="let q of questions, let i= index" class="mt20">
<mat-card-content>
<p> Q {{i+1}}) <span [innerHTML]="q.content"></span> </p>
<mat-divider></mat-divider>
<div class="row mt20" >
<div class="col-md-6">
<input type="radio" [value]="q.option1"
[name]="i"
// this is where i am getting error
[(ngModel)] ="q.givenAnswer"
/>
{{q.option1}}
{{i}}
</div>
<div class="col-md-6">
<input type="radio" [value]="q.option2"
[name]="i"
// this is where i am getting error
[(ngModel)] ="q.givenAnswer"
/>
{{q.option2}}
{{i}}
</div>
</div>
<div class="row mt20">
<div class="col-md-6">
<input type="radio" [value]="q.option3"
// this is where i am getting error
[name]="i"
[(ngModel)] ="q.givenAnswer"
/>
{{q.option3}}
{{i}}
</div>
<div class="col-md-6">
<input
type="radio"
[value]="q.option4"
// this is where i am getting error
[name]="i"
[(ngModel)] ="q.givenAnswer"
/>
{{q.option4}}
{{i}}
</div>
</div>
</mat-card-content>
</mat-card>
<div class="container text-center mt20">
<button (click)="submitQuiz()" mat-raised-button color="accent">Submit
Quiz</button>
</div>
</div>
<div class="col-md-2">
</div>
</div>
</div>
</div>
<!-- Show Result -->
<div class="bootstrap-wrapper" *ngIf="isSubmit">
<div class="row">
<div class="col-md-6 offset-md-3">
<mat-card>
<mat-card-header>
<mat-card-title>
<h1 class="text-center mall">Quiz Result</h1>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<h1>Marks Obtained: {{marksGot}}</h1>
<h1>Correct Ansers: {{correctAnswers}}</h1>
<h1>Questions Attempted: {{attempted}}</h1>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="accent">Print</button>
<button mat-raised-button color="accent" [routerLink]="'/user-
dashboard/0'">Home</button>
</mat-card-actions>
</mat-card>
</div>
</div>
</div>
Component.ts
import { LocationStrategy } from '#angular/common';
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { QuestionService } from 'src/app/services/question.service';
import Swal from 'sweetalert2';
#Component({
selector: 'app-start-quiz',
templateUrl: './start-quiz.component.html',
styleUrls: ['./start-quiz.component.css']
})
export class StartQuizComponent implements OnInit {
qid;
questions;
marksGot = 0;
correctAnswers = 0;
attempted = 0;
isSubmit = false;
constructor(private locationSt: LocationStrategy, private _route: ActivatedRoute, private
_question: QuestionService) { }
ngOnInit(): void {
this.preventBackButton();
this.qid = this._route.snapshot.params['qid'];
this.loadQuestions();
}
loadQuestions() {
this._question.getQuestionsOfQuizForTest(this.qid).subscribe(
(data: any) => {
this.questions = data;
this.questions.forEach((q) => {
q['givenAnswer'] = '';
});
console.log(data);
},
(error) => {
Swal.fire('Error', 'Error in loading questions of quiz', 'error');
}
);
}
preventBackButton() {
history.pushState(null, null, location.href);
this.locationSt.onPopState(() => {
history.pushState(null, null, location.href);
})
}
submitQuiz() {
Swal.fire({
title: 'Do you want to Submit quiz?',
showCancelButton: true,
confirmButtonText: 'Submit Quiz',
icon: 'info',
}).then((e) => {
if (e.isConfirmed) {
//calculation
this.isSubmit=true;
this.questions.forEach((q) => {
if (q.givenAnswer == q.answer) {
this.correctAnswers++;
let marksSingle = this.questions[0].quiz.maxMarks / this.questions.length;
this.marksGot += marksSingle;
}
if (q.givenAnswer.trim() != '') {
this.attempted++;
}
});
console.log("Correct Answers " + this.correctAnswers);
}
})
}
}
enter image description here
When i am name as [name] it is showing number is not assignable to type String and when i am using name it is compiling successfully but i have three questions in a quiz and while selecting an option of a particular question other options of other questions are getting deselected. what to do?
Thanks in Advance
[(ngModel)]="q.givenAnswer" type="radio" [value]="q.option1" name={{i}}
I am using js-year-calendar plugin to display all the months in a year at once. However, i can select just one day at a time and then store the date in the database. Please, is there a way to select multiple days e.g. 2020/08/04 - 2020-08-12 and then store this range in the database at once. Meanwhile, i have gone through the documentation but its was not clear to me. Please i need assistance to get a clear picture on how to go about this. Thanks
This is my view. I am sorry for the long code, i really assistance
HTML
<section id="filter" class="mb-3">
<div class="card">
<div class="col-sm-12">
<div class="card-header mt-3">
<div class="card-title-wrap bar-info">
<h4 class="card-title ml-4">{{'ALL_COMPANIES_HOLIDAY_CALENDER' | translate}}</h4>
</div>
</div>
<div class="card-body">
<div class="card-block">
<div class="row">
<div class="col-md-6 col-lg-6">
<div class="row">
<div class="col-md-4 col-lg-4 col-12 mt-1 ml-4">
<div class="form-group row">
<div class="col-md-12 searchcat">
<select class="form-control " [(ngModel)]="holidays.company_id" id="company" name="company" (change)="getSelectedCompany(holidays.company_id)" required>
<option value="" disabled selected hidden>{{'SELECT_COMPANY' | translate}}</option>
<option *ngFor="let data of companyList; let i = index" [value]="data.id" >{{data.company_name}}</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-5 ml-2">
<div class="col-md-12 col-lg-12">
<div id="calendar">
</div>
</div>
</div>
<div class="card-body">
<div class="card-block">
<div class="clearfix"></div>
<ngx-datatable #table class='bootstrap' [columnMode]="'force'" [headerHeight]="50" [footerHeight]="50"
[rowHeight]="'auto'" [limit]="50" [rows]='holidaysDataRows'>
<ngx-datatable-column name="{{'COMPANY_NAME' | translate}}" prop="company_name"></ngx-datatable-column>
<ngx-datatable-column name="{{'YEAR' | translate}}" prop="year"></ngx-datatable-column>
<ngx-datatable-column name="{{'HOLIDAY' | translate}}" prop="holidays_date">
</ngx-datatable-column>
<ngx-datatable-column name="{{'DESCRIPTION' | translate}}" prop="description" > </ngx-datatable-column>
<ngx-datatable-column name="{{'ACTIONS' | translate}}" prop="status">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row"
let-group="group" let-rowHeight="rowHeight">
<button (click)="editHoliday(editModal,row)" class="btn btn-raised mr-1 shadow-z-2 btn-info btn-sm ">{{'EDIT' | translate}}</button>
<button (click)="deleteModal(deleteholiday, row.holiday_id)" class="btn btn-raised mr-1 shadow-z-2 btn-danger btn-sm ">{{'DELETE' | translate}}</button>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
<div class="loader-div" *ngIf="showLoader">
<img src="assets/img/portrait/small/loader.gif" alt="spinner" class="loader">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
TS CODE
import { Component, OnInit, ViewChild } from '#angular/core';
import { SnotifyService } from 'ng-snotify';
import { Router, ActivatedRoute } from "#angular/router";
import { NgbModal, ModalDismissReasons, NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
import { HumanResourceService } from 'app/services/human-resource/human-resource.service';
import { TranslateService } from '#ngx-translate/core';
import { AuthenticationService } from 'app/services/authentication/authentication.service';
import { HumanResourceMasterService } from '../../../services/human-resource-master/human-resource-master.service';
import Calendar from 'js-year-calendar';
import * as moment from 'moment';
import { GlobalConstants } from 'app/shared/constants/global-constants';
#Component({
selector: 'app-all-company-holiday-calender',
templateUrl: './all-company-holiday-calender.component.html',
styleUrls: ['./all-company-holiday-calender.component.scss']
})
export class AllCompanyHolidayCalenderComponent implements OnInit {
#ViewChild('openModal') OpenModalotdeletepos;
companyList: any = [];
company_id: any = '';
datas: any;
dateFromModal : any;
closeResult: any;
currentHolidays: any = [];
holidaysDataRows: [];
HolidayDeleteData: any;
showLoader: boolean;
deleterowid: any;
holidaysData : any = [];
holidays: any = {
description: '',
date: '',
company_id:'',
hr_id: '',
hols_id: '',
}
selectedDate:any;
hrID: any;
eventDate: Date;
eventText: string;
myEvents: any[] = [];
constructor(private snotifyService: SnotifyService,private spinner:
FullLayoutComponent, private
route: ActivatedRoute, private modalService: NgbModal, private
loginService: LoginService,private hrMasterService:
HumanResourceMasterService, private hrService:
HumanResourceService, public authService: AuthenticationService,public
translate: TranslateService) {
ngOnInit() {
this.getDate();
}
getDate(){
var modalService = this.modalService;
var snotifyService = this.snotifyService;
var openModal = this.OpenModalotdeletepos;
var getDismiss = this.getDismissReason;
new Calendar('#calendar');
var holiday = this.holidays;
var translate = this.translate;
document.querySelector('#calendar').addEventListener('clickDay',
function(data) {
var hols = moment(data['date']).format(GlobalConstants.DATE_FORMAT);;
holiday.date = hols;
if(!holiday.company_id){
snotifyService.error(translate.instant('MESSAGE_PLEASE_SELECT_COMPANY'));
}
else{
modalService.open(openModal).result.then((result) => {
this.closeResult = GlobalConstants.CLOSE + `${result}`;
}, (reason) => {
this.closeResult = GlobalConstants.DISMISSED +
`${getDismiss(reason)}`;
});
}
})
}
getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return GlobalConstants.PRESS_ESC;
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return GlobalConstants.BACKDROP_CLICK;
} else {
return GlobalConstants.WITH + ` ${reason}`;
}
}
getHolidayData(){
this.showLoader = true;
this.hrMasterService.getHolidayData({}).subscribe(data => {
if(data.status_code = 200){
this.showLoader = false;
this.holidaysData = data.data;
this.holidaysDataRows = this.holidaysData;
}
else {
this.showLoader = false;
this.snotifyService.error(data.message);
}
})
}
saveHolidays(){
this.holidays.hr_id = this.hrID.id;
this.hrMasterService.createHolidays(this.holidays).subscribe(data =>{
if(data.status_code = 200){
this.snotifyService.success(this.translate.instant('MESSAGE_HOLIDAY_CREATED'));
this.modalService.dismissAll();
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
this.snotifyService.error(data.message);
}
},error=>{
});
}
onChangeDate(holiday){
}
getSelectedCompany(company_id: any){
}
}
Replace new Calendar('#calendar') with new Calendar('#calendar',{enableRangeSelection: true});
then you can get it here:
document.querySelector('#calendar').addEventListener('selectRange', function(e) {
console.log("Select the range: " + e.startDate + " - " + e.endDate);
})
Year Calendar Documentaion
Here is a Stackblitz
I've been making a simple To Do app with VueJS. I have the add new todo, delete todo and mark as done functionalities done, but I'm struggling with the "Double Click to edit a task" feature.
I've added an input field which should appear when the user double clicks on the task to edit it but nothing seems to happen? Any help would be awesome :)
App.vue:
<template>
<div id="app">
<div class="container">
<div class="row">
<h1>VueJS To Do Manager:</h1>
</div>
</div>
<div class="container">
<div class="row">
<input class="new-todo input-group col-xs-12"
placeholder="Enter a task and press enter. Use the checkbox to mark them as done."
v-model="newTodo"
#keyup.enter="addTodo">
</div>
</div>
<TodoCard v-for="(todo, key) in todos"
:todo="todo"
:key="key"
#remove="removeTodo(key)"/>
</div>
</template>
<script>
import TodoCard from './components/TodoCard'
export default {
data () {
return {
todos: [],
newTodo: ''
}
},
components: {
TodoCard
},
methods: {
addTodo: function () {
// Store the input value in a variable
let inputValue = this.newTodo && this.newTodo.trim()
// Check to see if inputed value was entered
if (!inputValue) {
return
}
// Add the new task to the todos array
this.todos.push(
{
text: inputValue,
done: false
}
)
// Set input field to empty
this.newTodo = ''
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
}
}
</script>
TodoCard.vue component:
<template>
<div id="todo">
<div class="container">
<div class="row">
<input class="check" type="checkbox" />
<h3 class="col strikethrough"
#dblclick="editTodo(todo)">{{ todo.text }}</h3>
<div v-show="todo.edit == false">
<input v-show="todo.edit == true"
v-model="todo.title"
v-on:blur="todo.edit=false; $emit('update')"
#keyup.enter="todo.edit=false; $emit('update')">
</div>
<hr>
<button #click="removeTodo"
type="button"
class="btn btn-danger btn-sm">Delete</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['todo'],
methods: {
removeTodo: function (todo) {
this.$emit('remove')
},
editTodo: function (todo) {
this.editedTodo = todo
}
}
}
</script>
I think you don't set todo.edit to true when double click todo description. Moreover the div that contains the todo edit input has v-show="todo.edit == false" while it should be v-show="todo.edit == true" or just v-show="todo.edit" if you are sure that todo.edit is always a boolean.