I created a Azure DevOps Server widget. When I tried to integrate the settings, the widget suddenly stopped loading. I checked my configuration with the example from Microsoft but did not find the solution.
My vss-extension.json:
"manifestVersion": 1,
"id": "ado-report-tfs-addon",
"version": "1.0.0",
"name": "ADO Report Widget",
"description": "You can see at a glance if a team has free capacity for new features.",
"publisher": "bci",
"icons": {
"default": "img/icon.png"
},
"targets": [
{
"id": "Microsoft.VisualStudio.Services"
}
],
"tags": [
"ado-report",
"report",
"widgets",
"dashboard"
],
"screenshots": [
{
"path": "img/sampleImage1.png"
},
{
"path": "img/sampleImage2.png"
}
],
"content": {
"details": {
"path": "README.md"
}
},
"links": {
"getstarted": {
"uri": "https://rb-tfsbci.de.bosch.com/tfs/OpConDev/Nx_Base/_git/ado-widgets"
},
"learn": {
"uri": "https://rb-tfsbci.de.bosch.com/tfs/OpConDev/Nx_Base/_git/ado-widgets"
},
"license": {
"uri": "https://rb-tfsbci.de.bosch.com/tfs/OpConDev/Nx_Base/_git/ado-widgets"
},
"support": {
"uri": "https://rb-tfsbci.de.bosch.com/tfs/OpConDev/Nx_Base/_git/ado-widgets"
}
},
"repository": {
"type": "git",
"uri": "https://rb-tfsbci.de.bosch.com/tfs/OpConDev/Nx_Base/_git/ado-widgets"
},
"branding": {
"color": "87C2E1",
"theme": "light"
},
"categories": [
"Collaborate"
],
"contributions": [
{
"id": "ADOReport1",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog",
"bci.ado-report-tfs-addon.ADOReport.Configuration"
],
"properties": {
"name": "ADO Report Widget",
"description": "You can see at a glance if a team has free capacity for new features.",
"catalogIconUrl": "img/icon.png",
"previewImageUrl": "img/icon.png",
"uri": "widget1.html",
"supportedSizes": [
{
"rowSpan": 5,
"columnSpan": 5
}
],
"supportedScopes": [
"project_team"
]
}
},
{
"id": "ADOReport.Configuration",
"type": "ms.vss-dashboards-web.widget-configuration",
"targets": [
"ms.vss-dashboards-web.widget-configuration"
],
"properties": {
"name": "ADO Report Configuration",
"description": "Configures ADOReport1",
"uri": "configuration.html"
}
}
],
"files": [
{
"path": "widget1.html",
"addressable": true
},
{
"path": "configuration.html",
"addressable": true
},
{
"path": "styles",
"addressable": true
},
{
"path": "sdk/scripts",
"addressable": true
},
{
"path": "img",
"addressable": true
}
],
"galleryFlags": [
"Preview"
],
"scopes": [
"vso.work",
"vso.code_write",
"vso.build_execute",
"vso.test",
"vso.build",
"vso.extension.data"
]
}
My widget1.html:
<!DOCTYPE html>
<html>
<head>
<!--<meta http-equiv="refresh" content="5">-->
<link rel="stylesheet" href="styles/main-style.css" />
<script src="sdk/scripts/VSS.SDK.min.js" type="text/javascript"></script>
<script src="sdk/scripts/main.js" type="text/javascript"></script>
</head>
<body>
<div class="widget">
<div class="title-wrapper">
<h2 class="title">ADO Widget</h2>
</div>
<div class="team-select-wrapper">
<div class="team-select-wrapper-select">
<div class="dropdown">
<label>
Team auswählen:
</label>
<div class="wrapper">
<select class="team-select-selector" id="teamSelect"> </select>
</div>
</div>
</div>
<div class="team-select-wrapper-loading">
<div class="loader" id="loading-icon"></div>
</div>
</div>
<div class="feature">
<div class="feature-wrapper">
<div class="feature-name-container" id="feature-name-container"></div>
<div class="feature-time-container" id="feature-time-container"></div>
</div>
<div class="feature-split-wrapper">
<hr class="feature-splitter" />
</div>
<div class="feature-evaluation-wrapper">
<div class="feature-evaluation-summ">
Summe
</div>
<div class="feature-evaluation-value" id="feature-evaluation-value">
</div>
</div>
</div>
<div class="kappa">
<div class="kappa-wrapper">
<div class="kappa-sprint-wrapper" id="sprint-wrapper"></div>
<div class="kappa-evaluation-wrapper">
<div class="kappa-splitter">
<hr class="kappa-splitter-item" />
</div>
<div class="kappa-evaluation">
<div class="kappa-evaluation-text">
Summe:
</div>
<div class="kappa-evaluation-evaluation" id="gesKappa"></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
My main.js:
var selectedTeam = "team1";
var teams = [];
var gesKapa = 0;
var loader;
function changeGesKapa(newVal) {
gesKapa = newVal;
var gesKapaNote = document.getElementById("gesKappa");
while (gesKapaNote.lastElementChild) {
gesKapaNote.removeChild(gesKapaNote.lastElementChild);
}
gesKapaNote.innerHTML = "";
gesKapaNote.append(document.createTextNode(gesKapa + "h"));
}
function startLoading() {
if (loader !== undefined) {
loader.classList.remove("hidden");
}
}
function stopLoading() {
if (loader !== undefined) {
loader.classList.add("hidden");
}
}
function getSelectedTeam() {
var mySelect = document.getElementById("teamSelect");
return mySelect.value;
}
function treatAsUTC(date) {
var result = new Date(date);
result.setMinutes(result.getMinutes() - result.getTimezoneOffset());
return result;
}
function daysBetween(startDate, endDate) {
var millisecondsPerDay = 24 * 60 * 60 * 1000;
return (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
}
function getBusinessDatesCount(startDate, endDate) {
var count = 0;
var curDate = startDate;
while (curDate <= endDate) {
var dayOfWeek = curDate.getDay();
if (!(dayOfWeek == 6 || dayOfWeek == 0)) count++;
curDate.setDate(curDate.getDate() + 1);
}
return count;
}
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
document.addEventListener("DOMContentLoaded", () => {
loader = document.getElementById("loading-icon");
var sel = document.getElementById("teamSelect");
sel.addEventListener("change", async () => {
startLoading();
await loadOverlay();
});
});
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true,
usePlatformScripts: true,
});
VSS.require(["TFS/Dashboards/WidgetHelpers"], async function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("ADOReport1", function () {
return {
load: function () {
return WidgetHelpers.WidgetStatusHelper.Success();
},
};
});
VSS.notifyLoadSucceeded();
});
VSS.require(["TFS/Core/RestClient"], async function loadTeams(TFS_Core_WebApi) {
TFS_Core_WebApi.getClient()
.getProjects(null, null, null)
.then(async (teamList) => {
await asyncForEach(teamList, async (teamListItem) => {
teams.push({
teams: await TFS_Core_WebApi.getClient().getTeams(
teamListItem.id,
null,
null
),
project: teamListItem,
});
});
teams.sort();
teams.forEach((element) => {
element.teams.sort();
});
var select = document.getElementById("teamSelect");
teams.forEach((element) => {
element.teams.forEach((teamsItem) => {
var option = document.createElement("option");
option.classList.add("team-select-item");
option.appendChild(
document.createTextNode(
element.project.name + " : " + teamsItem.name
)
);
option.value = teamsItem.id + ";" + element.project.id;
select.appendChild(option);
});
});
await loadOverlay();
},
(error) => {
console.log(error);
});
});
async function loadOverlay() {
document.getElementById("feature-name-container").innerHTML = "";
document.getElementById("feature-time-container").innerHTML = "";
gesKapa = 0;
VSS.require(["TFS/WorkItemTracking/RestClient"], async function loadWorkItems(
TFS_WorkItemTracking_WebApi
) {
var e = document.getElementById("teamSelect");
if (
e.options[e.selectedIndex] == undefined ||
e.options[e.selectedIndex] == null
) {
return;
}
var teamId = e.options[e.selectedIndex].value.split(";")[0];
var projectId = e.options[e.selectedIndex].value.split(";")[1];
if (teamId == undefined || teamId == null) {
return;
}
VSS.require(["TFS/Work/RestClient"], async function loadTeamIterations(
TFS_Work_WebApi
) {
TFS_Work_WebApi.getClient()
.getTeamIterations({
projectId: projectId,
teamId: teamId,
})
.then((sprints) => {
TFS_Work_WebApi.getClient()
.getTeamFieldValues({
projectId: projectId,
teamId: teamId,
})
.then((resultValues) => {
// It can happen that the Path Area is null
if (resultValues.defaultValue !== null) {
TFS_WorkItemTracking_WebApi.getClient()
.queryByWiql(
{
query:
"SELECT [System.Id] FROM WorkItems WHERE [Work Item Type] = 'Feature' AND [Board Column] = 'New' AND [Area Path] = '" +
resultValues.defaultValue +
"'",
},
projectId,
teamId
)
.then((workItemIds) => {
var ids = [];
workItemIds.workItems.forEach((workItemId) => {
ids.push(workItemId.id);
});
// It can be that no WorkItems are available
if (Array.isArray(ids) && ids.length) {
TFS_WorkItemTracking_WebApi.getClient()
.getWorkItems(
ids,
[
"System.Id",
"System.Title",
"Microsoft.VSTS.Scheduling.Effort",
],
null,
null
)
.then((workItemObject) => {
var summFeature = 0;
workItemObject.forEach((elementeNew) => {
if (
elementeNew.fields[
"Microsoft.VSTS.Scheduling.Effort"
] != undefined
) {
summFeature +=
elementeNew.fields[
"Microsoft.VSTS.Scheduling.Effort"
];
}
});
var summFeatureNode = document.getElementById(
"feature-evaluation-value"
);
summFeatureNode.innerHTML = "";
summFeatureNode.appendChild(
document.createTextNode(summFeature + "h")
);
var workItemWrapper = document.getElementById(
"feature-name-container"
);
workItemWrapper.innerHTML = "";
workItemObject.forEach((idsnew) => {
var workItemNameWrapper = document.createElement(
"div"
);
workItemNameWrapper.classList.add(
"feature-name-container-item"
);
var linkItem = document.createElement("a");
linkItem.classList.add(
"feature-name-container-link",
"animated-link"
);
linkItem.appendChild(
document.createTextNode(
idsnew.fields["System.Title"]
)
);
workItemNameWrapper.appendChild(linkItem);
workItemWrapper.appendChild(workItemNameWrapper);
});
var workItemTimeWrapper = document.getElementById(
"feature-time-container"
);
workItemTimeWrapper.innerHTML = "";
workItemObject.forEach((idsnew) => {
var workItemTimeSubContainer = document.createElement(
"div"
);
workItemTimeSubContainer.classList.add(
"feature-time-container-item"
);
if (
idsnew.fields[
"Microsoft.VSTS.Scheduling.Effort"
] != undefined
) {
workItemTimeSubContainer.appendChild(
document.createTextNode(
idsnew.fields[
"Microsoft.VSTS.Scheduling.Effort"
] + "h"
)
);
} else {
workItemTimeSubContainer.appendChild(
document.createTextNode("-")
);
}
workItemTimeWrapper.appendChild(
workItemTimeSubContainer
);
});
});
}
});
}
});
var sprintWrapper = document.getElementById("sprint-wrapper");
while (sprintWrapper.lastElementChild) {
sprintWrapper.removeChild(sprintWrapper.lastElementChild);
}
sprints.forEach((sprint) => {
// load kappa
TFS_Work_WebApi.getClient()
.getCapacities(
{
projectId: projectId,
teamId: teamId,
},
sprint.id
)
.then((result) => {
var kapa = 0;
result.forEach((teamMembers) => {
teamMembers.activities.forEach((activity) => {
kapa += activity.capacityPerDay;
});
});
kapa =
kapa *
getBusinessDatesCount(
sprint.attributes.startDate,
sprint.attributes.finishDate
);
changeGesKapa(gesKapa + kapa);
// create header
var sprintItem = document.createElement("div");
sprintItem.classList.add("kappa-sprint-item");
var sprintTitle = document.createElement("div");
sprintTitle.classList.add("kappa-sprint-title");
var sprintTitleLink = document.createElement("a");
sprintTitleLink.classList.add("kappa-sprint-title-link");
sprintTitleLink.classList.add("animated-link");
sprintTitleLink.text = sprint.name;
sprintTitleLink.href = sprint.url;
sprintTitle.appendChild(sprintTitleLink);
sprintItem.appendChild(sprintTitle);
// create sprint content
var sprintcontent = document.createElement("div");
sprintcontent.classList.add("kappa-sprint-content");
var yeStart = new Intl.DateTimeFormat("en", {
year: "numeric",
}).format(sprint.attributes.startDate);
var moStart = new Intl.DateTimeFormat("en", {
month: "short",
}).format(sprint.attributes.startDate);
var daStart = new Intl.DateTimeFormat("en", {
day: "2-digit",
}).format(sprint.attributes.startDate);
var yeEnde = new Intl.DateTimeFormat("en", {
year: "numeric",
}).format(sprint.attributes.finishDate);
var moEnde = new Intl.DateTimeFormat("en", {
month: "short",
}).format(sprint.attributes.finishDate);
var daEnde = new Intl.DateTimeFormat("en", {
day: "2-digit",
}).format(sprint.attributes.finishDate);
sprintcontent.appendChild(
document.createTextNode(
daStart +
"." +
moStart +
"." +
yeStart +
" - " +
daEnde +
"." +
moEnde +
"." +
yeEnde
)
);
sprintItem.appendChild(sprintcontent);
// create sprint evaluation
var sprintEvaluation = document.createElement("div");
sprintEvaluation.classList.add("kappa-sprint-evaluation");
sprintEvaluation.append(
document.createTextNode("Kapa: " + kapa + "h")
);
stopLoading();
sprintItem.appendChild(sprintEvaluation);
sprintWrapper.appendChild(sprintItem);
});
});
});
});
});
}
My configuration.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="sdk/scripts/VSS.SDK.min.js" type="text/javascript"></script>
<script>
VSS.init({
explicitNotifyLoaded: false,
usePlatformStyles: true,
});
VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetConfigurationStyles();
VSS.register(VSS.getContribution().id, function () {
return {
load: function (widgetSettings, widgetConfigurationContext) {
return WidgetHelpers.WidgetStatusHelper.Success();
},
};
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="container">
</div>
</body>
</html>
Can someone help me with this problem?
In main.js, you're missing the widgetSettings parameter in the load parameter, e.g.
return {
load: function (widgetSettings) {
return YourLoadFunction(widgetSettings);
},
reload: function (widgetSettings) {
return YourReloadFunction(widgetSettings);
}
}
Related
I'm using amChart with the plugin dataLoader to load data and eventData so I have something like this :
var defaultStockChartData = {
"dataLoader": {
"url": "urlToMyFile.csv",
"format": "csv",
"delimiter": ",",
"useColumnNames": true
},
/**
* data loader for events data
*/
"eventDataLoader": {
"url": urlToMyFile.scv,
"async": true,
"postProcess": function(data) {
for (var x in data) {
var color = "#85CDE6";
switch (data[x].Type) {
case 'A':
color = "#85CDE6";
break;
case 'B':
color = "#85C56E6";
break;
default:
color = "#cccccc";
break;
}
data[x] = {
"type": "pin",
"graph": "g1",
"backgroundColor": color,
"date": data[x].Date,
"text": data[x].Type,
"description": data[x].Description
};
}
return data;
}
}
...
}
Now what I need to do is a checkbox that shows all the event on the graph with the type 'A' when it's checked and hide all the event with the type 'A' when it's unchecked.
How I can I access the events of my chart and hide them follow their type?
A better way to do this is to modify the event data directly and toggle each element's graph property to null or the original graph to show/hide them so you don't have to hack around the charts' bullet handling on zoom and other events. For example:
HTML:
<div>
<label>Hide Event A <input type="checkbox" class="hide-event" data-event="A"></label>
<label>Hide Event B <input type="checkbox" class="hide-event" data-event="B"></label>
</div>
JS:
//show/hide events based on selected checkbox
Array.prototype.forEach.call(
document.querySelectorAll('.hide-event'),
function(checkbox) {
checkbox.addEventListener('change', function() {
var event = checkbox.dataset.event;
chart.dataSets[0].stockEvents.forEach(function(eventItem) {
if (eventItem.text === event) {
if (checkbox.checked) {
//copy graph reference to a dummy value and null out the original graph
eventItem.oldGraph = eventItem.graph;
eventItem.graph = null;
}
else {
//restore original graph and null out copy/dummy reference
eventItem.graph = eventItem.oldGraph;
eventItem.oldGraph = null;
}
}
});
chart.validateData(); //redraw the chart
});
}
);
Full demo below:
var chartData = [];
var eventData = [];
generateChartData();
//show/hide events based on selected checkbox
Array.prototype.forEach.call(
document.querySelectorAll('.hide-event'),
function(checkbox) {
checkbox.addEventListener('change', function() {
var event = checkbox.dataset.event;
chart.dataSets[0].stockEvents.forEach(function(eventItem) {
if (eventItem.text === event) {
if (checkbox.checked) {
//copy graph reference to a dummy value and null out the original graph
eventItem.oldGraph = eventItem.graph;
eventItem.graph = null;
}
else {
//restore original graph and null out copy/dummy reference
eventItem.graph = eventItem.oldGraph;
eventItem.oldGraph = null;
}
}
});
chart.validateData(); //redraw the chart
});
}
);
function generateChartData() {
var firstDate = new Date( 2012, 0, 1 );
firstDate.setDate( firstDate.getDate() - 500 );
firstDate.setHours( 0, 0, 0, 0 );
for ( var i = 0; i < 500; i++ ) {
var newDate = new Date( firstDate );
newDate.setDate( newDate.getDate() + i );
var a = Math.round( Math.random() * ( 40 + i ) ) + 100 + i;
var b = Math.round( Math.random() * 100000000 );
chartData.push( {
"date": newDate,
"value": a,
"volume": b
} );
if ((i + 1) % 8 === 0) {
eventData.push({
"date": newDate,
"type": "sign",
"backgroundColor": "#85CDE6",
"graph": "g1",
"text": (i + 1) % 5 == 0 ? "B" : "A",
"description": "Event " + ((i + 1) % 5 == 0 ? "B" : "A") + " at index " + i
})
}
}
}
var chart = AmCharts.makeChart( "chartdiv", {
"type": "stock",
"theme": "light",
"dataSets": [ {
"color": "#b0de09",
"fieldMappings": [ {
"fromField": "value",
"toField": "value"
}, {
"fromField": "volume",
"toField": "volume"
} ],
"dataProvider": chartData,
"categoryField": "date",
// EVENTS
"stockEvents": eventData
} ],
"panels": [ {
"title": "Value",
"stockGraphs": [ {
"id": "g1",
"valueField": "value"
} ],
"stockLegend": {
"valueTextRegular": " ",
"markerType": "none"
}
} ],
"chartScrollbarSettings": {
"graph": "g1"
},
"chartCursorSettings": {
"valueBalloonsEnabled": true,
"graphBulletSize": 1,
"valueLineBalloonEnabled": true,
"valueLineEnabled": true,
"valueLineAlpha": 0.5
}
} );
#chartdiv {
width: 100%;
height: 500px;
}
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/amstock.js"></script>
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<div>
<label>Hide Event A <input type="checkbox" class="hide-event" data-event="A"></label>
<label>Hide Event B <input type="checkbox" class="hide-event" data-event="B"></label>
</div>
<div id="chartdiv"></div>
I didn't find a way to do it with AmChart so I did it in javascript (not by type but just need to check the value)
First I need this function :
function hideShowGraphEvent()
{
if($("#chxEventA").prop("checked"))
{
$("g.amcharts-graph-bullet").show();
}
else
{
$("g.amcharts-graph-bullet").hide();
}
}
And call it each time you check or uncheck the box :
$("#chxEventA").change(function()
{
hideShowGraphEvent();
});
But if you use zoom (like me) it will stop working when you zoom so you need to call the function each time you zoom :
"listeners" : [
{
"event": "zoomed",
"method": function()
{
hideShowGraphEvent();
}
},
...
]
I am trying to sort a JSON array from JSON file a.json.
The array is coming like this:
{"data":
[
{"exception":"",
"name":"Data Server1",
"alias":"TOR-Server",
"delayed":"NO",
},
{"exception":"",
"name":"Data Server2",
"alias":"FRA-Server",
"delayed":"NO",
}
]
I need to sort the data by the "alias name", coming from the JSON file with Vue JS.
This is my Javascript Code for the display in Vue JS
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
json: null
},
computed: {
sorted: function() {
setTimeout(function() {
var app = this.app.json
if (app.length >= 6)
return app;
}, 5000);
}
},
methods: {
toggleClass(){
if (this.failed == true){
this.failed = false;
}
else
{
this.failed = true;
}
}
}
But the Sorted function is not working and if I try to display the servers in the sorted array, I receive a blank page.
And my for loop in the HTML page is:
<div class="mainbutton" v-for="(server, index) in json.data ">
Hope this makes sense and I could get it working.
You can use Array.sort inside a computed, to return the sorted array.
For example, to sort the array's items by name property, you can call: this.json.sort((t1,t2) => t1.name < t2.name ? -1 : 1).
Here's a working snippet:
var app = new Vue({
el: '#app',
data: {
json: [
{
"exception": "",
"name": "Data Server3",
"alias": "TOR-Server",
"delayed": "NO",
},
{
"exception": "",
"name": "Data Server1",
"alias": "TOR-Server",
"delayed": "NO",
},
{
"exception": "",
"name": "Data Server2",
"alias": "FRA-Server",
"delayed": "NO",
}
]
},
computed: {
sortedJson: function() {
return this.json.sort((t1,t2) => t1.name < t2.name ? -1 : 1);
},
sorted: function() {
setTimeout(function() {
var app = this.app.json
if (app.length >= 6)
return app;
}, 5000);
}
},
methods: {
toggleClass() {
if (this.failed == true) {
this.failed = false;
} else {
this.failed = true;
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<div class="mainbutton" v-for="(server, index) in sortedJson ">
{{server.name }}
</div>
</div>
I am trying to show a popup as explained and i am not able to add one. Here is the code HTML followed by JS.
I have added a button and on click of the button i am trying to show popup. I was able to show a popup without the webix.ready() function, but when i try to add that my code breaks. Any help regarding this would be of real help.
HTML CODE:
<body>
<div class="loader-holder" id="loader-holder">
<div class="loader-container">
<p>Loading......</p>
</div>
</div>
</body>
JS CODE:
var form = {
view:"form",
borderless:true,
elements: [
{ view:"text", label:'Login', name:"login" },
{ view:"text", label:'Email', name:"email" },
{ view:"button", value: "Submit", click:function(){
if (this.getParentView().validate()){ //validate form
webix.message("All is correct");
this.getTopParentView().hide(); //hide window
}
else
webix.message({ type:"error", text:"Form data is invalid" });
}}
],
rules:{
"email":webix.rules.isEmail,
"login":webix.rules.isNotEmpty
},
elementsConfig:{
labelPosition:"top",
}
};
function showForm(winId, node){
console.log(node);
$$(winId).getBody().clear();
$$(winId).show(node);
$$(winId).getBody().focus();
}
var popup = {view:"popup",id:"win1",width:300,head:false,body:webix.copy(form)};
function Start(){
var tbl = {
}
var cfg = {
container:"listC",
width:1500,
height:600,
rows:[
{
height: 35,
view:"toolbar",
elements:[
{view:"text", id:"grouplist_input",label:"Filter",css:"fltr", labelWidth:170}
]
},
{ view:"button",value: 'Click to show a popup with a form',click:function(){ showForm("win1", popup)}},
]
}
/////////////////////////////////
var bodycontent =
{
id:"tb", autoheight:true,autowidth:true,
view:"tabview",
cells:[
{
header:"LIVE",
body: tbl
},
{
header:"REMOTE WATCH",
body: { id:"remote_watch", template:"some_text"}
},
{
header:"CONFIG",
body:cfg//{ id:"config", template:"CONFIG is In developement........"}
} ]
}
var label = { view:"label", label:'some_text ',height:25, align:"left",css:"my_style"}
webix.ready(function ()
{
var el = document.getElementById('loader-holder');
el.parentNode.removeChild(el);
webix.i18n.parseFormatDate = webix.Date.strToDate("%m/%d/%Y");
webix.ui(
{
view: "layout",
id: "dashboard",
rows:
[
label,
bodycontent
]
})
});
}
You can use popup functionality from
Bootstrap. It has good documentation.
You need to initialize popup before using it. You can do it wherever you want to (ex. webix.ready(), "showForm" function)
var form = {
view:"form",
borderless:true,
elements: [
{ view:"text", label:'Login', name:"login" },
{ view:"text", label:'Email', name:"email" },
{ view:"button", value: "Submit", click:function(){
if (this.getParentView().validate()){ //validate form
webix.message("All is correct");
this.getTopParentView().hide(); //hide window
}
else
webix.message({ type:"error", text:"Form data is invalid" });
}}
],
rules:{
"email":webix.rules.isEmail,
"login":webix.rules.isNotEmpty
},
elementsConfig:{
labelPosition:"top",
}
};
function showForm(winId, node){
webix.ui(popup);
$$(winId).getBody().clear();
$$(winId).show(node);
$$(winId).getBody().focus();
}
var popup = {view:"popup",id:"win1",width:300,head:false,body:webix.copy(form)};
function Start(){
var tbl = {
}
var cfg = {
container:"listC",
width:1500,
height:600,
rows:[
{
height: 35,
view:"toolbar",
elements:[
{view:"text", id:"grouplist_input",label:"Filter",css:"fltr", labelWidth:170}
]
},
{ view:"button",value: 'Click to show a popup with a form',click:function(){ showForm("win1", this.$view)}},
]
}
/////////////////////////////////
var bodycontent =
{
id:"tb", autoheight:true,autowidth:true,
view:"tabview",
cells:[
{
header:"LIVE",
body: tbl
},
{
header:"REMOTE WATCH",
body: { id:"remote_watch", template:"some_text"}
},
{
header:"CONFIG",
body:cfg//{ id:"config", template:"CONFIG is In developement........"}
} ]
}
var label = { view:"label", label:'some_text ',height:25, align:"left",css:"my_style"}
webix.ready(function ()
{
var el = document.getElementById('loader-holder');
el.parentNode.removeChild(el);
webix.i18n.parseFormatDate = webix.Date.strToDate("%m/%d/%Y");
webix.ui(
{
view: "layout",
id: "dashboard",
rows:
[
label,
bodycontent
]
})
});
}
Start();
https://webix.com/snippet/459c4dd6
I'm trying to make a quiz app using JavaScript and jQuery, if I click on a radio button am setting an attribute as checked but if I move on to another question and click on any radio button there in the previous question radio button selects get removed how can I make sure that it is selected even if I move on to any question and select options there. I guess its happening because I have common name on the radio button Can someone help me out?
$(document).ready(function() {
$(function() {
var data = [{
"id": 1,
"question": "Which framework is best?",
"options": ["ReactJs", "AngularJs", "Vue.js", "Backbone"],
"correctanswer": "ReactJs",
"selectedanswer": ""
},
{
"id": 2,
"question": "What does MVC stands for?",
"options": ["Model View Controller", "Model view view Controller", "Model Controller view", "None of the above"],
"correctanswer": "Model View Controller",
"selectedanswer": ""
},
{
"id": 3,
"question": "Which is the best MOOC course website?",
"options": ["Coursera", "EDX", "Udacity", "Code School"],
"correctanswer": "Udacity",
"selectedanswer": ""
},
{
"id": 4,
"question": "which backend framework is not available in any of the MOOC site?",
"options": ["Ruby on Rails", "Node JS", "Metor Js", "Django"],
"correctanswer": "Django",
"selectedanswer": ""
},
{
"id": 5,
"question": "Which frontend framework is not available?",
"options": ["AngularJs", "ReactJs", "Backbone", "Knockout"],
"correctanswer": "ReactJs",
"selectedanswer": ""
}
];
var constructordata = function(data) {
this.id = data.id,
this.question = data.question,
this.options = data.options,
this.correctanswer = data.correctanswer,
this.selectedanswer = data.selectedanswer
};
var datas = [];
data.forEach(function(item) {
datas.push(new constructordata(item));
});
var controller = {
nextquestion: function() {
var currentelement = $('#app .questionsection.visible');
var nextelement = currentelement.next();
if (nextelement.hasClass('questionsection')) {
currentelement.removeClass('visible');
nextelement.addClass('visible');
}
if (!nextelement.next().hasClass('questionsection')) {
$("button").addClass('visible');
}
},
previousquestion: function() {
var currentelement = $('#app .questionsection.visible');
var previouselement = currentelement.prev();
if (previouselement.hasClass('questionsection')) {
currentelement.removeClass('visible');
previouselement.addClass('visible');
}
},
selectanswers: function(temp) {
for (var i = 0; i < datas.length; i++) {
if (datas[i].id == temp) {
datas[i].selectedanswer = $('input[name=optradio]:checked').val();
}
}
},
checkanswers: function() {
var score = 0;
datas.forEach(function(item) {
if (item.selectedanswer == item.correctanswer) {
score++;
}
});
console.log(score);
},
init: function() {
view.init();
}
};
var view = {
init: function() {
this.maintemplate = $("#app");
this.nextarrow = $("#next");
this.submit = $("button");
this.submit.on('click', function() {
controller.checkanswers();
})
this.nextarrow.on('click', function() {
controller.nextquestion();
});
this.previousarrow = $("#previous");
this.previousarrow.on('click', function() {
controller.previousquestion();
});
this.render();
},
render: function() {
var maintemplate = this.maintemplate;
for (var i = 0; i < datas.length; i++) {
maintemplate.append("<div class='questionsection'><p>" + datas[i].question + "</p></div>");
for (var j = 0; j < datas[i].options.length; j++) {
var options = "<div class='radio'><label><input type='radio' name='optradio' questionno=" + datas[i].id + " value=" + datas[i].options[j] + ">" + datas[i].options[j] + "</label></div>"
maintemplate.children('.questionsection').last().append(options);
}
}
maintemplate.children().first().addClass('visible');
var radio = $("input[name=optradio]");
radio.on('click', function() {
var temp = $(this).attr("questionno");
controller.selectanswers(temp);
$(this).attr("checked", "checked");
});
}
};
controller.init();
});
});
.questionsection {
display: none;
}
.btn {
display: none;
}
.visible {
display: block;
}
i.fa {
font-size: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-2">
<i id="previous" class="fa fa-arrow-left" aria-hidden="true"></i>
</div>
<div id="app" class="col-md-8">
</div>
<div class="col-md-2">
<i id="next" class="fa fa-arrow-right" aria-hidden="true"></i>
</div>
</div>
<button class="btn">Submit</button>
</div>
CodePen http://codepen.io/kannant14/pen/peLOXG
One way to solve it is to name your radiobuttons in the render function with a question index like:
var options="<div class='radio'><label><input type='radio' name='optradio"+i+"' questionno="+datas[i].id+" value="+datas[i].options[j]+">"+datas[i].options[j]+"</label></div>"
and the same in the selectanswers function:
selectanswers:function(temp) {
for(var i=0;i<datas.length;i++) {
if(datas[i].id==temp){
datas[i].selectedanswer=$('input[name=optradio'+i+']:checked').val();
}
}
}
then you just need to keep track of the question index when checking the correct answers.
I have taken about 6 runs at this and while I can get many PARTS of it working, I can't seem to get everything functioning like I want it to. This is the latest attempt. This was originally written with just JavaScript but as an exercise I am trying to change it to AngularJS.
Template normal.html:
<section id="container" class="container panels-backface-invisible" >
<div id="carousel" class="panels-backface-invisible 3rows">
<figure ng-repeat="movie in movies.data | filterMultiple:{genres:genList,wildcard:wcSearch}" class="folder figure"
style="transform: {{carousel.rotateFn}}({{carousel.theta*$index}}deg) translateZ({{carousel.radius}}px);" >
</figure></div>
</section>
<div class="searchbox"><label>Wildcard Title Search</label><input type="text" style="width: 100px;" ng-model="wcSearch">
</div>
<div style="display: inline-block;
position: absolute;
right: 8%;
bottom: 1px;
width: 15%;"
multi-select
input-model="genreList"
button-label="name"
item-label="icon"
tick-property="ticked"
selection-mode="multiple"
helper-elements=""
on-open="msOpen( data )"
on-close="msClose( data )"
on-item-click="msClick( data )"
>
</div>
app.js:
angular.module('MovieCarousel', ['ui.router','multi-select'])
.config(function config($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/');
$stateProvider.state("index",{
url:"",
controller:"MovieFlowCtrl",
templateUrl:"templates/normal.html",
resolve:{
movieData: function($http){
var promise = $http.get('moviedata.php').
success(function(data) {
var mData={};
mData.count = data.length*2;
mData.data = data;
return mData;
});
return promise;
}
}
})
$stateProvider.state("new",{
url:"/New",
controller:"MovieFlowCtrl",
templateUrl:"templates/New.html",
resolve:{
movieData: function($http){
var promise = $http.get('moviedata.php?type=N').
success(function(data) {
var mData={};
mData.count = data.length*2;
mData.data = data;
return mData;
});
return promise;
}
}
})
})
.controller('MovieFlowCtrl', function ($scope, $rootScope, movieData, $filter) {
$scope.carousel = {};
$scope.carousel.isHorizontal = true;
$scope.movies = movieData;
$scope.carousel.element = document.getElementById('carousel');
setTimeout( function(){
document.body.addClassName('ready');
}, 0);
$scope.clearSelection=function() {
$rootScope.genrefilter = [];
$('#selectable .ui-selected').removeClass('ui-selected');
}
$rootScope.genrefilter = [];
$scope.msClick = function(Data) {
console.log(Data);
var selectedind = $rootScope.genrefilter.indexOf(Data.name);
if(selectedind>=0){
$rootScope.genrefilter.splice(selectedind,1);
}
else{
$rootScope.genrefilter.push(Data.name);
}
$scope.carousel.filter();
// $filter('filterMultiple');
}
$rootScope.genreList = [
{ icon: "<img src=/img/Action.png />", name: "Action", ticked: false },
{ icon: "<img src=/img/Adventure.png>", name: "Adventure", ticked: false },
{ icon: "<img src=/img/Animation.png>", name: "Animation", ticked: false },
{ icon: "<img src=/img/Biography.png />", name: "Biography", ticked: false },
{ icon: "<img src=/img/Comedy.png />", name: "Comedy", ticked: false },
{ icon: "<img src=/img/Comic-Book.png />", name: "Comic-Book", ticked: false },
{ icon: "<img src=/img/Crime.png>", name: "Crime", ticked: false },
{ icon: "<img src=/img/Disaster.png />", name: "Disaster", ticked: false },
{ icon: "<img src=/img/Drama.png />", name: "Drama", ticked: false },
{ icon: "<img src=/img/Fantasy.png />", name: "Fantasy", ticked: false },
{ icon: "<img src=/img/History.png>", name: "History", ticked: false },
{ icon: "<img src=/img/Horror.png>", name: "Horror", ticked: false },
{ icon: "<img src=/img/Music.png />", name: "Music", ticked: false },
{ icon: "<img src=/img/Musical.png />", name: "Musical", ticked: false },
{ icon: "<img src=/img/Mystery.png />", name: "Mystery", ticked: false },
{ icon: "<img src=/img/Romance.png>", name: "Romance", ticked: false },
{ icon: "<img src=/img/Sci-Fi.png />", name: "Sci-Fi", ticked: false },
{ icon: "<img src=/img/Sport.png />", name: "Sport", ticked: false },
{ icon: "<img src=/img/Thriller.png />", name: "Thriller", ticked: false },
{ icon: "<img src=/img/War.png>", name: "War", ticked: false },
{ icon: "<img src=/img/Western.png />", name: "Western", ticked: false }
];
$scope.msOpen = function( data ) {
console.log("open");
draglock = 1;
}
$scope.msClose = function( data ) {
console.log("closed");
draglock = 0; }
$rootScope.modify = function(){
if(wheight>=930){
sz1=300;
sz2=2.4;
}
else if(wheight>700 && wheight<929) {
sz1=283;
sz2= 3;
}
else if(wheight>500 && wheight<699) {
sz1=221;
sz2= 3.8;
}
else if(wheight>400 && wheight<499) {
sz1=157;
sz2=5.2;
}
else{
sz1=125;
sz2=6.6;
}
console.log("Modify");
// sz1 = $(window).width()*0.19;
panelCount = $rootScope.panelCount;
$scope.carousel.sz1 = sz1;
$scope.carousel.panelSize = sz1;
// if(typeof $scope.carousel.element.children[0] != 'undefined'){$scope.carousel.panelSize = $scope.carousel.element.children[0].clientWidth;}
$scope.carousel.rotateFn = $scope.carousel.isHorizontal ? 'rotateY' : 'rotateX';
// console.log(panelCount);
$scope.carousel.theta = 359 / ((panelCount+mrows));
theta = $scope.carousel.theta;
rotateXY = $scope.carousel.rotateFn;
var sizeadj = 12;
if(mrows == 1){sizeadj = 0.8;}
if(mrows == 2){sizeadj = sz2;}
$scope.carousel.radius = ($scope.carousel.panelSize*(mrows+panelCount/mrows))/(2*Math.PI); //Math.round(( $scope.carousel.panelSize/sizeadj) / Math.tan( Math.PI / (($scope.carousel.panelCount+mrows)) ) );
radius = $scope.carousel.radius;
$scope.carousel.rotation = Math.round( $scope.carousel.rotation / $scope.carousel.theta ) * $scope.carousel.theta;
rotation = $scope.carousel.rotation;
$scope.carousel.rotation = -4;
$scope.carousel.element.style[ transformProp ] = 'translateZ(-' + radius + 'px) ' + rotateXY + '(' + rotation + 'deg)';
// if(typeof $scope.carousel.element.children[0] != 'undefined'){$scope.carousel.sz1 = $scope.carousel.element.children[0].clientWidth;}
}
$scope.doubleClick = function(movieName){
console.log("test");
jQuery.post("/processrequest.php",
{name: movieName },
function(response){
if(response.length>5){alert(response);}
});
}
$rootScope.toggleCarousel = function(toMod){
panelCount = $rootScope.panelCount;
console.log("toggleCarousel");
if(toMod){return;}
if(panelCount > 20){
$('#container').addClass('container');
$('figure').addClass('figure');
filtered = 0;
$rootScope.modify(panelCount);
}
else{
$('figure').attr('style', '');
$('#carousel').attr('style', 'transform: translate(0px,0px)');
$('#container').removeClass('container');
$('figure').removeClass('figure');
filtered = 1;
}
$scope.carousel.element.style[ transformProp ] = 'translateZ(-' + radius + 'px) ' + rotateXY + '(' + rotation + 'deg)';
}
})
.directive('myDraggable', ['$document', function($document) {
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0;
element.css({
position: 'relative',
border: '1px solid red',
backgroundColor: 'lightgrey',
cursor: 'pointer'
});
element.on('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.pageX - x;
startY = event.pageY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
});
function mousemove(event) {
y = event.pageY - startY;
x = event.pageX - startX;
element.css({
top: y + 'px',
left: x + 'px'
});
}
function mouseup() {
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
};
}])
.filter('filterMultiple',["$rootScope", '$filter', function ($rootScope,$filter) {
return function (items, keyObj) {
var wcSearch = keyObj.wildcard;
var genres = keyObj.genres;
var filterObj=[];
for(var i=0;i<items.length;i++){
var filtered = true;
if(typeof wcSearch === 'undefined'){}
else if(wcSearch.length > 0){
if(wcSearch.toUpperCase().substring(0,wcSearch.length) != items[i].title.toUpperCase().substring(0,wcSearch.length) && wcSearch.toUpperCase().substring(0,wcSearch.length) != items[i].sortTitle.toUpperCase().substring(0,wcSearch.length)){filtered = false;}
}
if(filtered == true){
for(var x=0;x<$rootScope.genrefilter.length;x++){
if(items[i].genres.indexOf($rootScope.genrefilter[x])<0){filtered = false;}
}
}
if(filtered == true){
filterObj.push(items[i]);
}
}
*******Disabled due to infinite loop
// var ticked=[];
// var genreObj={};
// var $tempGenre=[];
// $tempGenre.genreList = $rootScope.genreList;
// for (var i=0;i<$tempGenre.genreList.length;i++){
// ticked[$tempGenre.genreList[i].name]=$tempGenre.genreList[i].ticked;
// }
// $tempGenre.genreList=[];
// if(typeof filterObj !== 'undefined'){
// for (var i=0;i<filterObj.length;i++){
// for (var z=0;z<filterObj[i].genres.length;z++){
// if($tempGenre.genreList.map(function(e) { return e.name; }).indexOf(filterObj[i].genres[z])==-1)
// {
// if(filterObj[i].genres[z] !=""){
// genreObj = {icon: "<img src=/img/"+filterObj[i].genres[z]+".png />", name: filterObj[i].genres[z], ticked: ticked[filterObj[i].genres[z]] };
// $tempGenre.genreList.push(genreObj);
// }
// }
// }
// }
// }
// $tempGenre.genreList.sort();
var toModify=false;
if($rootScope.panelCount == filterObj.length){toModify=true;}
console.log($rootScope.panelCount+","+filterObj.length);
$rootScope.panelCount = filterObj.length;
$rootScope.toggleCarousel(toModify);
$rootScope.genreList = $tempGenre;
return filterObj;
}
}])
.filter('unique', function() {
return function(input, key) {
var unique = {};
var uniqueList = [];
for(var i = 0; i < input.length; i++){
if(typeof unique[input[i][key]] == "undefined"){
unique[input[i][key]] = "";
uniqueList.push(input[i]);
}
}
return uniqueList;
};
})
.directive('resize', function($window) {
return function (scope, element, attr) {
var w = angular.element($window);
scope.$watch(function () {
return {
'h': w.height(),
'w': w.width()
};
}, function (newValue, oldValue) {
scope.windowHeight = newValue.h;
scope.windowWidth = newValue.w;
scope.resizeWithOffset = function (offsetH) {
scope.$eval(attr.notifier);
return {
'height': (newValue.h - offsetH) + 'px'
};
};
}, true);
w.bind('resize', function () {
scope.$apply();
});
}
})
Obviously I'm very new to AngularJS and am likely doing everything wrong. I have searched for similar solutions to learn from but can't find anything that is doing quite what I'm working on. The main issue I am having at this point is if I include the code that limits genres to only those available in the sub-set of movie results it loops infinitely, and no matter which angle I take I can't seem to get it all to work together. Some guidance would be greatly appreciated, even if its just to recommend a better structure, I'm sure I'm putting too much into the controller among other fails.
Other unfinished portions:
- views for each type of content
- re-size elements on window size
- Drag and scroll behavior