I wonder if someone can help to find what's wrong in this case. I get "Uncaught ReferenceError: text is not defined" in line 6 app.js:
((__t=( text ))==null?'':_.escape(__t))+
driver.js:
var Marionette = require('backbone.marionette');
var TodoView = require('./views/layout');
var initialData = {
items: [
{assignee: 'Scott', text: 'Write a book about Marionette'},
{assignee: 'Andrew', text: 'Do some coding'}
]
};
var App = new Marionette.Application({
onStart: function(options) {
var todo = new TodoView({
collection: new Backbone.Collection(options.initialData.items),
model: new ToDoModel()
});
todo.render();
todo.triggerMethod('show');
}
});
App.start({initialData: initialData});
views/layout.js
var Backbone = require('backbone');
var Marionette = require('backbone.marionette');
var ToDoModel = require('../models/todo');
var FormView = require('./form');
var ListView = require('./list');
var Layout = Marionette.View.extend({
el: '#app-hook',
template: require('../templates/layout.html'),
regions: {
form: '.form',
list: '.list'
},
collectionEvents: {
add: 'itemAdded'
},
onShow: function() {
var formView = new FormView({model: this.model});
var listView = new ListView({collection: this.collection});
this.showChildView('form', formView);
this.showChildView('list', listView);
},
onChildviewAddTodoItem: function(child) {
this.model.set({
assignee: child.ui.assignee.val(),
text: child.ui.text.val()
}, {validate: true});
var items = this.model.pick('assignee', 'text');
this.collection.add(items);
},
itemAdded: function() {
this.model.set({
assignee: '',
text: ''
});
}
});
module.exports = Layout;
todoitem.html
<%- item.text %> — <%- item.assignee %>
Any can me explain why text is not defined?
check your ToDoModel for a typo, the Backbone Model field should be "defaults" not "default", while parsing for a template Marionette view looks for "defaults" field:
https://marionettejs.com/docs/master/template.html#rendering-a-model
so the ToDoModel code should go like this:
...
var ToDo = Backbone.Model.extend({
defaults: {
assignee: '',
text: ''
},
...
You should take a look at the Marionnette's ItemView documentation which explain how to render a template with custom data.
var my_template_html = '<div><%= args.name %></div>'
var MyView = Marionette.ItemView.extend({
template : function(serialized_model) {
var name = serialized_model.name;
return _.template(my_template_html)({
name : name,
some_custom_attribute : some_custom_key
});
}
});
new MyView().render();
Note that using a template function allows passing custom arguments
into the .template function and allows for more control over how the
.template function is called.
With the code you provided at the moment, I can't help.
Marionette calls 'serializeModel' before passing the context to 'template'. So, if you have a backbone.model like
{
.
.
.
attributes: {
text: 'someText',
asignee: 'someAsignee'
}
.
.
}
your template will receive
{
text: 'someText',
assignee: 'someAsignee'
}
I have worked with handlebars but not underscore exactly. There {{this.text}} and {{this.assignee}} works like a charm in the template. So, try this.text or text in place of item.text, see if that works
Related
MyView.js:
define(['app/models/MyModel'],
function (MyModel) {
return Mn.LayoutView.extend({
template: '#my-template',
className: 'my-classname',
regions: {
content: '.content-region',
panel: '.panel-region'
}
initialize: function () {
_.bindAll(this, 'childButtonClicked');
},
onShow: function () {
this.getRegion('content').show(new AnotherView());
},
childEvents: {
'some-child-click': 'childButtonClicked'
},
childButtonClicked: function (view) {
var newView = new MyView({
model: new MyModel({
title: view.model.get('title')
})
});
this.getRegion('panel').show(newView);
}
});
});
I'm trying to nest instances of MyView within itself. This worked correctly when I was building the prototype by dumping everything into one function, like so:
var MyView = Mn.LayoutView.extend({
...
childButtonClicked: function(view) {
var newView = new MyView({
...
Now that I'm trying to separate the Views into their own files and use require.js, I can't figure out the syntax for a self-referential view.
When I run this code as is, I get an error like 'MyView is undefined'.
If I add it to the require header like so:
define(['app/models/MyModel', 'app/views/MyView'],
function (MyModel, MyView) {
I get the error 'MyView is not a function'.
EDIT for solution:
The marked solution works fine, I ended up using the obvious-in-hindslght:
define(['app/models/MyModel'],
function (MyModel) {
var MyView = Mn.LayoutView.extend({
template: '#my-template',
className: 'my-classname',
regions: {
content: '.content-region',
panel: '.panel-region'
}
initialize: function () {
_.bindAll(this, 'childButtonClicked');
},
onShow: function () {
this.getRegion('content').show(new AnotherView());
},
childEvents: {
'some-child-click': 'childButtonClicked'
},
childButtonClicked: function (view) {
var newView = new MyView({
model: new MyModel({
title: view.model.get('title')
})
});
this.getRegion('panel').show(newView);
}
});
return MyView;
});
You can require() in your module: var MyView = require(app/views/MyView);.
So for want of a better place:
childButtonClicked: function (view) {
var MyView = require(app/views/MyView);
var newView = new MyView({
model: new MyModel({
title: view.model.get('title')
})
});
this.getRegion('panel').show(newView);
}
I am relatively new to Backbone.js and having difficulty rendering a subView. I have subViews in other parts of the app working properly, but I cant even render simple text in this one.
View:
Feeduni.Views.UnifeedShow = Backbone.View.extend({
template: JST['unifeeds/show'],
tagName: "section",
className: "unifeed-show",
render: function() {
var content = this.template({ unifeed: this.model });
this.$el.html(content);
var subView;
var that = this;
this.model.stories().each(function(stories) {
subView = new Feeduni.Views.StoriesShow({ model: stories });
that.subViews.push(subView);
that.$el.find(".show-content").append(subView.render().$el);
});
return this;
},
});
Subview:
Feeduni.Views.StoriesShow = Backbone.View.extend({
template: JST['stories/show'],
tagName: "div",
className: 'stories-show',
render: function() {
this.$el.text("Nothing shows up here");
return this;
},
});
Model:
Feeduni.Models.Unifeed = Backbone.Model.extend({
urlRoot: "/api/uninews",
stories: function() {
this._stories = this._stories || new Feeduni.Subsets.StoriesSub([], {
parentCollection: Feeduni.all_unifeeds
});
return this._stories;
},
});
The text "Nothing shows up here" should be displaying in the "show content" element, but all I get is this:
<section class="unifeed-show">
<article class="show-content">
</article>
</section>
Below is a slight modification of your code showing a working main view managing some sub-views.
var UnifeedShow = Backbone.View.extend({
// I've hard-coded the template here just for a sample
template: _.template("Feed: <%= feedName %><br/> <ul class='show-content'></ul>"),
className: "unifeed-show",
initialize: function () {
// Create an array to store our sub-views
this.subViews = [];
},
render: function () {
var content = this.template(this.model.toJSON());
this.$el.html(content);
var subView;
var that = this;
var subViewContent = this.$el.find(".show-content");
this.model.stories().each(function (story) {
var subView = new StoryShow({
model: story
});
this.subViews.push(subView);
subViewContent.append(subView.render().$el);
}, this);
return this;
}
});
var StoryShow = Backbone.View.extend({
tagName: 'li',
// This template will show the title
template: _.template('Title: <%= title %>'),
className: 'stories-show',
render: function () {
var content = this.template(this.model.toJSON());
this.$el.html(content);
return this;
},
});
var Unifeed = Backbone.Model.extend({
stories: function () {
// I'm just returning the value set on this model as a collection;
// You may need to do something different.
return new Backbone.Collection(this.get('stories'));
}
});
// ================================
// Code below is creating the model & view, then rendering
// ================================
// Create our model
var feed = new Unifeed();
// Put some data in the model so we have something to show
feed.set('feedName', 'A Sample Feed');
feed.set('stories', [{
title: "Story #1",
id: 1
}, {
title: "Story #2",
id: 5
}]);
// Create our main view
var mainView = new UnifeedShow({
model: feed,
el: $('#main')
});
// Render it, which should render the sub-views
mainView.render();
Here's a working JSFiddle:
https://jsfiddle.net/pwagener/7o9k5d6j/7/
Note that while this manual sort of sub-view management works OK, you'll be better off using something like a Marionette LayoutView to help manage parent and sub-views. It builds good best practices for this sort of thing without you needing to do it yourself.
Have fun!
The subview is named Feeduni.Views.StoriesShow but in your main view you are instantiating new Feeduni.Views.StoryShow. Name them consistently and see if you still have problems.
I have created a very basic backbone app, to understand how it works.
In the router, I just wanna display just 1 model, i.e. a user, not the whole collection, by passing an id in the url, how to do that?
For example, I'd like to do someapp.com/app/#user/2, and this would display just user no2 details.
Please see my work in jsfiddle
// router
var ViewsRouter = Backbone.Router.extend({
routes: {
'': 'viewOne',
'one': 'viewOne',
'two': 'viewTwo',
'user/:id': 'user'
},
viewOne: function() {
var view = new TheViewOne({ model: new TheModel });
},
viewTwo: function() {
var view = new UserView({ model: new TheModel });
},
user: function(id) {
// how to get just 1 user with the corresponding id passed as argument
// and display it???
}
});
Many thanks.
https://github.com/coding-idiot/BackboneCRUD
I've written a complete Backbone CRUD with no backend stuff for beginners. Below is the part where we get the user from the collection and show/render it.
View
var UserEditView = Backbone.View.extend({
render: function(options) {
if (options && options.id) {
var template = _.template($("#user-edit-template").html(), {
user: userCollection.get(options.id)
});
this.$el.html(template);
} else {
var template = _.template($("#user-edit-template").html(), {
user: null
});
// console.log(template);
this.$el.html(template);
}
return this;
},
Router
router.on('route:editUser', function(id) {
console.log("Show edit user view : " + id);
var userEditView = new UserEditView({
el: '.content'
});
userEditView.render({
id: id
});
});
Update
Particularly, sticking to your code, the router will look something like this :
user: function(id) {
var view = new UserView({ model: userCollection.get(id) });
}
I am trying to get a Collection to render in a Backbone View, but it won't populate out the HTML. It will console log after the fetch, but does not console log inside of the _.each. Any clues? I am new to Backbone and am looking for assistance with populating based on REST calls. It works with hard data (inline entries from an array), but seems to trip up on the REST.
<script>
var featuredArticle = Backbone.Model.extend({
defaults: {
id: '',
headLine: '',
snippet: '',
fullStory: '',
location: '',
nsfw: '',
category: '',
relatedArticleIds: '',
hasVideoPlaceholder: '',
numberOfImages: ''
}
});
var featuredArticles = Backbone.Collection.extend({
model: featuredArticle,
url: 'http://myrestendpoint'
});
var articleView = Backbone.View.extend({
tagName: 'ul',
render: function () {
var that = this;
var getfeaturedArticles = new featuredArticles();
getfeaturedArticles.fetch();
console.log(getfeaturedArticles);
_.each(that.collection, function (value) {
console.log(value.headLine);
$(that.el).append('<li>' + value.headLine + '</li>');
})
return that;
}
});
var articles = new articleView({collection: featuredArticles});
$("body").append(articles.render().el);
</script>
While creating articleView instance, you need to pass an instance of collection not the variable containing the definition.
Hence change :
var articles = new articleView({collection: featuredArticles});
to
var articles = new articleView({collection: new featuredArticles});
In articleView's render method, use the collection for fetch.
Change :
var getfeaturedArticles = new featuredArticles();
getfeaturedArticles.fetch();
console.log(getfeaturedArticles);
to
this.collection.fetch();
console.log(this.collection);
I am trying to send a "point" parameter through my "discover "route, and pass it into the corresponding 'discoverPointView' function's model selector, so that I can change the model in the view based on the parameter. Can anyone explain to me how and why this isn't working? And also, you'll notice that I'm using a lot of comparative logic in the route function...can you also explain a better way to alternate between views? Thank you!
Routes:
App.Router.Main = Backbone.Router.extend({
routes: {
//DISCOVER VIGNETTE ROUTES
'discover': 'discoverView',
'discover/:point':'discoverPointView',
},
discoverView: function() {
var contentDiscover = document.getElementById('discoverWrapper');
this.hidePages();
contentDiscover.classList.add('active');
var discoverView = new AllDiscoverPointsView({collection: allDiscoverPoints});
$("#discoverWrapper").html(discoverView.render().el);
//$(".point").removeClass("active");
//$("#" + point).addClass("active");
},
discoverPointView: function(point) {
var contentDiscover = document.getElementById('discoverWrapper');
this.hidePages();
contentDiscover.classList.add('active');
var discoverView = new AllDiscoverPointsView({collection: allDiscoverPoints});
$("#discoverWrapper").html(discoverView.render().el);
if(point == "glasselephant"){
var singleDiscoverPointView = new SingleDiscoverPointView({ model: point });
$("#discoverWrapper").append(singleDiscoverPointView.render().el);
$(".subpage").removeClass("active");
$(singleDiscoverPointView.el).addClass("active");
}
if(point == "carvedstatue"){
var singleDiscoverPointView = new SingleDiscoverPointView({ model: point });
$("#discoverWrapper").append(singleDiscoverPointView.render().el);
$(".subpage").removeClass("active");
$(singleDiscoverPointView.el).addClass("active");
}
},
views:
// FULL DISCOVER POINT COLLECTION VIEW
var AllDiscoverPointsView = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(function(model) {
var discoverPointView = new DiscoverPointView({ model: model });
this.$el.append(discoverPointView.render().el);
}, this);
return this;
}
});
// SINGLE DISCOVER POINT VIEW
var DiscoverPointView = Backbone.View.extend({
tagName:'li',
className:'point',
events: {
"click": "select"
},
select: function(){
$('.point').removeClass('active');
this.$el.addClass('active');
},
template: _.template(template('discoverViewTemplate')),
render: function(){
this.id = this.model.get('idWord');
this.$el.attr('id',this.id).html(this.template(this.model.toJSON()));
return this;
}
});
// SINGLE DISCOVER POINT VIEW
var SingleDiscoverPointView = Backbone.View.extend({
tagName: 'div',
className:'subpage',
template: _.template(template('singleDiscoverViewTemplate')),
render: function(){
this.id = this.model.get('idWord');
this.$el.attr('id',this.id).html(this.template(this.model.toJSON()));
return this;
}
});
models/collections:
// COLLECTION OF ALL POINTS IN DISCOVER
var AllDiscoverPoints = Backbone.Collection.extend({
model: DiscoverPoint
});
// MODEL OF A SINGLE DISCOVER POINT
var DiscoverPoint = Backbone.Model.extend({
defaults: {
title: 'Glass Elephant',
imgPath: 'root/public/img/glass-elephant.png',
caption: 'The Imagists were extreme collectors, scouring countless thrift shops and beyond!',
link: 'discover/glasselephant',
misc: 'Any extra info goes here...'
}
});
// DISCOVER POINT MODEL INSTANCES
var glasselephant = new DiscoverPoint({
id: 1,
idWord: 'glasselephant',
title: 'Glass Elephant',
imgPath: 'root/public/img/glass-elephant.png',
caption: 'The Imagists were extreme collectors, scouring countless thrift shops and beyond!',
link: 'discover/glasselephant',
misc: 'Any extra info goes here...'
});
var carvedstatue = new DiscoverPoint({
id: 2,
idWord: 'carvedstatue',
title: 'Carved Statue',
imgPath: 'root/public/img/carved-statue.png',
caption: 'The Imagists were extreme collectors, scouring countless thrift shops and beyond!',
link: 'discover/carvedstatue',
misc: 'Any extra info goes here...'
});