Backbone data logic inside view - javascript

I am not sure where to perform the action of preparing the data for the template of my view. Currently I have this piece of code.
getTemplateData: function () {
var inventoryStatus = selectedDevice.get("inventoryStatus"),
data = {},
statusName,
inventoryDate;
statusName = getConfigValue("pdp", "statusMap", inventoryStatus);
data.message = getConfigValue("pdp", "statusMessage", statusName);
data.className = "";
data.dataAttribute = null;
data.tooltipValue = null;
data.displayError = false;
var redirectCode = (allDevices.get("thirdPartyRedirectCode") !== null) ? allDevices.get("thirdPartyRedirectCode") : "";
if (redirectCode) {
if (redirectCode === 9999) {
data.buttonDisabled = false;
data.buttonText = "Design Yours";
} else if (redirectCode === 9998) {
data.buttonDisabled = true;
data.buttonText = "Design Yours";
}
return false;
}
switch(inventoryStatus) {
case 1001: //Out of Stock
data.buttonDisabled = true;
data.displayError = true;
break;
case 1002: //Pre Order
data.buttonDisabled = false;
break;
}
return data;
}
This getTemplateData() I call inside my render function of the view. This seems wrong by the looks of it and i am unsure where to place this code.
Should I create different getters in my model or should i leave them inside my main view. Please help.

From what I know the "correct" way of doing this is to put it in the model, and in the view have
getTemplateData: function () {
return this.model.getTemplateData();
}
EDIT
In case of multiple models for a view (which shouldn't happen, without getting into your decisions at the moment) you can have a getTemplateData for each, and call them with something like extend:
getTemplateData: function () {
var data = this.model1.getTemplateData();
data = $.extend(data, this.model2.getTemplateData());
return data;
}
BUT
What you really should do, IMHO, is give each it's own view, where one of them is smaller and intended to be included in the other. (i.e. bigView.$el.append(smallView.el))

Related

Google Cloud SQL not updating with script

I have a long script which is designed to edit a specific row in the Cloud SQL table. The code is long so i will shorten it.
Client Side:
function build_profile(){
var cbid = sessionStorage.getItem("client_id");
var self = this;
var createSuccess = function(data){
var statuse = ["Active", "Wiating", "Discharged"];
if(data !== false){
data = data.split(",");
var dec = app.pages.Profile.descendants;
dec.fname.text = data[1];
dec.sname.text = data[3];
sessionStorage.setItem("school_id", data[9]);
app.popups.Loading.visible = false;
}
};
var init = function() {google.script.run.withSuccessHandler(createSuccess).get_user_data(cbid);};
app.popups.Loading.visible = true;
init();
}
function save_profile() {
var createSuccess = function(data){
var dec = app.pages.Profile.descendants;
console.log(data);
if(data !== -1){
var ds = app.datasources.Clients;
ds.load(function(){
ds.selectIndex(data);
console.log("editing:"+ds.item.CBID);
ds.item.fname = dec.fname_edit.value;
ds.item.sname = dec.sname_edit.value;
ds.load(function(){build_profile();});
});
}
}};
var init = function() {google.script.run.withSuccessHandler(createSuccess).update_client(sessionStorage.getItem("client_id"));};
init();
}
Server Side:
function get_user_data(cbid){
try{
var query = app.models.Clients.newQuery();
query.filters.CBID._equals = parseInt(cbid);
var results = query.run();
if(results.length > 0){
var arr = [
results[0].Id, //0
results[0].fname, //1
results[0].sname //3
];
return arr.join(",");
}else{
return false;
}
}catch(e){
console.error(e);
console.log("function get_user_data");
return false;
}
}
function update_client(cbid) {
try{
var ds = app.models.Clients;
var query = ds.newQuery();
query.filters.CBID._equals = parseInt(cbid);
var results = query.run();
if(results.length > 0){
var id = results[0]._key;
return id+1;
}else{
return -1;
}
}catch(e){
console.error(e);
return -1;
}
}
This gets the Clients table and updates the row for the selected client, then rebuilds the profile with the new information.
EDIT: I have managed to get to a point where its telling me that i cannot run the query (ds.load()) while processing its results. There does not seem to be a manual check to see if it has processed?
Note: datasource.saveChanges() does not work as it saves automatically.
You error is being produced by the client side function save_profile() and it is exactly in this block:
ds.load(function(){
ds.selectIndex(data);
console.log("editing:"+ds.item.CBID);
ds.item.fname = dec.fname_edit.value;
ds.item.sname = dec.sname_edit.value;
ds.load(function(){build_profile();});
});
So what you are doing is reloading the datasource almost immediately before it finishes loading hence you are getting that error
cannot run the query (ds.load()) while processing its results
This is just a matter of timing. A setTimeout can take of the issue. Just do the following:
ds.load(function(){
ds.selectIndex(data);
console.log("editing:"+ds.item.CBID);
ds.item.fname = dec.fname_edit.value;
ds.item.sname = dec.sname_edit.value;
setTimeout(function(){
ds.load(function(){build_profile();});
},1000);
});
I have manage to find a solution to this particular issue. It requires Manual Saving but it saves a lot of hassle as one of the inbuilt solutions can be used rather than relying on dealing with errors or timeouts.
function client_query_and_result(){
var createSuccess = function(data){ //callback function
console.log(data);
};
app.datasources.SomeTable.saveChanges(function(){//ensures all changes have been saved
app.datasources.SomeTable.load(function(){//makes sure to reload the datasource
google.script.run.withSuccessHandler(createSuccess).server_query_and_result(); //at this point All data has been saved and reloaded
});
});
}
The Server side code is the exact same methods. To enable manual saving you can select the table in App Maker -> Datasources -> check "Manual save mode".
Hope this can be useful to someone else.

angular().service() wont get a update in the object I pass as parameter

Well fist time here, so sorry for the mess or the long text.
I pass $scope.alertsensorconfigs as tableData to my service, and wen I delete a item, my $scope.alertsensorconfigs changes but wen It try to reload the table I see that tableData had not changed.
Detailed explanation:
I have a site that uses NgTable to create a lot of tables in different pages, but all the logic to convert and filter the data that I want to show in the page was coded in the pages and it repeated a lot of times but it was working, so i decided to thy make a service that do all that.
So my problem is, I created the service, it i almost all working like before, but I notices that wen I delete a item from the table, it deletes the data but keep showing it on the table until I reload the page.
I notice that it is because even that my object on the page have changed after delete, wen it is inside my service the object apear to be the unchanged one, since I passed my object as parameter to my service i took it wold pass like a reference to the object and wen it changes on the page it wold apear changed in my service, but looks like it is not the case, it looks like wen it calls for the fist time my service it makes a copy of my object and wen it calls again it wont get the updated one.
My page code.
$scope.funcFilter = function(pfilter){
return function (item) {
return item.name.toUpperCase().indexOf(pfilter) >= 0
||
item.criticality1.toString().toUpperCase().indexOf(pfilter) >= 0
||
item.criticality2.toString().toUpperCase().indexOf(pfilter) >= 0
|| $filter('translate')
(item.active.toString().toUpperCase()).indexOf(pfilter) >= 0;
}
}
$scope.searchTable = {filter: ""};
$scope.tableParams =
NgTableDataService.getGenericTableParams($scope.alertsensorconfigs,
$scope.funcFilter, $scope.searchTable.filter)
Delete function in my page:
AlertSensorConfig.remove({id: obj.id}, function () {
$scope.alertsensorconfigs.splice($scope.alertsensorconfigs.indexOf(obj), 1);
$scope.tableParams.reload().then(function(data) {
if (data.length === 0 && $scope.tableParams.total() > 0) {
$scope.tableParams.page($scope.tableParams.page() - 1);
$scope.tableParams.reload();
}
});
},
My service:
angular.module('control-room').service('NgTableDataService',
function ($filter, NgTableParams, Session) {
var session = Session.get();
this.getGenericTableParams = function(tableData, funcFilter, searchTableFilter){
return new NgTableParams({
count: session.user.tablePagination,
filter: searchTableFilter
}, {
counts: rowsPerPageTemplate,
getData: function (params) {
if (params.filter() == ''){
var pfilter = '';
}else{
var pfilter = params.filter().filter.toUpperCase();
}
let filteredData = params.filter() ? $filter('filter')(tableData, funcFilter(pfilter)) : tableData;
if (!!filteredData && filteredData.length >= 0) {
params.total(filteredData.length);
var rowsPerPageTemplateWithAllData = rowsPerPageTemplate.slice();
var isFound = rowsPerPageTemplateWithAllData.some(function (element) {
return element === filteredData.length;
});
params.settings().counts = rowsPerPageTemplateWithAllData.filter(item=>item<filteredData.length)
if (filteredData.length >= 5){
params.settings().counts.push(filteredData.length);
}
rowsPerPageTemplateWithAllData.push(filteredData.length + (isFound ? 1 : 0));
if (session.user.tablePagination >= params.settings().counts[params.settings().counts.length-1]){
params.settings().count = params.settings().counts[params.settings().counts.length-1];
}else{
params.settings().count = session.user.tablePagination;
}
if (params.total() <= params.count()) {
params.page(1);
}
var x = $filter('orderBy')(filteredData, params.orderBy());
var y = x.slice((params.page() - 1) * params.count(), params.page() * params.count());
return y;
} else {
params.settings().counts = [];
return null;
}
}
});
};
And the ng-table function that reload the table after delete:
this.reload = function() {
var self = this,
pData = null;
settings.$loading = true;
prevParamsMemento = angular.copy(createComparableParams());
isCommittedDataset = true;
if (self.hasGroup()) {
pData = runInterceptorPipeline($q.when(settings.getGroups(self)));
} else {
pData = runInterceptorPipeline($q.when(settings.getData(self)));
}
log('ngTable: reload data');
var oldData = self.data;
return pData.then(function(data) {
settings.$loading = false;
errParamsMemento = null;
self.data = data;
event even when data === oldData
ngTableEventsChannel.publishAfterReloadData(self, data, oldData);
self.reloadPages();
return data;
}).catch(function(reason){
errParamsMemento = prevParamsMemento;
// "rethrow"
return $q.reject(reason);
});
};
there is some way to make sure the object I pas as parameter to my service is updated every time I call it, like some binding?
I think i manage to solve it.
The NgTable have his own reload(), but it wasn't working because what it does is, get the current filteredData and replace with the one tableData, but the tableData on my service was only set wen it was called the fist time and wasn't getting updated wen I updated the $scope.alertsensorconfigs.
So what I manage to do after a lot of headache, was to create in my service:
A var serviceTableData that receives the $scope.alertsensorconfigs wen the service is called so it is global in my service.
And my own reload() function that I replaced in every place that my controller used the NgTable reload().
My service reload() wen called receives as parameter from the controller the $scope.alertsensorconfigs after the controller delete or edit the item, then it will set the serviceTableData to the updated data received as param and then calls the NgTable reload().
So it ended like this:
angular.module().service('NgTableDataService',
function ($filter, NgTableParams, Session, MY_CONSTANTS) {
var session = Session.get();
var serviceTableParam = null;
var serviceTableData = null;
this.reloadTableData = function (tableDataAtt){
serviceTableData = tableDataAtt;
serviceTableParam.reload().then(function(data) {
if (data.length === 0 && serviceTableData.total() > 0) {
serviceTableParam.page(serviceTableParam.page() - 1);
serviceTableParam.reload();
}
return this.tableData = tableDataAtt;
});
};
this.getGenericTableParams = function(tableData, searchTableFilter, funcFilter){
serviceTableData = tableData;
return serviceTableParam = new NgTableParams({
// etc...
Edit : I misunderstood the question
According to your comment what you want is to update a variable in your service and retrieve it later.
What you can do is :
In your service
angular
.module('app.core')
.factory('', dataservice);
dataservice.$inject = ['$http'];
function dataservice($http) {
return {
myFunction: yourServiceFunction,
myData: myData
};
var myData = [];
function yourServiceFunction(tableData) {
// DO SOMETHING
myData = tableData;
// DO SOMETHING
}
}
Like this you can access to myData in your service or in your controller which is updated in your function with the value of tableData that you passed when you called your function.
And if you want to update myData in your service everytime $scope.alertsensorconfigs changes, add a $watch to this variable and create a function in your service who is called whenever your $scope value change and which simply update myData in your service.
I hope that I answered to the right question this time :)
.
.
.
Here after : old response which do not answer to the question
there is some way to make sure the object I pas as parameter to my service is updated every time I call it, like some binding?
Yes.
I am sorry to not answer with your code but it is a bit hard to get into it.
The simpliest way is to do something like this :
$scope.alertsensorconfigs =
yourService.yourMethod($scope.alertsensorconfigs, param2, param3).tableData;
While your services' method does something like :
yourMethod = function (tableData, param2, param3) {
// DO SOMETHING
return { tableData : tableDataAfterModification, elem2 : 'elem2', elem3 : 'elem3' };
};

Changing the boolean value of an object stored in array

I'm just starting my journey with JS so pleace be patient :)
I'm building a small app. The images on the left side are stored in an array. If someone clicks one of them I want to change its height but also the boolean value from false to true so that I know which one the user clicked (for further purposes). In my mind it goes like this: creating an array->add EventListener to the item->chain it witch the function choosen(change the value to true)->run setClick function that changes the height. What's wrong there?
I aprecciate any advice.
CODEPEN http://codepen.io/finewitch/pen/ZBKMKm
var storage = new Array();
storage[0] = document.getElementById("grandFatherCh");
storage[1] = document.getElementById("grandMotherCh");
storage[2] = document.getElementById("sisterCh");
storage[3] = document.getElementById("brotherCh");
storage[4] = document.getElementById("fatherCh");
storage[5] = document.getElementById("motherCh");
storage[0].clicked = false;
storage[1].clicked = false;
storage[2].clicked = false;
storage[3].clicked = false;
storage[4].clicked = false;
storage[5].clicked = false;
for (var i=0; i<storage.length; i++){
storage[i].addEventListener("click",choosen, setClick);
console.log("clicked");
};
function choosen(){
if (storage[i].clicked == false)
{
return "stillFalse"
storage[i].clicked = true;
}
};
function setClick(){
if(storage[i].clicked === true){
return "setClickWorks"
storage[i].style.height = "400px";
}else{
console.log('failed');
}
};
Maybe I'm missing something but I think you have the return statement in the wrong place no? Should be
function choosen(){
if (storage[i].clicked == false) {
return "stillFalse"
} else {
storage[i].clicked = true;
}
}
function setClick(){
if (storage[i].clicked === true){
storage[i].style.height = "400px";
return "setClickWorks"
} else {
console.log('failed');
}
}

Trying to convert existing synchronous XmlHttpRequest object to be asynchronous to keep up with current fads

Soo, I keep getting slammed with cautions from Chrome about how synchronous XmlHttpRequest calls are being deprecated, and I've decided to have a go at trying to convert my use-case over in order to keep up with this fad...
In this case, I have an ~9 year old JS object that has been used as the central (and exemplary) means of transporting data between the server and our web-based applications using synchronous XHR calls. I've created a chopped-down version to post here (by gutting out a lot of sanity, safety and syntax checking):
function GlobalData()
{
this.protocol = "https://";
this.adminPHP = "DataMgmt.php";
this.ajax = false;
this.sessionId = "123456789AB";
this.validSession = true;
this.baseLocation = "http://www.example.com/";
this.loadResult = null;
this.AjaxPrep = function()
{
this.ajax = false;
if (window.XMLHttpRequest) {
try { this.ajax = new XMLHttpRequest(); } catch(e) { this.ajax = false; } }
}
this.FetchData = function (strUrl)
{
if ((typeof strURL=='string') && (strURL.length > 0))
{
if (this.ajax === false)
{
this.AjaxPrep();
if (this.ajax === false) { alert('Unable to initialise AJAX!'); return ""; }
}
strURL = strURL.replace("http://",this.protocol); // We'll only ask for data from secure (encrypted-channel) locations...
if (strURL.indexOf(this.protocol) < 0) strURL = this.protocol + this.adminPHP + strURL;
strURL += ((strURL.indexOf('?')>= 0) ? '&' : '?') + 'dynamicdata=' + Math.floor(Math.random() * this.sessionId);
if (this.validSession) strURL += "&sessionId=" + this.sessionId;
this.ajax.open("GET", strURL, false);
this.ajax.send();
if (this.ajax.status==200) strResult = this.ajax.responseText;
else alert("There was an error attempting to communicate with the server!\r\n\r\n(" + this.ajax.status + ") " + strURL);
if (strResult == "result = \"No valid Session information was provided.\";")
{
alert('Your session is no longer valid!');
window.location.href = this.baseLocation;
}
}
else console.log('Invalid data was passed to the Global.FetchData() function. [Ajax.obj.js line 62]');
return strResult;
}
this.LoadData = function(strURL)
{
var s = this.FetchData(strURL);
if ((s.length>0) && (s.indexOf('unction adminPHP()')>0))
{
try
{
s += "\r\nGlobal.loadResult = new adminPHP();";
eval(s);
if ((typeof Global.loadResult=='object') && (typeof Global.loadResult.get=='function')) return Global.loadResult;
} catch(e) { Global.Log("[AjaxObj.js] Error on Line 112: " + e.message); }
}
if ( (typeof s=='string') && (s.trim().length<4) )
s = new (function() { this.rowCount = function() { return -1; }; this.success = false; });
return s;
}
}
var Global = new GlobalData();
This "Global" object is referenced literally hundreds of times across 10's of thousands of lines code as so:
// Sample data request...
var myData = Global.LoadData("?fn=fetchCustomerData&sortByFields=lastName,firstName&sortOrder=asc");
if ((myData.success && (myData.rowCount()>0))
{
// Do Stuff...
// (typically build and populate a form, input control
// or table with the data)
}
The server side API is designed to handle all of the myriad kinds of requests encountered, and, in each case, to perform whatever magic is necessary to return the data sought by the calling function. A sample of the plain-text response to a query follows (the API turns the result(s) from any SQL query into this format automatically; adjusting the fields and data to reflect the retrieved data on the fly; the sample data below has been anonymized;):
/* Sample return result (plain text) from server:
function adminPHP()
{
var base = new DataInterchangeBase();
this.success = true;
this.colName = function(idNo) { return base.colName(idNo); }
this.addRow = function(arrRow) { base.addRow(arrRow); }
this.get = function(cellId,rowId) { return base.getByAbsPos(cellId,rowId); }
this.getById = function(cellId,rowId) { return base.getByIdVal(cellId,rowId); }
this.colExists = function(colName) { return ((typeof colName=='string') && (colName.length>0)) ? base.findCellId(colName) : -1; }
base.addCols( [ 'id','email','firstName','lastName','namePrefix','nameSuffix','phoneNbr','companyName' ] );
this.id = function(rowId) { return base.getByAbsPos(0,rowId); }
this.email = function(rowId) { return base.getByAbsPos(1,rowId); }
this.firstName = function(rowId) { return base.getByAbsPos(2,rowId); }
this.lastName = function(rowId) { return base.getByAbsPos(3,rowId); }
this.longName = function(rowId) { return base.getByAbsPos(5,rowId); }
this.namePrefix = function(rowId) { return base.getByAbsPos(6,rowId); }
this.nameSuffix = function(rowId) { return base.getByAbsPos(7,rowId); }
this.companyName = function(rowId) { return base.getByAbsPos(13,rowId); }
base.addRow( [ "2","biff#nexuscons.com","biff","broccoli","Mr.","PhD","5557891234","Nexus Consulting",null ] );
base.addRow( [ "15","happy#daysrhere.uk","joseph","chromebottom","Mr.","","5554323456","Retirement Planning Co.",null ] );
base.addRow( [ "51","michael#sunrisetravel.com","mike","dolittle","Mr.","",""5552461357","SunRise Travel",null ] );
base.addRow( [ "54","info#lumoxchemical.au","patricia","foxtrot","Mrs,","","5559876543","Lumox Chem Supplies",null ] );
this.query = function() { return " SELECT `u`.* FROM `users` AS `u` WHERE (`deleted`=0) ORDER BY `u`.`lastName` ASC, `u`.`firstName` LIMIT 4"; }
this.url = function() { return "https://www.example.com/DataMgmt.php?fn=fetchCustomerData&sortByFields=lastName,firstName&sortOrder=asc&dynamicdata=13647037920&sessionId=123456789AB\"; }
this.rowCount = function() { return base.rows.length; }
this.colCount = function() { return base.cols.length; }
this.getBase = function() { return base; }
}
*/
In virtually every instance where this code is called, the calling function cannot perform its work until it receives all of the data from the request in the object form that it expects.
So, I've read a bunch of stuff about performing the asynchronous calls, and the necessity to invoke a call-back function that's notified when the data is ready, but I'm a loss as to figuring out a way to return the resultant data back to the original (calling) function that's waiting for it without having to visit every one of those hundreds of instances and make major changes in every one (i.e. change the calling code to expect a call-back function as the result instead of the expected data and act accordingly; times 100's of instances...)
Sooo, any guidance, help or suggestions on how to proceed would be greatly appreciated!

Durandal: how to cache calls?

In my durandal app I need to know if the user is logged in different places. So currently i'm doing a call to get the user state in every views that needs it.
Is possible to do something like this:
//login.js
define(function(require) {
var http = require('durandal/http')
var isLogged;
function getLogin() {
if (isLogged != undefined) return isLogged
return http.get('/api/login').then(function(data) {
isLogged = data.logged
return isLogged
})
}
return {
getLogin: getLogin
}
//view.js
define(function(require) {
var login = require('login')
function vm() {
var self = this;
self.isLogged;
self.activate = function() {
self.isLogged = login.getLogin()
}
}
return vm
})
The above doesn't work because in the view activate method I need to return a promise. How can I achieve that?
You can use guardRouter function!
This will be triggered in all navigation.
// Shell main or other file that runs before any view!
// Define the router guard function!
require('plugin/router', 'Q', function(router, q){
var cache = {};
router.guardRoute = function(instance, instruction) {
var key = instruction.fragment.toLowerCase();
return cache[key] !== undefined ?
cache[key]:
q($.get(url,data)).then(_setCache, _fail);
function _fail(/*jqhxr*/) {
/* do something */
}
function _setCache(result) {
cache[key] = result;
return cache[key];
}
}
});
If you return true, the navigation will proceed! in case of string returned, durandal will navigate into it!
The cache works as you define (check for javascript memoization)
About durandal Auth chech this gitHub for inspiration.

Categories

Resources