Why doesn't my javascript quiz load properly? - javascript

I am trying to make a simple quiz. Somehow I always have an error
"Vue is not defined"
However, it actually is defined right there. I don't exactly undertand what is the reason. The website link is here.
The script is right in a head of the page. I have tried to use javascript code as a separate file, but the error is the same. I am a novice in javascript, so any help would be really appreciated.
"use strict";
window.onload = function() {
var quiz = {
title: 'What superhero are you?',
questions: [{
text: "How would you describe your personality?",
responses: [{
text: 'Im serious and dark',
value: 'Batman'
},
{
text: 'Arrogant, but charming',
value: 'Superman'
},
{
text: 'Fun and easy going',
value: 'The Flash'
}
]
},
{
text: "Why did you want to become a superhero?",
responses: [{
text: 'For the thrills',
value: 'The Flash'
},
{
text: 'For justice',
value: 'Batman'
},
{
text: 'For popularity',
value: 'Superman'
}
]
},
{
text: "Who would you most hang around with?",
responses: [{
text: 'Wonder Woman',
value: 'Superman'
},
{
text: 'Green Arrow',
value: 'The Flash'
},
{
text: 'Robin',
value: 'Batman'
}
]
},
{
text: "What's your favourite colour?",
responses: [{
text: 'Black',
value: 'Batman'
},
{
text: 'Red',
value: 'The Flash'
},
{
text: 'Blue',
value: 'Superman'
}
]
},
{
text: "When do you help people?",
responses: [{
text: 'Every chance I can',
value: 'The Flash'
},
{
text: 'At night',
value: 'Batman'
},
{
text: 'When they need me to',
value: 'Superman'
}
]
},
]
};
var app = new Vue({
el: '#app',
data: {
quiz: quiz,
questionIndex: 0,
userResponses: Array()
},
methods: {
// Go to next question
next: function() {
this.questionIndex++;
console.log(this.userResponses);
},
// Go to previous question
prev: function() {
this.questionIndex--;
},
score: function() {
//find the highest occurence in responses
var modeMap = {};
var maxEl = this.userResponses[0],
maxCount = 1;
for (var i = 0; i < this.userResponses.length; i++) {
var el = this.userResponses[i];
if (modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
}
});
}

If you'll check the source code of your site You will see script for Vue is not loaded. So giving error Vue is not defined. Add below script in head of your page.
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.22/dist/vue.js"></script>
You can read here, how to use Vue in your project.

You are actually not loading Vue. Make sure to load Vue before instantiating an instance of it.
You can either load the script using a CDN, as Ashish showed, or save the script to your local machine, and including it in your page.
<script src="/js/vue-2.5.22.min.js" type="text/javascript"/>
Where /js/vue-2.5.22.min.js is the relative path to the location of Vue.

Related

How to add new element to array which inside nested object by its path?

I want to add new element to array which inside nested object by using its path. I searched but didn't anything about that. For example my object is :
const sample = {
enum: 4,
key: "COMMON_TYPE",
steps: [
{
title: "STEP ONE",
description: "des1",
instructions: [
{
icon: "step_power",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_ONE_ICONTEXT",
color: "A11111",
location: "top",
},
},
],
},
{
title: "STEP TWO",
description: "des2",
instructions: [
{
icon: "step_power",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_TWO_ICONTEXT_ONE",
color: "A11111",
location: "top",
},
},
],
},
],
};
and I want to new element for this path 'steps.0.instructions' and second item should be copy of first index for that array. In onClick event I got path of key as 'steps.0.instructions'. Now I need a function which return newObject for given issue. My output should be like this:
const sample = {
enum: 4,
key: "COMMON_TYPE",
steps: [
{
title: "STEP ONE",
description: "des1",
instructions: [
{
icon: "step_power",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_ONE_ICONTEXT",
color: "A11111",
location: "top",
},
{
icon: "step_power",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_ONE_ICONTEXT",
color: "A11111",
location: "top",
}
},
],
},
{
title: "STEP TWO",
description: "des2",
instructions: [
{
icon: "step_power",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_TWO_ICONTEXT_ONE",
color: "A11111",
location: "top",
},
},
],
},
],
};
Thanks..
use unshift function to add new item to the beginning of array.
like
sample.steps.instructions.unshift( {
icon: "step_power#2",
label: {
text: "METHOD_OVEN_DEFAULT_STEP_ONE_ICONTEXT",
color: "A11111",
location: "top",
}
})
If you wanna copy an object, lookup what deep copy means.
Here are some helpful links:
stackoverflow: how to deep copy in js
MDN StructuredClone
This does what you ask for.
I used JSON.parse(JSON.stringify(sample)) for a deep copy. You can do it in your preferred way.
const addToPath = (sample, path) => {
const parts = path.split(".")
const newSample = JSON.parse(JSON.stringify(sample))
const instructionsArray = newSample[parts[0]][parts[1]][parts[2]]
instructionsArray.push(instructionsArray[0])
return newSample
}
And it will be called as follows:
const newSample = addToPath(sample, "steps.0.instructions")

react TypeError: Cannot read property 'type1' of undefined

I'm studying the react-beautiful-dnd example, and I want to add a conditional repulsion of an element, but I get an error that the field cannot be read.
This data
const initialData = {
tasks: {
'task-1': { id: 'task-1', content: 'Take out the garbage',type1: 'w' },
'task-2': { id: 'task-2', content: 'Watch my favorite show',type1: 'a' },
'task-3': { id: 'task-3', content: 'Charge my phone',type1: 'h' },
'task-4': { id: 'task-4', content: 'Cook dinner',type1: 'w' }
},
columns: {
'column-1': {
id: 'column-1',
title: 'To do',
type2: 'all',
taskIds: ['task-1', 'task-2', 'task-3', 'task-4']
},
'column-2': {
id: 'column-2',
title: 'In progress',
type2: 'w',
taskIds: []
},
'column-3': {
id: 'column-3',
title: 'Done',
type2: 'a',
taskIds: []
}
},
// Facilitate reordering of the columns
columnOrder: ['column-1', 'column-2', 'column-3'] }export default initialData
so they are connected
import initialData from './initial-data'
so it is assigned in state
state = initialData
this is how it is used and everything that I described it works and this is the code from the example
const start = this.state.columns[source.droppableId]
const finish = this.state.columns[destination.droppableId]
if (start === finish) {
const newTaskIds = Array.from(start.taskIds)
now below i want to add my condition and it throws an error
const typeDrag = this.state.tasks[source.draggableId]
const typeDrop = this.state.columns[destination.droppableId]
if(typeDrag.type1!==typeDrop.type2)
{return}
and I don't understand why start.taskIds works, but typeDrag.type1 reports that there is no such field
similarly executable codesandbox example
example
Solution:
instead of: const typeDrag = this.state.tasks[source.draggableId]
use: const typeDrag = this.state.tasks[draggableId]
Explanation:
In your code source doesn't have property draggableId.
So typeDrag is undefined because source.draggableId is undefined.
Argument of your onDragEnd hook looks like that:
{
"draggableId": "task-2",
"type": "TASK",
"source": {
"index": 1,
"droppableId": "column-1"
},
"destination": {
"droppableId": "column-2",
"index": 0
},
"reason": "DROP"
}
By checking your sandbox sample I see that you've already extracted draggableId property to a constant from the method argument ("result"):
const { destination, source, draggableId } = result; // line:28
So using draggableId instead of source.draggableId resolves TypeError: Cannot read property 'type1' of undefined.

Why the value in this array (apparently) changes immediately

I couldn't find any similar question, I don't know what could be happening there, (and maybe could be something stupid) but I haven't found any clue about what could be happening.
I have this array:
const superVillains = [
{ value: '1', label: 'Thanos' },
{ value: '2', label: 'The Joker' },
{ value: '3', label: 'Ultron', disabled: true },
{ value: '4', label: 'The Riddler' },
{ value: '5', label: 'Lex Luthor' },
{ value: '6', label: 'Green Goblin' },
{ value: '7', label: 'Bain', disabled: true },
{ value: '8', label: 'The Penguin' },
{ value: '9', label: 'Doctor Octopus' },
{ value: '10', label: 'Poison Ivy' },
{ value: '11', label: 'Magneto' },
{ value: '12', label: 'Mr. Glass' },
{ value: '13', label: 'General Zod' },
{ value: '14', label: 'Red Skull', disabled: true },
{ value: '15', label: 'Baron Von Zemo' }
];
I copied this array into another called optionsState in a react state
const [optionsState, setOptionsState] = useState(superVillains);
and applied the following operations:
const index = 0;
optionsState[index]['selected'] = true;
console.log(optionsState[index]['selected']);
console.log(optionsState[index]);
console.log(optionsState);
This is the result in console:
In the first console output it seems that the selected value is true as it should, the same for the second console output, but without changing nothing in the code the third console output shows that the selected value is false.
the question is: Why does the selected value apparently changes without applying any operations on it (besides a console log statement)?
If a place another
console.log(optionsState[index]);
after the last console log it will show the same as before:
{value: "1", label: "Thanos", selected: true}
so I don't know if it is an issue with the browser or an issue with the react states or an issue with me.
Any ideas on this?
Now that you edited your question, I get it :)
You are mutating your state, and react is based in immutability (google for more), a quick fix would be :
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index] , selected: true };
return { oldOptions };
})
As already said, you're directly mutating the state, which is a bad idea in React. To properly update the state use functional form of setState plus map:
const index = 0;
setOptionsState(options => {
return options.map((option, i) => {
if (index === i) {
return {
...option,
selected: true
}
}
return option;
})
})
optionsState[index]['selected'] = true;
You should not mutate state like this. If you want to add a new attribute to the state, you should do it using the function provided, in your case it is going to be setOptionsState.
The easiest way to achieve this should be something like this:
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index], selected: true };
return { oldOptions };
})
Please try this approach and see if it's any different (I hope so!)
if you want to change the value of optionsState use setOptionsState because the state value is updated after the refresh if you dont use setOptionsState.

CRM 2016 AutoComplete

I'm currently trying to implement the new CRM's Autocomplete in a CRM online 2016 environment.
I've used the code from Sample: Auto-complete in CRM controls and have verified that it works on the Account form and another custom entity that already exists. However, when I use it one 1 specific custom entity and any of its string fields, the autocomplete box does not appear.
Attempts:
Creating a new form
Creating a brand new text field for the autocomplete to run on
Validated that it's hitting ext.getEventSource().showAutoComplete(resultSet);
Validated that no errors are being thrown from my JS
Anyone have any ideas of what might possibly be wrong? I'm thinking it has something to do with my entity or the entity form instead of the code or the text field.
/** Sample JavaScript code to demonstrate the auto-completion feature.
This sample configures the auto-complete feature for the "Account Name"
field in the account form. */
function suggestAccounts() {
// List of sample account names to suggest
accounts = [
{ name: 'A. Datum Corporation', code: 'A01' },
{ name: 'Adventure Works Cycles', code: 'A02' },
{ name: 'Alpine Ski House', code: 'A03' },
{ name: 'Bellows College', code: 'A04' },
{ name: 'Best For You Organics Company', code: 'A05' },
{ name: 'Blue Yonder Airlines', code: 'A06' },
{ name: 'City Power & Light', code: 'A07' },
{ name: 'Coho Vineyard', code: 'A08' },
{ name: 'Coho Winery', code: 'A09' },
{ name: 'Coho Vineyard & Winery', code: 'A10' },
{ name: 'Contoso, Ltd.', code: 'A11' },
{ name: 'Contoso Pharmaceuticals', code: 'A12' },
{ name: 'Contoso Suites', code: 'A13' },
{ name: 'Consolidated Messenger', code: 'A14' },
{ name: '​Fabrikam, Inc.', code: 'A15' },
{ name: 'Fabrikam Residences', code: 'A16' },
{ name: '​First Up Consultants', code: 'A17' },
{ name: 'Fourth Coffee', code: 'A18' },
{ name: 'Graphic Design Institute', code: 'A19' },
{ name: 'Humongous Insurance', code: 'A20' },
{ name: 'Lamna Healthcare Company', code: 'A21' },
{ name: 'Litware, Inc.', code: 'A22' },
{ name: 'Liberty Delightful Sinful Bakery & Cafe', code: 'A23' },
{ name: 'Lucerne Publishing', code: 'A24' },
{ name: 'Margie Travel', code: 'A25' },
{ name: '​Munson Pickles and Preserves Farm', code: 'A26' },
{ name: 'Nod Publishers', code: 'A27' },
{ name: 'Northwind Electric Cars', code: 'A28' },
{ name: 'Northwind Traders', code: 'A29' },
{ name: 'Proseware, Inc.', code: 'A30' },
{ name: 'Relecloud', code: 'A31' },
{ name: 'School of Fine Art', code: 'A32' },
{ name: 'Southridge Video', code: 'A33' },
{ name: 'Tailspin Toys', code: 'A34' },
{ name: 'Trey Research', code: 'A35' },
{ name: 'The Phone Company', code: 'A36' },
{ name: 'VanArsdel, Ltd.', code: 'A37' },
{ name: 'Wide World Importers', code: 'A38' },
{ name: '​Wingtip Toys', code: 'A39' },
{ name: 'Woodgrove Bank', code: 'A40' }
];
var keyPressFcn = function (ext) {
try {
var userInput = Xrm.Page.getControl("name").getValue();
resultSet = {
results: new Array(),
commands: {
id: "sp_commands",
label: "Learn More",
action: function () {
// Specify what you want to do when the user
// clicks the "Learn More" link at the bottom
// of the auto-completion list.
// For this sample, we are just opening a page
// that provides information on working with
// accounts in CRM.
window.open("http://www.microsoft.com/en-us/dynamics/crm-customer-center/create-or-edit-an-account.aspx");
}
}
};
var userInputLowerCase = userInput.toLowerCase();
for (i = 0; i < accounts.length; i++) {
if (userInputLowerCase === accounts[i].name.substring(0, userInputLowerCase.length).toLowerCase()) {
resultSet.results.push({
id: i,
fields: [accounts[i].name]
});
}
if (resultSet.results.length >= 10) break;
}
if (resultSet.results.length > 0) {
ext.getEventSource().showAutoComplete(resultSet);
} else {
ext.getEventSource().hideAutoComplete();
}
} catch (e) {
// Handle any exceptions. In the sample code,
// we are just displaying the exception, if any.
console.log(e);
}
};
Xrm.Page.getControl("name").addOnKeyPress(keyPressFcn);
}
You must have a lookup control on the form in order for the autocomplete to render. It sounds weird but this is currently the best workaround. I set mine to not be visible. Note: this is any lookup field it does not matter the relationship you choose. Having the lookup field sets something on the form to load the missing libraries.
I spend most of my weekend trying to figure out a similar situation. I could describe at great lengths the events that led to this discovery, but I will spare you.
I assume that Microsoft is trying to not include resources where they do not see a configured need and that is why the back-end autocomplete files are missing (until you add in a requirement for them).

Stacked graph with KendoUI

I'm trying to display data in a stacked graph using kendo ui. Here is my code:
var data = [
// June
{ Start: "2014-06-01T00:00:00", Name : "Series 1", Value: 1 },
{ Start: "2014-06-01T00:00:00", Name : "Series 2", Value: 2 },
{ Start: "2014-06-01T00:00:00", Name : "Series 3", Value: 10 },
// July
{ Start: "2014-07-01T00:00:00", Name : "Series 1", Value: 2 },
{ Start: "2014-07-01T00:00:00", Name : "Series 2", Value: 2 },
{ Start: "2014-07-01T00:00:00", Name : "Series 3", Value: 2 },
// August
{ Start: "2014-08-01T00:00:00", Name : "Series 1", Value: 3 },
{ Start: "2014-08-01T00:00:00", Name : "Series 2", Value: 2 },
{ Start: "2014-08-01T00:00:00", Name : "Series 3", Value: 1 },
// September
{ Start: "2014-09-01T00:00:00", Name : "Series 2", Value: 2 },
{ Start: "2014-09-01T00:00:00", Name : "Series 3", Value: 3 },
// October
{ Start: "2014-10-01T00:00:00", Name : "Series 1", Value: 1 },
{ Start: "2014-10-01T00:00:00", Name : "Series 3", Value: 3 }
]
var stocksDataSource = new kendo.data.DataSource({
data: data,
group: {
field: "Name"
},
sort: [{ field: "Start", dir: "asc"} ]
});
function createChart() {
$("#chart").kendoChart({
dataSource: stocksDataSource,
series: [{
type: "column",
field: "Value",
name: "#= group.value #",
stack: true,
tooltip: {
template: "#=kendo.toString(new Date(category), 'd MMM yyyy')#<br/>" +
"#=dataItem.Name#<br/>"+
"Value: #=dataItem.Value#",
visible: true
},
}],
categoryAxis: {
field: "Start",
type: "date",
labels: {
format: "MMM"
}
}
});
}
$(document).ready(createChart);
$(document).bind("kendo:skinChange", createChart);
Note that September and October data do not have values for some series. This completely screws up the chart display in quite unexplainable way:
As you can see both September and October data do not match the json. It's especially weird with October data because three values are displayed whereas only 2 are given.
Here is JSFiddle: http://jsfiddle.net/12ob7qmx/6/
Are there any settings on the chart that I can set so it works, or will I have to loop through the dataset and fill in missing data with zero values?
The way I solved this issue is by looping through my data and adding missing values with 0's.
I don't think there is a better way, than what you suggested yourself. :(
I found a question concerning this issue on the Telerik forums:
The behavior you have observed is expected as the categorical charts
(bar, area etc.) require a matching set of data points (the value can
be null but it should persist in the data). I am afraid there is no
built-in functionality which will automatically set 0 for the missing
values - you should modify your data.
I am afraid the implementation of this functionality is not in our immediate plans, however we may consider it for future versions of the product.
I haven't found more recent information, but as far as I know this hasn't been fixed.
It' looks like I'm out of luck and I need to fix up the data. In my actual solution I'm doing that server-side, but for posterity if anyone needs a kickstart with a purely js fix, this is the starting point:
function fixData(data) {
var lookup =[], start = [], name = [], result =[];
for (i = 0, len = data.length; i < len; ++i) {
start[data[i].Start] = true;
name[data[i].Name] = true;
lookup[data[i].Start + "," + data[i].Name] = data[i].Value;
}
for (var currentStart in start) {
for (var currentName in name) {
var entry = {Start: currentStart, Name: currentName};
entry.Value = lookup[currentStart + "," + currentName] || 0;
result.push(entry);
}
}
return result;
}
JSFiddle: http://jsfiddle.net/12ob7qmx/8/

Categories

Resources