您需要通过身份验证才能对投票进行投票。投票时,有两个问题:
您可以无限次地投票,直到您离开或重新加载页面
当您重新加载页面时,您最终无法进行投票,但是您没有预先选择投票选项,而是始终预选的第二个选项。
应该发生什么:
注册/登录,然后投票。您点击某个选项时,您所做的投票投票选择已被锁定,您无法对该投票进行更多投票。
我的当前代码的流程如何运作:
当用户点击某个选项时,计数器会增加,然后投票会保存在推送到数据库中User对象的数组中。
当组件加载时,当前登录用户的投票数据库数据将通过本地ngOninit()
变量中的votes
方法保存,然后用于检查用户已经进行的轮询投票和他做了什么投票。问题是,如果事实并非如此,则选择总是选择2。
我理解为什么你可以多次投票直到页面重新加载,但我只是不知道如何在用户投票后,在客户端和后端立即锁定投票(如果用户阻止更多投票被注册)已投票通过投票。)
至于为什么它已经是预选的第二选择,我不知道。
CODE:
HTML
<div class="formWidth">
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="form-group">
<label class="inputTitle" for="title">Poll Title</label>
<input
type="text"
id="title"
class="form-control"
[ngModel]="poll?.title"
name="title"
required maxlenth="30">
<label class="inputTitle" for="choice1">Choice1</label>
<input
type="text"
id="choice1"
class="form-control"
[ngModel]="poll?.choice1"
name="choice1"
required maxlenth="20">
<label class="inputTitle" for="choice2">Choice2</label>
<input
type="text"
id="choice2"
class="form-control"
[ngModel]="poll?.choice2"
name="choice2"
required maxlenth="20">
</div>
<button type="button" class="btn btn-danger" (click)="onClear(f)">Clear</button>
<button class="btn btn-primary" type="submit">Save</button>
</form>
</div>
COMPONENT
export class PollComponent {
@Input() poll: Poll;
constructor(private pollService: PollService) {}
votes: any;
// Pie
public pieChartLabels:string[] = [];
public pieChartData:number[] = [];
public pieChartType:string = 'pie';
public pieChartOptions:any = {};
ngOnInit() {
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
this.pieChartLabels = [this.poll.choice1, this.poll.choice2];
this.pieChartType = 'pie';
this.pieChartOptions = {
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function(tooltipItem, data) {
var allData = data.datasets[tooltipItem.datasetIndex].data;
var tooltipLabel = data.labels[tooltipItem.index];
var tooltipData = allData[tooltipItem.index];
return tooltipLabel + ": " + tooltipData + "%";
}
}
}
}
this.pollService.voted(localStorage.getItem('userId')).subscribe(
data => {
var result = JSON.parse(data);
this.votes = result.votes;
},
err => { console.log("NGONINIT ERROR: "+ err) },
() => { }
);
}
onEdit() {
this.pollService.editPoll(this.poll);
}
onDelete() {
this.pollService.deletePoll(this.poll)
.subscribe(
result => console.log(result)
);
}
onChoice1() {
this.pollService.increaseCounter1(this.poll);
this.onVote1();
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
}
onChoice2() {
this.pollService.increaseCounter2(this.poll);
this.onVote2();
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
}
onVote1() {
this.pollService.voteOn(this.poll.pollID, localStorage.getItem('userId'), 1);
}
onVote2() {
this.pollService.voteOn(this.poll.pollID, localStorage.getItem('userId'), 2);
}
belongsToUser() {
return localStorage.getItem('userId') == this.poll.userId;
}
alreadyVotedFor(choice: number) {
var result = "";
if (this.votes) {
for (var i = 0; i < this.votes.length; i ++) {
if (this.votes[i].pollID == this.poll.pollID) {
result = "disabled";
if (this.votes[i].choice == choice) {
result = "selected";
}
}
}
}
return result;
}
// events
public chartClicked(e:any):void {
}
public chartHovered(e:any):void {
}
}
服务
updatePoll(poll: Poll) {
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/poll/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
});
}
increaseCounter1(poll: Poll) {
poll.counter1++;
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
this.http.patch('https://voting-app-10.herokuapp.com/poll/vote/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
increaseCounter2(poll: Poll) {
poll.counter2++;
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/poll/vote/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
voteOn(pollID: string, userID: string, choice: number) {
var user;
this.http.get('https://voting-app-10.herokuapp.com/user/'+userID)
.map(response => response.json())
.subscribe(
json => {
user = JSON.parse(json);
if (user.votes == undefined) {
user.votes = [{pollID, choice}];
} else {
user.votes.push({pollID, choice});
}
const body = user;
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/user/', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
)
}
voted(userID: string) {
const headers = new Headers({'Content-Type': 'application/json'});
return this.http.get('https://voting-app-10.herokuapp.com/user/'+userID,{headers: headers})
.map(response => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
});
}
路线(后退)
router.patch('/vote/:id', function (req, res, next) {
var decoded = jwt.decode(getToken(req));
Poll.findById(req.params.id, function (err, poll) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
if (!poll) {
return res.status(500).json({
title: 'No Poll Found!',
error: {poll: 'Poll not found'}
});
}
poll.title = req.body.title;
poll.choice1 = req.body.choice1;
poll.choice2 = req.body.choice2;
poll.counter1 = req.body.counter1;
poll.counter2 = req.body.counter2;
poll.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
poll: 'Updated poll',
obj: result
});
});
});
});
router.patch('/:id', function (req, res, next) {
var decoded = jwt.decode(getToken(req));
Poll.findById(req.params.id, function (err, poll) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
if (!poll) {
return res.status(500).json({
title: 'No Poll Found!',
error: {poll: 'Poll not found'}
});
}
if (poll.user != decoded.user._id) {
return res.status(401).json({
title: 'Not Authenticated',
error: {poll: 'Users do not match'}
});
}
poll.title = req.body.title;
poll.choice1 = req.body.choice1;
poll.choice2 = req.body.choice2;
poll.counter1 = req.body.counter1;
poll.counter2 = req.body.counter2;
poll.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
poll: 'Updated poll',
obj: result
});
});
});
});
答案 0 :(得分:1)
好的,首先你的单选按钮不会被禁用,因为你在保存投票后没有更新poll.component.ts
中的投票数组。
我不确定这是不是一个好的解决方案:
在poll.service.ts
:
voteOn(pollID: string, userID: string, choice: number) {
var user;
return new Promise((resolve) => { //Create a new promise to wrap the Subscriptions
this.http.get('http://localhost:3000/user/' + userID)
.map(response => response.json())
.subscribe(
json => {
user = JSON.parse(json);
if (user.votes == undefined) {
...
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
}).subscribe(() => {
resolve(user.votes); // <- Resolve your promise
})
}
)
});
}
在您的poll.component.ts
:
voteOn(...).then((votes) => {
this.votes = votes; // To update your votes array
this.updateVote();
})
我不建议在绑定中调用函数,因为它经常调用函数来“检测更改”,就像在组件中一样。 所以我会按照以下方式更改代码:
在poll.component.ts
:
vote:any //Added to your poll component
updateVote() {
this.vote = this.votes.find((vote) => {
return vote.pollID === this.poll.pollID;
});
}
您需要在ngOnInit
方法中调用此方法:
this.pollService.voted(localStorage.getItem('userId')).subscribe(
data => {
var result = JSON.parse(data);
this.votes = result.votes;
this.updateVote(); // <- To select the vote of this poll
},
err => { console.log("NGONINIT ERROR: " + err) },
() => { }
);
在您的poll.component.html
:
<fieldset [disabled]="vote">
{{ poll.counter1 }} votes <input type="radio" id="{{ poll.choice1 }}" name="my_radio" value="{{ poll.choice1 }}" (click)="onChoice1(form)" [checked]="vote?.choice == 1"> {{ poll.choice1 }}
<br>
{{ poll.counter2 }} votes <input type="radio" id="{{ poll.choice2 }}" name="my_radio" value="{{ poll.choice2 }}" (click)="onChoice2(form)" [checked]="vote?.choice == 2"> {{ poll.choice2 }}
</fieldset>
但如果您不想以这种方式更改代码,请告诉我,我可以提供另一种解决方案。