I have a template in underscore having a button inside it. I want a click event to be mentioned in the event hash in the backbone view.
template code is:
<script type="text/template" id="ledgerListing">
<button class="btn btn-danger pull-right" id="addLedgerButton">Add Ledger</button>
</script>
View Code is:
app.ledgerView=Backbone.View.extend({
el:"#container",
template:_.template($("#ledgerListing").html()),
events: {},
initialize: function(){
},
render: function()
{
this.$el.html(template())
}
});
Now how to specify the click event in the event hash for the button with id addLedgerButton
Your can add events as the following, (format should be event type and a space and the element
events: {
'click #addLedgerButton': 'myclick'
},
and define a function named myclick:
myclick: function () {
alert(1);
}
Here is jsfiddle. http://jsfiddle.net/w2jm7/
Related
I have an angular application which contains several delete buttons somewhat like this:
<button class="btn btn-default" ng-click="delete($index)">x</button>
Now as we are nearing production deployment and I want delete buttons to behave nicely and "alert()" the user before actually deleting the object.
How can I retro-fit this feature in all the delete buttons through a directive. I would like to have a directive called "ask":
<button ask class="btn btn-default btn-xs" ng-click="delete($index)">x</button>
which I can use to affect the behaviour of any button.
Somehow I am not able to think through this
app.directive("ask", function() {
return function(scope, elems, attrs) {
// what to do here:
// 1. shall I change the elems.attr("ng-click")
// 2. or scope.attrs("ngClick")
????
}
});
Please guide me with the best practice and some code samples. Also note that the ng-click callback of all the delete buttons are different and the application already makes extensive use of isolate scope and child scope directives.
Try this implementation
angular
.module('test', [])
.directive('ask', function() {
return {
restrict: 'A',
scope: {ask: '#', onAsk: '&'},
link: function(scope, elem, attrs) {
elem.on('click', function(e) {
if(confirm(attrs.ask)) {
scope.onAsk();
scope.$apply();
}
});
}
};
})
.controller('ItemsCtrl', function($scope) {
$scope.items = [1,2,3,4,5];
$scope["delete"] = function(index) {
$scope.items.splice(index, 1);
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="ItemsCtrl">
<ul><li ng-repeat="item in items track by $index">{{item}}
<button ask="Are you sure you want to delete item?" on-ask="delete($index)">x</button>
</li>
</ul>
</div>
You can build a directive for this and in template of directive have button element and the ng-click event.
Pass index and delete function as inputs to this directive.
app.directive("deleteFruit", function(){
return{
// You can also pass one more binding if you want to have a specific alert message
scope:{
index: '#', delete:'&'
},
template:'<button class="btn btn-default" ng-click="deleteFruit()">x</button>',
link: function(scope){
scope.deleteFruit = function(){
// When user clicks, first call the alert function then use the promise returned by dialog and check whether user wants to delete or not
//modal.dialog().then(function(userSelectedOption){
// If user wants to delete then go a head and call delete function on main controller
// if(userSelectedOption){
// scope.delete(scope.index);
// }
//})
scope.delete(scope.index);
}
}
}
})
MainCtrl HTML
<delete-fruit delete="delete(index)" index={{$index}}></delete-fruit>
Plunker.
This question already has an answer here:
Backbone click event not firing in template View
(1 answer)
Closed 6 years ago.
I am getting the entire html code using jquery get() method and setting it on the el of backbone view.The view gets rendered perfectly but the click events i added are not firing. As i am a newbie to backbone i am not able to find the issue. Any help would be appreciated.
The currentTabID is contain the div id on which i want this html to be rendered.
view.js
var MyFirstView = Backbone.View.extend({
currentTabID:'',
initialize:function(){
this.render();
},
render: function (){
var self = this;
self.el = self.options.currentTabID;
$.get('resources/html/myBB.html', function(data) {
$(self.el).html(_.template(data));
});
return this;
},
events: {
'click .savebtnBB': 'invokeME'
},
invokeME: function (){
console.log('Fired');
}
});
Html looks something like below
myBB.html
<div id="sample_tab">
<div class="sub-main">
<form>
..
</form>
</div>
<div class="button">
<button class="savebtnBB">click me</button>
</div>
</div>
view.el is an actual dom element holding the event listeners for your view. You're replacing view's reference to that element with some number and appending the template to some other element.
Your view should act like an isolated unit as much as possible. Your code for appending it to something else should be outside the view, where you're creating it. Your code should look something like the following:
var MyFirstView = Backbone.View.extend({
initialize: function() {
var self = this;
$.get('resources/html/myBB.html', function(html) {
self.template = _.template(html);
this.render();
});
},
events: {
'click .savebtnBB': 'invokeME'
},
render: function() {
this.$el.html(this.template({ /*some data for template*/ }));
},
invokeME: function() {
console.log('Fired');
}
});
var viewInstance = new MyFirstView();
/*append to whatever you want*/
$(currentTabID).append(viewInstance.el);
I am seeing the click event is triggered multiple times for each row on the itemview
return Marionette.ItemView.extend( {
template: ItemViewTemplate,
tagName: 'tr',
className: 'ItemRow',
templateHelpers: viewHelpers,
events: {
'click .editRowItem': 'editRowItem'
The editRowItem() function is triggered multiple times. What is the correct way to trigger the click just on that particular row?
Thanks!
Usually it shouldn't trigger multiple times. It can happen however, for example when:
Nested elements with the same class in a view. Event bubbling will cause multiple events to be fired.
Parent views listening to click events on a class which is present in some or all children views.
An example (stripped non-relevant parts):
<script type="text/template" id="movie-list-item">
<button type="button" class="btn btn-success some-button">
<span class="some-button">Click here</span>
</button>
</script>
// Itemview
var movieItemView = Marionette.ItemView.extend({
template: "#movie-list-item",
ui: {
viewButton: '.some-button'
},
events: {
'click #ui.viewButton': 'clickHandler'
},
clickHandler: function(ev) {
// Log the click
console.log('There was a click in an itemView!');
// Uncomment the following to prevent multiple events:
//ev.stopPropagation();
}
});
// Composite view
var movieCompView = Marionette.CompositeView.extend({
template: "#movie-list",
itemView: movieItemView,
ui: {
viewButton: '.some-button'
},
events: {
'click #ui.viewButton': 'clickHandler'
},
clickHandler: function(ev) {
// Log the click
console.log('There was a click in a collectionView!');
// Uncomment the following to prevent multiple events:
//ev.stopPropagation();
}
});
Demo here: http://jsfiddle.net/Cardiff/7d3fC/2/
Note the following, if we don't use ev.stopPropagation() in this case to prevent the event from bubbling, the console will log 4 entries; being two for the itemView and two for the collectionView. To prevent this behaviour (and you shouldn't use a click event in the collectionView in this case) and thus receive one instead of two events we use ev.stopPropagation().
Also keep in mind that using the ui attribute of a view to describe the components is considered good practice and can make your life a little easier.
Just try following if you want to apply click event to each row item in the template:
events: {
'click' : 'editRowItem'
}
I am a backbone newbie and I am trying to develop a Todo like App.
I have a Main view which is a list view and it has subviews. - the subview content can be edited on double click and it would get saved when the enter key is pressed. - very similar to todo example given in backbone github code.
var SubView = Backbone.View.extend({
tagName: "li",
events: {
"dblclick" : "show_edit_view",
"blur .element" : "close_edit_view",
"keypress .element" : "save_edit_view",
"click button.remove" : "remove_question"
},
initialize: function(){
this.render();
this.listenTo(this.model, "change", this.render);
},
render: function(){
this.$el.html(_.template($("#sub_view_template").html(),this.model.toJSON()));
return this;
},
show_edit_view: function() {
this.$el.find("div.view").addClass("no_show");
this.$el.find("input").removeClass("no_show");
},
close_edit_view: function(){
this.$el.find("div.view").removeClass("no_show");
this.$el.find("input").addClass("no_show");
},
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: e.currentTarget.value});
this.close_edit_view();
}
}
});
And the template for this is
<script id="sub_view_template" type="text/x-template">
<div class="view"><%= name %></div>
<input class="element no_show" value="<%= name %>" type="text" /> <button class="remove">Remove</button>
</script>
This one works fine, the model is updated in the view and the update post request is sent to the server.
But, when I change the initialization and save_edit_view functions, only the first change event is fired and not the change events.
initialize: function(){
this.render();
this.listenTo(this.model, "change", this.render);
this.input = this.$("input.element");
},
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: $(this.input).val()});
this.close_edit_view();
}
}
I was wondering what could the problem be?
Thanks for any help!!!
The problem is you are referring to only one object. This means when you make the assignment:
this.input = this.$('input.element'); // match the current elements.
You are only getting the value from that exact object. After the first change, this.input is not the same object that contains your new value, and fails to save the model with a new value.
A demonstration that may help:
console.log(this.$('input.element') != this.$('input.element')); // true
This is why the following would work:
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: this.$('input.element').val()});
this.close_edit_view();
}
}
I guess this.$("input.element"); refers to the first item from the list.
And when you first time change model value with the value from the first item it works. But second time it doesn't works because the value of the first item still the same.
That is why you have to get input value from the event - e.currentTarget.value
I found a good solution for inline editing content in angular js that is created by running ng-repeat on a model: https://stackoverflow.com/a/16739227/2228613
To expand on that solution I added a button to the page that has a ng-click directive as so:
<button ng-click="addCategory()" class="btn btn-large btn-primary" type="button">
<i class="icon-white icon-plus"></i> Add Category
</button>
The addCategory function is defined in my controller:
$scope.addCategory = function(){
var newCategory = {id:0, name:"Category Name"};
$scope.categories.unshift(newCategory);
}
The goal here is to allow the user to add a new record and automatically trigger the inline-edit directive once the view is updated with the new row. How can I trigger the inline-edit directive in such a manner?
One technique that i've used is to have a boolean change values and have a $watch on it inside the directive that needs to be triggered.
myApp.directive('myDirective', function () {
return function (scope, element, attr) {
scope.$watch('someValue', function (val) {
if (val)
// allow edit
else
// hide edit
});
}
});
Then in your controller you'd set $scope.someValue = true; in your ng-click for the button.
plunker: http://plnkr.co/edit/aK0HDY?p=preview
UPDATE
I've gone a bit further with the above answer. I've made something more along the lines with what you're after.
Here's the plunk for it: http://plnkr.co/edit/y7iZpb?p=preview
This is the new directive:
.directive('editCar', function ($compile) {
return {
restrict: 'E',
link: function (scope, element, attr) {
var template = '<span class="car-edit">'+
'<input type="text" ng-model="car.name" />' +
'<button ng-click="someValue = false" class="btn btn-primary">Save</button></span>';
scope.$watch('someValue', function (val) {
if (val) {
$(element).html(template).show();
$compile($('.car-edit'))(scope);
}
else
$(element).hide();
});
}
}
})
It replaces the <edit-car></edit-car> element with the above template. The save button adds the values to an array called editedCars. I've left in some dummy code for submitting the entire thing using $http.post()
I have one possible solution for you: http://plnkr.co/edit/uzuKki (I worked on the original plunk as you mentioned.)
My idea is
Add "editMode" property to TODO model
Instead of passing in just todo.title to directive's scope, passing in the whole TODO object, which is inline-edit="todo" in index.html
In inline-edit.html, change every editMode to model.editMode (and every model to model.title to display title correctly)
In your controller's add method, create new object with editMode = true, e.g.
var newTodo = {id:0, name:"TODO Name", editMode: true};
$scope.todos.unshift(newTodo);