Underscore templating error with backbonejs [duplicate] - backbone.js

This question already has an answer here:
Underscore.js Template Issue - Cannot call method 'replace' of null
(1 answer)
Closed 9 years ago.
When this html code with Backbonejs is opened Chrome Java script console is throwing the following error - uncaught TypeError: Cannot call method 'replace' of undefined ,
but when i remove this one line of code containing usage of underscore templating this.template = _.template($('#listing').html())
from the List_view's initialize method its working fine . Why is the usage of underscore templating throwing the error ??
Here is the code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example Backbone Whisky APP</title>
</head>
<body>
<script src="LIB/json2.js"></script>
<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>
<script type = "text/template" id="listing">
<li>%= value %</li>
</script>
<script>
Whisky = Backbone.Model.extend();
firstWhisky = new Whisky({
name : 'Blenders Pride'
});
Whiskies = Backbone.Collection.extend({
Model:Whisky ,
url:"#"
});
first_view = Backbone.View.extend({
el : 'body',
initialize : function() {
this.$el.empty();
this.render();
} ,
render : function() {
this.$el.append("<h1>The Whisky APP</h1>");
this.list_view = new List_view();
this.$el.append(this.list_view.render().el);
return this ;
}
});
List_view = Backbone.View.extend({
tagName : 'ul' ,
initialize : function() {
this.template = _.template($('#listing').html());
} ,
render : function() {
this.$el.empty();
this.$el.append("<li>Royal stag</li>");
this.$el.append("<li>Signature </li> ");
return this ;
}
});
index_view = new first_view();
</script>
</body>
</html>

The problem is that your call to $("#listing").html() is returning undefined as the element isn't available yet. You'd need to wait for the DOM to have loaded to access the element by ID. You could confirm this by doing a simple alert inline. You need to delay the retrieval until the DOM is ready.
In this case, it's because you've got the script tags in the body, making them unavailable at the time that you've requested them. Move the script tags (and templates), and it will work:
http://jsbin.com/obahux/2/
One issue you'll have is that your syntax is wrong, you should be using <% and %> for executing code or emitting values:
<script type = "text/template" id="listing">
<li><%= value %></li>
</script>

That's because, in your first_view initialize function you're clearing el with this.$el.empty() that makes body empty and there is nothing in there, all scripts and templates in there will be ommited.
You can find a better solution for clearing it. or just wrap that in another div tag

Related

Problems relate to directive in angular JS

I am new to Angular Js and i have just done basic tags of angularjs,controller and when i started directive part i understood the concept but was unable to fetch data input written in template which is one of the directive property..Please guide me so that i can take one step further in AngularJS.
Thanks in Advance!!
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body>
<div ng-app="appname" directive-name></div>
<script>
var app = angular.module("appname",[]);
app.directive("directiveName",function()
{
return
{
template : "Hi i am template"
};
});
</script>
</body>
</html>
Nothing wrong with your logic or Angular, it's because of JavaScript automatic semicolon insertion. Lines that do not end with a semicolon, but could be the end of a statement are automatically terminated. So, your return statement -
app.directive("directiveName",function()
{
return //automatic semicolon insertion here
{
template : "Hi i am template"
};
});
Should be indeed -
app.directive("directiveName",function()
{
return{
template : "Hi i am template"
};
});
Your close:
<!doctype html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-app="appname" ng-controller="MyController">
<h1>{{Header}}</h1>
<my-template></my-template>
<script>
var app = angular.module("appname",[]);
app.controller("MyController", function ($scope){
$scope.Header = "My Directive";
});
app.directive("myTemplate",function() {
return {
template : "<span>Hi i am template</span>",
restrict: 'E',
};
});
</script>
</body>
</html>
I've tested the above code and will render your directive.
everything is allright, except the linebreak after the return.
return {
template : "Hi i am template"
};
There are many problems:
1. You should use https for cdn
2. You have to put var app = angular.module("appname",[]); before the ng-app tag
the return should be
return { //the brace should be after return
template : "Hi i am template"
};
because in modern browsers semicolons are not mandatory(they are added automatically), so return means returns nothing.
finally:
<script>
var app = angular.module("appname",[]);
app.directive("directiveName",function()
{
return{
template : "Hi i am template"
};
});
</script>

Using ReactJS with AngularJS

I am trying to use ReactJS with AngularJS but it is not working out. Could anyone please direct me on how to gel them together? Or please point out what am missing here?
My index.html is as follows:
<!DOCTYPE html>
<html data-ng-app="MyApp">
<head>
<title>My Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body data-ng-controller="Ctrl1">
<div id="myDiv">
<button id="btn1" data-ng-click="clickMe()">Click Me</button>
</div>
<script src="http://fb.me/react-0.8.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.8.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>
<script type="text/jsx" src="reactExample.js"></script>
<script src="angularExample.js"></script>
</body>
</html>
Here is how my reactExample.js has been written:
/**
* #jsx React.DOM
*/
var testMy = React.createClass({
render: function(){
return ( <p>Hello</p>)
}
});
And my angularExample.js is as follows:
var myapp = angular.module('MyApp',[]);
myapp.controller('Ctrl1',['$scope',function($scope){
$scope.clickMe = function(){
alert("Clicked!");
React.renderComponent(testMy, elem[0]);
}
}]);
It does not display anything (other than the alert). Am expecting to see 'Hello' printed there but it throws the following error in the console:
Error: Invariant Violation: prepareEnvironmentForDOM(...): Target container is not a DOM element
Any help would be much appreciated.
Already #Simon Smith mentioned why the error occour React.renderComponent expect second argument but the way you play DOM manipulation inside controller is not appropriate. In AngularJs DOM manipulation should be in directive. Lets see how it could be
From Facebook's React vs AngularJS: A Closer Look blog
React components are far more powerful than Angular templates; they should be compared with Angular's directives instead.
Bottom of this blog Using React and AngularJS together section you can see how angular and react can play together.
From react website
React components implement a render() method that takes input data and returns what to display.
In angularjs components are rendered by directive
See this plunker where I integrate angularjs and react.
In react-example.js I have created virtual dom element
var Hello = React.createClass({
render: function() {
return React.DOM.div({}, 'Hello ' + this.props.name);
}
});
And myMessage directive render the virtual dom
React.renderComponent(Hello({name: scope.myModel.message}), document.getElementById('example'));
Where virtual dom's name property will bind with scope.myModel.message
To use React in my controller, i do this
myApp.controller(function($scope){
$scope.myComponent = {};
$scope.myComponent.setState({data: "something"});
});
and in my React component:
window.myComponent = React.createClass({
getInitialState: function(){
return {
data:''
}
},
componentWillMount: function(){
var scope = this.props.scope;
scope.myComponent = this;
},
render:func .....
});
I'm using the ngReact directive from David Chang, which passes the $scope as a property into a component. So now you can call setState from your controller, forcing the react component to re-render :)
I have a bigger example of above in my React-Sandbox
I would consider doing the integration via a directive as that is often the recommended approach for integrating non angular code with angular.
Here is an example:
angular.module('app').directive('greeting',[function(){
return {
restrict:'AE',
scope:{name:'#'},
link: function (scope, elem, attrs){
var GreetingSection = React.createClass({displayName: 'Greeting',
render: function() {
var message = "Good morning " + this.props.data.name + ", how are you?";
return React.createElement('div', {className: "greeting"},message);
}
});
React.renderComponent(GreetingSection({data:scope}), elem[0]);
}
};
}]);
More info here (link died; points to archive.org copy):
http://www.syntaxsuccess.com/viewarticle/547bbd04fa0710440b45e41c
Instead try using ngReact angular module rather than using ReactJs directly. This provides seamless integration with angularJs as well. Check the samples/examples # https://github.com/davidchang/ngReact
renderComponent expects a DOM element as a second argument to inject the component. It seems that is what the error is complaining about.

Why doesn't this.$el.append() work?

I'm trying to follow along http://addyosmani.github.io/backbone-fundamentals. I'm not getting how $el is supposed to work in a view.
Here's my HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Dashboard</title>
</head>
<body>
<h1>Dashboard</h1>
<ol class="foo" id="recent-station">
</ol>
<!-- Templates -->
<script type="text/template" id="station-template">
<li><%= station %></li>
</script>
<!-- Javascript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script src="static/js/script.js"></script>
</body>
</html>
And script.js is:
var RecentStation = Backbone.Model.extend( {
defaults: {
station: "",
},
initialize: function() {
console.log('initialized: ' + JSON.stringify(this));
this.on('change', function() {
console.log('changed: ' + JSON.stringify(this));
})
}
});
var RecentStationView = Backbone.View.extend( {
tagName: 'ol',
id: 'recent-station',
initialize: function() {
this.model.bind('change', _.bind(this.render, this));
},
render: function() {
console.log('render');
this.$el.append('<li>foo</li>');
$('ol#recent-station').append('<li>bar</li>');
return this;
},
});
var recent = new RecentStation();
var recentView = new RecentStationView({model: recent});
recent.set('station', 'My Station');
The interesting stuff is happening in the render function. I can see "render" logged to the console, and the "bar" text gets appended to the node, but not the "foo" text. I thought this.$el and $('ol#recent-station') were the same thing, but obviously not. What am I missing?
If you don't specify a dom element using el attribute, one will be created using tagName,id,className, and attributes from the view.
In your case you don't specify an el attribute in your view so you create an element that looks like:
<ol id='recent-station'></ol>
You then append <li>foo</li> into it, but your view element is still not in the DOM.
$('ol#recent-station') returns the dom element included in your html which is different than your view element, but has the same attributes.
So, in your example you would need to specify an existing element by supplying an el attribute.
var RecentStationView = Backbone.View.extend( {
// remove tagName and id
el:'#recent-station',
/* rest of your code below */
A fiddle with the changes, http://jsfiddle.net/DsRJH/.

Using External Views with Backbone.js

So I have seen other posts pertaining to this question but regardless of my efforts to incorporate their suggestions, I don't seem to be able to solve the issue. So here is the code:
What am I doing wrong here and what is the best way to get the application started?
Entire Application:
var Person = Backbone.Model.extend({
defaults : {
name : ""
}
});
var PersonView = Backbone.View.extend({
el : $('body'),
tagName: 'li',
initialize : function() {
this.render();
},
render : function(){
var template = _.template( $("#person").html(), {} );
this.$el.html(template);
return this;
}
});
HTML w/ Template:
<script type="text/template" id="person">
<h1><%= name %></h1>
</script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script type="text/javascript" src="js/underscore.js"></script>
<script type="text/javascript" src="js/backbone.js"></script>
<script type="text/javascript" src="app.js"></script>
At first glance, it looks like you're defining the DOM location incorrectly.
el : $('body'),
tagName: 'li',
el should be the jQuery selector string rather than the object
el : 'body'
but then, this.$el will be $('body') effectively, so this.$el.html(template) will try to put data into the <body> tag.
It looks like, from the tagName: 'li' that you want to have multiple PersonViews, so I suggest not defining the el property at all, then appending this.$el once the view has been rendered to whatever parent element you need it in.
On further review, "entire application" suggests that you're not initialising anything.
The Javascript you have there is defining a Person class and a PersonView class. Presumably, you will want to instantiate one or more objects of the Person class and then also a PersonView for each Person, this is achieved by using new, as mu is too short mentions.
Effectively, the code you have is the setup, now you need to start it.
var joe = new Person;
var joeView = new PersonView({ person: joe });
That snippet assumes that you'll be expecting { person: joe } in PersonView, which you're not, but the errors thrown should point you in a better direction.

template not loading in backbone.js ( TypeError: text is undefined )

I'm learning backbone.js and I'm pretty much in the beginning. I want to add a template through underscore template method but it's not working for me. I searched for this error but couldn't fix it myself. How can I move forward if it's not showing the template. Need some help guys.
Here is the code (this code is from addyosmani's book backbone-fundamentals):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>testing</title>
</head>
<body>
<script src="scripts/jquery.js"></script>
<script src="scripts/underscore.js"></script>
<script src="scripts/backbone.js"></script>
<script>
var TodoView = Backbone.View.extend({
tagName: 'li',
// Cache the template function for a single item.
todoTpl: _.template( $('#item-template').html() ),
events: {
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'blur .edit': 'close'
},
// Re-render the titles of the todo item.
render: function() {
this.$el.html( this.todoTpl( this.model.toJSON() ) );
this.input = this.$('.edit');
return this;
},
edit: function() {
// executed when todo label is double clicked
},
close: function() {
// executed when todo loses focus
},
updateOnEnter: function( e ) {
// executed on each keypress when in todo edit mode,
// but we'll wait for enter to get in action
}
});
var todoView = new TodoView();
// logs reference to a DOM element that cooresponds to the view instance
console.log(todoView.el);
If the template is defined after your script it will not work.
wrap your entry point into
$(function(){
var todoView = new TodoView();
});
so you dont get this kind of error.
I got the same error. Make sure that template with defined id exists on the page.
In my case I used wrong id for template, and this was a reason of error "TypeError: n is undefined".

Resources