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.
Related
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.
Working through the setElement part of Addy Osmani's backbone.js tutorial.
He presents this example:
// We create two DOM elements representing buttons
// which could easily be containers or something else
var button1 = $('<button></button>');
var button2 = $('<button></button>');
// Define a new view
var View = Backbone.View.extend({
events: {
click: function(e) {
console.log(view.el === e.target);
}
}
});
// Create a new instance of the view, applying it
// to button1
var view = new View({el: button1});
// Apply the view to button2 using setElement
view.setElement(button2);
button1.trigger('click');
button2.trigger('click');
However, he doesn't explain why there is different output for button1.trigger('click'); vs. button2.trigger('click'); -- possibly a dumb question, and I know that these are different ways of attaching the view to the button elements, but why does button2.trigger('click'); also return true?
button1.trigger('click'); shouldn't produce any output at all from that code.
setElement is fairly simple:
setElement view.setElement(element)
If you'd like to apply a Backbone view to a different DOM element, use setElement, which will also create the cached $el reference and move the view's delegated events from the old element to the new one.
So view.setElement(e) does four things:
Unbinds the view's events from view.el.
Sets view.el = e.
Sets view.$el = Backbone.$(e).
Binds the view's events to the new view.el.
The result is that nothing will be left listening to events from button1 and view will be listening to events from button2.
A more thorough example might help so let us attach some more click event handlers:
var button1 = $('<button id="button1"></button>').click(function() {
console.log('button1 jQuery', this);
});
var button2 = $('<button id="button2"></button>').click(function() {
console.log('button2 jQuery', this);
});
var View = Backbone.View.extend({
events: {
click: function(e) {
console.log('Backbone ', view.el, view.el === e.target);
}
}
});
var view = new View({el: button1});
view.setElement(button2);
button1.trigger('click');
button2.trigger('click');
Demo: http://jsfiddle.net/ambiguous/S7A9z/
That should give you something like this in the console:
button1 jQuery <button id="button1"></button>
button2 jQuery <button id="button2"></button>
Backbone <button id="button2"></button> true
Both raw jQuery event handlers will be triggered as expected but we'll only get the button2 event through Backbone because the setElement call happened before the trigger calls.
So why is view.el === e.target true? Well, you're clicking on button2 so e.target will be button2 and the view.setElement(button2) call replaces view.el so this.el inside the Backbone click handler will also be button2.
How to monitor change in input in backbone?
In AngularJs
<div ng-controller="W3">
<input type="text" ng-model="val" >
<p>{{val}}</p>
</div>
I want that field value has been displayed in <p></p>.
You have to add it as an event on your view:
var MyView = Backbone.View.extend({
events: {
'keyup input': 'updateParagraph'
},
updateParagraph: function(ev) {
this.$('p').html(ev.target.value);
}
});
This assumes that your HTML for the view is just like what you have in your question. If you want to use multiple events, you will need to add each one to the hash. Like:
events: {
'keyup input': 'updateParagraph',
'propertychange input': 'updateParagraph',
// etc.
}
And if your view is tied to a model and the input should update the model, I would write it this way instead:
var MyView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'change:text', this.updateParagraph);
},
events: {
'keyup input': 'updateModel'
},
updateModel: function(ev) {
var target = ev.target;
// Assuming that the input name is the model attribute
// To simplify, I'm just going to set something specific
this.model.set('text', target.value);
},
updateParagraph: function(model, value) {
// Set the input value as well, so it stays in sync
this.$('input').val(value);
this.$('p').html(value);
}
});
That makes it so that if you change that attribute on the model in any other view, the paragraph would still update, regardless of whether it was that input or not.
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/
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).