Ember.TextField binding changed in Ember RC1? - javascript

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/

Related

Unable to pass string in to jquery function

I wish to grab the current value of a search input and then clear the input box on focus, store the string and pass it into a function to re-populate the input box on blur.
$("input#search").bind('focus', function() {
var search_text = document.getElementById('search').value;
$("input#search").val("");
}).bind('blur', function(search_text) {
$("input#search").val(search_text);
});
Currently, this successfully grabs the value on focus and clears the input box, but on blur, it populates the input with [object Object].
Am I correctly passing the string on line 4?
Firstly, don't use bind(). It was deprecated a long time ago. Use on() instead.
With regard to your issue, you can't directly pass a parameter to the anonymous handler function in the manner you're attempting. As it stands your search_text variable will hold the blur event.
To fix this you could store the variable in a data attribute on the #search element itself. Try this:
$("input#search").on('focus', function() {
$(this).data('search-text', this.value).val('');
}).on('blur', function(search_text) {
$(this).val($(this).data('search-text'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="search" value="foo bar" />
Also, the behaviour you're creating here is similar to the placeholder attribute. It may be worth investigating that to see if it meets your needs.
Use .on() of jQuery to register event.
var search_text;
$("input#search").on('focus', function() {
search_text = document.getElementById('search').value;
$("input#search").val("");
});
$("input#search").on('blur', function() {
$("input#search").val(search_text);
});
Alternate could be ,
$("input#search").on('focus', function() {
var search_text = document.getElementById('search').value;
$("input#search").val("");
$(this).on('blur', function() {
$("input#search").val(search_text);
});
});
No. Here is how you would do, declaring the variable before the event listeners so that it's in the scope:
var search_text;
$("input#search").bind('focus', function() {
search_text = document.getElementById('search').value;
$("input#search").val("");
}).bind('blur', function() {
$("input#search").val(search_text);
});
The convention in JavaScript for naming variables in Camel Case, so you would rather use searchText (not that it really matters).

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.

Ember: set value of property on text field

I have two inputs, one for a number of miles and the other for a number of kilometers. I'm using a keyup event in each input to trigger a calculation in the other input. So, if I type 5 in the kilometers input, the value of the miles input will automatically update to 3.1068.
The miles input is bound to a miles property on a model, but the programmatic update of the miles field doesn't trigger the bound property to update. How do I make this work?
(Also: I'm only interested in the value of the kilometers input for calculation and display purposes. It doesn't need to be a property.)
Here are the input views:
App.MilesInput = Ember.TextField.extend({
elementId: 'miles-input',
focusIn: function() {
this.set('value', '');
},
keyUp: function() {
var miles = $('#miles-input').val();
$('#kilometers-input').val(miles * 1.609344);
}
});
App.KilometersInput = Ember.TextField.extend({
elementId: 'kilometers-input',
focusIn: function() {
this.set('value', '');
},
keyUp: function() {
var kilometers = $('#kilometers-input').val();
$('#miles-input').val(kilometers * 0.621371192);
}
});
and their corresponding template elements:
<div>
{{view App.MilesInput value=miles type="number"}}
<label>Mi</label>
{{view App.KilometersInput type="number"}}
<label>K</label>
</div>
Thanks!
Use a computed property and assign the same computed property for
App.IndexController = Ember.Controller.extend({
miles:'',
kilometers: function(){
return this.get('miles') *0.621371192;
}.property('miles')
});
In your template
{{input value=miles}}
{{input value=kilometers}}
As far as I know you dont need a custom viewElement for this.
I suggest you to read this
http://emberjs.com/guides/object-model/computed-properties/
Here is a JSBin
http://emberjs.jsbin.com/kitus/1/
Tell me if this works for you
App.IndexController = Ember.Controller.extend({
miles:'',
kilometers:'',
calcK: function (){
this.set('kilometers',this.get('miles')*0.621371192)
}.observes('miles'),
calcM: function (){
this.set('miles',this.get('kilometers')/0.621371192)
}.observes('kilometers')
});
jsbin
http://emberjs.jsbin.com/yopew/2/edit
You need to temporarily disable observers when there are mutually dependant properties in order to stop feedback loops. There is precious little documentation on this but I have used this approach and it does work.
The code below disables and re-enables the opposite observer around the setting of the property:
App.IndexController = Ember.Controller.extend({
miles:'',
kilometers:'',
calcK: function (obj,key){
Ember._suspendObserver(this, 'kilometers', null, 'calcM', function() {
obj.set('kilometers', obj.get('miles')*0.621371192);
});
}.observes('miles'),
calcM: function (obj,key){
Ember._suspendObserver(this, 'miles', null, 'calcK', function() {
obj.set('miles', obj.get('kilometers')/0.621371192)
});
}.observes('kilometers')
});
Since the above code avoids the feedback loop the input field the user is typing into wont get updated and their input won't get affected.
Here is a working JSBin http://emberjs.jsbin.com/boqos/1/

How to update knockoutjs view model on user confirm?

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).

Categories

Resources