How to checked radio button in Vue.js using V-Model? - javascript

Im new to laravel and vue.js. I'm developing a simple online examination and Im having a hard time in showing the answer of the user if he/she will click the previous button.
This my template I used v-for to loop the questions:
<div class="myQuestion" v-for="(question, index) in questions" :key="index + uuid">
<div class="row">
<div class="col-md-6">
<blockquote >
Total Questions {{ index+1 }} / {{questions.length}}
</blockquote>
<h2 class="question">Q. {{question.question}}</h2>
<form class="myForm" action="/quiz_start" v-on:submit.prevent="createQuestion(question.question_id, question.answer, auth.id, question.topic_id)" method="post">
<input class="radioBtn" v-bind:id="'radio'+ index" type="radio" v-model="result.user_answer" value="A" aria-checked="false" > <span>{{question.a}}</span><br>
<input class="radioBtn" v-bind:id="'radio'+ index+1" type="radio" v-model="result.user_answer" value="B" aria-checked="false"> <span>{{question.b}}</span><br>
<input class="radioBtn" v-bind:id="'radio'+ index+2" type="radio" v-model="result.user_answer" value="C" aria-checked="false"> <span>{{question.c}}</span><br>
<input class="radioBtn" v-bind:id="'radio'+ index+3" type="radio" v-model="result.user_answer" value="D" aria-checked="false"> <span>{{question.d}}</span><br>
<div class="row">
<div class="col-md-6 col-xs-8">
<button type="submit" class="btn btn-wave btn-block nextbtn">Next</button>
</div>
</div>
<div class="row">
<div class="col-md-6 col-xs-8">
<button type="submit" class="btn btn-wave btn-block prebtn">Previous</button>
</div>
</div>
</form>
</div>
</div>
</div>
This is my script to fetch the data and insert the data array to questions variable.
<script>
import { v4 as uuidv4 } from 'uuid';
export default {
props: ['topic_id'],
data () {
return {
questions: [],
answers: [],
uuid:0,
result: {
question_id: '',
answer: '',
user_id: '',
user_answer:0,
topic_id: '',
},
auth: [],
}
},
created () {
this.fetchQuestions();
},
methods: {
fetchQuestions() {
this.$http.get(`${this.$props.topic_id}/quiz/${this.$props.topic_id}`).then(response => {
this.questions = response.data.questions;
this.auth = response.data.auth;
this.uuid=uuidv4();
console.log(this.questions);
}).catch((e) => {
console.log(e)
});
},
createQuestion(id, ans, user_id, topic_id) {
this.result.question_id = id;
this.result.answer = ans;
this.result.user_id = user_id;
this.result.topic_id = this.$props.topic_id;
this.$http.post(`${this.$props.topic_id}/quiz`, this.result).then((response) => {
console.log(response.data.message);
let newdata=response.data.newdata;
this.questions.splice(newdata[0]["index"],1,newdata[0]);
}).catch((e) => {
console.log(e);
});
this.result.topic_id = '';
this.result.user_answer =0;
}
}
}
</script>
I used jQuery next() and prev() for the next and previous buttons. In questions variable, I store my array of objects from database which contains the questions and choices so after the user click next it will update the element of questions to insert the answer chosen by the user. My problem is how can I checked the answer by default chosen by the user if the question showed was answered already by the user. This is the time when the user wants to review his/her answers before finishing the exam.

Related

Vuejs get index in multiple input forms

I have an array of strings like:
questions: [
"Question 1?",
"Question 2?",
"Question 3?",
"Question 4?",
],
Then I have form fields in my data() like:
legitForm: {
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
},
Now, the problem I'm facing is when I fill inputs for any of questions
above the same field for other questions gets same value.
Here is my template code:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForm.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForm.duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForm.description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
And this is my post method that sends data to backend:
legitStore(question) {
this.legitForm.name = question; // set "name" in `legitForm` to value of `question` string
axios.post('/api/auth/userLegitsStore', this.legitForm, {
headers: {
Authorization: localStorage.getItem('access_token')
}
})
.then(res => {
// reset my data after success
this.legitForm = {
name: '',
answer: '',
description: '',
duration: '',
};
})
.catch(error => {
var errors = error.response.data;
let errorsHtml = '<ol>';
$.each(errors.errors,function (k,v) {
errorsHtml += '<li>'+ v + '</li>';
});
errorsHtml += '</ol>';
console.log(errorsHtml);
})
},
Here is issue screenshot:
Note: I've tried to
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
but I've got errors so i wasn't sure what I'm doing wrong, that's why
I'm asking here.
If you think of your questions as questions and answers, you can do something like this:
questions: [
{
question: 'Question 1',
answer: null,
description: null,
duration: null,
},
{
question: 'Question 2',
answer: null,
description: null,
duration: null,
},
]
Then when looping through your form, it would be more like this:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
And in the storing function you could send the data in the question instead of this.legitForm
Like you said you tried here:
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
You are supposed to be doing that.
I would change legitForm to:
//create legitForms equal to the length of questions
const legitForms = [];
for (i in questions) legitForms.push(
{
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
}
);
and in template:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForms[index].answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForms[index].duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForms[index].description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
<div v-for="(question, index) in questions" :key="index">
In your template you iterate through questions and within this tag render object legitForm it all questions will refer to the same 1 object that's why all question have the same data.
You should have had create an array of question contains it own question's content like
<template>
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<div class="col-md-12">
<p>
<strong>{{question.id}}</strong>
</p>
</div>
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
</template>
<script>
class QuestionForm {
// pass default value if you want
name = ''
answer = ''
description = ''
duration = ''
id = ''
constructor(form) {
Object.assign(this, form)
}
}
function initQuestions(num) {
// can generate a Set object cause question are unique
return Array.from({ length: num }, (v, i) => i).map(_j => new QuestionForm())
}
export default {
data() {
return {
questions: initQuestions(5), // pass number of question you want to generate
}
}
}
</script>

Conditionally render a div with vue.js

I am trying to build a quiz which when the user chooses a selection and submits the answer the notification div displays whether choice is correct or not. I have got this logic working however what is happening is the div is disappearing each time the next question is answered. I want to keep the previous question result on the screen and lock the user out of answering the question again and show the correct answer.
Here is what I have so far:
<div
class="container"
v-for="options in quiz"
v-bind:key="options.quizId"
>
<h3>{{ options.question }}</h3>
<div
class="control"
>
<label class="radio">
<input
type="radio"
:value="options.op1"
v-model="selectedAnswer"
/>
{{ options.op1 }}
</label>
</div>
<div class="control">
<label class="radio">
<input
type="radio"
:value="options.op2"
v-model="selectedAnswer"
/>
{{ options.op2 }}
</label>
</div>
<div class="control">
<label class="radio">
<input
type="radio"
:value="options.op3"
v-model="selectedAnswer"
/>
{{ options.op3 }}
</label>
</div>
<div class="control">
<label class="radio">
<input
type="radio"
:value="options.op4"
v-model="selectedAnswer"
/>
{{ options.op4 }}
</label>
</div>
<div class="control mt-4" >
<button class="button is-link" #click="call(options.id)">
Submit
</button>
</div>
<br />
<template v-if="options.id === quizId">
<template v-if="quizResult == 'correct'">
<template v-if="correctAnswer">
<div class="notification is-success mt-4">
Correct. Well done!
</div>
</template>
<br />
</template>
<template v-if="quizResult == 'incorrect'">
<div class="notification is-danger mt-4">
Incorrect. Please try again.
</div>
<br />
</template>
</template>
</div>
</div>
</div>
</div> <br>
</section>
</div>
</div>
<!-- <hr /> -->
</template>
<script>
import axios from "axios";
export default {
data() {
return {
course: {},
lessons: [],
comments: [],
activeLesson: null,
errors: [],
showModalFlag1: false,
okPressed1: false,
quiz: {},
questionIndex: 0,
loading: true,
quizId: null,
selectedAnswer: "",
quizResult: null,
correctAnswer: false,
userScore: {
username: '',
lesson_id: '',
lesson_score: '',
},
images: [
{
photo: "",
},
],
comment: {
name: "",
content: "",
},
};
},
methods: {
call(id){
this.submitQuiz(id);
console.log('checkid#call',id)
},
submitQuiz(e) {
console.log('check-id#submit', e)
const quizArray = this.quiz;
const choice = this.selectedAnswer;
console.log('chosen: ', choice)
const result = quizArray.filter( obj => obj.op1 === choice || obj.op2 === choice ||
obj.op3 === choice || obj.op4 === choice)[0];
console.log('result',result.id);
for (const prop in result) {
if ( result.hasOwnProperty(prop) ) {
if (result.id == e) {
this.quizId = result.id;
if (choice == result.answer) {
this.quizResult = "correct";
this.correctAnswer = true;
}
if(choice !== result.answer){
this.quizId = result.id;
this.quizResult = "incorrect";
}
}
}
}
},
You could have another div or in your notification div to display last answered question.
You can either have a new object that gets overwritten each time you answer correctly or track the index of your loop and use the value of index - 1 to get last answered question. Add a check if index !== 0.
To get index of your loop in html do: v-for="(options, ) in quiz
I'm seeing conditions with inner conditions for the correct answer notification. So if your quizId is the next one, the outer condition already fails.
It's likely you have an array with questions and thus have an index for the current question. You could save a property a property on that index that shows if the question is correct, so your condition can be extended to also look back for the previous question
Something like this (pseudo code)
<template v-if="showCorrectMsg">
Good!
...
and then have a computed showCorrectMsg method something like this (pseudo code):
if ( questions[currentIndex].quizResult == 'correct' ||
( currentIndex > 0 && questions[currentIndex - 1].quizResult == 'correct' )
) return true;
return false;
So for this to work you will have to store that flag on each answer given (again, pseudo code):
questions[currentIndex].quizResult = [ theAnswer == theRightAnswer ] ? 'correct' : 'wrong';

How can I validate form fields using JavaScript in Multi-step HTML Form?

I'm working on registration form that has three sections. A user moves to the next section of the form when the button "Next" is clicked. Everything is working well except that validation errors are only showing on the last section of the Form. I would like to validate the form before moving to the next section. For now, when "Next" button is clicked, the user can move to the next section even without filling the fields. I'm not so experienced in JavaScript, please help.
HTML:
<section>
<div class="container">
<form>
<div class="step step-1 active">
<div class="form-group">
<label for="firstName">First Name</label>
<input type="text" id="firstName" name="first-name">
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" name="last-name">
</div>
<div class="form-group">
<label for="nickName">Nick Name</label>
<input type="text" id="nickName" name="nick-name">
</div>
<button type="button" class="next-btn">Next</button>
</div>
<div class="step step-2">
<div class="form-group">
<label for="email">Email</label>
<input type="text" id="email" name="email">
</div>
<div class="form-group">
<label for="phone">Phone</label>
<input type="number" id="phone" name="phone-number">
</div>
<button type="button" class="previous-btn">Prev</button>
<button type="button" class="next-btn">Next</button>
</div>
<div class="step step-3">
<div class="form-group">
<label for="country">country</label>
<input type="text" id="country" name="country">
</div>
<div class="form-group">
<label for="city">City</label>
<input type="text" id="city" name="city">
</div>
<div class="form-group">
<label for="postCode">Post Code</label>
<input type="text" id="postCode" name="post-code">
</div>
<button type="button" class="previous-btn">Prev</button>
<button type="submit" class="submit-btn">Submit</button>
</div>
</form>
</div>
</section>
JavaScript:
const steps = Array.from(document.querySelectorAll("form .step"));
const nextBtn = document.querySelectorAll("form .next-btn");
const prevBtn = document.querySelectorAll("form .previous-btn");
const form = document.querySelector("form");
nextBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("next");
});
});
prevBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("prev");
});
});
form.addEventListener("submit", (e) => {
e.preventDefault();
const inputs = [];
form.querySelectorAll("input").forEach((input) => {
const { name, value } = input;
inputs.push({ name, value });
});
console.log(inputs);
form.reset();
});
function changeStep(btn) {
let index = 0;
const active = document.querySelector(".active");
index = steps.indexOf(active);
steps[index].classList.remove("active");
if (btn === "next") {
index++;
} else if (btn === "prev") {
index--;
}
steps[index].classList.add("active");
}
If you want to validate one section of the form before moving on to the next one you should do something like this:
nextBtn.forEach(button => {
button.addEventListener("click", () => {
handleEvent("next")
})
})
prevBtn.forEach(button => {
button.addEventListener("click", () => {
handleEvent("prev")
})
})
where handleEvent is:
function handleEvent(btn) {
if (!handleFormValidation(btn)) return "error message here";
changeStep(btn)
}
Here handleFormValidation would be a function that checks weather the input is correct and returns true if it is and false if it isn't
If you want to make sure the user fills in the first form first before going to the second you can do it by making the second form appear only after the next button is pressed, but that would require a major rework of your system. (i do however advise it because when i copied your code to test it i noticed quite a lot of bugs)
Here are some mdn articles describing how to make, delete and append elements using javascript:
making an element
removing an element
removing an element
appending an element to another element
appending an element to another element
I highly encourage you to do your own research as well.
I also just want to apologise if anything in my answer isn't understandable. I'm new at contributing to this community so there will likely be mistakes I've made.

Vue.js: A value in a v-for loop is not staying with the correct array items

I am trying to create a simple application to request a car key for a service department. Obviously the code could be written better, but this is my third day with Vue.js. The time function that is called in the first p tag in the code updates every minutes to keep count of an elapsed time. The problem I am having is when I request a new key the time function doesn't follow the array items as intended. For example, if there are no other requests the first request I submit works perfectly. However, when I submit a new request the elapsed time from my first request goes to my second request. I am sure it could have something to do with the glued together code, but I have tried everything I can think of. Any help would be appreciated.
<template>
<div class="row">
<div class="card col-md-6" v-for="(key, index) in keys" :key="index">
<div class="card-body">
<h5 class="card-title">Service Tag: {{ key.service_tag }}</h5>
<p class="card-text"> {{time}} {{key.reqTimestamp}}min</p>
<p class="invisible">{{ start(key.reqTimestamp) }}</p>
<p class="card-text">Associates Name: {{key.requestor_name}}</p>
<p class="card-text">Model: {{key.model}}</p>
<p class="card-text">Color: {{key.color}}</p>
<p class="card-text">Year: {{key.year}}</p>
<p class="card-text">Comments: {{key.comments}}</p>
<p class="card-text">Valet: {{key.valet}}</p>
<input class="form-control" v-model="key.valet" placeholder="Name of the person getting the car...">
<button
#click="claimedKey(key.id, key.valet)"
type="submit"
class="btn btn-primary"
>Claim</button>
<button v-if="key.valet !== 'Unclaimed'"
#click="unclaimedKey(key.id, key.valet)"
type="submit"
class="btn btn-primary"
>Unclaim</button>
<button class="btn btn-success" #click="complete(key.id)">Complete</button>
</div>
</div>
<!-- END OF CARD -->
<!-- START OF FORM -->
<div class="row justify-content-md-center request">
<div class="col-md-auto">
<h1 class="display-4">Operation Tiger Teeth</h1>
<form class="form-inline" #submit="newKey(service_tag, requestor_name, comments, model, year, color, valet, reqTimestamp)">
<div class="form-group col-md-6">
<label for="service_tag">Service Tag: </label>
<input class="form-control form-control-lg" v-model="service_tag" placeholder="ex: TB1234">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Associates Name: </label>
<!-- <input class="form-control form-control-lg" v-model="requestor_name" placeholder="Your name goes here..."> -->
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select v-model="requestor_name" class="form-control" id="requestor_name">
<option>James Shiflett</option>
<option>Austin Hughes</option>
</select>
</div>
</div>
<div class="form-group col-md-6">
<label for="service_tag">Model: </label>
<input class="form-control form-control-lg" v-model="model" placeholder="What is the model of the vehicle?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Color: </label>
<input class="form-control form-control-lg" v-model="color" placeholder="What is the color of the vehicle?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Year: </label>
<input class="form-control form-control-lg" v-model="year" placeholder="What year is the car?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Comments: </label>
<input class="form-control form-control-lg" v-model="comments" placeholder="Place any additional comments here...">
</div>
<div class="form-group col-md-6 invisible">
<label for="service_tag">Valet: </label>
<input v-model="valet">
</div>
<div class="form-group col-md-6 invisible">
<label for="service_tag">Timestamp: </label>
<input v-model="reqTimestamp">
</div>
<div class="col-md-12">
<button class="btn btn-outline-primary" type="submit">Request A Key</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import { db } from "../main";
import { setInterval } from 'timers';
export default {
name: "HelloWorld",
data() {
return {
keys: [],
reqTimestamp: this.newDate(),
service_tag: "",
requestor_name: "",
comments: "",
color: "",
model: "",
year: "",
inputValet: true,
valet: "Unclaimed",
state: "started",
startTime: '',
currentTime: Date.now(),
interval: null,
};
},
firestore() {
return {
keys: db.collection("keyRequests").where("completion", "==", "Incomplete")
};
},
methods: {
newKey(service_tag, requestor_name, comments, model, year, color, valet, reqTimestamp, completion) {
// <-- and here
db.collection("keyRequests").add({
service_tag,
requestor_name,
comments,
color,
model,
year,
valet,
reqTimestamp,
completion: "Incomplete",
});
this.service_tag = "";
this.requestor_name = "";
this.comments = "";
this.color = "";
this.model = "";
this.year = "";
this.reqTimestamp = this.newDate()
},
complete(id) {
db.collection("keyRequests").doc(id).update({
completion: "Complete"
})
},
// deleteKey(id) {
// db.collection("keyRequests")
// .doc(id)
// .delete();
claimedKey(id, valet) {
console.log(id);
this.inputValet = false
db.collection("keyRequests").doc(id).update({
valet: valet,
claimTimestamp: new Date()
})
},
moment: function () {
return moment();
},
newDate () {
var today = new Date()
return today
},
updateCurrentTime: function() {
if (this.$data.state == "started") {
this.currentTime = Date.now();
}
},
start(timestamp) {
return this.startTime = timestamp.seconds * 1000
}
},
mounted: function () {
this.interval = setInterval(this.updateCurrentTime, 1000);
},
destroyed: function() {
clearInterval(this.interval)
},
computed: {
time: function() {
return Math.floor((this.currentTime - this.startTime) /60000);
}
}
}
</script>
Ideally I am looking for the time lapse to follow each request.
So the problem lines in the template are:
<p class="card-text"> {{time}} {{key.reqTimestamp}}min</p>
<p class="invisible">{{ start(key.reqTimestamp) }}</p>
The call to start has side-effects, which is a major no-no for rendering a component. In this case it changes the value of startTime, which in turn causes time to change. I'm a little surprised this isn't triggering the infinite rendering recursion warning...
Instead we should just use the relevant data for the current iteration item, which you've called key. I'd introduce a method that calculates the elapsed time given a key:
methods: {
elapsedTime (key) {
const timestamp = key.reqTimestamp;
const startTime = timestamp.seconds * 1000;
return Math.floor((this.currentTime - startTime) / 60000);
}
}
You'll notice this combines aspects of the functions start and time. Importantly it doesn't modify anything on this.
Then you can call it from within your template:
<p class="card-text"> {{elapsedTime(key)}} {{key.reqTimestamp}}min</p>

Aurelia issue with setting element class based on obj.id === $parent.selectedId

I completed the contact-manager tut from Aurelia.io and am incorporating it into as task manager tut I'm putting together. The markup below sets the li class based on task.id === $parent.id.
task-list.html
<template>
<div class="task-list">
<ul class="list-group">
<li repeat.for="task of tasks" class="list-group-item ${task.id === $parent.selectedId ? 'active' : ''}">
<a route-href="route: tasks; params.bind: {id:task.id}" click.delegate="$parent.select(task)">
<h4 class="list-group-item-heading">${task.name}</h4>
<span class="list-group-item-text ">${task.due | dateFormat}</span>
<p class="list-group-item-text">${task.isCompleted}</p>
</a>
</li>
</ul>
</div>
task-list.js
#inject(WebAPI, EventAggregator)
export class TaskList {
constructor(api, ea) {
this.api = api;
this.tasks = [];
ea.subscribe(TaskViewed, x => this.select(x.task));
ea.subscribe(TaskUpdated, x => {
let id = x.task.id;
let task = this.tasks.find(x => x.id == id);
Object.assign(task, x.task);
});
}
created() {
this.api.getList().then( x => this.tasks = x);
}
select(task) {
this.selectedId = task.id;
return true;
}
}
If I edit the current task, represented by
task-detail.html
<template>
<require from="resources/attributes/DatePicker"></require>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Edit Task Profile</h3>
</div>
<div class="panel-body">
<form role="form" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input type="text" placeholder="name" class="form-control" value.bind="task.name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<input type="text" placeholder="description" class="form-control" value.bind="task.description">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Due Date</label>
<div class="col-sm-10">
<div class="input-group date">
<input type="text" datepicker class="form-control" value.bind="task.due | dateFormat:'L'"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Urgency</label>
<div class="col-sm-10">
<input type="range" min="1" max="5" step="1" class="form-control" value.bind="task.urgency">
</div>
</div>
</form>
</div>
</div>
<div class="button-bar">
<button class="btn btn-info" click.delegate="addTask(task)" >Add New</button>
<button class="btn btn-success" click.delegate="save()" disabled.bind="!canSave">Save Edit</button>
</div>
</template>
task-detail.js
#inject(WebAPI, EventAggregator, Utils, DialogService)
export class TaskDetail {
constructor(api, ea, utils, dialogService) {
this.api = api;
this.ea = ea;
this.utils = utils;
this.dialogService = dialogService;
}
activate(params, routeConfig) {
this.routeConfig = routeConfig;
return this.api.getTaskDetails(params.id).then(task => {
this.task = task;
this.routeConfig.navModel.setTitle(task.name);
this.originalTask = this.utils.copyObj(task);
this.ea.publish(new TaskViewed(task));
});
}
get canSave() {
return this.task.name && !this.api.isRequesting;
}
save() {
console.log(this.task);
this.api.saveTask(this.task).then(task => {
this.task = task;
this.routeConfig.navModel.setTitle(task.name);
this.originalTask = this.utils.copyObj(task);
this.ea.publish(new TaskUpdated(this.task));
});
}
canDeactivate() {
if (!this.utils.objEq(this.originalTask, this.task)) {
let result = confirm('You have unsaved changes. Are you sure you wish to leave?');
if (!result) {
this.ea.publish(new TaskViewed(this.task));
}
return result;
}
return true;
}
addTask(task) {
var original = this.utils.copyObj(task);
this.dialogService.open({viewModel: AddTask, model: this.utils.copyObj(this.task)})
.then(result => {
if (result.wasCancelled) {
this.task.name = original.title;
this.task.description = original.description;
}
});
}
}
If a value has changed, navigation away from the current task is not allowed, and that works -- that is, the contact-detail part of the UI doesn't change. However, the task <li>, that one tries to navigate to still gets the active class applied. That's not supposed to happen.
If I step along in dev tools, on the Aurelia.io contact-manager, I see that the active class is briefly applied to the list item, then it goes away.
from the contact-manager's contact-list.js This was run when clicking an <li> and no prior item selected.
select(contact) {
this.selectedId = contact.id;
console.log(contact);
return true;
}
This logs
Object {__observers__: Object}
Object {id: 2, firstName: "Clive", lastName: "Lewis", email: "lewis#inklings.com", phoneNumber: "867-5309"}
The same code on my task-manager's (obviously with "contact" replaced by task") task-list.js logs
Object {description: "Meeting With The Bobs", urgency: "5", __observers__: Object}
Object {id: 2, name: "Meeting", description: "Meeting With The Bobs", due: "2016-09-27T22:30:00.000Z", isCompleted: falseā€¦}
My first instinct is to say it's got something to do with this.selectedId = contact.id
this there would refer to what I assume is a function called select (looks like the function keyword is missing in your example?) or the global object (i.e. window)
select(contact) {
this.selectedId = contact.id;
console.log(contact);
return true;
}
Fixed it. It wasn't working because I had pushstate enabled. That clears things up. Thanks therealklanni.

Categories