The scenario is to add the values entered in form fields into a table on click of Add button. I am new to this both and not sure how data binding works.
My initial html is
<table class="table table-bordered">
<thead>
<tr>
<th>Model</th>
<th>Brand</th>
<th>Year</th>
<th>Action</th>
</tr>
<tr>
<td><input class="form-control" id="modelname"></td>
<td><input class="form-control" id="brandname"></td>
<td><input class="form-control" id="yearname"></td>
<td><button class="btn btn-primary add">Add</button></td>
</tr>
</thead>
<tbody class="product-list">
</tbody>
</table>
</div>
<script type="text/x-handlebars-template" id="product-template">
{{#each []}}
<tr>
<td>{{ model }}</td>
<td>{{ brand }}</td>
<td>{{ year }}</td>
<td><div class="btn btn-primary">Edit</div> <div class="btn btn-danger">Delete</div></td>
</tr>
{{/each}}
</script>
I messed up in js for purpose of using handlebars as
var Product=Backbone.Model.extend({
model:'',
brand:'',
year:''
});
var ProductCollection=Backbone.Collection.extend({
model:Product
});
var modelname= document.getElementById('modelname').value;
var brandname=document.getElementById('brandname').value;
var yearname= document.getElementById('yearname').value;
var ProductView = Backbone.View.extend({
el: '.product-list',
tagName: 'tr',
events:{
"click .add": "create"
},
initialize:function(){
this.render();
},
render:function()
{
var source=$('#product-template').html();
var template=Handlebars.compile(source);
var html=template(this.products.toJSON());
},
create: function(e){
var product=new Product({
model:modelname,
brand:brandname,
year:yearname
})
console.log(product.toJSON);
products.add(product);
modelname="";
yearname="";
brandname="";
}
});
var products=new ProductCollection();
Share me an idea how to proceed. I don't get an error and at the same time, nothing happens! I am very new to backbone. Please tolerate blunders.
I make and example how can achieve that with underscore template and handlebars. Use it for iterating over a collection of models to display a list of products.
Underscore.js
<tbody class="product-list">
<script type="text/template" id="product-template">
<% _.each(products.models, function(product){ %>
<tr>
<td><%= product.get('modelName') %></td>
<td><%= product.get('brand') %></td>
<td><%= product.get('year') %></td>
</tr>
<% }) %>
</script>
</tbody>
In script file define model:
var Product = Backbone.Model.extend({});
Next, define a collection and add those model to the collection:
var ProductList = Backbone.Collection.extend({
model: Product
});
Most of the time we use view in Backbone application to do the rendering:
var ProductView = Backbone.View.extend({
el: '.product-list',
template: _.template($('#product-template').html()),
render: function(products){
this.$el.html(this.template({
products: products
}));
}
});
You can see from working code full app, and see that we call render method from productView and pass it collection as argument: this.productView.render(this.collection)
Now we can use it as a list in template to iterate and display modelName, brand and year for each product in lists.
Working code: jsFiddle
I am particularly asked to use handlebars
Handlebars.js
You have many errors in your code:
define instance of view var products = new ProductView();, instead of that you define instance of ProductCollection();
var html=template(this.products.toJSON()); Cannot read property 'toJSON' of undefined , check initialize method in working example, you need to define collection and listen to him, because every time we add something to collection we want to render ProductView
el: '.table' not el: '.product-list',
var modelname= document.getElementById('modelname').value; var brandname ... - you make them as a global variables, instead of that place that variables inside create() method
replace var html=template(this.products.toJSON()); with $('.product-list').html(template(this.products.toJSON()))
First of all read documentation if something isn't clear: backbone.js and check working example: jsFiddle
Related
As stated in the latest Marionette docs:
CompositeView is deprecated. You should use the replaceElement option on Region.show and
render a CollectionView into a region inside a View to achieve this functionality.
I still can't understand how CompositeView functionality should be achieved now.
Previously, CompositeView was perfect for using with such template:
<script id="table-template" type="text/html">
<table>
<% if (items.length) { %>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<% } %>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3">some footer information</td>
</tr>
</tfoot>
</table>
new MyCompositeView({
template: "#table-template",
templateContext: function() {
return { items: this.collection.toJSON() };
}
// ... other options
});
If we decide to use LayoutView instead of CompositeView then we need to code manually a lot of event bindings (for example to show / hide table header based on number of items in collection). This makes things harder.
Are there any clean and not complicated ways to live without CompositeView?
Thank you for any help or advice.
It looks like Marionette 3 is going to get rid of some concepts to make the framework simpler overall, and easier to understand.
Marionette.View in 3 is going to include functionality from what was ItemView and LayoutView. CompositeView is deprecated in favour of just using RegionManager, which is now included into View.
v2 --> v3
View -> AbstractView
ItemView, LayoutView -> View
Here's a quick example app:
var color_data = [ { title:'red' }, { title:'green' }, { title:'blue' } ];
var Color = Backbone.Model.extend({
defaults: { title: '' }
});
var Colors = Backbone.Collection.extend({
model: Color
});
var ColorView = Mn.View.extend({
tagName: 'tr',
template: '#colorTpl'
});
var ColorsView = Mn.CollectionView.extend({
tagName: 'tbody',
childView: ColorView
});
var AppView = Mn.View.extend({
template: '#appTpl',
templateContext: function(){
return {
items: this.collection.toJSON()
};
},
ui: {
input: '.input'
},
regions: {
list: {
selector: 'tbody',
replaceElement: true
},
},
onRender: function(){
this.getRegion('list').show(new ColorsView({
collection: this.collection
}));
},
events: {
'submit form': 'onSubmit'
},
onSubmit: function(e){
e.preventDefault();
this.collection.add({
title: this.ui.input.val()
});
this.ui.input.val('');
}
});
var appView = new AppView({
collection: new Colors(color_data)
});
appView.render().$el.appendTo(document.body);
<script src='http://libjs.surge.sh/jquery2.2.2-underscore1.8.3-backbone1.3.2-radio1.0.4-babysitter0.1.11-marionette3rc1.js'></script>
<script id="colorTpl" type="text/template">
<td><%=title%></td>
<td style="background-color:<%=title%>"> </td>
</script>
<script id="appTpl" type="text/template">
<table width="100%">
<% if(items.length) { %>
<thead>
<tr>
<th width="1%">Title</th>
<th>Color</th>
</tr>
</thead>
<% } %>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="2">
<form><input type="text" class="input" autofocus><input type="submit" value="Add Color"></form>
</td>
</tr>
</tfoot>
</table>
</script>
I'm getting a collection is not defined error in backbone.
var CategoryList = Backbone.View.extend({
el:'#content',
render: function() {
var that = this;
var cats = new Categories();
cats.fetch({
success: function(cats) {
var template = _.template($('#category-list-template').html(), {cats: cats.models});
cats.each(function(it) {
console.log(it.toJSON());
});
that.$el.html(template);
}
})
}
});
And in this script I'm running an each loop to add data to the table, but I'm getting an 'cats is not defined error'.
<script type="text/template" id="category-list-template">
<table class="table striped">
<thead>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
</thead>
<tbody>
<% _.each(cats, function(category) { %>
<tr>
<td><%= category.name %> </td>
</tr>
<% }); %>
</tbody>
</table>
</script>
You have to transform collection to plain javascript object using toJSON method.
var template = _.template($('#category-list-template').text());
var result = template({collection: cats.toJSON()});
that.$el.html(result);
I am new to angularjs and trying to work with ng-repeat but somehow ng-repeat's key/value is not visible if I am trying to print it in nested tags
working:
<div>
<table>
<tr ng-repeat="prop in array">
<td><span ng-bind-html="prop.field1"></span></td>
</tr>
</table>
</div>
And below code is not working:-
<div ng-repeat="prop in array">
<table>
<tr>
<td><span ng-bind-html="prop.field1"></span></td>
</tr>
</table>
</div>
Updated:
var $app = angular.module('apps', ['ngSanitize']);
$app.controller('cntr', ['$scope', function($scope) {
$scope.guestList = [{
dob: '12/12/12'
}];
}]);
For html to show properly in angular js you have to 'sanitize' it, using the $sce provider from AngularJS. Read here: https://docs.angularjs.org/api/ng/service/$sce
In principle, before you bind your variable to html output, you have to sanitize it like this:
$scope.guest.sanitizedInput = $sce.trustAsHtml($scope.guest.res_addr1);
and html:
<td class="table-column-value res-addr1-value"><span ng-bind-html="guest.sanitizedInput"></span>
Part of a project I am working on is listing members, members companies, etc... I used $http within my controller to connect to a JSON file to get everything wired up and test functionality. I am using ng-repeat to populate the page and ng-scope to allow visitors to search the listings.
Everything worked as expected until I wired up Firebase to hold the data. Again, the data populates the page, but now my ng-model is broke and there's no way to search the listings. My Angular skills are still being polished, to say the least, so I'm sure I am having a noob moment. Any chance someone could take a look, I 'de be very grateful. Here is my code.
My Controller
.controller( 'AlliedCtrl', function AlliedCtrl( $scope, $firebase) {
var ref = new Firebase("https://mybase.firebaseio.com/members");
var sync = $firebase(ref);
var syncObject = sync.$asObject();
syncObject.$bindTo($scope, "members");
});
My HTML
<div class="search-sort">
<div class="row">
<div class="col-sm-12 col-xs-12">
<input ng-model="query" placeholder="Search and Sort by Keyword e.g. Accounting, Law, Mobile etc..." class="small">
</div>
</div>
</div>
<table class="table table-condensed table-responsive">
<thead>
<tr>
<th></th>
<th>Member Company</th>
<th>Company Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="m in members | filter:query">
<td style="width:250px"></td>
<td style="width:250px"><b>{{ m.company }}</b><br>{{ m.address }}<br>{{ m.address2 }}<br>{{ m.url }}<br><br>{{ m.name }}<br><em>{{ m.title }}</em><br>{{ m.phone }}<br><a mailto="{{ m.email }}">{{ m.email }}</a><br><br></td>
<td>{{ m.description }}</td>
</tr>
</tbody>
</table>
If you want to use ng-repeat on a collection from Firebase, you have to call $asArray and not $asObject + $bindTo.
So in your controller:
var ref = new Firebase("https://mybase.firebaseio.com/members");
var sync = $firebase(ref);
var syncObject = sync.$asArray();
$scope.members = syncObject;
If I understand your problem correctly, you need to wait until the syncObject has loaded so...
//Controller
var members = syncObject.$loaded().then(function() {
members.$bindTo($scope, "members");
}
As in the title: How can I use Angular to allow downloading the content of a div (ng-repeat directive in this case) in a new HTML file?
I have tried this:
var content = 'file content';
var blob = new Blob([ content ], { type : 'text/html' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );
HTML:
<a download="content.txt" ng-href="{{ url }}">download</a>
but it doesn't work.
I know how to achieve in pure JS, but I was wondering if there is some shorter, handy way of achieving that with Angular?
You can use FileSaver.js to output the table as a file for the user to download.
<div id="exportable">
<table width="100%">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>DoB</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.dob | date:'MM/dd/yy'}}</td>
</tr>
</tbody>
</table>
</div>
Export call:
var blob = new Blob([document.getElementById('exportable').innerHTML], {
type: "text/html;charset=utf-8"
});
saveAs(blob, "content.html");
};
Demo: http://jsfiddle.net/XNVj3/108/