I did a fetch in my collection and I did a console.log() and it's working but when I set to my template not show the values.
in my View is it:
var reposCollection = new Sice.Collections.RepositoryList();
reposCollection.fetch();
this.$el.html(this.template({collection: reposCollection.models}));
my template is it:
<% _.each(collection, function(repos) { %>
<tr>
<td><%= repos.attributes.name %></td>
<td><%= repos.attributes.description %></td>
<td><%= repos.attributes.language %></td>
</tr>
<% }); %>
I don't know what is happening!
Your template is being rendered before your fetch is completed. You need to call the rendering code in the success callback of fetch:
var reposCollection = new Sice.Collections.RepositoryList();
reposCollection.fetch({
success: (collection, response, options) => {
this.$el.html(this.template({collection: collection.models}));
}
});
Backbone.Collection.prototype.fetch documentation
Related
I am pretty new to angular and I am working on a data entry web page. The web page has three tabs. Vendor, Products and Types. I started working on the Types tab first. I'd be happy if I could just display the results of a GET request to my Rest API. My Rest API works:
# curl http://192.168.1.115:8080/type
[
{"_id":"56415e7703aba26400fcdb67","type":"Skiing","__v":0},
{"_id":"56417a8503aba26400fcdb68","type":"Bannana","__v":0},
{"_id":"56417a8d03aba26400fcdb69","type":"Carrot","__v":0},
{"_id":"56417a9603aba26400fcdb6a","type":"Beer","__v":0}
]
Here's the pertinent part of my html UPDATED I now have st-safe-src=all_typesbut still no joy ...
<div ng-controller="typeCtrl" class="tab-pane" id="types-v">
<p>The number {{3 + 4}}.</p>
<p>message is {{message}}</p>
<table st-table="displayedCollection" st-safe-src="all_types" class="table table-striped">
<tbody>
<tr ng-repeat="x in displayedCollection">
<td>{{x.type}}</td>
</tr>
</tbody>
</table>
</div> <!-- end Types Tab -->
... and here is my typeCtrl.js ...
app.controller("typeCtrl", function($scope,$http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$scope.all_types = function () {
$http.get("http://192.168.1.115:8080/type").then(function(response) {
console.log(response);
console.log(response.data);
return response.data;
});
}
});
... but when I click on the Types tab my data does not display. I looked developer console and I do not even see the GET request kickoff. And my web page looks like this ...
... what am I doing wrong?
There is nothing that calls all_types. Run http.get and assign the response to all_types
app.controller("typeCtrl", function($scope,$http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$http.get("http://192.168.1.115:8080/type").then(function(response) {
$scope.all_types = response;
});
}
});
My understanding is that you want a get request to be fired whenever you click on the Types tab, right? If so, just use ng-click to call your all_types function as follows:
<div ng-controller="typeCtrl" ng-click="all_types()" class="tab-pane" id="types-v" >
Also, you do not need to return response.data in your controller. Just assign the data to a scope object and use it in the template.
And finally, I would suggest wrapping all your ajax calls in factories and then inject those factories in your controllers.
Here is your code
<div ng-controller="typeCtrl" class="tab-pane" id="types-v">
<p>The number {{3 + 4}}.</p>
<p>message is {{message}}</p>
<table st-table="types" class="table table-striped"><!-- Do not need st-safe-src -->
<tbody>
<tr ng-repeat="x in types"><!-- Use the Collection name as types-->
<td>{{x.type}}</td>
</tr>
</tbody>
</table>
</div>
Controller Code
app.controller('typeCtrl', function($scope, $http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$http.get("http://192.168.1.115:8080/type").then(function(response) {
console.log(response.data);
$scope.types = response.data;
});
});
Here is working the plunker
I have a controller that reads data via rest and displays it in a table - this part is working fine. Additionally i added a WebSocket and want to update the table if Websocket receives data. Here is the Code:
app.controller('allBookingsCtrl', function($scope, $http, currentUser){
$scope.bookingsList = [];
var ws = new WebSocket(wsRootUrl + '/allbookings');
ws.onmessage = function(message){
$scope.$apply(function(){
$scope.bookingsList = message.data;
alert(message.data);//displays correct data!
});
};
$http.get(rootUrl + '/timetracker/booking/all').success(function(response) {
$scope.bookingsList = response;
});
});
The problem is the table is not updated on call of apply. I debugged and could trigger onmessage by changing data from another browser. the content of data is also correct, no error is thrown.
So how to update the table/scope with data received by websocket?
here is html:
<table ng-controller="allBookingsCtrl" class="table">
<thead>
<tr>
<th>#</th>
<th>user</th>
<th>project</th>
<th>start</th>
<th>end</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="booking in bookingsList">
<td>{{$index + 1}}</td>
<td>{{booking.usersProjects.user.name}}</td>
<td>{{booking.usersProjects.project.name}}</td>
<td>{{booking.start | date:'yyyy-MM-dd HH:mm:ss' }}</td>
<td>{{booking.end | date:'yyyy-MM-dd HH:mm:ss' }}</td>
</tr>
</tbody>
</table>
Small Edit:
if i add alert(data); at the end of on message i see the alert with correct data! So only the apply with the list isn't working correctly.
added plunkr
I tried to reproduce this in plunkr and with no websocket - tried to get the update on ng-click. But this isn't working neither - here the click is not doing anything.
Can you change this line
$scope.bookingsList = data;
to
$scope.bookingsList = message.data;
I'm having some problems rendering headers in a composite or collection view. I tried both, but this simply won't render.
Let me show you a little bit of my code:
// composite view
define(function(require) {
var BusinessTableTemplate = require("text!templates/BusinessTableTemplate.html");
var BusinessRowView = require("views/BusinessRowView");
var Marionette = require("marionette");
return BusinessTableView = Marionette.CompositeView.extend({
tagName: "table",
className: "table table-hover",
template: BusinessTableTemplate,
itemView: BusinessRowView,
appendHtml: function(collectionView, itemView){
collectionView.$("tbody").append(itemView.el);
}
});
});
// BusinessTableTemplate
<script type="text/template">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>Building</th>
<th>Phone</th>
<th>Website</th>
<th>Categories</th>
</tr>
</thead>
<tbody></tbody>
</script>
// itemview
define(function(require) {
var BusinessRowTemplate = require("text!templates/BusinessRowTemplate.html");
var Marionette = require("marionette");
return BusinessRowView = Marionette.ItemView.extend({
template: BusinessRowTemplate,
tagName: "tr"
});
});
// BusinessRowTemplate
<script type="text/template">
<td>
<%= name %>
</td>
<td>
<% if (address) { %>
<%= address %>
<% } else { %>
NA
<% } %>
</td>
<td>
<% if (building) { %>
<%= building %>
<% } else { %>
NA
<% } %>
</td>
<td>
<td>
<% _.each(phones, function(phone) { %>
<span class="label label-default label-info"><%= phone %></span>
<% }); %>
</td>
<td>
<% if (website) { %>
<%= website %>
<% } else { %>
NA
<% } %>
</td>
<td>
<% _.each(tags, function(category) { %>
<span class="label label-default label-info"><%= category %></span>
<% }); %>
</td>
<td>
Ações
</td>
</script>
// signal or event where I render the view
vent.on("search:seeAll", function(){
if (self.results) {
var businessDirectoryView = new BusinessDirectoryView();
self.layout.modals.show(businessDirectoryView);
var resultCollection = new Businesses(self.results, {renderInMap: false});
var businessTableView = new BusinessTableView({
collection: resultCollection
});
$("#businesses-container").html(businessTableView.render().$el);
}
});
I followed a tutorial somewhere, but they did not specified headers for the table.
A little help here to render this?
Thanks!
What is the Marionette JS version you are using?
Here is a fiddle using the version 2.4.1 (latest available) and it is working:
https://jsfiddle.net/aokrhw1w/2/
There are some changes in the composite view with previous versions, the 'itemView' was changed to 'childView'.
var BusinessTableView = Marionette.CompositeView.extend({
tagName: "table",
className: "table table-hover",
template: '#BusinessTableTpl',
childView: BusinessRowView,
childViewContainer: "tbody",
appendHtml: function (collectionView, itemView) {
console.log('append');
this.$("tbody").append(itemView.el);
}
});
Hope this solves your problem.
CollectionView renders one child view per model in a collection. It doesn't give you any way to add extra HTML like a header.
CompositeView renders collection items too, but you can also add extra HTML. Your example uses CompositeView, so you have made the right choice.
Because you can have custom HTML around the rendered collection, you need to tell Marionette where to render the collection item views. The childViewContainer property does that:
Marionette.CompositeView.extend({
tagName: "table",
className: "table table-hover",
template: BusinessTableTemplate,
itemView: BusinessRowView,
childViewContainer: 'tbody'
});
You don't need to use attachHtml for simple cases like this - it's for edge cases where you need to change Marionette's default behaviour.
See the CompositeView docs for more detail.
Marionette 3 deprecated the CompositeView class. Instead, a region can now overwrite its el with the rendered contents of the
inner View with the new replaceElement option.
See this example to render a table:
var RowView = Marionette.View.extend({
tagName: 'tr',
template: '#row-template'
});
var TableBody = Marionette.CollectionView.extend({
tagName: 'tbody',
childView: RowView
});
var TableView = Marionette.View.extend({
tagName: 'table',
className: 'table table-hover',
template: '#table',
regions: {
body: {
el: 'tbody',
replaceElement: true
}
},
onRender: function() {
this.showChildView('body', new TableBody({
collection: this.collection
}));
}
});
var list = new Backbone.Collection([
{id: 1, text: 'My text'},
{id: 2, text: 'Another Item'}
]);
var myTable = new TableView({
collection: list
});
myTable.render();
Note to overzealous moderators: I gave the same answer here. Both questions are not duplicates but do refer to the same deprecated class. It makes no sense to spend time to "tailor the answer to both questions"; it's just a waste of time since the only important part is: "This class is deprecated, use that one instead. Here's an example and link to the doc".
I've come across this backbone / underscore template binding issue that I can't get my head around.
The underscore template is being provided a collection of objects however when traversing the collection and building the resultant HTML not all the individual object attributes are being resolved.
I believe the template is receiving the collection correctly (asynchronous call to the server). I've debugged the received collection and its populated correctly.
I've "rendered" the raw collection items within the HTML to verify I'm dealing with the correct objects...all appears correct.
I've simplified the code here in the hopes of not blurring the issue. A click event is responsible for selecting a section(not included in code)
Here is the code:
//Underscore Template
<script id="articles-template2" type="text/html">
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Date</th>
<th>Data</th>
</tr>
</thead>
<tbody>
<% _.each(articles, function(a){ %>
<tr>
<td><%= JSON.stringify(a)%></td>
<td><%= a.title %></td>
<td><%= a.description%></td>
<td><%= new Date(a.date)%></td>
</tr>
<%});%>
</tbody>
</table>
</script>
// Backbone View ----------------
window.ArticlesView2 = Backbone.View.extend({
initialize: function () {
var self = this;
self.collection.on("reset", this.render, this);
},
template: _.template($('#articles-template2').html()),
render: function () {
var self = this;
$(self.el).html(self.template({ articles: self.collection.models }));
$('#section-list').append(self.el);
return self;
},
events: {
"click a": "clicked"
},
clicked: function (e) {
var self = this;
e.preventDefault();
var id = $(e.currentTarget).data("id");
var item = self.collection.get(id);
var name = item.get("title");
alert(name);
}
});
// Router ----------------
var AppRouter = Backbone.Router.extend(
{
routes: {
'section/:title': 'viewSection'
},
viewSection:function(section)
{
var self = this;
self.articles = new ArticleList({ selectedSection: section });
self.view = new ArticlesView2({ collection: self.articles });
self.articles.fetch({ reset: true });
}
}
);
// Initialize ----------------
var app = new AppRouter();
Backbone.history.start();
app.navigate('section/Home', { trigger: true });
The rendered HTML is as follows :
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Date</th>
<th>Data</th>
</tr>
</thead>
<tbody>
<tr>
<td>{"id":"527c61082241f813c09c7041","title":"title here","description":" test descript here","date":"2005-02-08T05:00:00.0000000Z"}</td>
<td></td>
<td></td>
<td>Invalid Date</td>
</tr>
</tbody>
</table>
I'm not sure why one can Stringfy() the object and get data but fail to interrogate its attributes successfully?????
Is this a event issue, man what am I missing?
Thanks
You're passing the raw array of your models to your template and a model is not a hash, you don't have direct access to the properties.
Either use collection.toJSON
self.template({ articles: self.collection.toJSON() })
or use model.get in your template
<%= a.get('title') %>
Note that in your example JSON.stringify will give you the expected representation of your data because
toJSON behavior
If an object being stringified has a property named toJSON whose value
is a function, then the toJSON method customizes JSON stringification
behavior: instead of the object being serialized, the value returned
by the toJSON method when called will be serialized.
and your models have a toJSON method.
I've got this Backbone.Model representing a Google Books API volume:
var Book = Backbone.Model.extend({
defaults: {
volumeInfo : {
title: 'n.a.',
authors: 'n.a.',
publisher: 'n.a.',
publishedDate: 'n.a.',
imageLinks : {
smallThumbnail: '/unavailable.jpg'
}
}
},
parse: function(resp) {
if (resp.volumeInfo.authors) {
resp.volumeInfo.authors = resp.volumeInfo.authors.join(',');
}
return resp;
}
});
Which is fed to this template:
<script type="text/template" id="bookCollectionRow">
<tr>
<td><img class="thumbnail" src="<%= volumeInfo.imageLinks.smallThumbnail %>" /></td>
<td><a target="_blank" href="<%= volumeInfo.canonicalVolumeLink %>"><%= volumeInfo.title %></a></td>
<td><%= volumeInfo.authors %></td>
<td><%= volumeInfo.publisher %></td>
<td><%= volumeInfo.publishedDate %></td>
</tr>
</script>
Upon parsing the template, when a volume JSON does not contain an imageLinks I receive this error:
Uncaught TypeError: Cannot read property 'smallThumbnail' of undefined.
I know I could fix it by checking with an if in the Model or in the template but what's the purpose of defaults model property then? Does that work only if not overriding parse?
A few things. First, you shouldn't have nested objects as backbone model attributes in general - it can be OK if you can always treat the attribute atomically, but this is a perfect example of when you can't. From a data-model perspective, imageLinks should be its own backbone model class, as should volumeInfo.
Second, if defaults is an object literal ({}) instead of a function, the same object is used as the default attrs for each model instance. I think you want this:
defaults: function(){
return {
volumeInfo : {} // should be new VolumeInfo({}) imo
};
},
But the data model is the bigger issue - .defaults doesn't do the kind of nested-object-template thing you seem to be going for, and for good reason: it doesn't work well, this will just be the first of many gotchas you'll run into if you don't keep your instance data pretty flat.