Backbone events not firing & general feedback on use of the framework - backbone.js
I am very new to backbone.js and I am struggling a little. I figured out a way to get data from the server (in json) onto the screen successfully but am I doing it the right/best way?
All my view have this.el set but the events of the views are not firing ()... What is the best way forward?
Here is the code:
var surveyUrl = "/api/Survey?format=json&callback=?";
AnswerOption = Backbone.Model.extend({});
AnswerOptionView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'updateCheckedState');
},
events: {
"click .answerOptionControl": "updateCheckedState" //still noy firing :-(
},
render: function() {
this.model.get('answerOption').questionChoiceType = this.model.get('questionChoiceType');
var template = _.template($("#questionAnswerOptionTemplate").html(), this.model.get('answerOption'));
$(this.el).html(template);
return this;
},
updateCheckedState: function(e) {
alert("Here is my event origin: " + e.target)
}
});
Question = Backbone.Model.extend({});
QuestionView = Backbone.View.extend({
render: function() {
var template = _.template($("#questionTemplate").html(), this.model.get('question'));
$(this.el).html(template);
/*validator code removed*/
for (var i = 0; i < this.model.get('question').answerOptions.length; i++) {
var qModel = new AnswerOption({
answerOption: this.model.get('question').answerOptions[i]
});
var view = new AnswerOptionView({ model: qModel });
this.$('fieldset').append(view.render().el.innerHTML);
}
return this;
}
});
Survey = Backbone.Model.extend({
url: function () { return this.get("id") ? surveyUrl + '/' + this.get("id") : surveyUrl; }
});
SurveyList = Backbone.Collection.extend({
model: Survey,
url: surveyUrl
});
SurveyView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.model.bind('refresh', this.render);
this.model.bind('change', this.render);
},
// Re-render the contents
render: function () {
for (var i = 0; i < this.model.attributes[0].questions.length; i++) {
var view = new QuestionView();
var qModel = new Question({
question: this.model.attributes[0].questions[i]
});
view.model = qModel;
$(this.el).append(view.render().el.innerHTML);
}
this.el.trigger('create');
}
});
$(document).ready(
function () {
aSurvey = new Survey({ Id: 1 });
window.App = new SurveyView({ model: aSurvey, el: $("#questions") });
aSurvey.fetch();
});
-html
<body>
<div id="questions"></div>
<!-- Templates -->
<script type="text/template" id="questionAnswerOptionTemplate">
<input name="answerOptionGroup<%= questionId %>" id="answerOptionInput<%= id %>" type="checkbox" class="answerOptionControl"/>
<label for="answerOptionInput<%= id %>"><%= text %></label>
</script>
<script type="text/template" id="questionTemplate">
<div id="question<%=id %>" class="questionWithCurve">
<h1><%= headerText %></h1>
<h2><%= subText %></h2>
<div data-role="fieldcontain" id="answerOptions<%= id %>" >
<fieldset data-role="controlgroup" data-type="vertical">
<legend> </legend>
</fieldset>
</div>
</div>
</script>
</body>
And the JSON from the server:
? ({
"name": "Survey",
"questions": [{
"surveyId": 1,
"headerText": "Question 1",
"subText": "subtext",
"type": "Choice",
"positionOrder": 1,
"answerOptions": [{
"questionId": 1,
"text": "Question 1 - Option 1",
"positionOrder": 1,
"id": 1,
"createdOn": "\/Date(1333666034297+0100)\/"
}, {
"questionId": 1,
"text": "Question 1 - Option 2",
"positionOrder": 2,
"id": 2,
"createdOn": "\/Date(1333666034340+0100)\/"
}, {
"questionId": 1,
"text": "Question 1 - Option 3",
"positionOrder": 3,
"id": 3,
"createdOn": "\/Date(1333666034350+0100)\/"
}],
"questionValidators": [{
"questionId": 1,
"value": "3",
"type": "MaxAnswers",
"id": 1,
"createdOn": "\/Date(1333666034267+0100)\/"
}, {
"questionId": 1,
"value": "1",
"type": "MinAnswers",
"id": 2,
"createdOn": "\/Date(1333666034283+0100)\/"
}],
"id": 1,
"createdOn": "\/Date(1333666034257+0100)\/"
}, {
"surveyId": 1,
"headerText": "Question 2",
"subText": "subtext",
"type": "Choice",
"positionOrder": 2,
"answerOptions": [{
"questionId": 2,
"text": "Question 2 - Option 1",
"positionOrder": 1,
"id": 4,
"createdOn": "\/Date(1333666034427+0100)\/"
}, {
"questionId": 2,
"text": "Question 2 - Option 2",
"positionOrder": 2,
"id": 5,
"createdOn": "\/Date(1333666034440+0100)\/"
}, {
"questionId": 2,
"text": "Question 2 - Option 3",
"positionOrder": 3,
"id": 6,
"createdOn": "\/Date(1333666034447+0100)\/"
}],
"questionValidators": [{
"questionId": 2,
"value": "3",
"type": "MaxAnswers",
"id": 3,
"createdOn": "\/Date(1333666034407+0100)\/"
}, {
"questionId": 2,
"value": "1",
"type": "MinAnswers",
"id": 4,
"createdOn": "\/Date(1333666034417+0100)\/"
}],
"id": 2,
"createdOn": "\/Date(1333666034377+0100)\/"
}, {
"surveyId": 1,
"headerText": "Question 3",
"subText": "subtext",
"type": "Choice",
"positionOrder": 3,
"answerOptions": [{
"questionId": 3,
"text": "Question 3 - Option 1",
"positionOrder": 1,
"id": 7,
"createdOn": "\/Date(1333666034477+0100)\/"
}, {
"questionId": 3,
"text": "Question 3 - Option 2",
"positionOrder": 2,
"id": 8,
"createdOn": "\/Date(1333666034483+0100)\/"
}, {
"questionId": 3,
"text": "Question 3 - Option 3",
"positionOrder": 3,
"id": 9,
"createdOn": "\/Date(1333666034487+0100)\/"
}],
"questionValidators": [{
"questionId": 3,
"value": "3",
"type": "MaxAnswers",
"id": 5,
"createdOn": "\/Date(1333666034463+0100)\/"
}, {
"questionId": 3,
"value": "1",
"type": "MinAnswers",
"id": 6,
"createdOn": "\/Date(1333666034470+0100)\/"
}],
"id": 3,
"createdOn": "\/Date(1333666034457+0100)\/"
}, {
"surveyId": 1,
"headerText": "Question 4",
"subText": "subtext",
"type": "Choice",
"positionOrder": 4,
"answerOptions": [{
"questionId": 4,
"text": "Question 4 - Option 1",
"positionOrder": 1,
"id": 10,
"createdOn": "\/Date(1333666034500+0100)\/"
}, {
"questionId": 4,
"text": "Question 4 - Option 2",
"positionOrder": 2,
"id": 11,
"createdOn": "\/Date(1333666034507+0100)\/"
}, {
"questionId": 4,
"text": "Question 4 - Option 3",
"positionOrder": 3,
"id": 12,
"createdOn": "\/Date(1333666034507+0100)\/"
}],
"questionValidators": [{
"questionId": 4,
"value": "3",
"type": "MaxAnswers",
"id": 7,
"createdOn": "\/Date(1333666034493+0100)\/"
}, {
"questionId": 4,
"value": "1",
"type": "MinAnswers",
"id": 8,
"createdOn": "\/Date(1333666034497+0100)\/"
}],
"id": 4,
"createdOn": "\/Date(1333666034490+0100)\/"
}],
"id": 1,
"createdOn": "\/Date(1333666034243+0100)\/"
})
It doesn't work because of how you append the subview
this.$('fieldset').append(view.render().el.innerHTML);
The way the events handling works in Backbone.Views is that events rather then being attached to elements are attached to the root element of the view and then they are delegated to the children elements. In your case instead of appending the root element of the subview to the parent view you append its contents breaking the event handling as the element to which you bind events never makes it to the DOM.
this.$('fieldset').append(view.render().el);
remove the innerHTML property and it will work again. If you dont want to have wrapping element when instantiating Backbone.Views you can always pass element to be claimed as el options property.
var view = new AnswerOptionView({ model: qModel, el: this.$('fieldset') }).render();
this way the AnswerOptinView will claim the fieldset element and will bind the events to it.
Related
Create array of objects for a select based on items from a data table
So I have a data table in Vuetify that's containing eventlog items. For example, it looks like this: [{ "id": 1, "actionBy": "John", "eventAction": "CREATED", "Description": "" "actionDate": "2022-09-02T10:31:57.223765" }, { "id": 2, "actionBy": "Anna", "eventAction": "MODIFIED", "Description": "" "actionDate": "2022-09-07T13:44:29.892831" }, { "id": 3, "actionBy": "Eric", "eventAction": "REMOVE_IMAGE", "Description": "Test description" "actionDate": "2022-09-07T13:44:39.800381" }, { "id": 4, "actionBy": "Sysadmin", "eventAction": "REMOVE_IMAGE", "Description": "Test description" "actionDate": "2022-09-08T09:21:29.272312" }, { "id": 5, "actionBy": "Sysadmin", "eventAction": "MODIFIED", "Description": "Test description" "actionDate": "2022-09-08T09:21:54.991851" }, { "id": 6, "actionBy": "Sysadmin", "eventAction": "REMOVE_IMAGE", "Description": "Test description" "actionDate": "2022-09-08T13:55:00.469676" } ] Now I want to use a select(in Vuetify it's a v-select). Where I want to get the select options like this: [ { value: 'CREATED', count: 1 }, { value: 'MODIFIED', count: 2 }, { value: 'REMOVE_IMAGE', count: 3 }, ] I found some ways, but it doesn't feel really clean. I want to know if there is a efficient way to do this. So it doesn't look dirty. ;-)
You can use a group by from here and a mapping from here to create an array: computed: { selectOptions() { const obj = this.events.reduce((ar, x) => { ar[x.eventAction] = (ar[x.eventAction] || 0) + 1; return ar; }, {}); return Object.keys(obj).map(key => ({value: key, count: obj[key]})); }
How to filter elements of an array based on a property inside double nested array
Well, i have a complicated issue. at least it is complicated for myself. So i have an Array which has an Array that has an Array in it. and i want to filter The very top array based on properties inside the deepest array. Lets say i have this Array of objects var garages = [{ "GarageId": 1, "GarageName": "Garage_001", "Sections": [{ "SectionId": 1, "Name": "Section_002", "Cars": [{ "Id": 5, "Model": "Bmw" }, { "Id": 6, "Model": "Mercedes" }] }, { "SectionId": 2, "Name": "Section_003", "Cars": [{ "Id": 8, "Model": "Toyota" }, { "Id": 6, "Model": "Mercedes" }] }] }, { "GarageId": 6, "GarageName": "Garage_006", "Sections": [{ "Id": 1, "Name": "Section_007", "Cars": [{ "Id": 5, "Model": "Bmw" }, { "Id": 6, "Model": "Mercedes" }] }, { "Id": 2, "Name": "Section_003", "Cars": [{ "Id": 8, "Model": "Toyota" }, { "Id": 6, "Model": "Mercedes" }] }] } ] And i want to retrieve a list of garages that contain a Hyundai for example. how can i do it? i have been trying for hours and this is what i came up with. it may be a stupid piece of code but i just got confused dealing with this much nested Arrays! So my code is this: garages: any[] = []; selectedCarModel: number: 8; filterOnCarModel(carId) { this.garages = getGaragesFromServed(); this.selectedCarModel = this.CarModels.find(c => c.Id == id); let filteredArray = this.garages .filter((garage) => garage.Sections). filter((section) => study.Cars.find((car) => car.Id == carId)); this.garages = filteredArray; } Thank you for understanding
var filteredGarages = garages.filter(garage => garage.Sections.filter(section => section.Cars.filter(car => car.Model.indexOf("Bmw")>=0) .length > 0) .length > 0)
calculating correct answers and displaying total score in AngularJS quiz
I'm building a quiz application in AngularJS but I do not know how to iterate over the questions to add and display the correct answers. Please help I am new to programming. The quiz in JSON format as shown below: "questions": [{ "Id": 5, "Name": "In a village, the number of people infected with HIV in 2001 was 100. In 2004, 250 people were infected. Find the ratio of those infected in 2001 to those infected in 2004.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 1:5", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 2:5", "Answer": true }, { "Id": 3, "QuestionId": 1, "Name": "C. 3:5", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. 5:1", "Answer": false }, { "Id": 5, "QuestionId": 1, "Name": "E. 5:2", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 6, "Name": "The ratio of the length to the breadth of a room is 7:5. Find the length of the room if the breadth is 10m.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 10m", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 12m", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. 14m", "Answer": true }, { "Id": 4, "QuestionId": 1, "Name": "D. 21m", "Answer": false }, { "Id": 5, "QuestionId": 1, "Name": "E. 35m", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 7, "Name": "Increase 80 by 10%.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 8m", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 10m", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. 80m", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. 88m", "Answer": true }, { "Id": 5, "QuestionId": 1, "Name": "E. 108m", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 8, "Name": "Odiri bought 12 cartons of biscuits for N3,000.00. Find the cost of 7.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. ₦250.00", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. ₦442.00", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. ₦840.00", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. ₦1,750.00", "Answer": true }, { "Id": 5, "QuestionId": 1, "Name": "E. ₦2,700.00", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } },
You want to look at converting the JSON into an object using JSON.parse() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse Once in an object you could then loop through it using ng-repeat https://docs.angularjs.org/api/ng/directive/ngRepeat
here is a poc : check answers thanks to radio buttons and show correct/false thanks to ng-if you must check the docs/do a tutorial in order to improve your skills Maybe next time send a first work on a platform like stackblitz for example.
hie i had crack at it please view the code pen https://codepen.io/thefallen78/pen/ZPOgKL (function () { 'use strict'; angular.module("myapp",[]).controller("helloController", function($scope){ $scope.quiz = {"questions": [{ "Id": 5, "Name": "In a village, the number of people infected with HIV in 2001 was 100. In 2004, 250 people were infected. Find the ratio of those infected in 2001 to those infected in 2004.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 1:5", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 2:5", "Answer": true }, { "Id": 3, "QuestionId": 1, "Name": "C. 3:5", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. 5:1", "Answer": false }, { "Id": 5, "QuestionId": 1, "Name": "E. 5:2", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 6, "Name": "The ratio of the length to the breadth of a room is 7:5. Find the length of the room if the breadth is 10m.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 10m", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 12m", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. 14m", "Answer": true }, { "Id": 4, "QuestionId": 1, "Name": "D. 21m", "Answer": false }, { "Id": 5, "QuestionId": 1, "Name": "E. 35m", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 7, "Name": "Increase 80 by 10%.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. 8m", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. 10m", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. 80m", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. 88m", "Answer": true }, { "Id": 5, "QuestionId": 1, "Name": "E. 108m", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }, { "Id": 8, "Name": "Odiri bought 12 cartons of biscuits for N3,000.00. Find the cost of 7.", "QuestionTypeId": 1, "Options": [ { "Id": 1, "QuestionId": 1, "Name": "A. ₦250.00", "Answer": false }, { "Id": 2, "QuestionId": 1, "Name": "B. ₦442.00", "Answer": false }, { "Id": 3, "QuestionId": 1, "Name": "C. ₦840.00", "Answer": false }, { "Id": 4, "QuestionId": 1, "Name": "D. ₦1,750.00", "Answer": true }, { "Id": 5, "QuestionId": 1, "Name": "E. ₦2,700.00", "Answer": false }], "QuestionType": { "Id": 1, "Name": "Multiple Choice", "Active": true } }]} }); })(); <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>the quiz</title> </head> <body ng-app = "myapp"> <div class = "container"> <div class = "row" ng-controller = "helloController"> <div class = "col-md-6 col-md-offset-3" style = "background-color:red;"> <form action = "POST" ng-repeat = "(key,value) in quiz"> <label>{{key}}</label> <div class = "form-group" ng-repeat = "item in value"> <label>{{item.Name}}</label> <select class = "form-control" > <option ng-repeat = "options in item.Options">{{options.Name}}</option> </select> </div> </form> </div> </div> </div> </body> </html>
How to display left menu using angular tree module with different child and sub child objects keys in JSON
[ { "id": 1, "title": "node1", "nodes": [ { "id": 11, "title": "node1.1", "nodess": [ { "id": 111, "title": "node1.1.1", "nodes": [] } ] }, { "id": 12, "title": "node1.2", "nodes": [] } ] }, { "id": 2, "title": "node2", "nodrop": true, "nodes": [ { "id": 21, "title": "node2.1", "nodes": [] }, { "id": 22, "title": "node2.2", "nodes": [] } ] }, { "id": 3, "title": "node3", "nodes": [ { "id": 31, "title": "node3.1", "nodes": [] } ] } ]
Fixed the above issue by writing a custom function in the ng-click event handler and in that function created the new nodes and pushed the data to that particular property....Example var nodeData = scope.$modelValue.nodess[0]; nodeData.nodes.push({ id: scope.$modelValue.id+ '_' +1 * 10 + nodeData.nodes.length, title:'test', nodes: [] });
Use this awesome library, it has everything for nested json and tree that you need https://github.com/wix/angular-tree-control Example <div id="treeview" treecontrol class="tree-classic" tree-model="slides" options="treeOptions" selected-node="node" on-selection="showSelected(node)" filter-expression="node.children.length" filter-comparator="comparator" expanded-nodes="expandedNodes" ng-click=""> <span>{{node.Title}}</span> </div> Controller Stuff $scope.slides = []; var treeOptions = { nodeChildren: "children", dirSelectable: false, injectClasses: { ul: "a1", li: "a2", liSelected: "a7", iExpanded: "a3", iCollapsed: "a4", iLeaf: "a5", label: "a6", labelSelected: "a8" }, isLeaf: function(node) { return node.IsSlide; }, allowDeselect: false } $scope.node = null; //select a by default node if you want $scope.expandedNodes = []; //by default expand if you want Check this JSFIDDLE out
AngularUI-Bootstrap Typeahead: Grouping titles by type
I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a example of result [{ "id": 1, "label": "type_1", "titles": [{ "id": 1, "label": "title_1" }, { "id": 2, "label": "title_2" }, { "id": 3, "label": "title_3" }] }, { "id": 2, "label": "type_2", "titles": [{ "id": 4, "label": "title_4" }, { "id": 6, "label": "title_6" }] }, { "id": 3, "label": "type_3", "titles": [{ "id": 8, "label": "title_8" }, { "id": 9, "label": "title_9" }] }] How can grouping titles by type in AngularUI-Bootstrap Typeahead
JB Nizet is right, you should use a custom template (default). Look at // view <script type="text/ng-template" id="typeahead-match.html"> <div ng-if="match.model.isGroup">{{match.label}}</div> <a ng-if="!match.model.isGroup" ng-bind-html="match.label | uibTypeaheadHighlight:query"> {{match.label}} </a> </script> <input type="text" ng-model="selected" uib-typeahead="item as item.label for item in getItems($viewValue)" class="form-control" typeahead-template-url="typeahead-match.html"> // controller myApp.controller('MainController', ['$scope', function($scope) { var data = [{ "id": 1, "label": "type_1", "titles": [{ "id": 1, "label": "title_1" }, { "id": 2, "label": "title_2" }, { "id": 3, "label": "title_3" }] }, { "id": 2, "label": "type_2", "titles": [{ "id": 4, "label": "title_4" }, { "id": 6, "label": "title_6" }] }, { "id": 3, "label": "type_3", "titles": [{ "id": 8, "label": "title_8" }, { "id": 9, "label": "title_9" }] }]; $scope.getItems = function(text) { var result = []; _.each(data, function(group) { result.push({ label: group.label, isGroup: true }); _.each(group.titles, function(item) { if(_.startsWith(item.label, text)) { result.push(item); } }); if(result && result[result.length-1].isGroup) { result.pop(); } }); return result; }; }]); Example