How to implement cancel functionality using knockout.js - html

I am working on a asp.net MVC project. and i am using knockout.js on my view page.
I am trying to develop a data entry grid. Everything works fine except Cancel button.
If i change something on my UI ( view) and clicked on cancel , It is not showing me old values .it only show me the latest values.
Steps:
When i click on edit button it display update and cancel button.
Let us say i have edited data and click on cancel , it should not reflect on my UI.
Right now , even if you edit and click on cancel button , it is able to revert to old state.
I am not going back to old state when i clickd on cancel button.
Please suggest me some examples .
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Type Lookup....</title>
<script src="~/Scripts/jquery-2.1.0.js"></script>
<script src="~/Scripts/knockout-3.0.0.js"></script>
<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<script type="text/javascript">
viewModel = {
lookupCollection: ko.observableArray()
};
//wrapper to an observable that requires accept/cancel
ko.protectedObservable = function (initialValue) {
//private variables
var _actualValue = ko.observable(initialValue),
_tempValue = initialValue;
//computed observable that we will return
var result = ko.computed({
//always return the actual value
read: function () {
return _actualValue();
},
//stored in a temporary spot until commit
write: function (newValue) {
_tempValue = newValue;
}
});
//if different, commit temp value
result.commit = function () {
if (_tempValue !== _actualValue()) {
_actualValue(_tempValue);
}
};
//force subscribers to take original
result.reset = function () {
_actualValue.valueHasMutated();
_tempValue = _actualValue(); //reset temp value
};
return result;
};
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/Home/GetIndex",
}).done(function (data) {
$(data).each(function (index, element) {
var mappedItem =
{
Id: ko.observable(element.Id),
Key: ko.observable(element.Key),
Value: ko.observable(element.Value),
Mode: ko.observable("display")
};
viewModel.lookupCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
}).error(function (ex) {
alert("Error.....");
});
$(document).on("click", ".kout-edit", null, function (ev) {
var current = ko.dataFor(this);
current.Mode("edit");
});
$(document).on("click", ".kout-update", null, function (ev) {
var current = ko.dataFor(this);
saveData(current);
current.Mode("display");
});
$(document).on("click", ".kout-cancel", null, function (ev) {
var current = ko.dataFor(this);
current.Mode("display");
});
$(document).on("click", "#create", null, function (ev) {
var current = {
Id: ko.observable(0),
Key: ko.observable(),
Value: ko.observable(),
Mode: ko.observable("edit")
}
viewModel.lookupCollection.push(current);
});
function saveData(currentData) {
var postUrl = "";
var submitData = {
Id: currentData.Id(),
Key: currentData.Key(),
Value: currentData.Value()
};
if (currentData.Id && currentData.Id() > 0) {
postUrl = "/Home/Edit"
}
else {
postUrl = "/Home/Create"
}
$.ajax({
type: "POST",
contentType: "application/json",
url: postUrl,
data: JSON.stringify(submitData)
}).done(function (id) {
currentData.Id(id);
}).error(function (ex) {
alert("ERROR Saving....");
})
}
});
</script>
</head>
<body>
<div>
<p>
<button class="btn btn-primary" id="create">Create</button>
</p>
<table class="table">
<tr>
<th>Key
</th>
<th>Value
</th>
<th>Action
</th>
</tr>
<tbody data-bind="foreach: lookupCollection">
<tr data-bind="template: { name: Mode, data: $data }">
</tr>
</tbody>
</table>
<script type="text/html" id="display">
<td data-bind="text: Key"></td>
<td data-bind="text: Value"></td>
<td>
<button class="btn btn-success kout-edit">Edit</button>
<button class="btn btn-danger kout-delete">Delete</button>
</td>
</script>
<script type="text/html" id="edit">
<td>
<input type="text" data-bind="value: Key" /></td>
<td>
<input type="text" data-bind="value: Value" /></td>
<td>
<button class="btn btn-success kout-update">Update</button>
<button class="btn btn-danger kout-cancel">Cancel</button>
</td>
</script>
</div>
</body>
</html>

I have modified your code little bit.
var mappedItem =
{
Id: ko.observable(element.Id),
Key: ko.observable(element.Key),
Value: ko.observable(element.Value),
Mode: ko.observable("display"),
oldData: ko.observable()
};
I have added oldData observable to retain previous data when edit button clicked. So now when you click on cancel data will be restored by "olData" observable.
see the below code.
$(document).on("click", ".kout-edit", null, function (ev) {
var current = ko.dataFor(this);
current.oldData(ko.toJS(current));
current.Mode("edit");
});
$(document).on("click", ".kout-update", null, function (ev) {
var current = ko.dataFor(this);
current.oldData(null);
saveData(current);
current.Mode("display");
});
$(document).on("click", ".kout-cancel", null, function (ev) {
var current = ko.dataFor(this);
current.Id(current.oldData().Id);
current.Value(current.oldData().Value);
current.Key(current.oldData().Key);
current.Mode("display");
current.oldData(null);
});
Here is working example on Jsfiddle

Use this
ko.observable.fn.revertable = function () {
var self = this, originalValue = self();
if (!originalValue) {
self.subscribe(function () {
originalValue = originalValue || self();
});
}
self.commit = function () {
originalValue = self();
};
self.revert = function () {
self(originalValue || '');
};
self.isDirty = function () {
return (self() != originalValue);
};
return self;
};
and your observables like this
this.text=ko.observable().revertable();
i assume you have cancel function so in that you can do it like
this.text.revert();
and if you want to save those changes then
this.text.commit();

You have the protectedObservable in your code, but you're not using it. Turn your 'model' (the data you want to 'cancel') into a protectedObservable. On cancel, call data.reset(). On save, call data.commit().
Little example (not complete):
$(document).on("click", ".kout-cancel", null, function (ev) {
var current = ko.dataFor(this);
current.reset();
current.Mode("display");
});
$(document).on("click", "#create", null, function (ev) {
var current = ko.protectedObservable({
Id: ko.observable(0),
Key: ko.observable(),
Value: ko.observable(),
Mode: ko.observable("edit")
});
viewModel.lookupCollection.push(current);
});

Related

Retrieving data from a MYSQL table instead of a .csv file with angular 'select'

My WebApp displays a radar from data stored in a .cvs file. Instead, I'd like to get the data from a mysql table.
Here is the HTML/PHP code that selects the .csv file as a source of data:
<body class="container" ng-controller="MainCtrl as radar">
<!-- START VIZ-->
<div class="visualization col-xs-7">
<p>Select other sources:
<select ng-options="example for example in radar.examples" ng-model="radar.exampleSelected" ng-change="radar.selectExample(radar.exampleSelected)"></select>
</p>
<div class="visualization">
<radar csv="radar.csv" config="radar.config"></radar>
</div>
</div>
<!-- END OF VIZ-->
</body>
The HTML/PHP file calls these files at the end:
<!-- scripts -->
<script src="https://code.angularjs.org/1.3.5/angular.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="app.js"></script>
<script src="radar.js"></script>
<script src="radarDraw.js"></script>
<script>
<!-- end scripts -->
Below is the content of a file called 'app.js':
(function() {
angular.module("RadarChart", [])
.directive("radar", radar)
.directive("onReadFile", onReadFile)
.controller("MainCtrl", MainCtrl);
// controller function MainCtrl
function MainCtrl($http) {
var ctrl = this;
init();
// function init
function init() {
// initialize controller variables
ctrl.examples = [
"radar-data.csv",
"data_plant_seasons.csv",
"data_car_ratings.csv"
];
ctrl.exampleSelected = ctrl.examples[0];
ctrl.getData = getData;
ctrl.selectExample = selectExample;
// initialize controller functions
ctrl.selectExample(ctrl.exampleSelected);
ctrl.config = {
w: 300,
h: 300,
facet: false,
levels: 5,
levelScale: 0.85,
[ETC...]
};
}
// function getData
function getData($fileContent) {
ctrl.csv = $fileContent;
}
// function selectExample
function selectExample(item) {
var file = item ; //+ ".csv";
$http.get(file).success(function(data) {
ctrl.csv = data;
});
}
}
// directive function sunburst
function radar() {
return {
restrict: "E",
scope: {
csv: "=",
config: "="
},
link: radarDraw
};
}
// directive function onReadFile
function onReadFile($parse) {
return {
restrict: "A",
scope: false,
link: function(scope, element, attrs) {
var fn = $parse(attrs.onReadFile);
element.on("change", function(onChangeEvent) {
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function() {
fn(scope, {
$fileContent: onLoadEvent.target.result
});
});
};
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
});
}
};
}
})();
Thank you for your help.

I want to use ng-contextmenu on my html page for providing different menu items. But my JS class is not getting called.

This is my html class, I have used one example from net for understanding how it's works.
<form>
<div ng-controller="ListController">
<div>
<strong>Gold: </strong>
{{player.gold}}
</div>
<div class="list-group">
<a href="#"
class="list-group-item"
ng-repeat="item in items"
context-menu="menuOptions">
<span class="badge">{{item.cost}}</span>
{{item.name}}
</a>
</div>
</div>
</form>
This is controller :
controller('ListController', ['$scope',
function ($scope) {
$scope.player = {
gold: 100
};
$scope.items = [
{ name: 'Small Health Potion', cost: 4 },
{ name: 'Small Mana Potion', cost: 5 },
{ name: 'Iron Short Sword', cost: 12 }
];
$scope.menuOptions = [
['Buy', function ($itemScope) {
$scope.player.gold -= $itemScope.item.cost;
}],
null,
['Sell', function ($itemScope) {
$scope.player.gold += $itemScope.item.cost;
}]
];
}
]);
This is my JS file, which is being used for contextmenu:
var app = angular.module("contextMenu",[]);
app.directive('contextMenu', function ($parse) {
var renderContextMenu = function ($scope, event, options) {
if (!$) { var $ = angular.element; }
$(event.currentTarget).addClass('context');
var $contextMenu = $('<div>');
$contextMenu.addClass('dropdown clearfix');
var $ul = $('<ul>');
$ul.addClass('dropdown-menu');
$ul.attr({ 'role': 'menu' });
$ul.css({
display: 'block',
position: 'absolute',
left: event.pageX + 'px',
top: event.pageY + 'px'
});
angular.forEach(options, function (item, i) {
var $li = $('<li>');
if (item === null) {
$li.addClass('divider');
} else {
$a = $('<a>');
$a.attr({ tabindex: '-1', href: '#' });
$a.text(typeof item[0] == 'string' ? item[0] : item[0].call($scope, $scope));
$li.append($a);
$li.on('click', function ($event) {
$event.preventDefault();
$scope.$apply(function () {
$(event.currentTarget).removeClass('context');
$contextMenu.remove();
item[1].call($scope, $scope);
});
});
}
$ul.append($li);
});
$contextMenu.append($ul);
var height = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
$contextMenu.css({
width: '100%',
height: height + 'px',
position: 'absolute',
top: 0,
left: 0,
zIndex: 9999
});
$(document).find('body').append($contextMenu);
$contextMenu.on("mousedown", function (e) {
if ($(e.target).hasClass('dropdown')) {
$(event.currentTarget).removeClass('context');
$contextMenu.remove();
}
}).on('contextmenu', function (event) {
$(event.currentTarget).removeClass('context');
event.preventDefault();
$contextMenu.remove();
});
};
return function ($scope, element, attrs) {
element.on('contextmenu', function (event) {
$scope.$apply(function () {
event.preventDefault();
var options = $scope.$eval(attrs.contextMenu);
if (options instanceof Array) {
renderContextMenu($scope, event, options);
} else {
throw '"' + attrs.contextMenu + '" not an array';
}
});
});
};
});
But this code is not working for me. My debug point never comes on contextmenu js file. And I am getting default window menu on right click.
Can anyone please suggest what I am doing wrong or missing in this. It would be a great help.
First you have to create a master app.js file to define your modules something like below. The file needs to be loaded first.
(function () {
var module = angular.module('app', [
'contextMenu',
'menu'
]);
})();
Now the file which holds controller needs to be something like below.
(function () {
var module = angular.module('menu');
module.controller('ListController', [
'$scope'
function ($scope) {
//Your controller code goes here
}]);
})();
Your html needs to be something like below.
<body ng-app="app">
//Your html goes here
</body>
Your js files needs to be added in below order.
Angularjs
app.js
menu.js
That's all i can say for now.

How to add Google Drive Picker in Google web app

what I'm trying to do is to show the Google Picker in my Google Web app. I already tried many ways to accomplish that, but nothing works.
At the moment my code looks like this:
WebApp.html
<!-- rest of the code -->
<button type="button" id="pick">Pick File</button>
</div>
<script>
function initPicker() {
var picker = new FilePicker({
apiKey: "####################",
clientId: "##########-##########################",
buttonEl: document.getElementById('pick'),
onSelect: function(file) {
alert('Selected ' + file.title);
} // onSelect
}); // var picker
} // function initPicker()
</script>
<!-- rest of the code -->
WebAppJS.html
/* rest of the code */
var FilePicker = window.FilePicker = function(options) {
this.apiKey = options.apiKey;
this.clientId = options.clientId;
this.buttonEl = options.buttonEl;
this.onSelect = options.onSelect;
this.buttonEl.addEventListener('click', this.open.bind(this));
this.buttonEl.disabled = true;
gapi.client.setApiKey(this.apiKey);
gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this));
google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) });
}
FilePicker.prototype = {
open: function() {
var token = gapi.auth.getToken();
if (token) {
this._showPicker();
} else {
this._doAuth(false, function() { this._showPicker(); }.bind(this));
}
},
_showPicker: function() {
var accessToken = gapi.auth.getToken().access_token;
this.picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.DOCUMENTS).
setAppId(this.clientId).
setOAuthToken(accessToken).
setCallback(this._pickerCallback.bind(this)).
build().
setVisible(true);
},
_pickerCallback: function(data) {
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var file = data[google.picker.Response.DOCUMENTS][0],
id = file[google.picker.Document.ID],
request = gapi.client.drive.files.get({ fileId: id });
request.execute(this._fileGetCallback.bind(this));
}
},
_fileGetCallback: function(file) {
if (this.onSelect) {
this.onSelect(file);
}
},
_pickerApiLoaded: function() {
this.buttonEl.disabled = false;
},
_driveApiLoaded: function() {
this._doAuth(true);
},
_doAuth: function(immediate1, callback) {
gapi.auth.authorize({
client_id: this.clientId + '.apps.googleusercontent.com',
scope: 'https://www.googleapis.com/auth/drive.readonly',
immediate: immediate1
}, callback);
}
}; // FilePicker.prototype
/* rest of the code */
For now, what this code does is showing kind of a popup, but empty. Code is based on Daniel15's code.
What I already tried is:
relocating chunks of code, to server-side and client-side,
using htmlOutput, htmlTemplate - non of those works,
many other things, that i can't exactly remember.
What I would like to get is answer to the question: Why this code doesn't show Google Picker.
Thanks in advance.
Try adding a call origin and developer key
_showPicker: function() {
var accessToken = gapi.auth.getToken().access_token;
this.picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.DOCUMENTS)
.setAppId(this.clientId)
.setOAuthToken(accessToken)
.setCallback(this._pickerCallback.bind(this))
.setOrigin('https://script.google.com') //
.setDeveloperKey(BROWSERKEYCREATEDINAPICONSOLE) //
.build()
.setVisible(true);
},

Ajax call - Which button pressed?

<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.1.js">
</script>
<script>
$(document).ready(function(){
$("#btn1").click(function(){
txtname=$("#btn1").val();
$.ajax({url:"doSome.php",data:{name:txtname},success: function(ajaxresult){
$("#ajaxrequest").html(ajaxresult);
}});
});
});
</script>
</head>
<body>
<button id="btn1" value="myButton">Click to send1</button>
<button id="btn2" value="myButton2">Click to send2</button>
<div id="ajaxrequest"></div>
</body>
</html>
How can I recognize which button pressed in line: $("#btn1").click(function(){
without having to write again the same code for btn2?
You can use a selector on all buttons and then get back the ID when clicked
$('button').click(function()
{
alert( $(this).attr('id') ); // Button ID clicked
});
You can use
$( '#btn1' ).click(function(){
submitForm( 'btnOne' );
});
$( '#btn2' ).click(function(){
submitForm( 'btnTwo' );
});
And then pass your parameter in a single function:
function submitForm( type ){
var dataString = $('#form_confirm_delete').serialize();
dataString += "&type=" + type;
$.ajax({
type: "POST",
url: "doSome.php",
data: dataString,
dataType: 'json',
cache: false,
success: (function(response)
{
alert('Yes');
})
});
}
Actually, simply use multiple selectors:
$('#btn1, #btn2').click(function(e) {
var btn1Clicked = $(this).is('#btn1');
var btn2Clicked = $(this).is('#btn2');
});

clearInterval() not working after stop button click

i am trying to Refresh div using java script . setInterval() and clearInterval (), its working fine, but i want stop Refresh process for single div when i clicked stop button ..clear Interval not working herer
<script type ="text/javascript">
$(document).ready(function () {
$('.output').click(function () {
var id = $(this).closest('.g').attr('id');
Go(id);
})
$('.bt').click(function () {
var id = $(this).closest('.g').attr('id');
stop(id)
});
function Go(id) {
id = setInterval(function () {
Chat_msg('Ch04', id, u)
}, 3000);
};
function stop(id) {
clearInterval(id);
}
})
</script>
</head>
<body>
<div id="a" class='g'>
<div class="output"></div>
<input id="Button1" type="button" value="stop" class="bt" />
</div>
<div id="b">
<div class="output"></div>
<input id="Button2" type="button" value="stop" class="bt"/>
</div>
<div id="c">
<div class="output"></div>
<input id="Button3" type="button" value="stop" class="bt" />
</div>
</body>
</html>
Use a global variable for your interval.
var interv = null;
interv = setInterval(function { ... }, 5000);
$('#btn').on('click', function(){
if (interv) clearInterval(intev);
})
It's likely the reference you associated with setInterval is not within the scope of your stop-button handler.
$("#start").on("click", function(){
var interv = setInterval(function(){ /*...*/ }, 1000);
});
$("#stop").on("click", function(){
clearInterval(interv);
});
In the code above, our interv variable is not within the scope of our #stop button handler. We could change this by moving it up another level:
var interv;
$("#start").on("click", function(){
interv = setInterval(function(){ /*...*/ }, 1000);
});
$("#stop").on("click", function(){
clearInterval(interv);
});
Now both handlers have access to the interv variable.
Looks like a combination of a scoping issue, and interchangeably using id DOM attributes with setInterval response values.
<script type ="text/javascript">
$(document).ready(function () {
var timeouts = {};
$('.output').click(function () {
var id = $(this).closest('.g').attr('id');
go(id);
});
$('.bt').click(function () {
var id = $(this).closest('.g').attr('id');
stop(id);
});
function go(id) {
timeouts[id] = setInterval(function () {
Chat_msg('Ch04', id, u)
}, 3000);
}
function stop(id) {
clearInterval(timeouts[id]);
}
});
</script>
The way I have gone about this in the past is using a function that uses a set timeout to call itself.
var stop = false
function caller () {
if (stop === true){
//do something
setTimeout(caller() , 1000);
}
else{
//do something after button has been click and stop is set to true
}
}