Backbone view pass value to another view - backbone.js

MenuView.js
events: {
"click ul li": "MenuClick"
},
MenuClick: function (e) {
var elm = e.currentTarget;
return elm.firstChild.innerText;
},
ModuleView.js
ModuleView = Backbone.View.extend({
initialize: function () {
var moduleDt = new ModuleCol();
moduleDt.fetch({
data: JSON.stringify({ Code: "Pass to here" }),
dataType: "json",
type: 'POST',
contentType: "application/json; charset=UTF-8"
});
},
The code should work like, when clicking the ul li, I will get the value from the li and pass to the moduleView.js to proceed with backbone fetch, but I dont have any idea on how actually to make it, I had search through quite some link, but still can't figure out the correct, are it is correct to make it?
Update1:
View/MenuView.js
define(['underscore', 'backbone', '../Collection/MenuCol'], function (_, Backbone, MenuCol) {
var MenuView = Backbone.View.extend({
initialize: function () {
//var vent = _.extend({}, Backbone.Events);
var mnCol = new MenuCol();
mnCol.fetch({
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendMenu, this),
});
},
AppendMenu: function (msg) {
var feed = JSON.parse(JSON.stringify(msg));
//var title = _.pluck(_.flatten(feed[0]), "Title");
_.each(_.flatten(feed[0]), function (data) {
this.$('ul').append('<li class="inactive"><p class="none">' + data.Code + '</p><a>' + data.Title + ' </a></li>');
});
},
events: {
"click ul li": "MenuClick"
},
MenuClick: function (e) {
var elm = e.currentTarget
console.log(elm.firstChild.innerText);
this.trigger('itemChoose', elm.firstChild.innerText);
},
});
return MenuView;
});
View/ModuleView.js
define(['underscore', 'backbone', 'datatables', '../Collection/ModuleCol', '../View/MenuView'], function (_, Backbone, dataTable, ModuleCol, MenuView) {
var ModuleView = Backbone.View.extend({
initialize: function () {
//MenuView.vent.on('itmChoose');
MenuView.on('itemChoose', function (item_value) { });
var mdCol = new ModuleCol();
mdCol.fetch({
data: JSON.stringify({ Code: "" }),
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendModule, this),
});
},
AppendModule: function (msg) {
var feed = JSON.parse(JSON.stringify(msg));
$('table[id$=gvMenu]').dataTable({
"bAutoWidth": false,
"aoColumns": [
{ "sWidth": "10%" },
{ "sWidth": "90%" },
],
"bFilter": false,
"bInfo": false,
"bLengthChange": false,
"bSort": false,
"bPaginate": false,
"aLengthMenu": [
[25, 50, 100, 200, -1],
[25, 50, 100, 200, "All"]
],
"iDisplayLength": -1,
"aoColumns": [
{
"sTitle": "Code", "mDataProp": "Title",
},
{
"sTitle": "Description", "mDataProp": "Description",
}],
sAjaxSource: "",
sAjaxDataProp: "",
fnServerData: function (sSource, aoData, fnCallback) {
//console.log(feed.d);
//fnCallback(feed.d);
},
});
}
});
//MenuView.on('itmChoose', function (item_value) { });
return ModuleView;
});
master.js
require(['jquery', '../JScripts/View/MenuView', '../JScripts/View/ModuleView'], function ($, menu, module) {
$(document).ready(function () {
new menu({ el: $('#sidebar') });
new module({});
});
});
This is my code, but still, it is correct to code like this? I had spent a night to work it out, but it still say is undefined.
When li click, I get the value of the li, and pass to module.js to ajax data so that I can create datatable.
I continue to puzzle it out and see if I can make it today while waiting for your guide :) thanks

According your updates, you must supply instance of MenuView to ModuleView instance:
master.js:
require(['jquery', '../JScripts/View/MenuView', '../JScripts/View/ModuleView'], function ($, menu, module) {
$(document).ready(function () {
var menuInstance = new menu({ el: $('#sidebar') });
new module({menu: menuInstance});
});
});
ModuleView.js:
var ModuleView = Backbone.View.extend({
initialize: function (options) {
//MenuView.vent.on('itmChoose');
options.menu.on('itemChoose', function (item_value) { });
var mdCol = new ModuleCol();
mdCol.fetch({
data: JSON.stringify({ Code: "" }),
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendModule, this),
});
},

If your ModuleView and MenuView are equal components of system - pick out in your code some central component (e.g. descendant Backbone.Router) and make communication through it.
If your views aren`t equal components (e.g. ModuleView aggregates MenuView), you can subscribe on some event from MenuView in ModuleView and pass value:
//in ModuleView
menuView.on('itemChoosen', function(item_value) { .... });
//in MenuView
MenuClick: function (e) {
var elm = e.currentTarget;
this.trigger('itemChoosen', elm.firstChild.innerText);
},

Related

Backbonejs does not render data once deployed on a server?

I have a django + backbonejs application. The problem is that the data rendered by backbonejs after the api call is not rendered inside the data table on the live site. It works perfectly on my local machine.
My local machine shows the data
The live site shows empty row for each record
What can be the reason of this strange behavior?
var PatientPageView = Backbone.View.extend({
el: ".st-content",
initialize: function () {
this.$dataTable = this.createDataTable();
this.$inputTextField = this.$("#myInputTextField");
this.listenTo(this.collection, "add", this.addObject);
this.listenTo(this.collection, "reset", this.fetchAll);
this.listenTo(app.vent, "patient:create", this.save);
this.collection.fetch({reset: true});
},
events: {
'click #addBtn': 'openDialog',
'keyup #myInputTextField': 'searchTable',
'click .viewPatient': 'viewPatient',
'click .NotesIcon': 'viewNote',
'click .paperclipIcon': 'viewAttachment',
'click #export': 'export',
'click #print': 'print'
},
save: function (object) {
this.collection.add(object);
},
addObject: function (patient) {
var view = new PatientItemView({model: patient});
$('#patientsTable').DataTable().row.add(view.render().el).draw();
},
openDialog: function () {
var view = new PatientAddSubView({model: new Patient.Model});
view.open();
},
createDataTable: function () {
return $('#patientsTable').DataTable({
"aoColumns": [
null, null, null, null, null, null, null, null, null, {"bSortable": false}, {"bSortable": false}
],
"order": [[0, "asc"]]
});
},
searchTable: function () {
this.$dataTable.search(this.$inputTextField.val()).draw();
},
export: function (e) {
view = new ExportPageView.MainView({
content_type: 'patients'
});
view.open();
},
print: function (e) {
window.print();
},
viewPatient: function (e) {
var patient = this.collection.get(e.target.id)
var editPatientSubView = new PatientEditSubView({
model: patient, remover: PatientDeleteSubView,
eventString: 'patient:deleted'
});
editPatientSubView.open();
},
viewNote: function (e) {
var patient = this.collection.get(e.target.id)
view = new NotePageView.MainView({
collection: new Note.Collection,
relatedTo: patient,
content_type: 10
});
view.open();
},
viewAttachment: function (e) {
var patient = this.collection.get(e.target.id)
view = new AttachmentPageView.EditView({
collection: new Attachment.Collection,
relatedTo: patient,
contentType: 10
})
view.open();
},
fetchAll: function () {
this.collection.each(this.addObject);
}
});
The function 'addObject' renders this list.
The patient item view is simple
var PatientItemView = core.ItemView.extend({
tagName: "tr",
//template: _.template($('#patient-view-template').html()),
template: _.template(ViewPatientTmpl)
});

How can we do paging with backbone marionette composite view?

I am new to backbone and marionette. Now i m trying to implement paging with compositeview of marionettejs. Below is my code, what happens here that when a new fetch is done through my custom pager, existing data is getting replaced by new set of data instead of appending. Please help me to overcome this! Thanks in advance.
define(['text!/Templates/projects/_GroupItem.html', 'collections/projects/groups'], function (ProjectGroupsTmpl, GroupCollection) {
var GroupItemView = Backbone.Marionette.ItemView.extend({
tagName: 'li',
template: _.template(ProjectGroupsTmpl)
});
var CompositeView = Backbone.Marionette.CompositeView.extend({
template: _.template("<ul id='ulgroups' ></ul>"),
itemView: GroupItemView,
itemViewContainer: '#ulgroups',
initialize: function (params) {
this.isLoading = false;
this.ProjectID = params.id;
this.collection = new GroupCollection();
this.getData();
var self = this;
$(window).scroll(function () {
self.checkScroll();
});
},
getData: function () {
var that = this;
this.isLoading = true;
this.collection.fetch({
data: { ProjectID: this.ProjectID },
success: function (collection, response, options) {
that.isLoading = false;
}
});
},
checkScroll: function () {
var triggerPoint = 100; // 100px from the bottom
if (!this.isLoading && $(window).scrollTop() + $(window).height() + triggerPoint > $(document).height()) {
this.collection.page += 1; // Load next page
this.getData();
}
},
appendHtml: function (collectionView, itemView, index) {
$(this.itemViewContainer).append(itemView.el);
}
});
return CompositeView;
});
I have used backbone.paginator to resolve above issue and it works well. Below are the new code used for that.
Collection:
define([
'jquery',
'underscore',
'backbone',
'helper',
'paginator'
], function ($, _, Backbone) {
var Groups = Backbone.PageableCollection.extend({
url: 'projects/_groups',
mode: "infinite",
state: {
pageSize: null
},
queryParams: {
totalPages: null,
totalRecords: null
}
});
return Groups;
});
Marionette CompositeView:
define(['text!/Templates/projects/_GroupItem.html', 'collections/projects/groups'], function (ProjectGroupsTmpl, GroupCollection) {
var GroupItemView = Backbone.Marionette.ItemView.extend({
tagName: 'li',
template: _.template(ProjectGroupsTmpl)
});
var CompositeView = Backbone.Marionette.CompositeView.extend({
template: _.template("<ul id='ulgroups' ></ul>"),
itemView: GroupItemView,
itemViewContainer: '#ulgroups',
initialize: function (params) {
this.isLoading = false;
this.ProjectID = params.id;
this.grpcollection = new GroupCollection([], {
queryParams: {
ProjectID: params.id
}
});
this.collection = this.grpcollection.fullCollection;
this.getData();
var self = this;
$(window).scroll(function () {
self.checkScroll();
});
},
getData: function () {
var that = this;
this.isLoading = true;
this.grpcollection.fetch({
success: function (collection, response, options) {
if (response.length > 0) {
that.isLoading = false;
}
}
});
},
getNextPage: function () {
var that = this;
this.isLoading = true;
this.grpcollection.getNextPage({
success: function (collection, response, options) {
if (response.length > 0) {
that.isLoading = false;
}
}
});
},
checkScroll: function () {
var triggerPoint = 100; // 100px from the bottom
if (!this.isLoading && $(window).scrollTop() + $(window).height() + triggerPoint > $(document).height()) {
this.getNextPage();
}
},
appendHtml: function (collectionView, itemView, index) {
$(this.itemViewContainer).append(itemView.el);
}
});
return CompositeView;
});
I solved a similar problem recently by creating a temporary collection to hold the models for each paginated request. My setup was slightly different to yours, however, in that I created a Marionette controller to negotiate between the data and the view. A "show" method on the controller handled the initial data request and a "showMore" method handled subsequent requests. Here is basically what I did:
(function ($, _, Backbone, Marionette) {
var carData = [
{
make: 'Audi',
model: 'A4',
year: '1994'
},
{
make: 'BMW',
model: '3 Series',
year: '1975'
},
{
make: 'Chevrolet',
model: 'Cruze',
year: '2008'
},
{
make: 'Daimler',
model: 'Six',
year: '1994'
},
{
make: 'Fiat',
model: '500X',
year: '2015'
},
{
make: 'Honda',
model: 'Civic',
year: '1972'
},
{
make: 'Kia',
model: 'Optima',
year: '2015'
},
{
make: 'Lada',
model: 'Priora',
year: '2007'
},
{
make: 'Mitusbishi',
model: 'Lancer',
year: '1973'
},
{
make: 'Nissan',
model: 'Pathfinder',
year: '1995'
}
];
var Car = Backbone.Model.extend({
defaults: {
make: '',
model: '',
year: ''
}
});
var Cars = Backbone.Collection.extend({
model: Car,
rows: 3,
page: 0
});
var CarView = Marionette.ItemView.extend({
tagName: 'tr',
template: '#row-template'
});
var CarsView = Marionette.CompositeView.extend({
childView: CarView,
childViewContainer: 'tbody',
template: '#table-template',
triggers: {
'click button': 'showMore'
}
});
var CarController = Marionette.Controller.extend({
initialize: function (options) {
this.collection = options.collection;
},
show: function () {
var cars = this.getData(this.collection.page);
var carsView = new CarsView({
collection: new Backbone.Collection(cars)
});
this.listenTo(carsView, 'showMore', this.showMore);
app.carsRegion.show(carsView);
},
showMore: function (options) {
var cars = this.getData(++this.collection.page);
options.collection.add(cars);
},
getData: function (page) {
var rows = this.collection.rows;
var start = page * rows;
var end = start + rows;
return this.collection.slice(start, end);
}
});
var app = new Marionette.Application();
var cars = new Cars(carData);
var carController = new CarController({
collection: cars
});
app.addRegions({
carsRegion: '#cars-region'
});
app.addInitializer(function () {
carController.show();
});
app.start();
}(jQuery, _, Backbone, Marionette));
This is also available as a JSFiddle.

Pass attributes or options to backbone model

I'm not getting any attributes or options in model. I need to pass a route number to it in order to build a url. anyone see what im missing or how I should be doing this? I tried setting the attribute I want on the model but it's not in the model when I try to grab it.
view
define([
'text!html/tplDirection.html',
'models/direction',
'core'
], function (template, Direction) {
return Backbone.View.extend({
el: '',
template: _.template(template),
initialize: function (options) {
this.model = new Direction();
this.model.set({rtnm: options.routeNumber});
console.log(this.model);
},
setup: function (routeNumber) {
var self = this;
// self.model.set({rtnm: routeNumber});
$.when(self.model.fetch())
.done(function () {
console.log(self.model.toJSON());
self.render();
})
.fail(function (response) {
console.log(response);
console.log('request for data has failed');
});
},
render: function () {
var data = {
model: this.model.toJSON()
};
this.$el.html(_.template(template, data));
},
Model
define([
'core'
], function () {
return Backbone.Model.extend({
initialize: function (attributes, options) {
console.log(attributes);
},
/* model: Routes,*/
//url: '/apiproxy.php?method=getdirections&rt=',
parse: function (data) {
var parsed = [];
$(data).find('dir').each(function (index) {
var dir = $(this).find('dir').text();
parsed.push({
dir: dir,
});
});
return parsed;
},
fetch: function (options) {
options = options || {};
options.dataType = "xml";
return Backbone.Model.prototype.fetch.call(this, options);
}
});
});
Solved by passing options to model on instantiating. What confused me is that they come through as attributes and not options in the model. How come?
view:
initialize: function (options) {
this.model = new Direction(options);
},
model:
initialize: function (attributes, options) {
console.log(attributes);
},
url: function () {
//'this' now contains attributes
var route = this.get("routeNumber);
//var route = this.attributes.routeNumber;
return '/apiproxy.php?method=getdirections&rt=' + route;
},

backbone.js history with only one route?

I'm developing my first backbone project and I have requirement that I'm not sure how to meet. I'm sure the solution has something to do with properly routing my app, but I'm not sure...
App.Router = Backbone.Router.extend({
initialize: function(options) {
this.el = options.el;
},
routes: {
'': 'search',
'search': 'search'
},
search: function() {
var search = new App.SearchView();
search.render();
}
}
});
I have three views:
// Defines the View for the Search Form
App.SearchView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.render();
},
template: _.template($('#search-form').html()),
el: $('#search-app'),
events: {
'click .n-button' : 'showResults'
},
showResults: function() {
this.input = $('#search');
var search = new App.ResultsSearchView();
var grid = new App.GridView({ query: this.input.val() });
search.render();
grid.render();
},
render: function() {
$(this.el).html(this.template());
return this;
},
name: function() { return this.model.name(); }
}); // App.SearchView
//Defines the View for the Search Form when showing results
App.ResultsSearchView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.render();
},
template: _.template($('#results-search-form').html()),
el: $('#search-input'),
render: function() {
$(this.el).html(this.template());
return this;
},
events: {
'click .n-button' : 'showResults'
},
showResults: function() {
this.input = $('#search');
var grid = new App.GridView({ query: this.input.val() });
grid.render();
},
name: function() { return this.model.name(); }
}); // App.ResultsSearchView
// Defines the View for the Query Results
App.GridView = Backbone.View.extend({
initialize: function(options) {
var resultsData = new App.Results();
resultsData.on("reset", function(collection) {
});
resultsData.fetch({
data: JSON.stringify({"query":this.options.query, "scope": null}),
type: 'POST',
contentType: 'application/json',
success: function(collection, response) {
$('#grid').kendoGrid({
dataSource: {
data: response.results,
pageSize: 5
},
columns: response.columns,
pageable: true,
resizable: true,
sortable: {
mode: "single",
allowUnsort: false
},
dataBinding: function(e) {
},
dataBound: function(){
}
});
},
error: function(collection, response) {
console.log("Error: " + response.responseText);
}
});
_.bindAll(this, 'render');
this.render();
},
el: $('#search-app'),
template: _.template($('#results-grid').html()),
render: function() {
$(this.el).html(this.template());
return this;
}
}); // App.GridView
The issue I am having is that we want our users to be able to use the back button to navigate back to the initial search and also from there, be able to move forward again to their search results. I just have no idea how to do this. Any assistance would be a huge help.
Thanks!
Backbone handles the browser history -- all you have to do is call Backbone.history.start() on startup. Well, that and make sure to call Router.navigate whenever you want to save the current navigation state.
In your example, the appropriate time would be when the user clicks "search". In the searchView.showResults method, instead of creating and rendering the results view, call:
myRouter.navigate("results/" + this.input.val(), { trigger: true });
This causes the router to go to the results/query route, which you have to add:
'results/:query': 'results'
Finally, create the results method within your router, and put the view-creating logic there:
results: function(query) {
var search = new App.ResultsSearchView();
var grid = new App.GridView({ query: query });
search.render();
grid.render();
}
Here's a working demo -- it's a bit hard to see on JSFiddle because the page is within an iFrame, but you can confirm it's working by hitting Alt+Left, Alt+Right to call the browser's back and forward respectively.
And for contrast, here's a similar demo, except it uses a single route. It calls router.navigate without trigger: true. You can see that, using this single-route method, you're able to navigate back; however, you can't go forward again to the results view, because Backbone has no way to re-trace the steps to get there.
App
var HomeView = Backbone.View.extend({
initialize: function() {
this.render();
},
el: "#container",
events: {
"submit #search": "search"
},
template: _.template($("#search-template").html()),
render: function() {
var html = this.template();
this.$el.html(html);
},
search: function(e) {
e.preventDefault();
router.navigate("results/" + $(e.target).find("[type=text]").val(), { trigger: true });
}
});
var ResultsView = Backbone.View.extend({
initialize: function() {
this.render();
},
el: "#container",
render: function() {
var html = "Results test: " + this.model.get("query");
this.$el.html(html);
}
});
var Router = Backbone.Router.extend({
routes: {
"" : "search",
"results/:query": "results"
},
search: function() {
console.log("search");
var v = new HomeView();
},
results: function(query) {
console.log("results");
var v = new ResultsView({ model: new Backbone.Model({ query: query }) });
}
});
var router = new Router();
Backbone.history.start();
HTML
<script type='text/template' id='search-template'>
<form id="search">
<input type='text' placeholder='Enter search term' />
<input type='submit' value='Search' />
</form>
</script>
<div id="container"></div>​

Backbone.LayoutManager Delegated View Events

I've been working on a prototype Backbone application using Backbone.LayoutManager and I'm running into something I don't understand.
The scenario is that I have a form for adding "people" {firstname, lastname} to a list view, I save the model fine and the new item shows up in the list. I also have a remove function that works when after the page is refreshed, but if I try to delete the person I just created without a page refresh, the removeUser() function never gets called.
My code is below. Can someone help me out? I'm just trying to learn Backbone and if you have the answer to this question as well as any other criticisms, I'd be grateful. Thanks.
define([
// Global application context.
"app",
// Third-party libraries.
"backbone"
],
function (app, Backbone) {
var User = app.module();
User.Model = Backbone.Model.extend({
defaults : {
firstName: "",
lastName: ""
}
});
User.Collection = Backbone.Collection.extend({
model: User.Model,
cache: true,
url: "/rest/user"
});
User.Views.EmptyList = Backbone.View.extend({
template: "users/empty-list",
className: "table-data-no-content",
render: function (manage) {
return manage(this).render().then(function () {
this
.$el
.insertAfter(".table-data-header")
.hide()
.slideDown();
});
}
});
User.Views.Item = Backbone.View.extend({
template: "users/user",
tagName: "ul",
className: "table-data-row"
events: {
"click .remove": "removeUser"
},
removeUser: function () {
console.log(this.model);
this.model.destroy();
this.collection.remove(this.model);
this.$el.slideUp();
if (this.collection.length === 0) {
this.insertView(new User.Views.EmptyList).render();
}
}
});
User.Views.List = Backbone.View.extend({
initialize: function () {
this.collection.on("change", this.render, this);
},
render: function (manage) {
if (this.collection.length > 0) {
jQuery(".table-data-no-content").slideUp("fast", function() {
$(this).remove();
});
this.collection.each(function(model) {
this.insertView(new User.Views.Item({
model: model,
collection: this.collection,
serialize: model.toJSON()
}));
}, this);
} else {
this.insertView(new User.Views.EmptyList());
}
// You still must return this view to render, works identical to
// existing functionality.
return manage(this).render();
}
});
User.Views.AddUser = Backbone.View.extend({
template: "users/add-user",
events: {
"click input#saveUser": "saveUser"
},
render: function (manage) {
return manage(this).render().then(function () {
$("input[type='text']")
.clearField()
.eq(0)
.focus();
});
},
saveUser: function () {
var user = new User.Model({
firstName: $(".first-name").val(),
lastName: $(".last-name").val()
});
this.collection.create(user);
this
.$("input[type='text']")
.val("")
.clearField("refresh")
.removeAttr("style")
.eq(0)
.focus();
}
});
return User;
});
The problem turned out to be an incorrect response from the server. Once the server sent back the correct JSON object, everything worked correctly.

Resources