I am using Durandal for a very simple website. In all of my browser tabs the page title is coming up with "undefined|" appended to the front of the application's title. Where is Durandal getting and setting that value?
pthalacker
Ultimately Durandal's router plugin is setting the document.title.
https://github.com/dFiddle/dFiddle-1.2/blob/gh-pages/App/durandal/plugins/router.js#L254
onNavigationComplete: function (routeInfo, params, module) {
if (app.title) {
document.title = routeInfo.caption + " | " + app.title;
} else {
document.title = routeInfo.caption;
}
},...
Typically Durandal is able to construct a missing caption propterty on the route object, so maybe there's something different in the way the routes are set up.
https://github.com/dFiddle/dFiddle-1.2/blob/gh-pages/App/samples/shell.js#L6
router.map([
{ url: 'hello', moduleId: 'samples/hello/index', name: 'Hello World', visible: true },
{ url: 'hello/:name', moduleId: 'samples/hello/index', name: 'Examples' },...
]);
You can set the page title when the viewmodel gets activated:
activate: function (params) {
// Setting page title
params.routeInfo.caption = "My Page Title";
return true;
}
Replacing page titles in Durandal 2.1.0
(if you want to the page title to be something distinct from the last level of the route)
Slight modification to RainerAtSpirit's answer: Specify 'title' in the route instead of 'name'.
router.map([
{ url: 'hello', moduleId: 'samples/hello/index', title: 'Hello World', visible: true },
{ url: 'hello/:name', moduleId: 'samples/hello/index', title: 'Examples' },...
]);
Related
I am just starting with Framework7 and I want to navigate to another page when a click a paragraph. I tried a few things and googled for the past hour but no luck.
Here is the code I have in my-app.js:
var $$ = Dom7;
var app = new Framework7({
// App root element
root: '#app',
// App Name
name: 'My App',
// App id
id: 'com.myapp.test',
// Enable swipe panel
panel: {
swipe: 'left',
},
view : {
stackPages: true
},
// Add default routes
routes: [
{
name: 'about',
path: '/about/',
url: 'about.html',
},
{
name: 'index',
path: '/index/',
url: 'index.html'
},
{
name: 'preview',
path: '/preview/',
url: './preview.html'
},
],
// ... other parameters
});
var mainView = app.views.create('.view-main');
$$('p').on('click', function(){
mainView.router.navigate('/preview/');
});
When I click a paragraph nothing happens and there are no errors when I run the inspector.
What am I doing wrong?
Thank you.
You can do that by using this:
self.app.router.navigate('/preview/', {reloadCurrent: true});
Also make sure that html layout like this:
<div class="view">
<!-- Initial Page, "data-name" contains page name -->
<div data-name="preview" class="page">
<!-- Scrollable page content -->
<div class="page-content">
preview page content
</div>
</div>
</div>
Update 1: In F7 ver 4 you can use this:
app.views.main.router.navigate('/login/', {reloadCurrent: true});
app.mainView.router.navigate('/preview/')
Try this if the answer #Anis offered but its supposed to work that answer
It should work using : app.router.navigate('/preview/')
To go some specific routes you can use
app.views.main.router.navigate('your route');
If you have a scenario like after create some task and you have to redirect to listing page, where if you use
if(formSubmitSuccessfull){
app.views.main.router.navigate('your router')
}
It will redirect to the previous page (In this case form create) after redirect to the listing page, When you try to back to the dashboard or other page from listing
In this case you have to use
app.views.main.router.back({
url: '/your router where you want to go.../',
force: true,
ignoreCache: true
})
I'm currently facing a big problems for days. I'm using ember simple-auth plugin which provide me a session object accessible through the code or the templates. That session object store the account information such as username, id and rights.
My models are like this :
App.Right = DS.Model.extend({
label: DS.attr('string', { defaultValue: undefined })
});
App.Right.FIXTURES = [
{
id: 1,
label: 'Admin'
}, {
id: 2,
label: 'Manager'
}, {
id: 3,
label: 'User'
}
];
App.User = DS.Model.extend({
username: DS.attr('string'),
rights: DS.hasMany('right', {async: true})
});
App.User.FIXTURES = [
{
id: 1,
username: "Someone",
rights: [1]
}
];
Then I have (as specified on the simple-auth documentation) this setup :
App.initializer({
name: 'authentication',
initialize: function(container, application) {
Ember.SimpleAuth.Session.reopen({
account: function() {
var userId = this.get('userId');
if (!Ember.isEmpty(userId)) {
return container.lookup('store:main').find('user', userId);
}
}.property('userId')
});
...
}
});
Inside one of my view I'm doing this:
this.get('context.session.account.rights').toArray()
but it gives me an empty array. That piece of code is executed inside an Ember.computed property.
The question is how can I resolve the childrens of account before rendering the view ?
Since async: true this.get('context.session.account.rights') will return a promise object so you will have to use this.get('context.session.account.rights').then(... see: http://emberjs.com/api/classes/Ember.RSVP.Promise.html#method_then
Okay so I finally got it to work. It doesn't solve the original question because the original question was completely stupid. It's just IMPOSSIBLE to resolve relationships synchronously when you use the async: true. Trying to resolve it in advance is NOT the solution because you will still not know when it has actually resolved.
So here is the solution:
$.each(this.get('cellContent.buttonList'), function(i, button) {
button.set('hasAccess', false);
this.get('context.session.account').then(function(res) {
res.get('rights').then(function(result) {
button.set('hasAccess', Utils.hasAccess(result.toArray(), button.rights));
});
});
});
Using the following cellContent.buttonList definition:
buttonList: [
Ember.Object.create({
route: 'order',
label: 'Consult',
rights: 'all'
}), Ember.Object.create({
route: 'order.edit',
label: 'Edit',
rights: [1, 2]
})
]
Explanation
We have to use Ember.Object in order to have access to the set method. Using an Ember object is very handy. It allows us to change the value of properties after the render process making the view to update according to the new value you just set.
Because it updates the view, you don't have to care anymore whether your model has resolved or not.
I hope this will help people as much as it helps me.
I am trying to figure out how to properly use createChildRouter. After going in circles for a day and a half trying to find code that will give me some results, I have settled for the moment on the following code (which still doesn't give me results):
shell.js
var routes = [
{ route: '', moduleId: 'home', title: 'Home', nav: 1 },
{ route: 'inventory/*index', moduleId: 'inventory/inventory', title: 'Inventory', nav: 2 }];
return router.makeRelative({ moduleId: 'viewmodels' }) // router will look here for viewmodels by convention
.map(routes) // Map the routes
.buildNavigationModel() // Finds all nav routes and readies them
.activate(); // Activate the router
inventory.js
define(['services/logger', 'plugins/router'], function (logger, router) {
var title = 'Details';
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/inventory',
fromParent: true
}).map([
{ route: 'index', moduleId: 'inventory', title: 'Inventory', nav: 3 },
{ route: 'inventory', moduleId: 'items', title: 'Items', nav: 4 }])
.buildNavigationModel();
var vm = {
router: childRouter,
activate: activate,
title: title
};
return vm;
//#region Internal Methods
function activate() {
logger.log(title + ' View Activated', null, title, true);
}
//#endregion
});
My tree structure looks like this:
Because of my failure to get any results, I'm starting to suspect that it's not my routing code that is the problem, but rather my navigation menu or perception of how/whether child routers are supposed to appear and be visible without additional work. I'm using a plain vanilla template (hot towel) and the only thing I've changed is the logging code in router.js because I'm trying to figure out what the router code is looking for. The result I get from the above code (with my modified logging) is as follows when I click on the button to switch from my "Home" view to my "Inventory" view:
LOG: /^$/ != inventory/*index
LOG: /^inventory\/(.*?)$/ == inventory/*index
LOG: Activating[object Object]
LOG: [Details] Details View Activated
LOG: Navigation Complete[object Object][object Object]
LOG: /^index$/ != *index
LOG: /^inventory$/ != *index
LOG: Route '*index' Not Found
So my questions are, is this what the routing code is supposed to do? Is it supposed to fail to match the splat route with any of the child routes? Do I need to do something to make navigation buttons for the child routes appear? Were they supposed to appear automatically? My view does switch to inventory.js/html, but no new navigation buttons appear when this happens.
What am I missing?
Check out knockout samples for a working implementation of child routes. Child routes will NOT be added to the primary navigation if that's what you're expecting. They are intended to be used with a secondary navigation. The HTML version can be seen live at http://dfiddle.github.io/dFiddle-2.0/#knockout-samples.
I'm a Beginner trying to learn SPA / Durandal, Knockout etc...
Need some help with my routes for a Admin drop down button.
Here are my routes in the shell.js so far:
var routes = [
{ route: '', moduleId: 'home', title: 'Home', nav: 1 },
{ route: 'downtime', moduleId: 'downtime', title: 'Downtime', nav: 2 },
{ route: 'downtimeadd', moduleId: 'downtimeadd', title: 'Add A New Downtime', nav: false, settings: { admin: true } },
{ route: 'production', moduleId: 'production', title: 'Production', nav: 4 }];
I've also created adminRoutes in the shell.js to bind to the view with KO:
var adminRoutes = ko.computed(function () {
return router.routes.filter(function (r) {
return r.settings.admin;
});
});
From research, they say that router.routes is an array, but when I bind this to my view the button shows 0 items for the drop down.
When I do (below) I can get all the routes, but I only need the admin routes...
var adminRoutes = ko.computed(function () {
return router.routes;
});
How should I proceed? It seems like router.routes is not actually an array?
If I try to print it out to the console:
console.log(router.routes[0]); //Chrome says its undefined...
console.log(router.routes); //Shows array of size 4...
Yup no clue... Help would be appreciated!
Update:-------------------------------------------------------------------
Even after RainerAtSpirit's suggestions I still get an empty array when I filter in code.
var router = require('plugins/router');
//Array size 4
console.log(router.routes);
//Array size 0
console.log(router.routes.filter(function (r) { return r; }));
However when I run this in the "chrome console":
var router = require('plugins/router')
router.routes.filter(function (r) { return r; })
I do get the array back, so I don't know why in code it doesn't work.
I got this to work, although maybe not as perfectly as I would like.
The first problem I noticed was an issue that others were not seeing. In my example (with druandal 2.0 from the HotTowel template version 1.1 for VS 2013) I noticed that this code for adding the adminRoutes was called prior to the activate method. Therefore, my route.routes was an empty array. To fix this problem, I switched to just using the array of routes that the routes are getting mapped from the config.js with (note: config.routes rather than route.routes):
var adminRoutes = ko.computed(function () {
return config.routes.filter(function(r) {
return r.admin;
});
});
Finally, I'm return "r.admin". This is the part that would like to make work better because I added an admin property to the admin route without the "settings" group, see "admin: true":
var routes = [
{ route: '', moduleId: 'equipment', title: 'Equipment', nav: 1 },
{ route: 'testresults', moduleId: 'testresults', title: 'Test Results', nav: 2 },
{ route: 'testresultdetail/:id', moduleId: 'testresultdetail', title: 'View a test result', nav: false },
{ route: 'testresultadd', moduleId: 'testresultadd', title: 'Add a Test Result', nav: false, caption: '<i class="fa fa-plus"></i> Add Test Result', admin: true }
];
With these two changes, the menu item showed up.
In knockout when you make a property to be observable it turns in to a function wrapping ,so when you need to get the actual value you need to access it as a function. That means
router.routes[0] will be undefined but router.routes()[0] will work correct !
You can try
var adminRoutes = ko.computed(function () {
return router.routes().filter(function (r) {
return r.settings.admin;
});
});
In the shell.html for HotTowel template we have:
<!--ko compose: {model: router.activeItem,
afterCompose: router.afterCompose,
transition: 'entrance'} -->
<!--/ko-->
which will automatically insert the proper view by convention. I am trying to inject different views based on the user's role in a HotTowel/Durandal App. For example,
I have two Views,
productEditor_Admin.html
productEditor_Superviser.html
(instead of these two views, I used to have only productEditor.html, by convention everything worked)
and only a single ViewModel:
productEditor.js
Now, I want to have a function in productEditor.js that will let me decide which view to insert based on user's role. I see in the Composition documentation, we can do function strategy(settings) : promise but I am not sure what's the best way to accomplish this in the HotTowel template. Anyone have already tried and got an answer for that?
It's possible to return a 'viewUrl' property in the view model, so hopefully something like the following will crack the door open ;-).
define(function () {
viewUrl = function () {
var role = 'role2'; //Hardcoded for demo
var roleViewMap = {
'default': 'samples/viewComposition/dFiddle/index.html',
role1: 'samples/viewComposition/dFiddle/role1.html',
role2: 'samples/viewComposition/dFiddle/role2.html'
};
return roleViewMap[role];
}
return {
viewUrl: viewUrl(),
propertyOne: 'This is a databound property from the root context.',
propertyTwo: 'This property demonstrates that binding contexts flow through composed views.'
};
});
Did you take a look at John Papa's JumpStart course on PluralSight.
Look at the source code from that app here: https://github.com/johnpapa/PluralsightSpaJumpStartFinal
In App/Config.js file he adds other routes which are visible by default as :
var routes = [{
url: 'sessions',
moduleId: 'viewmodels/sessions',
name: 'Sessions',
visible: true,
caption: 'Sessions',
settings: { caption: '<i class="icon-book"></i> Sessions' }
}, {
url: 'speakers',
moduleId: 'viewmodels/speakers',
name: 'Speakers',
caption: 'Speakers',
visible: true,
settings: { caption: '<i class="icon-user"></i> Speakers' }
}, {
url: 'sessiondetail/:id',
moduleId: 'viewmodels/sessiondetail',
name: 'Edit Session',
caption: 'Edit Session',
visible: false
}, {
url: 'sessionadd',
moduleId: 'viewmodels/sessionadd',
name: 'Add Session',
visible: false,
caption: 'Add Session',
settings: { admin: true, caption: '<i class="icon-plus"></i> Add Session' }
}];
You can add routes to both the views here using the same logic and then in your productEditor.js you can decide which view to navigate and navigate to that using router.navigateTo() method.