I am new to things like angular and backbone and am trying to understand the structure better. I built some views but if I leave any spaces in the template chunk it breaks everything.
var HomeView = Backbone.View.extend({
template: '<h1>Home</h1><p>This is the first test. I think this is the way.</p>',
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
var AboutView = Backbone.View.extend({
template: '<h1>About</h1><p>This is the second test. I think this is the way.</p>',
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
var CoolView = Backbone.View.extend({
template: '<h1>Cool</h1><p>This is the third test. I think this is the way.</p>',
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
var AppRouter = Backbone.Router.extend({
routes: {
'': 'homeRoute',
'home': 'homeRoute',
'about': 'aboutRoute',
'cool': 'coolRoute',
},
homeRoute: function () {
var homeView = new HomeView();
$("#content").html(homeView.el);
},
aboutRoute: function () {
var aboutView = new AboutView();
$("#content").html(aboutView.el);
},
coolRoute: function () {
var coolView = new CoolView();
$("#content").html(coolView.el);
}
});
var appRouter = new AppRouter();
Backbone.history.start();
Is there a way to make this pull from external templates outside of the javascript. What's best practices if the pages are very elaborate?
Here is my jsfiddle link.
https://jsfiddle.net/galnova/k4ox8yap/14/
You can specify your template in your html
<script id='CoolViewTpl' type='template'>
<h1>Cool</h1><p>This is the third test. I think this is the way.</p>
</script>
Then in your render func, simply select the template by ID and get the content.
var CoolView = Backbone.View.extend({
template: "#CoolViewTpl",
initialize: function () {
this.render();
},
render: function () {
this.$el.html($(this.template).html());
}
});
If you are using a javascript module loader like RequireJs (which you'll probably find yourself doing when the application gets more complicated!) then the templates can be loaded from an external source using the RequireJs text plugin.
For example you might have a file called home.js which could look like:
require([ "backbone", "underscore", "text!templates/template.html" ],
function( Backbone, _, template ) {
return Backbone.View.extend({
template: _.template( template ),
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template);
}
});
}
);
Then an app.js file could contain your application logic and require your views:
require([ "backbone", "jquery", "home.js" ],
function( Backbone, $, HomeView ) {
var AppRouter = Backbone.Router.extend({
routes: {
'': 'homeRoute',
'home': 'homeRoute',
// etc
},
homeRoute: function () {
var homeView = new HomeView();
$("#content").html(homeView.el);
}, // etc
Related
I'm building a wagtail / django app using requirejs as js assets combiner, for the site front end.
I'm using it because I've ever been in a kind of JS dependencies hell, where nothing works because of multiple versions of same libs loaded, from different django apps... (I don't even know if it is a good solution)
I've to tell that I'm not a JS expert, and I've none arround me :(
I'm using the good old templates to render the pages, not using angular, react, riot nor vue : I'm a pretty old school dev :)
I've already adapted some scripts to use require, but I'm stuck for now...
I've installed the django_select2 application, and I'm trying to adapt the django_select2.js asset.
I've loaded select2 through bower, and I've updaetd my config.js:
"shim": {
select2: {
deps: ["jquery"],
exports: "$.fn.select2"
}
},
paths: {
...
select2: "select2/dist/js/select2"
}
Then I'm trying to adapt the django_select2.js:
require(['jquery', 'select2'], function ($, select2) {
return (function ($) {
var init = function ($element, options) {
$element.select2(options);
};
var initHeavy = function ($element, options) {
var settings = $.extend({
ajax: {
data: function (params) {
var result = {
term: params.term,
page: params.page,
field_id: $element.data('field_id')
}
var dependentFields = $element.data('select2-dependent-fields')
if (dependentFields) {
dependentFields = dependentFields.trim().split(/\s+/)
$.each(dependentFields, function (i, dependentField) {
result[dependentField] = $('[name=' + dependentField + ']', $element.closest('form')).val()
})
}
return result
},
processResults: function (data, page) {
return {
results: data.results,
pagination: {
more: data.more
}
}
}
}
}, options);
$element.select2(settings);
};
$.fn.djangoSelect2 = function (options) {
var settings = $.extend({}, options);
$.each(this, function (i, element) {
var $element = $(element);
if ($element.hasClass('django-select2-heavy')) {
initHeavy($element, settings);
} else {
init($element, settings);
}
});
return this;
};
$(function () {
$('.django-select2').djangoSelect2();
});
}($));
});
I'm having a Mismatched anonymous define() when running my page in the browser...
I'me realy not a JS expert, I'm coding by trial and error... Could anyone help me with this ?
Thanks !
OK, I have an auto-response...
I've inherited the mixin:
class _Select2Mixin(Select2Mixin):
def _get_media(self):
"""
Construct Media as a dynamic property.
.. Note:: For more information visit
https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
"""
return forms.Media(js=('django_select2/django_select2.js', ),
css={'screen': (settings.SELECT2_CSS,)})
media = property(_get_media)
class _Select2MultipleWidget(_Select2Mixin, forms.SelectMultiple):
pass
Then I can use the widget:
class DocumentationSearchForm(forms.Form):
...
document_domains = forms.ModelMultipleChoiceField(required=False,
label=_('Document domains'),
queryset=NotImplemented,
widget=_Select2MultipleWidget)
I've set my config.js file for path:
requirejs.config({
paths: {
jquery: 'jquery/dist/jquery',
select2: "select2/dist/js"
},
shim: {
"select2/select2": {
deps: ["jquery"],
exports: "$.fn.select2"
}
}
});
Then I've overridden the django_select2.js file, to wrap the content in a require satement:
require(['jquery', 'select2/select2'], function ($) {
(function ($) {
var init = function ($element, options) {
$element.select2(options);
};
var initHeavy = function ($element, options) {
var settings = $.extend({
ajax: {
data: function (params) {
var result = {
term: params.term,
page: params.page,
field_id: $element.data('field_id')
}
var dependentFields = $element.data('select2-dependent-fields')
if (dependentFields) {
dependentFields = dependentFields.trim().split(/\s+/)
$.each(dependentFields, function (i, dependentField) {
result[dependentField] = $('[name=' + dependentField + ']', $element.closest('form')).val()
})
}
return result
},
processResults: function (data, page) {
return {
results: data.results,
pagination: {
more: data.more
}
}
}
}
}, options);
$element.select2(settings);
};
$.fn.djangoSelect2 = function (options) {
var settings = $.extend({}, options);
$.each(this, function (i, element) {
var $element = $(element);
if ($element.hasClass('django-select2-heavy')) {
initHeavy($element, settings);
} else {
init($element, settings);
}
});
return this;
};
$(function () {
$('.django-select2').djangoSelect2();
});
}($));
});
That's all, folks !
I'm trying to remove an item from my state array when clicked. At the moment I have an onclick listener which calls a function passed into the props. However I get a warning: bind(): React component methods may only be bound to the component instance. See App... and it does not remove the item.
Thanks for any help regarding this issue! It has pretty much ground my progress to a halt.
(function (React) {
var data = [
'Go to work',
'Play Albion Online',
'Keep learning React'
]
var App = React.createClass({
getInitialState: function () {
return {data: []}
},
componentWillMount: function () {
this.state.data = data;
},
removeItem: function (i) {
console.log(i);
},
render: function () {
return (
<ToDoList onRemoveItem={this.removeItem} tasks={this.state.data} />
)
}
});
var ToDoList = React.createClass({
render: function () {
var scope = this;
var tasks = this.props.tasks.map(function (task, i) {
return <ToDo onClick={scope.props.onRemoveItem.bind(this, i)} key={task} task={task} />
});
return (
<ul>
{tasks}
</ul>
)
}
});
var ToDo = React.createClass({
render: function () {
return (
<li>{this.props.task}</li>
)
}
});
React.render(<App />, document.getElementById('example'));
})(React);
React actually auto-binds methods to the current component:
http://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html
In the TodoList component, rather than:
scope.props.onRemoveItem.bind(this, i)
Try:
scope.props.onRemoveItem.bind(null, i)
By providing null instead of this you'll allow React to do its own thing. Also you need to actually use the onClick handler:
<li onClick={this.props.onClick}>{this.props.task}</li>
I have two views and I want to render a second one inside the first one.
I am doing this:
First view:
var EventContainer = Backbone.View.extend({
el: '#sectionContainer',
initialize: function() {
this.render();
},
render: function() {
var that = this;
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {}); //Option to pass any dynamic values to template
$(that.el).append(that.template); //adding the template content to the main template.
}, 'html');
new EventList();
return this;
}
});
Template for the first view:
<div class="main-content pull-left">
<ul id="eventList" class="event-list no-list-style no-padding"> </ul>
</div>
Second view:
var EventList = Backbone.View.extend({
el: '#eventList',
initialize: function() {
var that = this;
this.EventsCollection = new EventsCollection();
this.EventsCollection.fetch({
success: function(data) {
that.collection = data;
that.setTemplate();
},
error: function() {}
});
},
setTemplate: function() {
var that = this;
$.get('templates/home/eventList.html', function(data) {
that.template = _.template(data);
that.render();
}, 'html');
},
render: function() {
var that = this;
this.collection.each(function(eventData) {
var eventTemplate = that.template(eventData.toJSON());
console.log('---> ' + $(that.el));
$(that.el).append(eventTemplate);
});
return this;
}
});
Template for the second View:
<li class="event-item">
<small><%= like_count%> </small>
</li>
When I try to render the second view, el is undefined.
What m I doing wrong?
Thanks.
If you have a look at this.el inside EventList#initialize, I think you'll find that it is undefined rather than the <ul> that you're expecting it to be. Why would that be? Where does #eventList come from? #eventList comes from EventContainer's template and that won't be in the DOM until this AJAX call:
$.get('templates/home/eventContainer.html', ...)
completes. That $.get won't complete until well after your new EventList();. When you instantiate your EventList, it will go looking for #eventList but won't find anything, that will leave you with this.el being undefined. Then later on, that $.get will return and #eventList will show up in the DOM; this is why $('#eventList') works.
The easy solution is to defer instantiating your EventList until after you've added the necessary things to the DOM:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList(); // <-----------------------
}, 'html');
If you're going to do this, then you could make the dependency explicit by dropping the el from the EventList definition and specifying it when you create the view instead:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList({ el: that.$('#eventList') });
}, 'html');
I'm a Backbone noob and I've been at a standstill for 2 days now and can't figure out where I'm going wrong. Could anyone help me out?
My app is retrieving a JSON file with a list of components in it. Each component has a category it belongs to. I create a view called "Components" that is a collapsible list. When a component category is clicked, it should open up to show the components in that category. Each of these components (list items) a separate view called "Component".
I'm using a lot of append()'s in the parent view and I don't think this is efficient. I tried to compile a string of html and then append it to the view in one statement but the events of the subviews weren't triggering.
There are probably a few errors going on here. Even though my sublist items should be wrapped in ul's they aren't being. If someone can put me on the path to enlightenment I'd be really grateful!
Here's my code
/* ----------------- PARENT VIEW ---------------------- */
var ComponentsView = Backbone.View.extend({
id: 'components-view',
className: 'components-view',
html: [
'<div class="panel panel--components">',
'<h3 class="panel__heading">add an item</h3>',
'<ul class="component-list"></ul>',
'</div>'
].join(''),
initialize: function(){
var types = [];
var currentTypeSelected = 1;
this.getTypes = function(){
return types;
}
this.getCurrentTypeSelected = function(){
return currentTypeSelected;
}
this.setCurrentTypeSelected = function(value){
currentTypeSelected = value;
}
if(this.collection.length){
this.collection.each(function(model){
var thisItemType = model.attributes.type;
if(types.indexOf(thisItemType)==-1){
types.push(thisItemType);
}
});
}
this.$el.html(this.html);
this.$componentList = this.$('.component-list');
this.render();
},
render: function(){
var that = this;
this.getTypes().forEach(function(type){
that.$('.component-list').append('<li class="component-type">' + type + '');
// now cycle through all the componenets of this type
that.$('.component-list').append('<ul>');
that.collection.byType(type).each(function(model){
that.$('.component-list').append('<li class="component">');
that.$('.component-list').append(that.renderIndividualComponent(model));
that.$('.component-list').append('</li>');
});
that.$('.component-list').append('</ul>');
});
},
renderIndividualComponent: function(model){
var componentView = new ComponentView({model: model});
return componentView.$el;
},
events: {
'click .component-type': 'onOpenSubList'
},
onOpenSubList: function (e) {
alert('open sub list');
}
});
/* ----------------- SUB (list item) VIEW ---------------------- */
var ComponentView = Backbone.View.extend({
tagName: "li",
className: "component",
initialize: function(model){
this.render();
},
render: function(){
var html = '' + this.model.attributes.description + ''//template(this.model.attributes);
$(this.el).append(html);
return this;
},
events: {
'click a': 'onAddComponent'
},
onAddComponent: function (e) {
e.preventDefault();
alert('add component');
}
});
I want to load external HTML template for Backbone view with Require.js text! plugin and make template path custom (get template path from View 'template' property). The problem is - template loads asynchronously and on render I still not have it. Here is my code:
custom View
define([
'views/abstract/base-view',
], function (BaseView) {
var AppView = BaseView.extend({
el: '#app-view',
template: 'app-view',
initialize: function () {
this.on('template:loaded', this.render, this);
BaseView.prototype.initialize.apply(this, arguments);
},
render: function () {
this.$el.empty();
var html = this.template();
this.$el.html(html);
}
});
return AppView;
});
and abstract View to inherit
define([], function () {
var AbstractView = Backbone.View.extend({
initialize: function(){
this.setTemplate();
},
setTemplate: function(){
var that = this;
require(['3p/text!templates/' + this.template + '.tpl'], function(tpl) {
that.template = _.template(tpl);
that.trigger('template:loaded');
});
}
});
return AbstractView;
});
It works but I don't like to listen to 'template:loaded' event for render. Any suggestions?
Thanks!
I had the same problem.
To avoid the async behaviour, there is no another way than add in some file the requires that will be dynamic and load all of them before.
I made something similar to an inject. I loaded in a require all the dynamic templates and put in a shared dictionary the paths of the template. Then I use _.template(require(inject["templateNAME"])) and I get the template at the moment.
The idea is something like the next:
define(["require"], function (require) {
require(["text!path/of/the/template",
... ]);
window.inject = {};
inject.templateNAME = "text!path/of/the/template";
Then you will use something like this:
var tpl = require(inject[this.templateName]);
this.template = _.template(tpl);