Rendering 2 views using single collection in Backbone - backbone.js

I am facing issue while rendering 2 views using single collection in Backbone.
In my app, there is a Main View, in which I am initializing Items Collection, so it can be accessible to all children views.
There is an Item View, which is being used to render single item in both the Views (Quick and normal) whenever any item is added to the collection.
Quick View has to be render for all the Routes of the App. Whereas, Normal view will rendered only domain/checkout route called. Domain/checkout can be called in 2 ways:
Click on checkout button – in this case, both are in sync and working fine.
Access domain/checkout Path directly – in this case, Quick and Normal views are not in sync.
// Main View
var mainView = Backbone.View.extend({
el: 'body',
template: {
header: Handlebars.compile(headerTemplate),
mainNav: Handlebars.compile(mainNavtemplate),
footer: Handlebars.compile(footerTemplate)
},
initialize: function() {
_.bindAll(this);
AW.collection.bag = new bagCollection();
AW.collection.bag.fetch({reset:true});
},
render: function() {
this.$el.html(this.template());
this.loadSubView('bagQV');
}
});
// Quick View
var bagQuickView = Backbone.View.extend({
tagName: 'div',
id: 'myBagQV',
template: Handlebars.compile(bagQuickViewTemplate),
initialize: function() {
_.bindAll(this);
//this.collection.fetch({reset:true});
//this.collection.bind("reset", _.bind(this.render, this));
this.listenTo(this.collection, 'add', this.addItem);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
if($('#myBagQV').length == 0) {
this.$el.html(this.template());
$('body').append(this.el);
}
this.addAllItems();
return this;
}
});
// Normal View
var bagView = Backbone.View.extend({
tagName: 'div',
id: 'myBag',
template: Handlebars.compile(checkoutBagTemplate),
initialize: function() {
_.bindAll(this);
//this.collection.fetch({reset:true});
//this.collection.bind("reset", _.bind(this.render, this));
this.listenTo(this.collection, 'add', this.addItem);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.$el.html(this.template());
$('#checkoutContainer #details').append(this.el);
this.addAllItems();
return this;
}
});

Related

Work with extjs controller

I have a question regarding ExtJS controllers. My code:
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
init: function() {
this.control({
'gridClients button[action=deleteClient]': {
click: this.onButtonClickDelete
},
'gridClients button[action=refreshClients]': {
click: this.onButtonClickRefresh
},
'gridClients button[action=printClients]': {
click: this.onButtonClickPrint
}
})
},
onButtonClickDelete: function(button, e, options) {
alert('DELETE?');
},
onButtonClickRefresh: function(button, e, options) {
alert('REFRESH?');
},
onButtonClickPrint: function(button, e, options) {
alert('PRINT?');
}
});
I'm going to refer to a grid named 'gridClients', and I'd like to know if there is any way to create a variable inside the driver file...
I'm going to refer to a grid named 'gridClients', and I would like to know if there is any way to create a variable inside the driver file, to refer to that grid.
Example, I would like something similar to:
Var Grid = Ext.ComponentQuery.query (#gridClients) [0];
And use it like this:
OnButtonClickRefresh: function (button, e, options) {
Grid.getStore (). Load ();
}
I really do not know where to declare that var...
In a controller, you are expected to work with the refs. Example:
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
init: function() {
...
},
refs:[{
ref:'clientsGridExample',
selector: '#gridClients'
}],
OnButtonClickRefresh: function (button, e, options) {
this // inside a controller, these functions are scoped to controller
.getClientsGridExample() // refs are automatically converted to getter methods
.getStore().load(); // Do whatever you want to do
}
});
It's pretty clear, if you check the Ext.app.Controller documentation.
You can set refs in your controller and use generated getter to get the grid you need. For example, if you have ref with value clientsGrid, getter getClientsGrid() will be created by ExtJS.
`
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
refs: [
{ ref: 'grid', selector: '#gridClients' }
],
init: function() {
this.control({
'gridClients button[action=deleteClient]': {
click: this.onButtonClickDelete
},
'gridClients button[action=refreshClients]': {
click: this.onButtonClickRefresh
},
'gridClients button[action=printClients]': {
click: this.onButtonClickPrint
}
})
},
onButtonClickDelete: function(button, e, options) {
this.getGrid().doSomething();
},
onButtonClickRefresh: function(button, e, options) {
alert('REFRESH?');
},
onButtonClickPrint: function(button, e, options) {
alert('PRINT?');
}
});
`
Inside the OnButtonClickRefresh listener you can get the grid like this:
var grid = button.up("grid");
Link to the fiddle

Directive does not update on service model change

I have a service that contains my application's model:
angular.module('adminClient').factory('myApi', function () {
var model = {};
model.myObject = {
label: 'New York City'
};
return {
model: function () {
return model;
}
};
});
A partial/controller has access to the model and can set a new myObject:
angular.module('adminClient').controller('MySelectNetworkCtrl', function ($scope, myApi) {
$scope.myObject = myApi.model().myObject;
});
In addition to this, I have a directive that should display the label contained in myObject:
angular.module('nav').directive('myTopBar', function(myApi) {
return {
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'nav/directive/my-top-bar/my-top-bar.html',
link: function(scope, element, attrs, fn) {
scope.myObject = myApi.model().myObject;
}
};
});
And here is the HTML:
<div id="my-top-bar">
<span ng-bind="myObject"></span>
</div>
When I run the application, the label is displayed fine (New York City), but as soon as the controller changes the myObject, the label in the directive remains unchanged. I can also see this in the Chrome Angular scope inspector.
Any idea how I can make the directive display the current value even after changed by the controller?
It all depends upon how the controller is updating the model object. Since your code
scope.myObject = myApi.model().myObject;
gets hold of this specific model object it works fine as long as model object myObject properties are changed.
But if the controller does
myApi.model().myObject= {}; //or any new object
Now the model returned by service has different myObject and the directive one is different. Therefore changes do not work.
Instead in directive do:
scope.myObject = myApi.model(); // bind to model
And update the bindings in the directive template accordingly. See if it works
Check this plunkr
If you want to modify your myObject inside your controller, and also want this change to be reflected inside your my-top-bar directive which then updates the directive's scope from myApi service, then you should do it like this.
Update the myObject in your controller and then also update it in your service through a setter method.
// Doing this will update your scope.myObject but
// will not change the moObject in your service
$scope.myObject = {
label: 'updated label'
};
// This will update your service, and anyone making a myApi.myApi.model() after this will get an updated myObject
myApi.setModel($scope.myObject);
// Notify directive of the change
$scope.$broadcast('updateDirective');
And in the factory:
angular.module('plunker').factory('myApi', function() {
var model = {};
model.myObject = {
label: 'New York City'
};
return {
model: function() {
return model;
},
setModel: function(newModel) {
model.myObject = newModel;
}
};
});
Finally, the directive:
angular.module('plunker').directive('myTopBar', function(myApi) {
return {
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'top-bar.html',
link: function(scope, element, attrs, fn) {
console.log('in directive link', myApi.model().myObject);
scope.myObject = myApi.model().myObject;
},
controller: function($scope) {
$scope.$on('updateDirective', function() {
console.log('updating the scope of directive so that ng-bind works');
$scope.$apply(function() {
$scope.myObject = myApi.model().myObject;
console.log($scope.myObject);
});
});
}
};
});

Angularjs Directive not loading PopOver content

I am trying to develop a FaceBook like notification (like when friend requests are received there is an icon that glows with number of notifications on the top right corner).
For this i wrote a popover directive.
app.directive('popOver', function ($compile) {
var itemsTemplate = "<div ng-repeat='item in items'>{{item}} <br/><hr/></div> ";
var getTemplate = function (contentType) {
var template = '';
switch (contentType) {
case 'items':
template = itemsTemplate;
break;
}
return template;
}
return {
restrict: "A",
transclude: true,
template: "<span ng-transclude></span>",
link: function (scope, element, attrs) {
var popOverContent = "<div></div>";
if (scope.items) {
var html = getTemplate("items");
popOverContent = $compile(html)(scope);
}
var options = {
content: popOverContent,
placement: "bottom",
html: true,
title: scope.title
};
$(element).popover(options);
},
scope: {
items: '=',
title: '#'
}
};
});
The items are populated in the Controller, and there i am using $timeout to fetch new data from database and fill scope.Items
In the UI i have a button which shows number of new notifications and on click of it i want to show a popover with items. The problem is when is click the button i the popover is not loading the new items.
<button pop-over items="items" class="navbar-btn btn-info round-button" title="Notifications:" > {{newAlertCount}} </button>
Directives have their own scope, so I'm supposing that when you change $scope.items in your controller you're talking about a different scope; I think that what you want is to look directly at the original $scope.items object, so I would add this:
scope : {
items : '=items'
},
to your directive.

ColorBox with AngularJS, with ng-click bindings

Ok, so it's fairly easy to create a directive for a colorbox, as explained here:
How to use Colorbox with Angular JS
But what if you want to then bind buttons to ng-click events? Best practice would suggest that the handler function for the action (delete in my case) should be in a directive defined on the controller. My colorbox directive looks like this:
mod.directive('colorbox', function() {
return {
restrict: 'A',
scope: true,
controller: function($scope, $element){
$scope.delete = function() {
console.log('I want this code to fire');
};
},
link: function (scope, element, attrs) {
$(element).colorbox({ inline: true, title: ' ', href: "#delconfirm", className: "delgroup", width: 450, height: 200, close: "" }, function() {
});
}
};
});
My template that will be loaded into the colorbox contains an action button with an ng-click:
<button ng-click="delete()">Delete</button>
However, that doesn't work. IF I move that delete function to my parent controller instead, and remove the controller from the directive, it then works. Any ideas why?

extjs - Menu click event gets fired twice. Why?

The controller that controls an event using the recommended this.control: construct :
Ext.define('Mb.controller.Sav_vpc', {
extend: 'Ext.app.Controller',
init: function() {
console.log('controller.init called');
this.control({
'#storeMenu menuitem': {
click: this.onStoreMenuClicked
}
});
}
});
The function onStoreMenuClicked gets called twice, because the init method of the controller gets called twice, therefore it listens twice to the event.
But when is the controller.init() called ? And why is it called twice ?
Here is my application.launch function:
Ext.define('Mb.Application', {
extend: 'Ext.app.Application',
launch: function() {
console.log('launching app');
var controller = Mb.app.getController('Name');
console.log('end launching app');
});
...
This will give this output in the console:
controller.init called
launching app
controller.init called
end launching app
When calling getController inside application.launch, the init method of the controller gets called again, even the controller was already initialized.
I don't know if this is by design or if it is kind a bug, but I found a solution based on the recommendations of #AlexTokarev 'Inter-controller communication'.
Controller:
Ext.define('Mb.controller.Sav', {
extend: 'Ext.app.Controller',
init: function(){
this.listen({
controller: {
'*': {
savOnLaunch: this.onUserLoaded
}
}
})
},
Application launch:
Ext.define('Mb.Application', {
extend: 'Ext.app.Application',
launch: function() {
this.fireEvent('savOnLaunch');
}

Resources