I am trying to show/hide on click of some image, but the formatting of the tr is going terribly wrong. Am I doing something wrong?? Attached is the result. I have also tried adding custom formatting but wont worked either.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:div style="position:relative;">
<xp:table id="table1">
<xp:tr style="width:100%">
<xp:td>a</xp:td>
<xp:td>b</xp:td>
<xp:td>c</xp:td>
<xp:td>d</xp:td>
<xp:td>e</xp:td>
</xp:tr>
<xp:tr id="tr0" style="width:100%">
<xp:td><xp:comboBox id="Vnr">
</xp:comboBox></xp:td>
<xp:td><xp:comboBox id="comboBox1"></xp:comboBox></xp:td>
<xp:td id="td1"><xp:image url="./add.png" id="image23">
<xp:eventHandler event="onclick" submit="false"
id="eventHandler3">
<xp:this.script><![CDATA[var tr0 = document.getElementById("#{id:tr1}");
tr0.style.display = "block";
]]></xp:this.script>
</xp:eventHandler>
</xp:image>
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[var tr0 = document.getElementById("#{id:tr1}");
tr0.style.display = "block";]]></xp:this.script>
</xp:eventHandler></xp:td>
<xp:td></xp:td>
<xp:td></xp:td>
</xp:tr>
<xp:tr id='tr1' style="width:100%;display:none;">
<xp:td>
<xp:comboBox id="comboBox2"></xp:comboBox>
</xp:td>
<xp:td>
<xp:comboBox id="comboBox3" style=""></xp:comboBox>
</xp:td>
<xp:td>
<xp:image url="./add.png" id="image1">
<xp:eventHandler event="onclick" submit="false"
id="eventHandler1">
<xp:this.script><![CDATA[var tr0 = document.getElementById("#{id:tr2}");
tr0.style.display = "block";
]]></xp:this.script>
</xp:eventHandler>
</xp:image>
</xp:td>
<xp:td></xp:td>
<xp:td></xp:td>
</xp:tr>
<xp:tr id="tr2" style="display:none;">
<xp:td>
<xp:comboBox id="comboBox4"></xp:comboBox>
</xp:td>
<xp:td>
<xp:comboBox id="comboBox5"></xp:comboBox>
</xp:td>
<xp:td>
<xp:image url="./add.png" id="image2">
</xp:image>
</xp:td>
<xp:td></xp:td>
<xp:td></xp:td>
</xp:tr></xp:table></xp:div></xp:view>
Just delete/empty the display style class with
var tr = document.getElementById("#{id:tr1}");
tr.style.display = "";
Then you get the default behavior: a table row in this case.
Related
I just can't find any examples that can help me. I have a drop-down list of customers and a table which displays services/products a customer provides. On page load the ddl is set to customer 0 and that customer's data is displayed in the table. I now need to changed the data-source and refresh the table when the drop-down is changed.
The main bits of my code are...
<div class="card-body">
<div class="row">
<div class="col-4">
<lable for="customerFilter" class="control-label">Customer Filter: </lable>
</div>
<div class="col-8">
<input id="customerFilter" class="form-control" />
</div>
</div>
<div id="toolbar">
<div class="alert alert-info">You can refine this list by entering an additional filter in the search box on the right. Any text you type will filter the list based on any of the fields containing the text typed.</div>
</div>
<div>
<table id="table"
data-classes="table table-hover table-condensed"
data-striped="true"
data-toolbar="#toolbar"
data-pagination="true"
data-click-to-select="true"
data-search="true"
data-show-export="true"
data-show-pagination-switch="true"
data-show-toggle="true"
data-show-columns="true"
data-url='#Url.Content("~/SSTCodes/GetSSTCodesByCustomer?CustomerID=0")'>
<thead>
<tr>
<th data-field="sstID" data-formatter="btnViewFormatter" data-sortable="true">ID</th>
<th data-field="sstRU" data-sortable="true" data-formatter="sstFormatter">Recoverability</th>
<th data-field="sstProductCode" data-sortable="true">Product Code</th>
<th data-field="sstProductName" data-sortable="true">Product Name</th>
<th data-field="sstStockLevel" data-formatter="lowStockFormatter">Stock Level</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<script type="text/javascript">
cData = #Html.Raw(ViewData("Customers"));
$("#customerFilter").kendoComboBox({
dataTextField: "Text",
dataValueField: "Value",
change: customerFilterChanged,
dataSource: cData,
filter: 'contains'
});
function customerFilterChanged() {
// NEED TO CHANGE THE DATASOURCE Url
var customer = this.value()
var url = '#Url.Content("~/SSTCodes/GetSSTCodesByCustomer?CustomerID=")' + customer;
// NEED TO SET THIS AS THE TABLES data-url AND REFRESH THE DATASOURCE....
}
var $table = $('#table');
function btnViewFormatter(value) {
return '<a class="btn btn-primary btn-sm" href="#Url.Content("~/SSTCodes/Edit?id=")' + value + '">' + value + '</a>';
}
function sstFormatter(value) {
//Removed for clarity
Return value
}
function lowStockFormatter(value) {
//Removed for clarity
Return value
}
function getSelectedRow() {
var index = $table.find('tr.success').data('index');
return $table.bootstrapTable('getData')[index];
}
$(function () {
$table.bootstrapTable({
fixedColumns: true,
fixedNumber: 1,
exportDataType:"all",
exportTypes:['csv', 'txt', 'xlsx']
});
$table.on('click-row.bs.table', function (e, row, $element) {
$('.success').removeClass('success');
$($element).addClass('success');
});
$table.on('dbl-click-row.bs.table', function (e, row, $element) {
var url = '#Url.Content("~/SSTCodes/Edit?id=")' + getSelectedRow().sstID;
window.open(url, '_self');
})
});
</script>
The function customerFilterChanged is where I need help.
I have changed my approach...
The customerFilterChange function now just redirects the page
function customerFilterChanged() {
window.location.href = "#Url.Content("~/SSTCodes/Index?id=")" + this.value();
}
The controller has been amended so that it has an optional id...
Function Index(Optional id As Integer = 0) As ActionResult
If IsNothing(id) Then
ViewData("CustomerID") = 0
Else
ViewData("CustomerID") = id
End If
ViewData("Customers") = Newtonsoft.Json.JsonConvert.SerializeObject(CustomerModel.CustomerDDLList.GetAllCustomers())
Return View()
End Function
And the data-url value for the table is now
data-url='#Url.Content("~/SSTCodes/GetSSTCodesByCustomer?CustomerID=") + #ViewData("CustomerID")' >
Job done...
I cannot explain further on the title so kindly base on the links below.
I am using DataGrid (not GridView), in ASP.net using VB code.
This is my current display in my datagrid
click here to view picture1
, and i want to make it like this clck here to view picture2
My purpose is to split my datagrid into multiple datagrid if i have multiple columns.
I need to split them so that I can print it Google Chrome nicely. Because the print preview in picture 1 using javascript's window.print is that it auto-fits my multicolumn in one dataGrid, thus, if i have, let's say, 20columns of data, the spacing of my columns will be small and it will not be readable to users. like this click here to view picture3
if you have suggestions on how to print my datagrid in browser, ill be thankful,
TIA.
<asp:DataGrid Visible ="true" OnItemDataBound="Item_Bound" ID="dgSheet"
runat="server" BackColor="White" BorderColor="black" BorderStyle="None"
BorderWidth="1px" CellSpacing="0" CellPadding="0" Width="100%" PageSize="5"
CssClass="Narrow" ForeColor="Black">
<EditItemStyle BackColor="#999999" />
<FooterStyle BackColor="#2980b9" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#2980b9" ForeColor="White" HorizontalAlign="Center" />
<ItemStyle Font-Size="12px" Width="200" ForeColor="#333333" />
<HeaderStyle Font-Bold="true" Font-Size="12px" Width="200" ForeColor="black" />
<SelectedItemStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
</asp:DataGrid>
My partial code behind for displaying my data in datagrid:
Dim o_Row As DataRow
Dim o_AdmDates As New Collection()
Dim s_LastAdmDate As String = ""
Dim s_AdmDate As String = ""
Dim o_DerivedTable As New DataTable()
With o_DerivedTable
.Columns.Add("TransDate")
.Columns.Add("Medication")
.Columns.Add("Dosage")
.Columns.Add("TransNum")
.Columns.Add("AdministeredDate")
.Columns.Add("newAdmed")
End With
'Sort by administered dates
Dim o_FoundRows As DataRow() = o_Dataset.Tables(0).Select("", "AdministeredDate Desc")
'Extract distinct administered dates
For Each o_Row In o_FoundRows
s_AdmDate = Format(CDate(o_Row.Item("AdministeredDate")), KC_Date_Format2)
If s_LastAdmDate <> s_AdmDate Then
s_LastAdmDate = s_AdmDate
o_AdmDates.Add(s_LastAdmDate)
End If
Next
'Add administred date to derived table
Dim o_Item As String
For Each o_Item In o_AdmDates
o_DerivedTable.Columns.Add(o_Item)
Next
'Loop through the administred date
Dim o_NewRow As DataRow
Dim o_NextRow As DataRow
Dim i_Ctr As Integer
Dim x_isNewRow As Boolean = True
Dim i_MaxRec As Integer
i_MaxRec = o_Dataset.Tables(0).Rows.Count - 1
For i_Ctr = 0 To i_MaxRec
o_Row = o_Dataset.Tables(0).Rows(i_Ctr)
If i_Ctr <> i_MaxRec Then
o_NextRow = o_Dataset.Tables(0).Rows(i_Ctr + 1)
End If
If x_isNewRow Then
o_NewRow = o_DerivedTable.NewRow()
End If
o_NewRow("TransDate") = o_Row("TransDate")
o_NewRow("Medication") = o_Row("Medication")
o_NewRow("Dosage") = o_Row("Dosage")
o_NewRow("TransNum") = o_Row("TransNum")
o_NewRow("AdministeredDate") = Format(CDate(o_Row("AdministeredDate")), KC_Date_Format2)
o_NewRow("newAdmed") = o_Row("newAdmed")
'Fill approriate result date column based on query
For Each o_Item In o_AdmDates
s_AdmDate = Format(CDate(o_Row("AdministeredDate")), KC_Date_Format2)
Dim AdmTim As DateTime = DateTime.Parse(o_Row("AdministeredDate"))
If s_AdmDate = o_Item Then
o_NewRow(s_AdmDate) = AdmTim.ToString("hh:mm tt") + " - " + o_Row("UserID")
End If
Next
If i_Ctr < i_MaxRec _
AndAlso Not o_NextRow Is Nothing _
AndAlso o_Row("TransDate") = o_NextRow("TransDate") _
AndAlso o_Row("Medication") = o_NextRow("Medication") _
AndAlso o_Row("AdministeredDate") = o_NextRow("AdministeredDate") Then
x_isNewRow = False
Else
o_DerivedTable.Rows.Add(o_NewRow)
x_isNewRow = True
End If
Next
dgSheetPrint.DataSource = o_DerivedTable
dgSheetPrint.DataBind()
First I want to say that you are not printing datagrid but the html generated by that.
Hence you need to do some thing on the client side. Since you have written you need to break the table and then print i am attaching an html page from where you can copy the code and change it to work with your html generated by the datagrid, you will need to atleast change the id of datagrid.
Code follows here
[since you didn't provided generated html i have made a similar table]
<table id="previousOrderExports" class="chromeTheme">
<tr>
<td class="bold">Export ID</td>
<td class="bold">Request Type</td>
<td class="bold">Timeframe</td>
<td class="bold">Search Filter</td>
<td class="bold">Search Term</td>
<td class="bold">Requested on</td>
<td class="bold">Processed on</td>
<td class="bold">Export Status</td>
<td class="bold">Download</td>
</tr>
<tr>
<td>143306</td>
<td>manual</td>
<td>11/8/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/08/14 03:37:00 AM PST</td>
<td>12/08/14 03:37:08 AM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142873</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/08/14 01:01:10 AM PST</td>
<td>12/08/14 01:03:00 AM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142766</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/07/14 11:01:03 PM PST</td>
<td>12/07/14 11:02:04 PM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142752</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/07/14 10:01:03 PM PST</td>
<td>12/07/14 10:02:05 PM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142738</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/07/14 09:01:03 PM PST</td>
<td>12/07/14 09:02:07 PM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142723</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/07/14 08:01:02 PM PST</td>
<td>12/07/14 08:02:04 PM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
<tr>
<td>142709</td>
<td>auto</td>
<td>12/7/14 - 12/8/14</td>
<td>Timespan</td>
<td></td>
<td>12/07/14 07:01:03 PM PST</td>
<td>12/07/14 07:02:04 PM PST</td>
<td>Done</td>
<td>Download</td>
</tr>
</table>
<input id="Button1" type="button" value="Print" onclick="print_breakup_table()" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" language="javascript" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
function print_breakup_table()
{
var tableid = "previousOrderExports"; //id of table in this case cliend side id of your datagrid
var fixedcols = 2;// no. of columns 0 index based that will be repeated on every page
var eachpagecols = 3; // no. of columns that will get printed on each page
var colCount = 0;
$('#' + tableid + ' tr:nth-child(1) td').each(function () {
if ($(this).attr('colspan')) {
colCount += +$(this).attr('colspan');
} else {
colCount++;
}
});
colCount--; //get 0 based columncount
var col_left_after_fixed_col = colCount - fixedcols;
var pagerequired = col_left_after_fixed_col / eachpagecols;
if ((col_left_after_fixed_col % eachpagecols)>0) {
pagerequired++;
}
for (var i = 0; i < pagerequired; i++) {
var html = "<table>";
var startcols = fixedcols + (i * eachpagecols);
var endcols = startcols + eachpagecols;
//add fixed cols
$('#' + tableid + ' tr').each(function () {
html += "<tr>";
$(this).children('td').each(function (index, object) {
if (index <= fixedcols) {
html += "<td>" + $(this).html() + "</td>";
} else {
if (index <= endcols && index >= startcols) {
html += "<td>" + $(this).html() + "</td>";
}
}
});
html += "</tr>";
});
html += "</table>";
myWindow = window.open('', '', 'width=800,height=600');
myWindow.document.write(html);
myWindow.document.close();
myWindow.focus();
myWindow.print();
}
}
</script>
If you still want to use window.print: do not rely on inline styles but create 2 separate style sheets: media="screen" & media="print"
Use a reporting engine to fully control the result, e.g. Crystal Report, Telerik Reporting etc
You will need to specifically program a view that have the tables the way you want. Then you can add a button to print it
<a class="miniprint" href="/url/to/print/view">Print</a>
Javascript:
$(document).on('click', '.miniprint', function (event) {
event.preventDefault();
var self = $(this);
var url = self.attr("href");
$("#printjob").remove();
$('<iframe id="printjob" src="' + url + '">')
.load(function () {
this.focus();
document.getElementById("printjob").contentWindow.print();
})
.css({ position: "absolute", left: "-10000px", top: "0px" })
.appendTo(document.body);
});
Also, I suggest to tell Google Chrome to print it in landscape using
#media print {
#page {size: landscape}
}
My app is based on the HotTowel template so it includes Durandal, Knockout & Breeze. I have a page with 3 tables side by side. The first table has a list of "templates', the 2nd table shows "sections" for the selected "template" and the 3rd table shows "items" for the selected "section". The "sections" and "items" tables are collections accessed via navigation properties. I find that I get intermittent binding issues. The data in the "templates" table always shows correctly, however related "sections" and "items" sometimes show correctly and other times one of the other is not populated. It would seem to be a timing issue. My view model and view are below. Am I just going about all of this the wrong way?
define(['services/dataservice', 'services/logger', 'services/model'],
function (ds, logger, model) {
var templates = ko.observableArray();
var selectedTemplate = ko.observable();
var selectedSection = ko.observable();
var selectedItem = ko.observable();
var newTemplateTitle = ko.observable();
var newSectionTitle = ko.observable();
var newItemTitle = ko.observable();
function activate() {
newTemplateTitle('');
newSectionTitle('');
newItemTitle('');
logger.log('Templates view activated', null, 'templates', false);
return ds.getTemplatePartials(templates, false, false);//.then(succeeded);
//function succeeded() {
// var firstTemplate = templates()[0];
// setSelectedTemplate(firstTemplate);
//}
}
templates.subscribe(function() {
var firstTemplate = templates()[0];
setSelectedTemplate(firstTemplate);
});
var deactivate = function () {
templates([]);
};
function refresh() {
return ds.getTemplatePartials(templates, true, false);
}
var viewAttached = function (view) {
bindEventToList(view, '#template-list', setSelectedTemplate);
bindEventToList(view, '#section-list', setSelectedSection);
bindEventToList(view, '#item-list', setSelectedItem);
return true;
};
var addTemplate = function () {
var newTemplate = ds.createEntity(model.entityNames.document);
newTemplate.title(newTemplateTitle());
newTemplate.isTemplate(true);
newTemplate.organisation(ds.getCurrentOrganisation()());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
templates.push(newTemplate);
templates.sort();
newTemplateTitle('');
}
};
var addSection = function () {
var newSection = ds.createEntity(model.entityNames.section);
newSection.title(newSectionTitle());
newSection.isTemplate(true);
newSection.document(selectedTemplate());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
newSectionTitle('');
}
};
var addItem = function () {
var newItem = ds.createEntity(model.entityNames.item);
newItem.title(newItemTitle());
newItem.isTemplate(true);
newItem.section(selectedSection());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
newItemTitle('');
}
};
var isTemplateSelected = function (template) {
if (template && selectedTemplate()) {
var thisId = ko.utils.unwrapObservable(selectedTemplate().id);
return ko.utils.unwrapObservable(template.id) == thisId;
}
return false;
};
var isSectionSelected = function (section) {
if (section && selectedSection()) {
var thisId = ko.utils.unwrapObservable(selectedSection().id);
return ko.utils.unwrapObservable(section.id) == thisId;
}
return false;
};
var isItemSelected = function(item) {
if (item && selectedItem()) {
var thisId = ko.utils.unwrapObservable(selectedItem().id);
return ko.utils.unwrapObservable(item.id) == thisId;
}
return false;
};
var vm = {
activate: activate,
deactivate: deactivate,
templates: templates,
//sections: sections,
//items: items,
selectedTemplate: selectedTemplate,
selectedSection: selectedSection,
selectedItem: selectedItem,
title: 'Template Maintenance',
refresh: refresh,
viewAttached: viewAttached,
addTemplate: addTemplate,
addSection: addSection,
addItem: addItem,
newTemplateTitle: newTemplateTitle,
newSectionTitle: newSectionTitle,
newItemTitle: newItemTitle,
isTemplateSelected: isTemplateSelected,
isSectionSelected: isSectionSelected,
isItemSelected: isItemSelected
};
return vm;
//#region internal methods
function setSelectedTemplate(data) {
if (data) {
selectedTemplate(data);
return selectedTemplate().entityAspect.loadNavigationProperty("sections").then(setFirstSectionSelected);
} else {
return false;
}
function setFirstSectionSelected() {
setSelectedSection(selectedTemplate().sections()[0]);
}
}
function setSelectedSection(data) {
if (data) {
selectedSection(data);
return selectedSection().entityAspect.loadNavigationProperty("items").then(setFirstItemSelected);
} else {
selectedSection();
selectedItem();
return false;
}
function setFirstItemSelected() {
setSelectedItem(selectedSection().items()[0]);
}
}
function setSelectedItem(data) {
if (data) {
selectedItem(data);
} else {
selectedItem();
}
}
function bindEventToList(rootSelector, selector, callback, eventName) {
var eName = eventName || 'click';
$(rootSelector).on(eName, selector, function () {
var item = ko.dataFor(this);
callback(item);
return false;
});
}
//#region
}
);
<section>
<div class="row-fluid">
<header class="span12">
<button class="btn btn-info pull-right push-down10" data-bind="click: refresh">
<i class="icon-refresh"></i> Refresh</button>
<h4 class="page-header" data-bind="text: title"></h4>
</header>
</div>
<div class="row-fluid">
<section class="span3">
<header class="input-append">
<input id="newTemplateName"
type="text"
data-bind="realTimeValue: newTemplateTitle"
placeholder="New template name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addTemplate, disable: newTemplateTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Templates</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: templates -->
<tr id="template-list" data-bind="css: { 'selected': $root.isTemplateSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: templates().length"></span></span>
</article>
</section>
<section class="span5">
<header class="input-append">
<input id="newSectionName"
type="text"
data-bind="realTimeValue: newSectionTitle"
placeholder="New section name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addSection, disable: newSectionTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article data-bind="if: selectedTemplate">
<table class="table table-striped table-bordered table-hover" >
<thead>
<tr>
<th data-bind="text: 'Sections for ' + selectedTemplate().title()"></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: selectedTemplate().sections() -->
<tr id="section-list" data-bind="css: { 'selected': $root.isSectionSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: selectedTemplate().sections().length"></span></span>
</article>
</section>
<section class="span4">
<header class="input-append">
<input id="newItemName"
type="text"
data-bind="realTimeValue: newItemTitle"
placeholder="New item name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addItem, disable: newItemTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article data-bind="if: selectedSection">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th data-bind="text: 'Items for ' + selectedSection().title()"></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: selectedSection().items() -->
<tr id="item-list" data-bind="css: { 'selected': $root.isItemSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: selectedSection().items().length"></span></span>
</article>
</section>
</div>
Similar issue (same server, same client but sometimes some navigators not working in breeze) happened to me. In my opinion it can be a timing bug. Or the create/load order of entities count.
I've changed this paralell async load entities from server:
return promise = Q.all([
getPackage(),
getClubPartials(null, true),
getAddressPartials(null, true),
getEventPartials(null, true)
]).then(success);
to this get them one by one:
return getClubPartials(null, true).then(function () {
getAddressPartials(null, true).then(function () {
getEventPartials(null, true).then(function () {
return getPackage().then(success);
})
})
});
And my problem gone!
There is just a lot of code to wade through and try to make sense of in the imagination.
You seem to say it works some of the time. That does sound like a timing issue.
One thing that is disturbing is the async methods (e.g. setSelectedTemplate) that return either false or a promise; not sure why the inconsistency. But that is probably not the real problem.
You could try putting a setTimeout(..., 10) before exiting the async methods. See if that changes the behavior.
If that doesn't reveal it, you'll have to boil it down to just those the essentials that reveal the problem.
Sadly no sudden flash of insight today.
Update June 3
My first concern is that some of your methods return promises and some return the value false. I'd feel more comfortable if they all returned promises. Look at Q.resolve().
Other code baffles me. For example, what is going on in the many variations on this pattern:
function setSelectedItem(data) {
if (data) {
selectedItem(data);
} else {
selectedItem();
}
}
The else{...} isn't doing anything useful. It unwraps the selectedItem property ... and discards the value. What is the point?
And what is the difference between this:
thisId = ko.utils.unwrapObservable(selectedTemplate().id); // obscure
and this?
thisId = selectedTemplate().id(); // clear
Are you unsure whether selectedTemplate().id is a KO observable? The only reason to use unwrapObservable is when you are unsure.
As for timing, you can let KO know that it should re-fresh a binding by calling valueHasMutated() on an observable. For example:
function setFirstItemSelected() {
setSelectedItem(selectedSection().items()[0]);
selectedItem.valueHasMutated();
}
It might help. I don't know ... you're stacking a long chain of dependencies and it isn't easy to see which are bailing out when. That would take study of actual data flows as well as the code. You cannot reasonably expect your StackOverflow audience to work that hard for free.
Try cutting this way down to a super-simple example that displays the troublesome behavior.
Best of luck.
I have created a form that has multiple sections that are hidden. A tab strip hides/shows the sections to create a page with a smaller footprint. While this makes the page a lot cleaner, it makes it hard to show errors to the user after validation. I want to make an indicator in the tabs that shows that the content in the specified tab has errors.
Main View:
<div>
<ul class="contentTabs">
<li onclick="switchTab(this)" class="selected">Contact</li>
<li onclick="switchTab(this)">Information</li>
<li onclick="switchTab(this)">Software</li>
<li onclick="switchTab(this)">Hardware</li>
<li onclick="switchTab(this)">Classification</li>
<li onclick="switchTab(this)" class="last">Solution</li>
</ul>
<div class="content">
<div id="contact" class="contentPane">
#Html.Partial("_Contact")
</div>
<div id="information" class="contentPane" style="display: none;">
#Html.Partial("_Information")
#Html.Partial("_Notes")
</div>
<div id="notes" class="contentPane" style="display: none;">
#Html.Partial("_Notes")
</div>
<div id="software" class="contentPane" style="display: none;">
#Html.Partial("_Software")
</div>
<div id="hardware" class="contentPane" style="display: none;">
#Html.Partial("_Hardware")
</div>
<div id="classification" class="contentPane" style="display: none;">
#Html.Partial("_Classification")
</div>
<div id="solution" class="contentPane" style="display: none;">
#Html.Partial("_Solution")
</div>
</div>
</div>
Partial View (Contact):
#code
Dim notifyTypes As ListItemCollection = DirectCast(ViewData("NotifyTypes"), ListItemCollection)
Dim callerTypes As ListItemCollection = DirectCast(ViewData("CallerTypes"), ListItemCollection)
Dim reportingTypes As ListItemCollection = DirectCast(ViewData("ReportingTypes"), ListItemCollection)
Dim myIncident As Library.BusinessLayer.Incident = DirectCast(Model, Library.BusinessLayer.Incident)
End Code
<table class="tableBorderless" style="width: 99%; margin: 0px auto">
<tr>
<td class="right">User Location</td>
<td class="left">
#Html.DropDownList("LocationId", DirectCast(ViewData("Locations"), SelectList), New With {.style = "width: 200px"})<br />
#Html.ValidationMessage("LocationId", New With {.class = "red"})
</td>
<td class="right">Notify</td>
<td class="left">
#For Each notificationType As ListItem In notifyTypes
#<input type="radio" name="Notify" value="#notificationType.Value" #IIf(notificationType.Selected, "checked", "") />#notificationType.Text
Next
</td>
</tr>
<tr>
<td class="right">Caller Type</td>
<td colspan="3" class="left">
#For Each callerType As ListItem In callerTypes
#<input type="radio" name="CallerType" value="#callerType.Value" #IIf(callerType.Selected, "checked", "") />#callerType.Text
Next
</td>
</tr>
<tr>
<td class="right">User Network ID</td>
<td class="left">
#Html.TextBox("UserId", myIncident.UserId, New With {.onchange = "UserId_onchange(this)", .maxlength = "30"})
</td>
<td class="right">User Name</td>
<td class="left">
#Html.TextBox("UserName", myIncident.UserName, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("UserName", New With{.class = "red"})
</td>
</tr>
<tr>
<td class="right">User Email</td>
<td class="left">
#Html.TextBox("UserEmail", myIncident.UserEmail, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("UserEmail", New With{.class = "red"})
</td>
<td class="right">User Phone</td>
<td class="left">
#Html.TextBox("UserPhone", myIncident.UserPhone, New With {.maxlength = "50"})
</td>
</tr>
<tr>
<td class="right">Reporting Type</td>
<td colspan="3" class="left">
#For Each reportingType As ListItem In ReportingTypes
#<input type="radio" name="ReportedByType" value="#reportingType.Value" #IIf(reportingType.Selected, "checked", "") />#reportingType.Text
Next
</td>
</tr>
<tr>
<td class="right">Reported by (Network ID)</td>
<td class="left">
#Html.TextBox("ReportedByUserId", myIncident.ReportedByUserId, New With {.onchange = "ReportedByUserId_onchange(this)", .maxlength = "30"})
</td>
<td class="right">Reported by Name</td>
<td class="left">
#Html.TextBox("ReportedByName", myIncident.ReportedByName, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("ReportedByName", New With {.class = "red"})
</td>
</tr>
<tr>
<td class="right">Reported by Email</td>
<td class="left">
#Html.TextBox("ReportedByEmail", myIncident.ReportedByEmail, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("ReportedByEmail", New With {.class = "red"})
</td>
<td class="right">Reported by Phone</td>
<td class="left">
#Html.TextBox("ReportedByPhone", myIncident.ReportedByPhone, New With {.maxlength = "50"})
</td>
</tr>
</table>
<script type="text/javascript">
function UserId_onchange(textField) {
var parms = {UserName: textField.value};
$.ajax({
url: '#Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
type: 'POST',
dataType: 'json',
data: parms,
success: function (data) {
$("#UserName").val(data.Name);
$("#UserEmail").val(data.Email);
$("#UserPhone").val(data.PhoneWork);
}
});
}
function ReportedByUserId_onchange(textField) {
var parms = { UserName: textField.value };
$.ajax({
url: '#Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
type: 'POST',
dataType: 'json',
data: parms,
success: function (data) {
$("#ReportedByName").val(data.Name);
$("#ReportedByEmail").val(data.Email);
$("#ReportedByPhone").val(data.PhoneWork);
}
});
}
</script>
You could check whether appropriate tab's div has any "input-validation-error" class applied (taken you use standard DataAnnotations). Combine this into jQuery function which would run through all needed divs (probably all divs specified in your li elements) and if length of elements with "input-validation-error" class is more than 0, as #rivarolle suggested apply "error" class to li element to highlight it in your preferred way.
This would be a possible script:
$( "li" ).each(function( index ) {
var searchPattern = ("#"+$(this).text()+" .input-validation-error");
if ($(searchPattern.toLowerCase()).length > 0){
$(this).addClass("error");
}
});
css:
.error {
background-color: red;
}
Give your li elements IDs
<li onclick="switchTab(this)" id="softwareTab">Software</li>
Then pass the collection of validation objects, or, better a list of affected tab names in your ViewModel, and store the list in one or more hidden fields. Then use jQuery to parse the list and add the error class as suggested by rivarolle...
$("#softwareTab").addClass("error")
You may have to clean up later with removeClass().
There are many ways to do this, all a bit kludgy, but sometimes that is the price of a good looking page...one hidden field with a comma seperated list, one hidden field per tab with a boolean value...or pseudo-boolean.
I think, The page should be divided into partial views. Each partial view needs to be validated before proceeding to next step. For that we can write a helper Method. When user fills the data and post the section, then controller checks and fills your custom validation error collection and it can be passed on as model metadata i.e. buddy class in your model. This way , you will render the errors. i.e. we are using model-metadata to send validation errors.
If you don't want to use model approach then you need to use ViewBag collection which is dynamic collection.
Hope this helps.
What you will probably need to do is use the visibility of the various validation messages.
The way I'd approach this is by adding a custom class to the validation messages for use within jquery:
#Html.ValidationMessage("UserName", New With{.class = "red validationMesssage"})
Then in the switchTab function do something like this:
function switchTab(el)
{
var tabId=$(el).text(); //Get the tab to be searched
var isValid=true; //Set default as valid
$("#"+tabId).find(".validationMessage:visible").each(function(){
isValid=false; //this should only fire if the validation message is visible
});
if(!isValid)
$(el).addClass("errors"); //If invalid..add error class to li element.
}
You can try #Html.ValidationSummary(false) at the top MAIN view. Its better from usability perspective as well.
You could change the color of the tab headers that contain errors to red for instance.
To do this, I would switch the css of the tags.
To take the Information tab:
No error:
--> <li onclick="switchTab(this)">Information</li>
Error:
--> <li onclick="switchTab(this)" class="error">Information</li>
The CSS class "error" will change the color to red or append an image to indicate validation failure.
I cannot see what is wrong here but the image does not display using the following Knockout template:
<script type="text/html" id="legend-template">
<div><input type="checkbox" data-bind="click : doSomething" ></input>
<img width="16px" height="16px" data-bind="src: 'imagePath'" />
<span data-bind="text : label"> </span>
</div>
</script>
The object this is being bound to looks like this:
tut.myObject= function (imagePath, label) {
this.label = ko.observable(label);
this.imagePath = ko.observable(imagePath || liveString + '/Content/images/marker.png');
};
tut.myObject.prototype = {
doSomething: function () { alert("do what?");
}
};
When the HTML object is rendered I see the label and clicking on the checkbox invokes doSomething.
TIA.
Only a few attributes can be bound directly; try using attr - it will let you set any attribute on an element.
<img width="16px" height="16px" data-bind="attr:{src: imagePath}" />