I have a backbone application which works fine but was getting a bit heavy to be in one file so I have started to separate it into different files I now have:
backbone-view.js
backbone-router.js
...
I am using my backbone router to instantiate views when the URL changes like so:
var Router = Backbone.Router.extend({
routes: {
'our-approach.php': 'instantiateOurApproach',
'our-work.php': 'instantiateOurWork',
'who-we-are.php': 'instantiateWhoWeAre',
'social-stream.php': 'instantiateSocialStream',
'contact.php': 'instantiateContact'
},
instantiateOurApproach: function() {
if(window.our_approach_view == null) {
window.our_approach_view = new OurApproachView();
}
},
instantiateOurWork: function() {
if(window.our_work_view == null) {
window.our_work_view = new OurWorkView();
}
},
instantiateWhoWeAre: function() {
if(window.who_we_are_view == null) {
window.who_we_are_view = new WhoWeAreView();
}
},
instantiateSocialStream: function() {
if(window.social_stream_view == null) {
window.social_stream_view = new SocialStreamView();
}
},
instantiateContact: function() {
if(window.contact_view == null) {
window.contact_view = new ContactView();
}
}
});
The problem I am now having is that I cannot access the views as they are declared in a separate file so the following are all undefined:
OurApproachView()
OurWorkView()
WhoWeAreView()
SocialStreamView()
ContactView()
I have tried doing:
window.OurApproachView()
But this doesn't work.
I am not sure what my next move is so if anyone can help that would be awesome.
Thanks!
EDIT
OK so it seems doing:
window.OurApproachView()
does actually work, my apologies there, but does anyone have a better suggestion?
You can take this approach:
// sample-view.js
var app = app || {};
$(function() {
app.SampleView = Backbone.View.extend({
el: '#sample-element',
template : // your template
events: {
// your events
},
initialize: function() {
// do stuff on initialize
},
render: function() {
// do stuff on render
}
});
});
Similarly, all your js files (models, collections, routers) can be setup like this. You would then be able to access any view from the router by doing:
var view = new app.SampleView();
This works:
window.our_work_view = new window.OurApproachView();
But I don't like it as a solution.
Anyone suggest anything better?
Related
I have a component into which I would like to inject a custom filter, status.
My component looks like this:
function ClaimsListController(dpClaimsListService) {
var ctrl = this;
ctrl.claims = null;
ctrl.searchCriterion = null;
ctrl.loadClaimsList = function() {
dpClaimsListService.getUploadRequests(1,
function (claims) {
ctrl.claims = claims.data;
},
function () {
// error handling? display message to user?
});
}
ctrl.loadClaimsList(1);
}
angular.module('dpApp').component('dpClaimsListService', {
templateUrl: '/templates/dp-claims-list.tmpl.html',
controller: ClaimsListController
});
I can't work out how to inject my status filter into my component in a DI-minificaiton safe way. Any help appreciated.
Turns out that I don't need to inject the filter; I neglected to include it in my bundle.
I'm not sure if this is a good question or even a right approach but I'm just wondering how I could possibly disable a button from my JS file using Meteor.
I attempted
$("#button").disabled = true;
And it didn't work. I'm not sure if this is the right approach or not. I thought of maybe creating a function under a script tag in my HTML file but I thought that would seem redundant considering I have a JS file which represents my model so I was just attempting on figuring out if I can access HTML tags from that file.
Here's the code:
Users = new Mongo.Collection("user-info");
if (Meteor.isClient) {
var myApp = angular.module('calorie-counter',['angular-meteor']);
myApp.controller('formCtrl',['$scope',function($scope) {
$("#button").disabled=true;
$scope.calories;
$scope.goal;
$scope.user;
$scope.submit = function() {
Meteor.call("submit",$scope.user);
$scope.clear();
}
$scope.clear = function() {
$scope.user = {
item1:'',
item2:'',
calories:'',
goal:''
};
}
}]);
}
First, I'm not angularjs user.
But your code must to run template render after.
try to change your code like below. (Note: "yourTemplate" change to yours)
Users = new Mongo.Collection("user-info");
if (Meteor.isClient) {
Template.yourTemplate.onRendered(function() {
var myApp = angular.module('calorie-counter', ['angular-meteor']);
myApp.controller('formCtrl', ['$scope',
function($scope) {
$("#button").disabled = true;
$scope.calories;
$scope.goal;
$scope.user;
$scope.submit = function() {
Meteor.call("submit", $scope.user);
$scope.clear();
}
$scope.clear = function() {
$scope.user = {
item1: '',
item2: '',
calories: '',
goal: ''
};
}
}
]);
});
}
On my current project, there are starting to be a few views that are modal views that are being used to delete items on the site. They are currently generic in that it's just a text description of the item they are deleting. Maybe in the future there will be an icon or a short description as well. There are now tasks to have that functionality to delete other stuff on our site. I'm new to the web, MVC, asp.net, etc, and what I want to know is if it's better to reuse our current modal view somehow, and pass in the objects we need to show in the view. Because the view needs to send the url back to the server on which items to delete, that part of code would need to be different for the view as well. Here is some of the stuff in our view along with a .cshtml template that's pretty generic that I didn't include.
Views.DeleteGiftModal = (function () {
return Backbone.View.extend({
template: Templates["template-gift-delete-modal"],
tagName: 'div',
initialize: function (options) {
$(window).bind("disposeModal", _.bind(this.disposeModal, this));
_.bindAll(this, "showDialog", "disposeModal", "displayResults");
this.eventAggregator = options.eventAggregator;
this.itemsToDelete = options.model;
this.errors = {};
this.render();
return this;
},
events: {
"click #delete-btn": "deleteItems",
"click #ok-btn": "disposeModal",
"click #cancel-btn": "disposeModal"
},
disposeModal: function (event, refresh) {
this.$el.modal("hide");
if (event != null && event.currentTarget != null && event.currentTarget.id == 'ok-btn')
refresh = true;
this.trigger("modalClosed", refresh);
this.remove();
this.unbind();
},
showDialog: function () {
this.$el.modal("show");
},
deleteItems: function () {
var self = this;
var element = this.$el;
var numberGifts = this.getKeys(this.itemsToDelete).length;
this.results = [];
var hasError = false;
element.find("#actions").hide();
element.find("#ok-actions").show();
$.each(this.itemsToDelete, function(i, v) {
// tell model to go away
var gift = new Gift({ id: i });
gift.destroy({
success: function (model, response) {
self.results.push({ id: model.id, response: response });
numberGifts--;
if (numberGifts <= 0) {
if (!hasError) {
self.disposeModal(null, true);
} else {
self.displayResults();
}
}
}
});
});
},
displayResults: function () {
var element = this.$el;
$.each(this.results, function(i, v) {
// to do check response for error message
var list = element.find("#delete-item-" + v.id);
if (v.response.message == "Deleted") {
list.append(" - <span align='right' style='color: green'>Deleted</span>");
} else {
hasError = true;
list.append(" - <span align='right' style='color: red'>" + v.response.message + "</span>");
}
});
},
render: function () {
this.$el.append(this.template);
this.$el.find("#ok-actions").hide();
// show list of item names
var list = this.$el.find("#items-to-delete-list");
$.each(this.itemsToDelete, function (i, v) {
$("<li id='delete-item-" + i + "'>" + v.name + "</li>").appendTo(list);
});
this.$el.attr('id', 'delete-gift-dialog');
return this;
}
});
})();
As I am looking through the code, and this being my first real project, it seems like a lot of things that could be quite similar, like deleting a Gift, deleting a Toy, etc have different Controllers for each (GiftController, ToyController), and hit different URLs. So currently things are all in their own class like that. I was wondering if that's the more standard way to approach these types of problems as well with views. Thanks in advance!
The app we're developing at work had a similar issue. We're using Backbone too so I can completely relate to this. What I ended up doing is have a sort of ModalBuilder that builds a form in a modal for you and binds events on the form elements for submit. The initialization of it could look like this:
new ModalBuilder({
form: [
{
tag: 'select[name="id"]',
options: [
{ name: 'Item 1', id: 12 },
{ name: 'Item 2', id: 32 }
]
},
{
tag: 'input[type="submit"]',
value: 'Delete'
}
],
events: function(){
$('input[type="submit"]').on('click', function(){
// Delete via ajax
})
}
})
What we do is we have different templates for every form element, inputfields and textareas and so on and we reuse it all over the place. ModalBuilder takes these arguments and builds a form
Also for certain cases it might be better to render the form server-side and deliver it to your modal via ajax. You have to weigh what makes your app more performant I suppose.
If I have a list of "albums," and I click on one, I navigate to another view (/#/album/:id) which is controlled by a controller called SingleAlbum. It fetches the data correctly, but I can't get it to render. I've looked over other 'Unknown Record' issues on SO and on the Spine Google Group, but no dice. Here's my code:
var SingleAlbum = Spine.Controller.sub({
apiObj: {
url: '/api/album',
processData: true,
data: {
id: ''
}
},
model: Album,
panel: $('.album_single'),
tmpl: $('#albumTpl'),
init: function() {
this.apiObj.url = this.model.url;
if(this.panel.children.length > 0) {
this.panel.html('');
}
},
render: function(id) {
console.log('render');
var template = singleAlbum.tmpl.html(),
data = Album.find(id), // <-- this doesn't want to work
html = Mustache.to_html(template, data);
singleAlbum.panel.html(html);
},
getData: function(id) {
//var me = this;
console.log('get data');
this.apiObj.data.id = id;
this.apiObj.url = this.model.url;
this.model.bind('refresh change', function(id) {
//me.render(id);
singleAlbum.render(id);
console.log('should be rendered');
});
this.model.fetch(this.apiObj);
console.log('record: ',this.model.find(id));
if(Object.keys(this.model.find(id)).length > 0) {
//this.render(id);
}
}
});
The problem happens when I call .render() on the event handler. I can manually see that Album.all() has records, and can do Album.find(id) anywhere else in the app, but when I do it on var data = Album.find(id) it fails. Is this a scope issue? Am I missing something obvious?
By the by, please excuse the verboseness of my code. I'm actually making a SingleItem controller, of which SingleArtist and SingleAlbum will be subclasses. I thought that might be an issue, so I ripped out the code to test it on it's own.
EDIT: Specifically, my route looks like this:
'/album/:id': function(params) {
console.log('navigated to /album/', params.id);
singleAlbum.getData(params.id);
}
Aha! I was passing id in my event handler, which was overwriting the other id variable. Here's what it should look like:
render: function(id) {
var template = this.tmpl.html(),
data = this.model.find(id),
html = Mustache.to_html(template, data);
this.panel.html(html);
},
getData: function(id) {
var me = this;
this.apiObj.data.id = id;
this.apiObj.url = this.model.url;
this.model.bind('refresh change', function() { // <-- don't need to pass anything
me.render(id);
});
this.model.fetch(this.apiObj);
}
I'm trying to set up my controller to respond to Ext.XTemplate events and avoid the mess of writing a bunch of hacked javascript.
Unfortunately, I can't find a way to either 1) log a click event or 2) use componentQuery to register the XTemplate.
Here's a basic controller config:
config: {
refs: {
reportChooser: 'xtemplate[id=jobReportChooser]'
},
control: {
reportChooser: {
tap: 'onAnonymousTap'
}
}
},
Is there a way to do it in the controller OR at least clean up the listeners?
I couldn't find a direct reference since XTemplate has no built-in Events, but here's a nice workaround that's a little cleaner.
In your view initialize method
initialize: function() {
this.element.on({
tap: function(e, dom) {
var el = Ext.get(e.target),
elParent = Ext.get(e.parent),
fireEvent;
window.jobAction = this;
window.jobEl = el;
if (el.hasCls('job-start') || el.parent().hasCls('job-start')) {
fireEvent = 'start';
} else if (el.hasCls('job-hold') || el.parent().hasCls('job-hold')) {
fireEvent = 'hold';
} else if (el.hasCls('job-report') || el.parent().hasCls('job-report')) {
Ext.Viewport.setMasked({ xtype: 'loadmask' });
var teamId = APPNAME.currentItem.data.teamId;
var jobId = APPNAME.currentItem.data.jobId;
APPNAME.app.dispatch({controller: 'Jobs', action: 'displayReportChooser', args:[teamId,'job',jobId]})
}
if (fireEvent) {
Ext.Viewport.setMasked({ xtype: 'loadmask' });
this.fireEvent(fireEvent, Alloy.currentJob, el);
}
},
delegate: '.job-info',
scope: this
});
}
What it means
Use the application dispatch method. It will make life a lot easier and help you still organize actions inside the controller.
http://docs.sencha.com/touch/2-0/#!/api/Ext.app.Application-method-dispatch