How to update knockoutjs view model on user confirm? - javascript

I have a edit in place section to which I want to add a confirmation of changes before the knockoutjs model is updated.
Here's the jsFiddle example of what I have now.
Here's what I would like it to do.
User clicks on editable section
textbox appears with save/cancel buttons next to it.
if user makes a change and clicks save, view model is updated
if user makes a change, but decides to keep the original content, they click cancel, view model remains unchanged, texbox is hidden, and editable element remains unchanged.
The behavior of the cancel click is what I'm not sure how to implement. Can anyone suggest how this could be done?

I prefer to use custom binding handler for this.
Example http://jsfiddle.net/7v6Dx/10/
Html
<div>
<span class="editField">
<span data-bind="text: Address1">Click here to edit</span>
<input type="text" data-bind="clickEditor: Address1">
</span>
</div>​
JavaScript
ko.bindingHandlers.clickEditor = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var $element = $(element).hide();
var $text = $element.prev();
var $buttons = $("<span class='editConfirm'> \
<button class='saveEdit' type='button'>Save</button> \
<button class='cancelEdit' type='button'>Cancel</button> \
</span>").hide().insertAfter($element);
var $editElements = $buttons.add($element);
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$buttons.remove();
});
var _toggle = function(edit) {
$text[edit? 'hide' : 'show']();
$editElements[edit? 'show' : 'hide']();
}
$text.click(function(e) {
_toggle(true);
});
$editElements.find('.saveEdit').click(function() {
_toggle(false);
valueAccessor()($element.val());
});
$editElements.find('.cancelEdit').click(function() {
_toggle(false);
$(element).val(ko.utils.unwrapObservable(valueAccessor()));
});
}
, update: function (element, valueAccessor) {
$(element).val(ko.utils.unwrapObservable(valueAccessor()));
}
};
$(document).ready(function() {
var helpText = "Click here to edit";
function appViewModel() {
this.Address1 = ko.observable(helpText);
}
ko.applyBindings(appViewModel());
});​

I was thinking you could probably use a writable computed property to handle this. But it might be easier to just have to separate properties. One property is the real property and the other shadows it. When you bring up the editable section, it's actually bound to the shadow value. When the ok button is clicked, you copy the shadow value to the real value. If cancel is clicked, you do the opposite (copy the real value to the shadow value).

Related

JavaScript's .select() method procs only from second attempt

With Angular, I'm trying to implement a way to change a value with an 'Edit' button click in such a way that when this button is clicked, an input is displayed over the text, and when the 'Save' button is clicked, the input's opacity becomes 0, and the model's value is applied.
I've created a jsfiddle to make my issue a bit more visual. JSFIDDLE DEMO
The issue is the following: I want to select the text to make it obvious for the user that it can be changed now, after the 'Edit' button is clicked. I do it this way:
var input = angular.element(document.querySelector('input'))[0];
input.focus();
input.select();
The only problem is that the input.select() only works on second attempt. You can see it in the demo. I have no rational explanation to this whatsoever. I need to mention that this app that I'm writing is for Electron, it means that it will only launch in Chromium, so I don't need cross-browser support for this.
When the 'Edit' button is clicked for the first time, no selection happens:
But when I click 'Save' and then 'Edit' again, everything works as expected:
Any thought would be much appreciated!
Use $timeout , it will trigger digest cycle
var app = angular.module('app', []);
app.controller('mainController', function($timeout,$scope) {
var vm = this;
vm.address = '127.0.0.1';
vm.name = 'anabelbreakfasts';
vm.editing = {
address: false
};
vm.temp = {
address: null
};
vm.changeClick = function(element) {
vm.editing[element] = !vm.editing[element];
if (vm.editing[element]) {
vm.temp[element] = vm[element];
var input = angular.element(document.querySelector('div.row.' + element + ' input'))[0];
$timeout(function(){
input.focus();
input.select();
});
} else {
vm[element] = vm.temp[element];
}
};
});
Fiddle
Use setTimeout:
setTimeout(function(){
input.select();
}, 0)
Also, input.focus() is kind of redundant

Avoid clicking twice to begin editing boolean (checkbox) cell in Backgrid

We are using Backgrid and have discovered that to begin editing a "boolean" (checkbox) cell in Backgrid, you must click twice: the first click is ignored and does not toggle the state of the checkbox. Ideally we would get to the root of what is causing this behavior (e.g. is preventDefault being called) and solve it there, but I at first I tried a different approach with the following extension of BooleanCell's enterEditMode method which seemed like a logical place since it was upon entering edit mode that the checkbox click was being ignored.
Problem is my attempt also toggles the state of the previously edited checkbox. Here is the code.
var BooleanCell = Backgrid.BooleanCell.extend({
/*
* see https://github.com/wyuenho/backgrid/issues/557
*/
enterEditMode: function () {
Backgrid.BooleanCell.prototype.enterEditMode.apply(this, arguments);
var checkbox = this.$('input');
checkbox.prop('checked', !checkbox.prop('checked'));
}
});
The following seems to work:
var BooleanCell = Backgrid.BooleanCell.extend({
editor: Backgrid.BooleanCellEditor.extend({
render: function () {
var model = this.model;
var columnName = this.column.get("name");
var val = this.formatter.fromRaw(model.get(columnName), model);
/*
* Toggle checked property since a click is what triggered enterEditMode
*/
this.$el.prop("checked", !val);
model.set(columnName, !val);
return this;
}
})
});
This is because the render method gets called by Backgrid.BooleanCell's enterEditMode method on click, and said method destroys and re-creates the checkbox as follows but in so doing loses the checked state (after the click) of the original "non-edit-mode" checkbox
this.$el.empty();
this.$el.append(this.currentEditor.$el);
this.currentEditor.render();
A simpler approach:
var OneClickBooleanCell = Backgrid.BooleanCell.extend({
events: {
'change input': function(e) {
this.model.set(this.column.get('name'), e.target.checked);
},
},
});
This bypasses the CellEditor mechanism entirely and just reacts to the input event on the checkbox by updating the model.

Backbone, Getting the id/name of the of changed element

I am using Backbone.js with stickit for binding. I have something like below. How do I know which element the user has clicked? (Radio buttons)
initialize: function() {
this.listenTo(this.model, 'change', this.blockDiv);
}
blockDiv : function() {
console.log('The changed element is '+); //How do i know which element the user has changed?
}
bindings : {
'[name=element1]' : element1,
'[name=element2]' : element2
}
You are listening changes from your model, not DOM events directly. You can check what attributes of model have changed with changedAttributes.

ListView doesn't fire selectionchanged

My HTML:
<div id="listViewBoxOffice"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemTemplate: select('#movieThumbnailTpl'), selectionMode: 'single' }">
</div>
My Javascript:
WinJS.UI.Pages.define("/pages/home/home.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
api.getBoxOffice().done(this.boxOffice, this.errBoxOffice);
listViewBoxOffice.winControl.addEventListener('selectionchanging', this.selectionchanging);
listViewBoxOffice.winControl.addEventListener('selectionchanged', this.selectionchanged);
},
boxOffice: function (movies) {
var list = new WinJS.Binding.List(movies);
listViewBoxOffice.winControl.itemDataSource = list.dataSource;
},
errBoxOffice: function (err) {
debugger;
},
selectionchanged: function (evt) {
console.log('changed');
},
selectionchanging: function (evt) {
console.log('changing');
}
});
My problem:
The event selectionchanged is never fired. The event selectionchanging is fired but with bad value in newSelection.
While the documentation isn't as clear about this as I think it should be, you'll need to set the tapBehavior property to "toggleSelect" so that an item is fully selected. By default, the behavior is invokeOnly and with that it doesn't fully select the item. It clicks, but isn't selected.
There's a decent example located in the MSDN documentation.
If you store off a copy of the listViewBoxOffice instance, then from the events, you can get the current list via a promise:
listViewBoxOffice.selection.getItems().done(function(items) {
// do something with the items...
});
To check wheather the selectionchanged event is working or not Right click on the listview item.I think when we just only click on the listview item it needs iteminvoke event and for selection we need to right click on the item.
Following is the code snipet which is firing the selectionchanged event
<div id="UserListView" data-win-control="WinJS.UI.ListView" style="border-top: 5px solid #000; min-width:500px;"
data-win-options="{
selectionMode:'multi',
itemTemplate:select('#itemsList'),
layout:{
type:WinJS.UI.GridLayout}}"
>
</div>
and in the js
UserListView.addEventListener("selectionchanged", selection);
function selection(evt) {
var test = "testing";
}
set the breakpoint and you can check in the evt the type="selectionchanged"
please try using Item invoked. Here's the Msdn link
Item invoked Winjs Listview
And try changing the selection accordingly.
Also if this does not work or you want selection changed only then Please post in the Template that you have designed. Will need to go through the entire code :)

Ember.TextField binding changed in Ember RC1?

I'm trying to build a view that will initially display text. If the user double-clicks, it will replace that text with an input field. This way the user can easily update the text (like using the "contenteditable" attribute).
I have an approach that works in Ember pre4, but not in Ember RC1. In RC1, the Ember.TextField does not initialize to the parent view's value property. When you double-click the label text, it creates an empty input field. Here are two fiddles:
Pre4 (working): http://jsfiddle.net/mattsonic/cq5yy/5
RC1 (same code - not working): http://jsfiddle.net/mattsonic/UUac9/15
Any idea what changed inside Ember? Thanks.
Here is the code:
App.InputView = Ember.TextField.extend({
classNames: ["input-small"],
valueBinding: "parentView.value",
didInsertElement: function () {
this.$().focus()
},
focusOut: function () {
parent = this.get("parentView");
parent.setLabelView();
}
});
App.LabelView = Ember.View.extend({
tagName: "span",
template: Ember.Handlebars.compile("{{view.value}}"),
valueBinding: "parentView.value",
doubleClick: function () {
parent = this.get("parentView");
parent.setInputView();
}
});
App.LabelEditView = Ember.ContainerView.extend({
tagName: "span",
labelView: App.LabelView.extend(),
inputView: App.InputView.extend(),
didInsertElement: function () {
this.setLabelView();
},
setInputView: function () {
this.set("currentView", this.get("inputView").create());
},
setLabelView: function () {
this.set("currentView", this.get("labelView").create());
}
});
I found a solution that I don't like at all. But, it solves the problem as described.
focusIn: function() {
var val = this.get("parentView.value");
this.set("value", "");
this.set("value", val);
},
If you set the input field's value to the correct value during the focusIn event, it still fails. But, if you set the input field's value to a different value and then switch it back, the input field will appear with the correct value.
I would love to know a better way to solve this problem. The Ember pre4 solution is more much elegant than this.
Working fiddle: http://jsfiddle.net/mattsonic/UUac9/19/

Categories

Resources