AngularJS dynamically set param of ngInclude based on route - javascript

I'm trying to dynamically include a template into my index.html. The general structure of index.html is:
<body>
<header ng-controller="Main">
<section>
<!-- global stuff -->
</section>
<section ng-include="moduleName + '/views/menubar.html'">
<!-- module-based stuff -->
</section>
</header>
<div id="view" ng-view></div>
</body>
Sample URL
example.com/<app_name>/index.html#/<module_name>[/method_name]
I can't figure out how to update $scope.moduleName when the route changes. My trouble is two-fold:
The header's controller is Main, not the controller associated with the view, so I can't? update $scope.moduleName from the view's controller (because Main and the view's controller are siblings).
In Main, I tried setting a $scope.$on('$routeChangeSuccess',…), but apparently it is not notified of route changes.
I've thought of setting up a $rootScope.$on listener (as described in SO#15355346) for the route change and broadcasting down to children, who then emit back up their route, which is broadcasted back down so it is available to Main. But that seems heinous.
And I would really prefer to keep the header outside of ng-view.
EDIT I noticed that $route.current.scope has an object named with module_name (possibly because the name of the controller associated with the route's module_name is the same). I'm wondering if I might be able to somehow use the name of that object…

It's hard to say what's wrong in your code without the full picture. Things you show look fine to me.
Please see this plunk I've created to display the ability to do it. Take note that you also can extend route objects with custom properties, like moduleName here:
$routeProvider.when('/page1', {
template: 'one',
controller: 'one',
moduleName: 'firstModule'
});

Related

Angular 1 how to properly include external component inside ng-include?

Situation: I have a view where I'm including a fragment with ng-include. The fragment loads perfectly and works as expected when inside view where controller is defined.
My problem is, that when I want to include external component inside the "ng-include" fragment "myView.html", it doesn't show up. When including it inside the main view where the controller is, it shows up and works as expected.
Main view:
<div ng-controller="MyController">
<div data-ng-include src="'views/myView.html'"></div>
<!-- When loaded here, the component shows up -->
<!-- <div id="componentDiv"></div> -->
</div>
Fragment "myView.html":
<div>
<div id="componentDiv"></div>
</div>
The component is loaded inside the "MyController", where "componentDiv" is the "id" of "div" where the component is placed:
var testObj = new TestObj({"container": "componentDiv"});
Trying to do this to be able to use this fragment with full functionality in several places.
Any ideas or suggestions what to look up or try?
IMHO I think that by saying "...to be able to use this fragment with full functionality in several places" you just answered your question. What you need is to build a custom directive and use it in any place you like.
see directive samples e.g. Angular documentation on directives

Two different layouts in aurelia app

I'd like to use two separate layouts for my aurelia app. Difference between them is that one doesn't have a sidebar. Currently I'm using one layout file defined as below:
<template>
<div class="container">
<router-view></router-view>
</div>
</template>
If an active route needs this sidebar to appear I'm just putting it into its view.
What I'd like to achieve is to add another layout that would have this sidebar by default:
<template>
<require from="../common/elements/sidemenu/sidemenu"></require>
<div class="container">
<sidemenu></sidemenu>
<router-view></router-view>
</div>
</template>
So the question is - how to do this? Is it even possible with an aurelia app to have multiple layouts (or master pages, however you call those)?
Use aurelia.setRoot()
You can manually set up your application by specifying a script with configure instructions in your index.html. Typically, this is set to main.
index.html
<body aurelia-app="main">
In this script you can specify a root view model using aurelia.setRoot('root'). If no argument is provided, the convention is to use 'app'.
main.js
aurelia.start().then(() => aurelia.setRoot());
However, you can inject the aurelia object anywhere in your application, and call the setRoot function at any time to load a different root view model.
home.js
#inject(aurelia)
export class HomeViewModel {
constructor(aurelia) {
this.aurelia = aurelia;
}
doStuff() {
this.aurelia.setRoot('withSidebar');
}
}
One common use case for this is having a login page, and I've created a complete template for this use case that you can review, clone, or fork here: http://davismj.me/portfolio/sentry/

UI-Router default view inside div

Say I have index.html with:
<div ui-view="viewA">
// content
</div>
Is it posible to put content inside div that will serve to me as default route? So without including or anything else, just need to put some markup directly in that div.
There is a working plunker
That is possible, and really pretty easy. So, let's have this inside of index.html
<div ui-view="">
this is the content of ui-view,
which serves as a template for a default state
</div>
And this could be the default state and otherwise
$urlRouterProvider.otherwise('/default');
// States
$stateProvider
.state('default', {
url: "/default",
})
Check it here

Angular.js - Two Concurrent But Exclusive Routes ($routeProvider)

<div id="header">
Header
</div>
<div id="wrapper">
<div id="slider" ng-view>
Slider
</div>
<div id="mainWindow" ng-view>
Main window
</div>
</div>
http://jsfiddle.net/abehnaz/k9Y4f/
I am working with a webapp that has two major views. The first is a main window (standard stuff), and the second is a "slider". The slider is more than a slide out menu (sometimes called a "shelf"). It has routing and secondary information associated with it.
In what seems to be the normal Angular paradigm, I would use a single ng-view in the main window, and that would populate the main window with the results of the $routeProvider's view template.
However, here I want to be able to ALSO have the slider have its own route. Angular does not seem to support this. I was thinking that I could potentially hack the functionality in by making a client side URL of the form:
www.app.com/MAINWINDOW/someView1/someView2/SLIDER/someViewa
Am I on the right track with this, or is there another functionality of Angular that I can use?
I would not recommend two routeParameters. Make your sidebar a service and put a controller on your wrapper, slider, and mainWindow. By having the ng-controller on #wrapper as well as the children, you can share data between the controllers (and in turn, inject the service into those controllers. I have done this and it was very successful. See an example below. In my code, my ng-view and routing returned everything within the <div ng-app="myApp">. You could easily put the ng-view in the MainCtrl and trigger routes from the sidebar.
In your template:
<div ng-app="myApp">
<div ng-view>
//Produced by routing
<div id="wrapper" ng-controller="RootCtrl">
<div id="Sidebar" ng-controller="SidebarCtrl">
...
<div ng-click="setUnit(mod.number, unit.number)">Unit 1</div>
...
</div>
<div id="mainWindow" ng-controller="MainCtrl">
</div>
</div>
</div>
</div>
In your javascript:
A service:
myApp.service('questionsSvc', function(Modules) {
var QuestionsSrc = {};
QuestionsSrc.questions = [];
var getQuestionsForModAndUnitFn = function(mod, unit) {
...bunch of code to have it populate QuestionSrc with questions
};
return {
getQuestionsForModAndUnit: getQuestionsForModAndUnitFn,
Questions: QuestionsSrc
};
});
For Controllers:
function RootCtrl($scope) {
$scope.data = {};
}
function SidebarCtrl($scope, Modules, questionsSvc) {
$scope.setUnit = function (mod, unit) {
questionsSvc.getQuestionsForModAndUnit(mod, unit);
$scope.data.questions = questionsSvc.Questions.questions;
//$scope.data.questions was used in the MainCtrl window to do an ng-repeat on the json array of questions bound to this model.
}
};
function MainCtrl($scope){
...whatever you need to manipulate the code...
}
With this example, I am sharing information across Controllers. The magic is with the $scope.data = {}; from the RootCtrl. It allows me to attach questions to the $scope based on an action in the Sidebar (clicking a label) and use that same $scope in the MainCtrl to display the questions in a pretty format. With what I just showed, I did not have to use $routeParameters to pass variables to go to another page (such as module and quiz) but I could have done so as you asked but would have had the sidebar ng-click change routes.
Hope this helps.
One solution is to use Angular UI router:
AngularUI Router is a routing framework for AngularJS, which allows
you to organize the parts of your interface into a state machine.
Unlike the $route service in Angular core, which is organized around
URL routes, UI-Router is organized around states, which may optionally
have routes, as well as other behavior, attached.
States are bound to named, nested and parallel views, allowing you to
powerfully manage your application's interface.
Just to give a flavor for how you could use AngularUI Router- It supports multiple named views (which you can read more about under "Multiple & Named Views" in their docs). So for instance you can use ui-view with names:
<div id="wrapper">
<div id="slider" ui-view="slider">
Slider
</div>
<div id="mainWindow" ui-view="main">
Main window
</div>
</div>
Then within config you can attach states to various routes and specify what each view should display for that state.
myapp.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('index', {
url: "/index",
views: {
"Slider": {
templateUrl:"route1.viewA.html"
},
"Main": {
templateUrl:"main1.viewA.html"
}
}
})
.state('slider2', {
url: "/slider2",
views: {
"Slider": {
templateUrl:"route2.viewA.html"
},
"Main": {
templateUrl:"main1.viewA.html"
}
}
})
There's a variety of other ways you could use AngularUI Router though. For instance, you may be able to get away with just using nested routes- which they recommend trying first.
Here's a couple more good reference material you might check out:
http://joelhooks.com/blog/2013/07/22/the-basics-of-using-ui-router-with-angularjs/
http://www.ng-newsletter.com/posts/angular-ui-router.html

viewContentLoaded - Without duplicating code per each controller - AngularJS

Hey all I'm trying to get a feel for angular and have ran into a little snag.
I have a container structure like the following:
<div class="main-container" ng-view>
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
Before I implemented Angular I just had a simple script that would check the height of the window and set the height of the left column and right column divs accordingly.
With angular there is probably a better way to do this then attaching an event function to the window object. Basically I want to fire a function everytime a new view is rendered but not duplicate the below code in all of my angular controllers like so:
$scope.$on('$viewContentLoaded', setColumnHeight);
Any help would be appreciated. Thanks!
I've solved this problem in my app by having a root "Application Controller" at the top of the DOM tree. For example:
<body ng-controller='applicationController'>
...
<div class="ng-view"></div>
...
</body>
applicationController is always there, and can set up bindings on its scope when it is created.
How about putting that event listener on the root scope:
$rootScope.$on('$viewContentLoaded', setColumnHeight);
You could use something like this:
<div class="main-container" ng-view>
{{ setColumnHeight() }}
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
and it will check the height every time a render is made, however, this seems to be a bit over processing.
The best approach would be to only update the height from both columns when the height of one of them changes. For that you could use DOM Mutation Observers, however they are not yet available on all browsers. If that's not a problem for you, check the mutation-summary lib.
If you are using jQuery you can also try this: https://github.com/jqui-dot-net/jQuery-mutate (examples here).

Categories

Resources