I am using the Bootstrap framework on my website. Under you can see some js used with semantic, and I want to do something similar in bootstrap.
some of the file: base.js
$.fn.api.settings.api = {
'get_user': '/api/v1/user/{username}',
};
search form in html file:
<div id="check_points" class="ui form">
<form>
<div class="field username">
<input type="text" placeholder="Username">
</div>
<button class="ui submit button">Submit</button>
</form>
<div class="ui response hidden"></div>
</div>
end of html file
<script type="text/javascript">
$('#check_points .submit.button')
.api({
action: 'get_user',
beforeSend: function(settings) {
settings.urlData = {
username: $('#check_points div.field.username input').val()
};
return settings;
},
successTest: function(response) {
return response.error == false || true;
},
onSuccess: function(response) {
var el = $('#check_points .response');
el.addClass('message green');
el.removeClass('red');
el.html('User <strong class="username">' + response.username + '</strong> has <strong class="points">' + response.points + '</strong> points and is ranked <strong class="rank">' + response.rank + '</strong>.');
if (el.hasClass('hidden')) {
el.transition({
animation: 'slide down',
duration: '500ms',
});
}
},
onFailure: function(response) {
var el = $('#check_points .response');
el.addClass('message red');
el.removeClass('green');
el.html('No user with the name <strong class="username">XXX</strong> found.');
el.find('strong.username').text($('#check_points div.field.username input').val());
if (el.hasClass('hidden')) {
el.transition({
animation: 'slide down',
duration: '500ms',
});
}
}
});
</script>
Related
Can someone help me to figure out how to remove my row data after an ajax call is successfully done(the data is removed from the data base)?
I have tried different ways to get the row then call the remove function from the row but it is not working.
I would like to find a way to get the row to remove in my deleteDataReportRecords function then pass it to ajax call ajaxRemoveTTvUidsCall, then if the response is successful, I remove from the grid the row passed as a parameter to the function ajaxRemoveTTvUidsCall
Here is the javaScript file:
function createKendoDataReportGrid(){
var year = $("#vehicleReportYearSelector").val();
var country = $("#vehicleReportCountrySelector").val();
var acodeFormatted = country + convertYearsIntoAcodeFormat(year);
$("#vehicleReportGrid").kendoGrid({
dataSource: {
transport: {
read: {
url: String.format('{0}/ajax/myData/records.ajax',
ASC.cfg.getContextPath()),
data: function () {
return {
acodes: JSON.stringify(acodeFormatted)
}
}
}
},
schema: {
parse: function(response) {
if (response.result !== null && response.result!== null&&
response.result.length!==0) {
if($("#isAdminUser").val() === "true"){
$("#deleteRecord").removeAttr('disabled');
}
return response.result;
}
else {
return [];
}
}
},
pageSize: 20
},
groupable: false,
sortable: true,
autoBind: true,
pageable: {
pageSizes: true,
buttonCount: 20
},
columns: [{
field: "colum1",
title: "ID",
width: '60px',
template: function(dataItem){
return '<span class="colum1">' + dataItem.colum1 + '</span>';
}
},
{
field : "",
title : '<input type="button"/>',
width: '55px',
template: function(dataItem){
var currentTtvUid = dataItem.ttvUid;
var deletedRecordId = "deleteRecord"+currentTtvUid;
if((dataItem.fvdUidMatch !==1
|| dataItem.cadaAcodeMatch ===0
|| dataItem.acodeStyleIdDuplicateCount>1
)&& $("#isAdminUser").val() === "true"){
'<div id="vehicleReportGrid" style="display: none;"> </div>'
return '<button id="'+deletedRecordId+'" class="btn" style="display:inline-block;margin: 10px 5px 5px 5px; float:right;" onclick="deleteDataReportRecords(event,this,'+currentTtvUid+')" enabled"/>Delete</button>';
}else{
return '<button id="'+deletedRecordId+'"class="btn" style="display:inline-block;margin: 10px 5px 5px 5px; float:right;" onclick="deleteDataReportRecords(event,this,'+currentTtvUid+')" disabled"/>Delete</button>';
}
},
width : "100px"
}]
});
}
function deleteDataReportRecords(e,variable,currentTtvUid){
var confirmDelete = confirm("Are you sure you want to delete the selected vehicle?");
if (confirmDelete) {
e.stopPropagation();
var ttvUids = [];
ttvUids.push(currentTtvUid);
//vehicleReportGrid.removeRow($(variable).closest("tr"));
var row = $('#vehicleReportGrid').find('tbody tr').has('input:button:enabled');
ajaxRemoveDataCall(row,ttvUids);
}
}
function ajaxRemoveDataCall(row,ttvUidArray){
$.ajax({
type : "POST",
url : String.format('{0}/ajax/delete/data/records.ajax', ASC.cfg.getContextPath()),
dataType: 'json',
data : {
"ttvuids": JSON.stringify(ttvUidArray)
},
success: function(response) {
if (response!==null){
row.remove();
} else {
alert("Error: unable to remove data");
}
},
error : function(e) {
alert("Error: unable to remove data: " + e);
}
});
}
Here is the JSP file:
<layout:master activeNavId="home" title="page.appLinker.title">
<jsp:attribute name="scripts">
<script type="text/javascript">
(function() {
var page = new com.src.appLinker.pages.MainPage();
$(document)
.ready(function() {
page.init();
});
})(window.jQuery);
</script>
</jsp:attribute>
<jsp:body>
<div id="mainContainer" class="appMarginLeft appMarginRight">
<div>
<div id="tabStrip" class="tabStrip">
<ul>
<li>
<label id="vehicleReportLabel" style="cursor: pointer" class="labelFont">
Report & Fix
</label>
</li>
</ul>
<div id="vehicleReportContainer" class="tabContainer" >
<h3 style="display: inline-block; padding: 5px;">Vehicle Report & Fix </h3>
<select id="vehicleReportCountrySelector" name="vehicleReportCountrySelector"
onchange="setYearsOnVehicleReportDropDown()"
style="width:10%; margin-left: 10px">
</select>
<select id="vehicleReportYearSelector" name="vehicleReportYearSelector" style="width:7%; margin-left: 10px" onchange="setSelectedYearOnVehicleReportDropDown()"></select>
<button class="btn" id="searchVehicleReportButton" style="margin-top:-10px" onclick="createKendoDataReportGrid()">Search</button>
<div id="vehicleReportGrid"> </div>
</div>
</div>
</div>
</div>
</jsp:body>
</layout:master>
Hi i've got a durandal app that send data trought ajax but i dont know how to implement the loading indicator , here is the codes:
this is the view that loads the data
loadinbox.html
<div class="modal-content messageBox">
<div class="modal-header">
<h3>LOGIN</h3>
</div>
<div class="modal-body">
<h3>Entre com suas credenciais.</h3>
<form data-bind="submit: ok">
<input type="text" data-bind="value: login" placeholder="CPF" class="form-control autofocus" />
<input type="text" data-bind="value: senha" placeholder="Senha" class="form-control autofocus" />
</form>
</div>
<div data-bind="if: $parent.loading">
<img src="img/loading.gif"/>
</div>
<div class="modal-footer">
<button class="btn btn-success" data-bind="click: ok, active: $parent.loading">Login</button>
</div>
</div>
this is the model that loads the data
loginBox.js
define(function (require) {
var dialog = require('plugins/dialog');
var loading = ko.observable();
var loginBox = function(){
this.login = '';
this.senha = '';
this.loading = false;
};
loginBox.prototype.ok = function () {
this.loading =true;
$.ajax({
type: "post",
data: { "LoginForm[cpf]" : this.login, "LoginForm[password]" : this.senha , 'ajax':'login-form' },
url: localStorage['baseurl']+localStorage['router']+'site/login',
success: function (data){
console.log(data);
},
error: function (request, status, error){
console.log(request.status);
console.log(status);
console.log(error);
},
complete: function (data) {
alert('hqweuiqhioehqio');
this.loading =false;
}
});
};
loginBox.show = function() {
return dialog.show(new loginBox());
};
return loginBox;
});
On the surface, your approach is sound, but your approach to modules in Durandal is a little muddled. For example, you've declared loading twice, once as a scalar and once as an observable.
So, let's create an instance module (which means that we're going to return a constructor function):
loginBox.html (view)
<div class="modal-content messageBox">
<div class="modal-header">
<h3>LOGIN</h3>
</div>
<div class="modal-body">
<h3>Entre com suas credenciais.</h3>
<form data-bind="submit: ok">
<input type="text" data-bind="value: login" placeholder="CPF" class="form-control autofocus" />
<input type="text" data-bind="value: senha" placeholder="Senha" class="form-control autofocus" />
</form>
</div>
<div data-bind="if: $parent.loading()">
<img src="img/loading.gif"/>
</div>
<div class="modal-footer">
<button class="btn btn-success" data-bind="click: ok, active: $parent.loading()">
Login
</button>
</div>
</div>
Notice that I changed your if binding to this:
"if: loading()"
referencing loading with parentheses. This performs an immediate eval using the default value supplied to the observable, and then a re-eval when the observable changes.
Also, it may be necessary to change "click: ok, active: $parent.loading()" to click: $parent.ok.bind($parent), active: $parent.loading(). Check your context using the debugger when the #ok function is entered.
A note on logic: It seems to me that what you might mean in the modal footer is
active: !$parent.loading()
Should the OK button really be active when the form is loading data?
loginBox.js (module instance approach)
define (
[
'plugins/dialog',
'knockout'
],
function (
dialog,
ko) {
var LoginBox = function () {
this.login = '';
this.senha = '';
this.loading = ko.observable(false);
};
LoginBox.prototype.ok = function () {
var _vm = this;
this.loading(true);
$.ajax( {
type: "post",
data: { "LoginForm[cpf]" : this.login, "LoginForm[password]" : this.senha , 'ajax':'login-form' },
url: localStorage['baseurl']+localStorage['router']+'site/login',
success: function (data) {
console.log(data);
},
error: function (request, status, error) {
console.log(request.status);
console.log(status);
console.log(error);
},
complete: function (data) {
alert('hqweuiqhioehqio');
_vm.loading(false);
}
});
};
LoginBox.prototype.show = function() {
dialog.show(this);
};
return LoginBox;
};
);
Take note of my treatment of this.loading. It is an observable, and observables are updated using the approach I show above (remember, they are functions). When you assign true in this manner--this.loading = true--you override the observable itself and turn it into a non-observable scalar. So, when the value later changes (from false to true to false), the view is not updated.
Also note that you must import KnockoutJS.
Another issue: you have a this reference issue in your #complete function. Note that I do this at the top of your #Ok function:
var _vm = this; //Some people are inclined to this format: var that = this;
and, then, in your #complete function, I do this:
_vm.loading(false);
Using this in your #complete function references the #complete function itself, not the viewModel. We have to save a reference to this outside the #complete function.
There was another problem: #show was not on the prototype.
I use a utility called Block UI
in my App JS file I call it like this using the Global Ajax setup.
By doing it this way you only do it once and then every Ajax call will show your loading gif and hide when any Ajax calls starts and finishes
HTML:
in the index.html page(your main page)
<div id="throbber" style="display:none;">
<img src="/images/gears.gif" />
</div>
Javascript
$(document).ready(function () {
$.blockUI.defaults.css.border = 'none';
$.blockUI.defaults.css.backgroundColor = 'transparent';
$.blockUI.defaults.message = $('#throbber');
$.blockUI.defaults.showOverlay = true;
$.blockUI.defaults.overlayCSS.backgroundColor = "#fff";
$(document).ajaxStart(function () {
$.blockUI();
}).ajaxStop(function () {
$.unblockUI();
});
});
CSS:
#throbber{
border:1px solid #346789;
padding: 15px;
background-Color: #fff;
-moz-border-radius:0.5em;
border-radius:0.7em;
opacity:0.8;
filter:alpha(opacity=80);
color: #000 ;
width:133px;
height:133px;
left: 50%;
top: 40%;
position: fixed;
}
I'm having trouble with a backbone.js app I'm working on as a learning exercise. I set up a jsfiddle for it.
Here's the JavaScript:
var app = {};
$(document).ready(function() {
app.Contact = Backbone.Model.extend({
defaults: {
firstName: '',
lastName: '',
email: ''
},
validate: function(attrs) {
var errors = [];
if (attrs.firstName.trim() == "") {
errors.push({
'message': 'Please enter a first name.',
'field': 'firstName'
});
}
if (attrs.lastName.trim() == "") {
errors.push({
'message': 'Please enter a last name.',
'field': 'lastName'
});
}
if (attrs.email.trim() == "") {
errors.push({
'message': 'Please enter an email address.',
'field': 'email'
});
}
if (errors.length) {
return errors;
} else {
return false;
}
}
});
app.ContactList = Backbone.Collection.extend({
model: app.Contact,
localStorage: new Store('backbone-addressbook')
});
app.contactList = new app.ContactList();
app.ContactView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#contact-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
var self = this;
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
this.model.on('change', function(model, response) {
//console.log(self.el);
$(self.el).removeClass('editing');
this.render;
})
},
events: {
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'click .destroy': 'destroy',
'click .save': 'close'
},
edit: function(e) {
this.$el.addClass('editing');
$(e.currentTarget).next('input').focus();
},
updateOnEnter: function(e) {
if (e.which == 13) {
this.close(e);
}
},
close: function(e) {
e.preventDefault();
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
this.model.save(updateObject, {validate: true});
},
destroy: function() {
this.model.destroy();
}
});
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
$(this).find('.has-error').removeClass('has-error');
$(this).remove('.help-block');
app.contactList.on('add', this.addOne, this);
app.contactList.fetch();
var self = this;
app.contactList.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
},
events: {
'click .add': 'createContact'
},
createContact: function(e) {
e.preventDefault();
app.contactList.create(this.newAttributes(), {validate: true});
},
addOne: function(contact) {
var view = new app.ContactView({model: contact});
$('#contactList').append(view.render().el);
$('form input[type="text"]').val('');
$('form input[type="text"]').parent().removeClass('has-error');
$('.help-block').remove();
},
newAttributes: function() {
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
return updateObject;
},
});
app.appView = new app.AppView();
});
And here's the HTML:
<div class="container">
<section id="addressbookapp">
<header id="header">
<h1>Address Book</h1>
<div class="well">
<form id="newContact" action="#" role="form">
<div class="form-group">
<label class="control-label" for="firstName">First Name</label>
<input data-field="firstName" class="newFirstName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="lastName">Last Name</label>
<input data-field="lastName" class="newLastName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="email">Email Address</label>
<input data-field="email" class="newEmail form-control input-sm" type="text" />
</div>
<button class="add btn-xs">Add</button>
</form>
</div>
</header>
<section id="main">
<table class="table table-striped">
<caption>Double-click to edit an entry.</caption>
<thead>
<tr>
<th>First</th>
<th>Last</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contactList"></tbody>
</table>
</section>
</section>
</div>
<script id="contact-template" type="text/template">
<form action="#" role="form">
<td>
<label class="control-label" for="firstName"><%- firstName %></label>
<input data-field="firstName" class="firstName input-sm edit" value="<%- firstName %>" type="text" />
</td>
<td>
<label class="control-label" for="lastName"><%- lastName %></label>
<input data-field="lastName" class="lastName input-sm edit" value="<%- lastName %>" type="text" />
</td>
<td>
<label class="control-label" for="email"><%- email %></label>
<input data-field="email" class="email input-sm edit" value="<%- email %>" type="email" />
</td>
<td>
<button class="btn-xs save">Save</button>
<button class="btn-xs destroy">Delete</button>
</td>
</form>
</script>
Specifically, when the user edits an entry in the list (by double-clicking), clears an input (a last name, for example) and then tries to save, there's (correctly) a validation error. The problem is that the form at the top (for creating a new entry) is also responding to the invalid event.
My question is not just how to keep this from happening but what would be the ideal way to organize things. This is a learning exercise for me, so I'd thankful for any tips -- anything you see that could be improved.
It's due to the way you've built the app: in both the "new" and "edit" forms, you tell the app to "display error messages if there's a validation problem in the collection". So when you try editing an existing model and there's a validation problem, the "new" form updates to display the errors.
What you need to do instead, is use a new (blank) model in the "new" form, display errors if it doesn't validate, and add it to the collection if it's valid. That way, both forms have their errors handled by different mechanisms and won't overlap.
See http://jsfiddle.net/n9yq2/3/
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
this.model = new app.Contact();
// edited for brevity
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
createContact: function(e) {
e.preventDefault();
var attrs = this.newAttributes();
if(this.model.set(attrs, {validate: true})){
app.contactList.create(attrs);
}
},
I have two tables:
nom_articole { id(PK), denumire, nom_um_id(FK) }
nom_um { id(PK), simbol, denumire }
I want to display:
nom_articole.id
nom_articole.denumire
nom_um.simbol
My html code:
<div data-options="dxView : { name: 'nom_articoleDetails', title: 'Nom_articole' } " >
<div data-options="dxContent : { targetPlaceholder: 'content' } " >
<div data-bind="dxScrollView: { }">
<h2 data-bind="text: nom_articole.denumire"></h2>
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">ID</div>
<div class="dx-field-value" data-bind="text: nom_articole.id"></div>
</div>
<div class="dx-field">
<div class="dx-field-label">Denumire</div>
<div class="dx-field-value" data-bind="text: nom_articole.denumire"></div>
</div>
<div class="dx-field">
<div class="dx-field-label">U.M.</div>
<div class="dx-field-value" data-bind="text: ????????????????"></div>
</div>
</div>
<div data-options="dxContentPlaceholder : { name: 'view-footer', transition: 'none' } " ></div>
</div>
</div>
My Javascrip code:
MobileApplication.nom_articoleDetails = function(params) {
return {
id: params.id,
//nom_um: new MobileApplication.nom_umViewModel(),
nom_articole: new MobileApplication.nom_articoleViewModel(),
handleDelete: function() {
DevExpress.ui.dialog.confirm("Are you sure you want to delete this item?", "Delete item").then($.proxy(function (result) {
if (result)
this.handleConfirmDelete();
}, this));
},
handleConfirmDelete: function() {
MobileApplication.db.nom_articole.remove(params.id).done(function() {
MobileApplication.app.navigate("nom_articole", { target: "back" });
});
},
viewShown: function() {
nom_articoleDetails = this;
MobileApplication.db.nom_articole.byKey(params.id).done(function(data) {
nom_articoleDetails.nom_articole.fromJS(data);
});
}
};
This code was generated by Devexpress multi-channel application wizard.
From the information you gave, and provided that nom_articole and nom_um share the same key, the code would be:
viewShown: function() {
// ... your code ...
MobileApplication.db.nom_um.byKey(params.id).done(function(data) {
nom_articoleDetails.nom_um.fromJS(data);
});
}
You uncomment the nom_um member and use text: nom_um.simbol as the binding text in the view.
Update:
To load an object by foreign key, you have two options. The first is cascading fetches:
MobileApplication.db.nom_articole.byKey(19).done(function (data) {
nom_articoleDetails.nom_articole.fromJS(data);
MobileApplication.db.nom_um.byKey(data.nom_um_id).done(function(data) {
nom_articoleDetails.nom_um.fromJS(data);
});
});
The second will work if your data service is properly configured OData. In this case you may use the expand feature:
MobileApplication.db.nom_articole.byKey(19, { expand: "nom_um" }).done(function (data) {
// data will contain a nested object
});
P.S. DevExpress provides Support service, and in case you cannot find the right answer here, you can ask them and share the solution.
I've been using Knockout for a few days now and this is what I've come up with to organize my view models and javascript models:
//******************************************************************************
// jQUERY START:
//******************************************************************************
$(document).ready(function()
{
var panel1 = $('#panel1>section');
var panel2 = $('#panel2>section');
$('#loader').ajaxStart(function()
{
$(this).fadeIn();
}).ajaxComplete(function()
{
$(this).fadeOut();
});
ko.applyBindings(new ViewModel(panel1));
});
//******************************************************************************
// VIEW MODEL:
//******************************************************************************
function ViewModel(_panel1)
{
var self = this;
this.vmHeader = new HeaderViewModel();
this.vmPanel1 = new Panel1ViewModel(_panel1);
}
//******************************************************************************
// Panel1ViewModel:
//******************************************************************************
function Panel1ViewModel(_element)
{
var self = this;
self.element = _element;
self.filters = ['Operations', 'Jobs', 'Shifts', 'Hours'];
self.selectedFilter = ko.observable();
self.vmOperations = new OperationsViewModel();
self.vmJobs = new JobsViewModel();
self.vmShifts = new ShiftsViewModel();
self.vmHours = new HoursViewModel();
//PUBLIC METHODS:
self.clickFilter = function(filter)
{
self.selectedFilter(filter);
switch(filter)
{
case 'Operations':
self.vmOperations.load(self.clear, self.element);
break;
case 'Jobs':
self.vmJobs.load(self.clear, self.element);
break;
case 'Shifts':
self.vmShifts.load(self.clear, self.element);
break;
case 'Hours':
self.vmHours.load(self.clear, self.element);
break;
}
}
self.clear = function()
{
//test each view model to see if it currently has items loaded and empty them.
if (self.vmOperations.operations() != null) { self.vmOperations.operations(null); }
if (self.vmJobs.jobs() != null) { self.vmJobs.jobs(null); }
if (self.vmShifts.shifts() != null) { self.vmShifts.shifts(null); }
if (self.vmHours.hours() != null) { self.vmHours.hours(null); }
};
}
//******************************************************************************
// ShiftsViewModel:
//******************************************************************************
function ShiftsViewModel()
{
var self = this;
self.shifts = ko.observableArray(null);
self.selected = ko.observable();
//PUBLIC METHODS:
self.load = function (callback, element)
{
var options = {
url: '/api/shifts',
type: 'GET',
data: {
operationID: 1
}
};
async_load(options, function (data)
{
callback();
self.shifts(
$.map(data.Items, function (item, index)
{
return new Shift(item);
})
);
element.overscroll(); <--- PROBLEM IS HERE!
});
}
self.click = function(shift)
{
self.selected(shift.id);
};
}
//******************************************************************************
// SHIFT MODEL:
//******************************************************************************
function Shift(data)
{
var self = this;
this.operation = data.Operation;
this.shopOrder = data.ShopOrder;
this.date = data.Date;
this.id = data.ID;
this.number = data.Number;
this.start = ko.observable(data.Start);
this.end = ko.observable(data.End);
this.isRunning = ko.observable(data.IsRunning);
//computed properties.
this.shiftDate = ko.computed(function()
{
var date = (this.end() == '') ? new Date() : new Date(this.end());
return date.toLocaleDateString();
}, this);
this.startTime = ko.computed(function()
{
return (new Date(this.start())).toLocaleTimeString();
}, this);
this.endTime = ko.computed(function()
{
var time = '---';
if (this.isRunning() == 'False')
{
time = (new Date(this.end())).toLocaleTimeString();
}
return time;
}, this);
}
//******************************************************************************
// GLOBAL FUNCTIONS:
//******************************************************************************
function async_load(options, callback)
{
$.ajax({
url: options.url,
async: true,
cache: false,
type: options.type,
data: options.data,
dataType: 'json',
contentType: 'application/json',
success: callback,
error: function (request, type, errorThrown)
{
error_handler(options.url, request, type, errorThrown);
}
});
}
function sync_load(options)
{
var error = false;
var data = $.ajax({
url: options.url,
async: false,
cache: false,
type: options.type,
data: options.data,
dataType: 'json',
contentType: 'application/json',
error: function (request, type, errorThrown)
{
error = true;
error_handler(options.url, request, type, errorThrown);
}
}).responseText;
if (!error)
{
data = eval('(' + data + ')');
}
return (error) ? null : data;
}
function error_handler(name, request, type, errorThrown)
{
switch (request.status)
{
case 404:
alert('The ' + name + ' could not be found.');
break;
case 500:
alert('There was an internal server error loading ' + name + '.');
//redirect the user to a page with further instructions.
break;
default:
alert('An error occurred: (' + request.status + ' ' + request.statusText + ').');
}
}
And here is the HTML:
<section id="panel1" data-bind="with: vmPanel1">
<nav data-bind="foreach: filters">
</nav>
<section>
<section id="panel1Data">
<!-- ko foreach: vmOperations.operations -->
<article class="operation" data-bind="css: { selected: $data.operationID == $root.vmPanel1.vmOperations.selected() }, click: $root.vmPanel1.vmOperations.click">
<div class="name" data-bind="text: name"></div>
<div class="number" data-bind="text: number"></div>
<div class="sequence" data-bind="text: sequence"></div>
</article>
<!-- /ko -->
<!-- ko foreach: vmJobs.jobs -->
<article data-bind="pageBreak: operation, label: 'operation', level: 1"></article>
<article class="job" data-bind="css: { selected: $data.jobID == $root.vmPanel1.vmJobs.selected() }, click: $root.vmPanel1.vmJobs.click">
<div class="start date">
<label data-bind="text: startMonth"></label>
<div data-bind="text: startDay"></div>
<span data-bind="text: startTime"></span>
</div>
<div class="end date">
<label data-bind="text: endMonth"></label>
<div data-bind="text: endDay"></div>
<span data-bind="text: endTime"></span>
</div>
<div class="shoporder" data-bind="text: shopOrder"></div>
<div class="toolconfig" data-bind="toolConfig: $data"></div>
<div class="lot" data-bind="text: lot"></div>
</article>
<!-- /ko -->
<!-- ko foreach: vmShifts.shifts -->
<article data-bind="pageBreak: operation, label: 'operation', level: 1"></article>
<article data-bind="pageBreak: shopOrder, label: 'job', level: 2"></article>
<article data-bind="pageBreak: shiftDate(), level: 3"></article>
<article class="shift" data-bind="css: { selected: $data.id == $root.vmPanel1.vmShifts.selected() }, click: $root.vmPanel1.vmShifts.click">
<div class="shift" data-bind="text: number"></div>
<div class="start time" data-bind="text: startTime()"></div>
<div class="end time" data-bind="text: endTime()"></div>
</article>
<!-- /ko -->
Depending on the filter that is selected, the appropriate list of data (operations, jobs, shifts, or hours) will be loaded into panel 1 and the jQuery Overscroll plugin is used to create an IPad scrolling effect.
The problem is in the ShiftsViewModel on the line where the Overscroll plugin is being called. When debugging, I noticed that the plugin isn't doing anything because the container element has no width/height. When running the program, the container is updated with the correct data, so it looks like the call to overscroll is running before the data is written to the DOM container by Knockout.
Since the overscroll call is inside the ajax callback which runs after data is received, I thought it would be OK. Is Knockout asynchronously updating the DOM? I'm not sure where to go from here... Any suggestions?
I figured out the problem:
In the jQuery ready function, I was caching the DOM element before any data was loaded and then using that cached element which didn't have anything inside the container element.
So, I changed this:
var panel1 = $('#panel1>section');
to:
var panel1 = '#panel1>section';
And then in the ShiftsViewModel, I changed this line:
element.overscroll();
to:
$(element).overscroll();
and it appears to be working now...