Dynamic Backbone Template Variable - javascript

// Template Helper
window.template = function (id) {
return _.template($('#' + id).html());
};
// Template Helper in view.render
this.$el.html( this.template(this.model.toJSON()) );
// This Template Call in View
template: template('response-form')
// This My If Else Construction in View Initialize
if (this.model.attributes.status === true) {
here i want set template id to response-auth
} else {
here i want set template id to response-form
}
I dont have idea, how i can dynamicly change value of template call? Did someone can help me?

You can change this.template whenever you want inside your view. So, you could check the status in your view's initialize:
initialize: function() {
if(this.model.get('status'))
this.template = template('response-auth');
else
this.template = template('response-form');
}
Or you could make the decision inside render:
render: function() {
var tmpl = null;
if(this.model.get('status'))
tmpl = template('response-auth');
else
tmpl = template('response-form');
// your current rendering code goes here but uses
// tmpl instead of this.template
}
Which approach you take depends on whether or not you're expecting the status to change.
You could, of course, compile both when building the view:
template_auth: template('response-auth'),
template_form: template('response-form'),
initialize: function() {
// Set this.template to this.template_auth or this.template_form...
},
render: function() {
// Or pick this.template_auth or this.template_form in here if
// that makes sense...
}
If you're expecting to use both templates then compiling them both would make sense, otherwise I'd leave the decision until it needs to be made (probably in render).

#mu is too short: Isn't any way by which we can pass dynamic data inside
template('response-auth');
Suppose I got more than 50 template, then I can't put if else statement.
$(`#channel-view-${targetPlatformId}`).html(_.template(templates.manage_store_form_template_Ebay)({
$(`#channel-view-${targetPlatformId}`).html(_.template(templates.manage_store_form_template_shopify)({
There is one difference in above line, that is channel name, Is there any way I can pass dynamic data inside _.template()

Related

Unable to set the Model Attributes after a Backbone model.fetch() call

I have come across a lot of examples where the backbone-view would be like var view1 = Backbone.View.extend( { } ) but unable to get one where the backbone view is returned directly. In the below code I am able to render the default values of the model attribute and display the same in the dust template but when I do model.fetch(), in the success function I am able to see the json response in the console but unable to set the fetched values to the model attributes and render the new values. Do, let me know what I am missing here. Any help is appreciated.
define(function (require) {
'use strict';
var $ = require('jquery');
var Backbone = require('backbone');
var g = require('global/dust-globals');
var template = require('text!/dust/table1.dust');
var SampleModel = Backbone.Model.extend({
initialize: function () {
},
defaults:{
SampleUpdate:'Test date',
SampleCount: 0
},
urlRoot: "/Sample"
});
var obj1 = new SampleModel();
return Backbone.View.extend({
events: {
// 'click .search-btn': 'searchBtnClick',
},
initialize: function(){
this.testfunc();
this.render();
this.model.on("change", this.render, this);
},
render: function () {
this.$el.html(g.renderTemplate('TabView', template, {}));
//template is compiled and rendered successfully
console.log('CHECK:'+obj1.get("lastUpdate"));
return this;
},
testfunc : function () {
obj1.fetch({
success: function (response) {
console.log(JSON.stringify(response));
obj1.set("SampleUpdate", response.get("sampleUpdate"));
obj1.set("SampleCount", response.get("sampleCount"));
console.log('CHECK1:'+obj1.get("SampleUpdate"));
}
});
}
});
});
My JS code calling the above code would be as below.
var TabView = require('/SampleTab');
return Backbone.View.extend({
initialize: function () {
this.tabView = new TabView({el: '#sample-div', model:this.model, appView: this});
this.render();
},
render: function() {
this.tabView.$el.show();
this.tabView.render();
}
});
I'm having trouble understanding what exactly it is you are trying to do with your code, but it doesn't look like you're using Backbone.View.extend({ ... }) correctly. From the documentation for Backbone.View.extend:
Get started with views by creating a custom view class. You'll want to override the render function, specify your declarative events, and perhaps the tagName, className, or id of the View's root element.
[Emphasis mine.]
The Backbone.View.extend is for creating your own Backbone View classes, not instantiating objects.
If you're looking for more information, I highly recommend that you read through Addy Osmani's free e-book, Developing Backbone.js Applications. You might know some of what it teaches already, but it has some good examples of extending Backbone Views and does a much better job of explaining other fundamentals of using Backbone.js than I could here.

Backbone: Subview depends on another subview being rendered

I have a Backbone SAP which has two subviews within its main App view. These are interdependent: the top one dispalys a music score rendered using Vexflow (Javascript music notation package), and the other below it displays an analysis of the score, also using Vexflow but with some extra objects (text, lines, clickable elements, etc).
The main problem I have is that a lot of the data I need for the analysis view doesn't come into existence until the score view has been rendered. For example, the x coordinate of a musical note is only available after the note has been drawn (the same isn't true of the y coordinate). Below is (in schematic terms) how my app view is set up:
var AppView = Backbone.View.extend({
//...
initialize: function() {
this.scoreView = new ScoreView();
this.analysisView = new AnalysisView({
data: this.getAnalysisData()
});
},
render: function() {
this.scoreView.render();
this.analysisView.render();
return this;
},
getAnalysisData: function() {
// Performs anaysis of this.scoreView,
// and returns result.
}
});
My work around is to move the analysis view setup into the render method, after the score view has been rendered. I dislike doing this, as the getAnalysisData method can be quite expensive, and I believe the render method should be reserved simply for rendering things, not processing.
So I'm wondering if - since there doesn't seem to be a Vexflow solution - there is a Backbone pattern that might fix this. I am familiar with the 'pub/sub' event aggregator pattern for decoupling views, as in:
this.vent = _.extend({}, Backbone.Events);
So on this pattern the analysis view render method subscribes to an event fired after the score view is rendered. I'm not sure how this would alter my code, however. Or perhaps use listenTo, like this:
// Score subview.
var ScoreView = Backbone.View.extend({
initialize: function() {
this.data = "Some data";
},
render: function() {
alert('score');
this.trigger('render');
}
});
// Analysis subview.
var AnalysisView = Backbone.View.extend({
initialize: function(options) {
this.data = options.data;
},
render: function() {
alert(this.data);
return this;
}
});
// Main view.
var AppView = Backbone.View.extend({
el: "#some-div",
initialize: function() {
this.scoreView = new ScoreView();
var view = this;
this.listenTo(this.scoreView, 'render', this.doAnalysis); // <- listen to 'render' event.
},
render: function() {
this.scoreView.render();
return this;
},
doAnalysis: function() {
this.analysisView = new AnalysisView({
data: this.getAnalysisData()
});
this.analysisView.render();
},
getAnalysisData: function() {
return this.scoreView.data;
}
});
Of course, the analysis step is still effectively being done 'during' the render process, but this seems a better pattern. It seems more like the Backbone way of doing things. Am I right? Or am I missing something?
Edit: I dont necessarily have to create the analysis view in the doAnalysis, I could still do that in the main view initialize (at the moment I'm not). But doAnalysis has to run after the score view has rendered, otherwise it cannot access the relevant score geometry information.

Backbone architecture and view management

I am struggling with when to destroy backbone views. I know I need to destroy the view somewhere, but I am not sure where.
I have the following code in router.js
routes: {
"names/search": "nameSearch",
"companies/search": "companySearch"
},
initialize: function(){
Backbone.history.start();
this.navigate("#/", true);
}
nameSearch: function () {
require(["app/views/RecordSearch"], function (RecordSearchView) {
var obj = {};
obj.Status = [utils.xlate("On Assignment"), utils.xlate("Candidate")];
var view = new RecordSearchView({ model: obj, el: $(".content") }, { "modelName": "Candidate" });
view.delegateEvents();
});
},
companySearch: function () {
require(["app/views/RecordSearch"], function (RecordSearchView) {
var view = new RecordSearchView({ model: {}, el: $(".content") }, { "modelName": "Company" });
view.delegateEvents();
});
}
And then in RecordSearchView.js I have the following function that is called when a user clicks the search button
doSearch: function () {
require(["app/utils/SearchHelper", "app/models/" + modelName, "app/views/SearchResults"], function (SearchHelper, Model, SearchResultsView) {
var obj = $("#searchForm").serializeArray();
var params = SearchHelper.getQuery(obj);
params["page"] = 1;
params["resultsPerPage"] = 25;
var collection = new Model[modelName + "Collection"]({}, { searchParams: params });
params["Fields"] = collection.getSearchFields();
collection.getPage(params["page"], function (data) {
require(["app/views/SearchResults"], function (SearchResultsView) {
App.Router.navigate(modelName + "/search/results");
var view = new SearchResultsView({ collection: data, el: $(".content") });
view.delegateEvents();
});
});
return false;
});
And SearchResults.js
return BaseView.extend({
init: function () {
this.render();
},
render: function () {
var data = this.collection.convertToSearchResults();
this.$el.html(template(data));
return this;
}
});
The problem is the second time I perform any search (calling the doSearch function from RecordSearch.js). As soon as I perform the second search, the data shown is that belonging to the previous search I performed. (For example I do a name search and it works, then do a company search but the screen shows company search results but then is quickly replaced with name search results).
My questions are
I suspect I need to call some cleanup code on the view before it is re-used. Where is the proper place within a backbone application to run this.
Is there anything wrong with the way I load SearchResults view from within RecordSearch view? SearchResults does not have a path on my router, but it is basically a form post, so I assume it shouldn't?
Any help is appreciated.
This problem is quite common and is known as Zombie Views. Derick Bailey explains this issue very well here: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
However unfortunately you can't simply solve it without changing the way you are loading your views.
Because you are loading them inside RequireJS modules that will keep it in the local var scope, you are losing the reference to the views once the route has been fully processed.
In order to solve this problem, you would need to keep the reference of the current view somewhere, and then properly dispose it before calling another view, something like this:
showView: function(view) {
this.currentView && this.currentView.remove();
this.currentView = view;
this.currentView.render();
$('#content').html(this.currentView.el);
}
More about this solution here: http://tiagorg.com/talk-backbone-tricks-or-treats-html5devconf/#/6
I personally suggest you adopting a solution that will take care of this for you, like Marionette.js
It will handle this and quite many other issues, by providing the missing gaps of every Backbone-based architecture.

backbone: render this collection

var Text = Backbone.Model.extend({});
Texts = Backbone.Collection.extend({
model: Text,
url: '/data.json',
});
var TextsView = Backbone.View.extend({
initialize: function() {
_.bindAll(this);
this.render();
},
el: "#Texts",
template: _.template($('#TextTemplate').html()),
render: function(e){
_.each(this.model.models, function(Text){
var TextTemplate = this.template(Text.toJSON());
$(this.el).append(TextTemplate);
}, this);
return this;
}
})
var Texts = new Texts();
Texts.fetch();
var TextView = new TextsView({collection: Texts});
this gives me Uncaught TypeError: Cannot read property 'models' of undefined and does not display anything on the page.
This this.model.models should be this.collection
In your render method in your view, you should use this.collection.each instead of _.each function.
render: function(e){
this.collection.each(function(Text){
var TextTemplate = this.template(Text.toJSON());
$(this.el).append(TextTemplate);
}, this);
return this;
}
If you want to use _.each function, then you will need to access the models array directly in your collection as #dfsq pointed out. This can be done by using this.collection.models.
render: function(e){
_.each(this.collection.models, function(Text){
var TextTemplate = this.template(Text.toJSON());
$(this.el).append(TextTemplate);
}, this);
return this;
}
EDIT 2
Here are some reasons your fetch call may not be working. First check that you are using a web server, since ajax requests may be blocked for security reasons using file system. I know this is blocked in Chrome unless you change a certain setting. Not sure about Firefox.
The second reason is that the fetch call is asynchronous. This means that mostly likely your data will not be loaded when you run initialize
This means you'll need to make the following adjustments. First you need to add a listener to the add event of your collection so that anytime an item is added, your view will be notified.
initialize: function() {
_.bindAll(this);
this.render();
// Listen to the `add` event in your collection
this.listenTo(this.collection,"add", this.renderText);
},
Next we need to add a function to your view that will render a single item
renderText: function(Text) {
var TextTemplate = this.template(Text.toJSON());
this.$el.append(TextTemplate);
}
Also to answer your other question about the user of this in the each loop. The last parameter in the each function is the scope you want to use in the inside the callback function that executes. So if you use this as the second parameter, it allows you to access your viewing using this.
this.collection.each(function(Text){
var TextTemplate = this.template(Text.toJSON());
$(this.el).append(TextTemplate);
}, this);
If you don't add this, then you'd need to do this:
var view = this;
this.collection.each(function(Text){
var TextTemplate = view.template(Text.toJSON());
$(view.el).append(TextTemplate);
});

How to use qunit in a backbone app

I'm currently working on an app that is built from Backbone.js, with Require.js to load dependencies. I had hoped to be able to load QUnit and have a page where the user can simply load the page and it will dynamically run unit tests. However, I've hit a snag. Whenever I load the page, the tests work, but if I navigate away from the page and then come back, QUnit breaks with the following error:
Uncaught Error: pushFailure() assertion outside test context,
Below is the code that I am currently working with. My real question is this: With my current Backbone.js layout, is it feasible to use QUnit in the way I want, or should I scrap this feature?
When the page loads, the parent view service_test.view.js is rendered:
define(['backbone', 'hgn', 'statemachine_test_view'],
function (Backbone, hgn, StateMachineTest) {
var DynamicTest = Backbone.View.extend({
// This is the main element of the application, it is what is cleared and populated for each page
el: $(".overwatch_container"),
// Build the Statemachine test view (statemachine_test.view.js)
statemachine_view: new StateMachineTest(),
render: function (data) {
// Empty out anything that's in the container already
this.$el.empty();
// Contain the 'this' reference, so it can be used throughout
var that = this;
// Pull in and populate the hogan template for the main parent elements
require(['hgn!templates/service_test.template.hogan'], function (tmpl) {
// JSON object with all of thd page's information to pass to the templates
resultset = {
"service_type": data.service_type
};
// Render the template with the given information, and then build child views
if( that.$el.html( tmpl.render( resultset ) ) )
{
// Build the child view
that.statemachine_view.render();
}
});
},
close: function () {
$(this.$el).empty();
return this;
}
});
// Return the view object, so it can be utilized when this script is require'd
return DynamicTest;
});
statemachine_test.view.js is where StateMachineTest is created:
define(['backbone', 'hgn'],
function (Backbone, hgn) {
var StateMachineTest = Backbone.View.extend({
render: function (options) {
// Dynamically set the element associated with this view, as it is not instantiated when this is first included by Require.js
this.setElement( $(".test_content") );
// Contain the 'this' reference, so it can be used throughout
var that = this;
require(['hgn!templates/qunit_base.template.hogan'], function (tmpl) {
// JSON object with all of thd page's information to pass to the templates
// Render the template with the given information, and then build child views
if( that.$el.html(tmpl.render()) )
{
// Once the template has been rendered, load the qUnit test script
// 'statemachine_dynamic' = statemachine_dynamic.test.js
require(['QUnit', 'statemachine_dynamic'], function(QUnit, statemachine) {
statemachine.run();
QUnit.load();
QUnit.start(); //THIS IS THE LINE THE ERROR POINTS TO
QUnit.done( function (details) {
_.each($("#qunit-tests li").children("a"), function (child) {
$(child).attr("href", function (index, attr) {
return attr+window.location.hash;
});
});
});
});
}
});
},
});
return StateMachineTest;
});
And this is my actual test script, statemachine_dynamic.test.js:
define([], function () {
var run = function() {
module("Statemachine Testing");
test( "Test 1", function() {
var value = "hello";
equal( value, "hello", "We expect value to be hello" );
});
test( "Test 2", function() {
var value = "hello";
equal( value, "hello", "We expect value to be hello" );
});
test( "Test 3", function() {
var value = "hello";
equal( value, "hello", "We expect value to be hello" );
});
};
return {run: run};
});
Qunit.start() doesn't start the tests. start() is for async testing if you comment it out your test will probably be run aswel altough i do not know require.js

Categories