I have an Aurelia project that uses server-side routing with MVC. I am using a layout file that points to a main "app" component, like this:
<div aurelia-app="main" start="app" data-model='#Json.Encode( Model )'></div>
Each of my views contain a reference to the layout file and something like this:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
My main.js is configured like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(a => {
let start = a.host.attributes.start.value;
a.setRoot(start);
});
}
And I am leveraging Aurelia's router (in app.js) like this:
export class App {
constructor() {
}
configureRouter(config, router) {
config.title = 'Aurelia';
config.options.pushState = true;
config.options.root = '/';
config.map([
{ route: ['', 'Aurelia/Home'], name: 'home', moduleId: 'home', title: 'home', nav: false },
{ route: 'Aurelia/SampleModule', name: 'sample module', moduleId: 'sample-module', title: 'sample module', nav: true },
]);
this.router = router;
}
}
This almost works. If I navigate to SampleModule using the Aurelia navigation link, it loads the module but doesn't hit the server - doesn't even hit the View. If I navigate to Aurelia/SampleModule manually, it loads sample-module twice, including the data from the server. The Aurelia router updates the url exactly as I would expect, so if I navigate and then hit refresh it loads from the server correctly.
I want to be able to use the navigation to change the view without refreshing the entire page, but still leverage my server-side routing and hit the Views and Controllers.
I was able to solve my problem by removing the reference to the app from each of the views:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
as well as removing the start logic from the layout file and main.js, so now my main.js looks something like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(() => aurelia.setRoot());
}
In the back end, the model has a unique property to each page:
public class AppViewModel
{
public HomeViewModel Home { get; set; }
public SampleModuleViewModel SampleModule { get; set; }
}
From the view model (i.e. sample-module.js), it checks to see if the associated property (AppViewModel.SampleModule) is populated, and if not, it makes an ajax call.
Related
I have an angular app, which consists of a website and system.
so I have made 2 sub roots under app root, websiteMaster, and systemMaster.
CSS files of the website don't have to be loaded when I'm logged in.
CSS files of the systems don't have to be loaded when I'm logged out.
so I need to load CSS files in websiteMaster only for website sub root components and to load CSS files in systemMaster only for system sub root components.
Is there a way to apply that using Angular 8?
Thanks in advance
You've to manually load/unload css files in a main/root(whatever you want to call it) component in one of your subroot module.
Let suppose websiteMaster module has following structure
Routes:
// Wrapping all routes in a parent component
const routes: Routes = [
{ path: '', component: WebsiteMasterRootComponent, children: [
{ path: 'any-path', component: AnyComponent },
{ path: 'any-other-path', component: AnyOtherComponent },
]
}];
WebsiteMasterRootComponent: which will load and unload css files related to this module
export class WebsiteMasterRootComponent implements OnInit, OnDestroy {
// css files required for current module
private styles = [
{ id: 'css-file-1', path: 'assets/css/css-file-1.css' },
{ id: 'css-file-2', path: 'assets/css/css-file-2.css' },
{ id: 'css-file-3', path: 'assets/css/css-file-3.css' },
];
constructor() {}
ngOnInit() {
this.styles.forEach(style => this.loadCss(style));
}
ngOnDestroy() {
// remove css files from DOM when component is getting destroying
this.styles.forEach(style => {
let element = document.getElementById(style.id);
element.parentNode.removeChild(element);
});
}
// append css file to DOM dynamically when current module is loaded
private loadCss(style: any) {
let head = document.getElementsByTagName('head')[0];
let link = document.createElement('link');
link.id = style.id;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = style.path;
head.appendChild(link);
}}
I have the following structure:
{
id: 23423-dsfsdf-32423,
name: Proj1,
services: [
{
id:sdfs-24423-sdf,
name:P1_Service1,
products:[{},{},{}]
},
{
id:sdfs-24jhh-sdf,
name:P1_Service2,
products:[{},{},{}]
},
{
id:sdfs-2jnbn3-sdf,
name:P1_Service3,
products:[{},{},{}]
}
]
},
{
id: 23423-cxcvx-32423,
name: Proj2,
services: [
{
id:sdfs-xvxcv-sdf,
name:P2_Service1,
characteristics:[{},{},{}]
},
{
id:sdfs-xvwqw-sdf,
name:P2_Service2,
characteristics:[{},{},{}]
},
{
id:sdfs-erdfd-sdf,
name:P2_Service3,
characteristics:[{},{},{}]
}
]
}
I have no problem creating a form this schema an insert form with quickForm.
But I cant figure out (and tried to read every tutorial and instruction and nothing worked) how to create an update form with all fields filled and (need to expand and fill the services and the characteristics arrays also:
of course, as i said, in update i need the services and characteristics to expend to the right size with all the fields.
But if i could understand how to fill the form fields i could understand myself how to expend the arrays...
i've tried:
{{> quickForm collection="Projects" id="updateProjectForm" collection="Projects" type="method" class="update-project-form" doc=project }}
with:
import SimpleSchema from 'simpl-schema';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
// Attaching the subscription to the template so we can reuse it
Template.ProjectSingle.onCreated(function(){
var self = this;
self.autorun(function(){
var id = FlowRouter.getParam('id');
self.subscribe('projectSingle', id);
});
});
Template.ProjectSingle.helpers({
project: ()=> {
var id = FlowRouter.getParam('id');
console.log(Projects.findOne({_id: id}));
return Projects.findOne({_id: id});
}
});
I can't even see the console.log() printing.
This solution at list didn't crash the meteor server... everything else i've tried crashed the server on many errors
Maybe i need to mention that i'm using partials so maybe there is a problem with the JS files but i don't think so as the onCreated method is being read.
10x.
EDIT:
I've removed the partial for the update template and it is now in the root Template with its own JS with the method:
projectDoc: ()=> {
var id = FlowRouter.getParam('id');
console.log("Update: " + Projects.findOne({_id: id}));
return Projects.findOne({_id: id});
}
Now i can see this method is being called but for some reason it is being called twice. First with the correct data and then getting undefined so i've still not getting the fields showing anything but if i could find why it is being called twice i will solve the first level form (no services and so on)
Solved it (Not sure this is the best way as i'm still having two calls to the method but this is working for now:
projectDoc: ()=> {
var id = FlowRouter.getParam('id');
if(Projects.findOne({_id: id}) != null){
console.log(Projects.findOne({_id: id}));
thisProject = Projects.findOne({_id: id});
return Projects.findOne({_id: id});
} else {
return thisProject;
}
}
Trying to use a child route in Aurelia. Can't seem to get my head around the workings of nested routes. Are all routes derived from the root of the app or relative to location of the current router?
Why wont my route-href work in this example? I have a route in this router named screen and it does have an :id parameter
screens/list.ts
#inject(Router)
export class ScreensList {
heading;
router;
screens: any[];
constructor(router){
this.heading = 'Child Router';
this.router = router;
this.screens = [
{
id: 1,
name: 'my screen'
},
{
id: 2,
name: 'my other screen'
}
]
router.configure(config => {
config.map([
// dynamic routes need a href, such as href: screen
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
});
}
}
List View
screens/list.html
<li repeat.for="screen of screens">
<a route-href="route: 'screen', params: { id: screen.id }"/>Screen #${screen.id}</a>
</li>
I then have a dummy VM/V at screens/screen/display.
Do I really have to specify the full filepath for a module in a nested child router. I thought it would be routes relative to the location of the parent router or at least the name (root) of the parent?
vendor-bundle.js:11582 ERROR [route-href] Error: A route with name ''screen', params: { id: screen.id }' could not be found.
Check that `name: ''screen', params: { id: screen.id }'` was specified in the route's config.
In your example, you are injecting the router, which is the router configured in app.js, and then calling its configure method. Aurelia is Convention-Over-Configuration. So, use the convention and you will be fine. The configureRouter method will do the tricks for you. For instance:
export class ScreensList {
configureRouter(config, router) {
config.map([
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
this.router = router;
}
}
Remember that ScreensList must be a screen of your router. It will not work if it is a custom element.
Take a look at the skeleton-navigation examples https://github.com/aurelia/skeleton-navigation. There are good examples, including child routing.
My app runs fine locally, but when I deploy it on xxxx.meteor.com, I'm getting the Iron Router "Organize your Meteor application" splash page. Additionally, once the browser hits the first Basecontroller.extend({, I get a ref. error that "BaseController is not defined". Maybe something to do with the load order of the route controller specific to deployment? This is my code:
client/main/routes.js
Router.route('/', {
name: 'splash',
controller: 'SplashController'
});
Router.route('/login', {
name: 'login',
controller: 'LoginController'
});
Router.route('/home', {
name: 'home',
controller: 'HomeController'
//...etc
client/main/controller.js
BaseController = RouteController.extend({
layoutTemplate: 'layout',
requiresAuth: false,
publicOnly: false,
// Called before anything happens
onBeforeAction: function () {
if (!Meteor.userId() && this.requiresAuth) {
return this.redirect('/login');
}
if (Meteor.userId() && this.publicOnly) {
return this.redirect('/home');
}
}
});
client/(all templates)/controller.js
TemplateController = BaseController.extend({
requiresAuth: false,
publicOnly: true,
action: function () {
this.render('template', {
to: 'content'
});
}
});
The issue belong in loading only. So, code which should always load first, try put inside <app-root>\lib directory.
Like your case, put route.js and controller.js inside lib directory.
Read more about loading order in meteor - http://docs.meteor.com/#/full/structuringyourapp
I'm trying to configure my routing so that navigating to #/my displays the contents for #/folder/62 (or some id stored in a variable) - and navigating to #/public displays the contents for #/folder/1 (same concept).
Additionally, I'd like the application to navigate to one of these routes upon loading, depending on whether or not the user is authenticated. The authentication stuff is done, but once the above routes have been configured, I'd like to activate on one of them.
Here's what I have:
activate: function () {
router.useConvention();
router.handleInvalidRoute = function (route, params) {
//debugger;
logger.logError("Invalid route", route, null, true);
};
router.map([
{ url: 'home', moduleId: 'viewmodels/home', name: 'Home', visible: false },
{ url: 'my', moduleId: 'viewmodels/folder', name: 'My Content', visible: false }, // Should display contents of /folder/2
{ url: 'public', moduleId: 'viewmodels/folder', name: 'Public Content', visible: false }, // Should display contents of /folder/3
{ url: 'set/:id', moduleId: 'viewmodels/set', name: 'Set', visible: false },
{ url: 'folder/:id', moduleId: 'viewmodels/folder', name: 'Folder', visible: false }
]);
if (auth.isAuthenticated)
return router.activate('my'); // should show details page of a particular folder
else {
return router.activate('public'); // should show details page of a particular folder
}
}
app.setRoot as described in https://groups.google.com/forum/#!searchin/durandaljs/app.setRoot/durandaljs/t1hrLfOh1oM/RtCekmY0bDAJ could be used to show different views for authenticated/non-authenticated users.
If only authenticated users should be allowed to see content of "folder/specialID" in the example then you might consider not to expose these via router. Use standard compose functionality in #my to load the specialID view/view model instead.