Backbone views not rendering in order - layout

In my app I have layouts and views for those layouts. Layouts only change on login/logout, but the other special case is on pageload I need to load the proper layout. However, in my defaultAction my layout does not actually render after it returns and so when the view tries to render, the el it is supposed to be contained in does not exist.
// Filename: router.js
var app_router;
define( [ 'views/layouts/beta', 'views/beta/requestInvite', 'views/beta/login', 'views/app/dashboard' ],
function(betaLayout, requestInviteView, loginView, dashboardView) {
var AppRouter = Backbone.Router.extend( {
routes : {
// Pages
'login' : 'login',
'dashboard' : 'dashboard',
// Default
'*actions' : 'defaultAction'
},
// Pages
login : function() {
loginView.render();
},
dashboard : function() {
dashboardView.render();
},
// Default
defaultAction : function(actions) {
betaLayout.render();
requestInviteView.render();
}
});
var initialize = function() {
app_router = new AppRouter;
$('a').live('click', function() {
var href = $(this).attr('href');
// only navigate to real links
if(href == undefined)
return;
app_router.navigate(href, {trigger: true});
return false;
});
Backbone.history.start({pushState: true});
};
return {
initialize : initialize
};
});
How can I have my layout render completely before my view?

Define a callback for betaLayout.render() to take as an argument, that gets executed when the render is actually complete.
E.g., betaLayout.render() would look something like:
render: function(callback) {
/* code to render your element,
using the following line when all
asynchronous calls are complete */
if (callback) callback();
}
and your defaultAction would use it like so, passing the second render() as its callback.
betaLayout.render(requestInviteView.render);

The problem was that because my layout didn't render before my view was initialized, the el was empty. What I did was convert all of my objects to return the object instead of the instance, and let them render once they were initialized. This way, when I declare a new MyLayout and then a new MyView, I can be guaranteed that MyView's el is valid.

Related

componentDidMount is not triggered

Hi Im new to React & nodeJS, i'm trying to call my nodejs api through react and my componentDidMount is not triggered at all even after rendering the page.
Can some one please give some idea on where exactly I might be missing.
var React = require('react');
module.exports = React.createClass({
getInitialState: function() {
return {
jobs: []
}
},
componentDidMount: function() {
console.log("mount");
var _this = this;
this.serverRequest = $.ajax({
....
}.bind(this)
});
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div><h1>Jobs</h1>
{this.state.jobs.map(function(job) {
return (
<div key={job.id} className="job">
{job.name}
{job.address}
</div>
);
})}
</div>
)
}
});
In my NodeServer.js file i'm calling in this way, Only Jobs is being displayed in my html page
app.get('/', function(request, response) {
var htmlCode = ReactDOMServer.renderToString(React.createElement(Component));
response.send(htmlCode);
})
You can't use the React component lifecycle methods to load data when you're rendering in the backend, this will only work in the client. When you render the component on the server, it is only rendered once, without the data. The data is loaded asynchronously, when it is finished, your component has already been rendered. You have to fetch your data outside the component, then pass it to the component as a prop.

Vuejs - Multiple components use same mixin (only last one gets fired)

I have around four components using the same mixin that I created. In order to get the dimensions of this.$el (the CORRECT ones), I had to implement window.onload = function() mixin. My mixin looks like this:
module.exports = {
methods: {
onPageload: function( callback ) {
window.onload = function() {
callback();
}
}
}
};
Very simple. However, more than one component uses it. I'm creating a slideshow. My Slides component uses it, my Slide component, my Thumb component, and my overall Slider component.
HOWEVER only the very last one to compile fires it.
var pageLoad = require('../../mixins/Pageload');
module.exports = {
template: require('./templates/thumbs.html'),
replace: true,
data: function() {
return {
style: {
width: 800
},
count: 2
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px'
}
}
},
mixins: [pageLoad],
props: ['count'],
attached: function() {
this.onPageload( this.setDimensions );
},
methods: {
// Set dimensions for the first time
setDimensions: function() {
console.log('setting thumb');
this.style.width = this.$parent.slideWidth;
}
}
};
My other "parent" components do the same thing to set their dimensions. This is the MOST inner child of the entire thing.. it's the ONLY one that fires. If I erase it from here, the next child up is the only one that fires. They are overwriting each other in a way. My vue instance is here:
new Vue({
el: '#slideshow',
components: {
'sliderarrows': require('../../components/slider/SliderArrows'),
'sliderthumb': require('../../components/slider/SliderThumb'),
'sliderslide': require('../../components/slider/SliderSlide'),
'slides': require('../../components/slider/SliderSlides'),
'slider': require('../../components/slider/Slider'),
'thumbnails': require('../../components/slider/SliderThumbs')
}
});
So the thumbnails component is the only one that's firing the mixin method. I feel like it's something to do with the whole compilation of require() but I'm not sure since I'm not that familiar with the Node.js module format.
Thanks for any help on how to get this to work!
I had to change my mixin to this:
module.exports = {
methods: {
onPageload: function( callback ) {
window.onload = callback();
}
}
};
It was something with the window.onload opening a new function.

Sailsjs: Multiple Layouts

I realize this is the same issue raised here: How to use multiple layout within a SailsJS app?. I'm not sure if something has changed with Sails.js or I'm just a numbskull. Hoping someone can help...
The application I'm developing has two sides to it, a public side, and an admin side. For the life of me, I cannot get a view to use a different layout than the default. I'm using the default "ejs" templating engine.
The location of the alternate layout resides here:
/views/layoutadmin.ejs
Here's my /config/routes.js file:
module.exports.routes = {
'/': {
view: 'home/index'
},
'/dashboard': {
view: 'admin/index'
,controller: 'dashboard'
,action: 'index'
}
};
Here's my /api/controllers/DashboardController.js file:
module.exports = {
index: function (req, res) {
res.view({ layout: 'layoutadmin' });
}
};
The problem here is that you're specifying both a view and a controller/action in your route config. You can only do either/or. You want:
module.exports.routes = {
'/': {
view: 'home/index'
},
'/dashboard': {
controller: 'dashboard'
,action: 'index'
}
};
And in DashboardController:
module.exports = {
index: function (req, res) {
res.view('admin/index', { layout: 'layoutadmin' });
}
};
You could create a policy and change the layout in it:
Check this out:
https://github.com/vimia/blew/blob/master/api/policies/isPjaxRequest.js
I change the layout with the param...
You can create a isAdminModule policy, at config/policies.js, put something like:
AdminController: [ '*': 'isAdminModule']
Then all the admin requests will have another layout...

Refresh(F5) ngview with angularJS

I've got an issue with my Angular application. The application is running well the first time, it is possible to navigate inside, but it fails when I try to refresh the page with F5. When F5 key is pressed, the partial is called to the server, and the server obviously responds with the partial view, but not the whole application.
Node routing:
app.get('/', node_routes.welcome);
...
app.get('/profile', pass.ensureAuthenticated, profile_routes.profile);
app.get('/profile/:name', pass.ensureAuthenticated, profile_routes.partials);
app.post('/profile/update', pass.ensureAuthenticated, profile_routes.update);
...
Controller:
exports.profile = function(req, res) {
var user = req.user;
Profile.findOne({ username: user.username }, function(err, profile) {
if (err) {
res.redirect('/');
}
res.render("profile");
});
};
exports.partials = function(req, res) {
var name = req.params.name;
var user = req.user;
Profile.findOne({ username: user.username }, function(err, profile) {
if (err) {
res.redirect('/');
}
res.render(path.join(__dirname + '/../views/profile/' + name));
});
};
exports.update = function(req, res){
var profile = req.body;
delete profile._id;
Profile.update({'username':profile.username},profile,{safe:true}, function(err, result){
if(err) {
console.log('Error updating profile. ' + err);
res.redirect('/profile');
}
else{
console.log('' + result + ' profile updated for user: ' + profile.username);
res.redirect('/profile');
}
});
};
Angular application
myApp.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider){
$routeProvider
.when('/profile/update', {
controller: 'myJobOfferListCtrl',
templateUrl: '/profile/update',
reloadOnSearch: false
})
.when('/profile', {
controller: 'myJobOfferListCtrl',
templateUrl: '/profile/dashboard'
})
.when('/profile/dashboard', {
controller: 'myJobOfferListCtrl',
templateUrl: '/profile/dashboard',
reloadOnSearch: false
})
.when('/profile/offers', {
controller: 'myJobOfferListCtrl',
templateUrl: '/profile/offers',
reloadOnSearch: false
})
.otherwise({redirectTo: '/profile'});
$locationProvider.html5Mode(true);
}]);
My profile page
extends layout
block content
div.container-fluid(ng-app="appProfile", ng-controller="myJobOfferListCtrl", ng-init="initProfile()")
div.row-fluid
div.span3(ng-controller="navBarCtrl")
div.well.sidebar-nav
ul.nav.nav-list
li.well.nav-header.
My Profile ({{data.profile.username}})
li(ng-class="{ active: isActive('dashboard')}")
a(href="/profile/dashboard") Dashboard
li(ng-class="{ active: isActive('update')}")
a(href="/profile/update") Update profile
li.divider
li.well.nav-header My Jobs
li(ng-class="{ active: isActive('offers')}")
a(href="/profile/offers") Offers
li(ng-class="{ active: isActive('application')}")
a(href="/profile/application") Applications
div.span9(ng-view, ng-cloak)
And a sample partial view page
div.row-fluid
div(ng-init="initMyOffers()")
accordion.span8.offset1(close-others="true")
accordion-group(ng-repeat="item in data.myJobs", heading="{{item.title}}")
accordion-heading
{{item.title}}
i.pull-right.icon-remove-circle.removeButton(ng:click="remove(item)")
p {{item.description}}
hr
div.footer
div.pull-left
i.icon-calendar.calendar
{{item.dueDate | date:'d MMMM yyyy'}}
div.pull-right
i.icon-user
{{item.author}}
How can I reload a partial page when refreshing with F5 ?? What I expected with angular is that when trying to refresh for example the page /profile/dashboard, the partial /views/profile/dashboard.jade is called but also the views/profile.jade. And what about the $scope ?
Sorry, but I'm a little confioused... thanks for you help !
I think it is a common problem and the user is not supposed to use F5 in your application. I do not think, it is possible to stop default browser action after F5 has been pressed.
One simple solution is to add this or similar script to every view:
<script>
if (angular == undefined)
// alert("It is SPA (Single Page Application) -- do not press F5 anymore, please.");
window.location.replace("your/main/url");
</script>
A bit more complicated would be realization of the following scenario.
user hits f5
server sends partial view for current url current_url
client codes detects lack of angular and send request to be redirected to current_url
server sends modified version of the index.html file [with e.g. some hidden input field, that stores current_url]
after angular has been loaded, the application checks the hidden field and change the location accordingly
The simplest and most elegant way to avoid losing stuff when refreshing an Angular app is to store whatever values you need in a cookie by using $cookies service. Here's an example. Hope this helps.
"use strict";
angular.module('myApp').factory('SessionSrv', [ '$cookies', function($cookies){
var _myString = $cookies.get('myString'); // e.g. "foo"
var _myObject = $cookies.getObject('myObject'); // e.g. {foo: "bar"}
return {
setMyString: function(myString){
_myString = myString;
$cookies.put('myString', _myString);
},
getMyString: function(){
return _myString;
},
setMyObject: function(myObject){
_myObject = myObject;
$cookies.putObject('myObject', _myObject);
},
getMyObject: function(){
return _myObject;
},
};
}]);
Finally implemented the solution as proposed by artur :
<script>
if (typeof angular == 'undefined')
window.location.replace("/main_url");
</script>

backbone.js and express: trouble searching a mongodb collection by field with a query string

I am new to backbone, express, and mongodb.
I am trying to pass a query string to search a mongodb collection by field.
I am doing something wrong. If I comment out the "fetch" from my router, the page is found.
If I try to fetch, then I get a page not found error.
I've tried to isolate where it's breaking, but the backbone architecture is still confusing to me. Thanks in advance. (I'm betting it's a syntax issue in my mongodb call)
kristin
Here is my code.
this URL should return a collection where "type" = 3.
localhost:8888/#content/3
model/models.js:
window.Content = Backbone.Model.extend({
urlRoot: "/content",
idAttribute: "_id"
});
window.ContentCollection = Backbone.Collection.extend({
model: Content,
url: "/content"
});
views/content.js
window.ContentListView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
//return this;
this.$el.append('<ul class="thumbnails">');
this.collection.each(function(model) {
this.$('.thumbnails').append(new ContentView({model: model}).render().el);
}, this);
return this;
} });
window.ContentView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
views/main.js
var AppRouter = Backbone.Router.extend({
routes: { "content/:type" : "contentType" },
contentType: function(type) {
var contentList = new ContentCollection({type : type});
contentList.fetch({success: function(){
$("#content").empty().append(new ContentListView({collection: contentList}).el);
}});
this.headerView.selectMenuItem('build-menu');
},
utils.loadTemplate([
'ContentView'
], function() {
app = new AppRouter();
Backbone.history.start(); });
contentView.html
name (<% tag won't print here)
routes/modules.js
exports.findContentByType = function(req, res) {
var type = req.params.type;
db.collection('content', function(err, collection) {
collection.find({'type': type.toString()}).toArray(function(err, items) {
res.send(items);
});
});
};
server.js
app.get('/content/:type', module.findContentByType);
I can see a couple of problems here:
this.headerView.selectMenuItem('build-menu'); (in the router) implies you've defined headerView in the router object, but it's not defined.
Similarly, this.template inside ContentView is not defined
When I remove the line in #1, and and define a dummy template in ContentView:
template: _.template("<div> Test: <%= version %> </div>"),
Then the view at least renders -- see here. (This is with dummy data -- I can't confirm that your server is returning valid/expected JSON.)

Resources