lately I have been reading through the ember documentation and following as best I can. I have made good progress which I thank God for but lately I have had troubles with routing. Specifically I can get templates to display in the application template but I cannot get children routes to display in the parent resources template. Instead the child template actually replaces it. Here's my code:
index.html
<script type="text/x-handlebars" data-template-name="modals">
<h2>Modals Parent Route</h2>
<p>This is the modals route which is common to all modals</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="modals/layout">
<h2>Layout Route</h2>
</script>
<script type="text/x-handlebars" data-template-name="modals/visuals">
<h2>Visuals Route</h2>
</script>
<script type="text/x-handlebars" data-template-name="modals/finish">
<h2>Finish Route</h2>
</script>
<script type="text/x-handlebars" data-template-name="modals/preview">
<h2>Preview Route</h2>
</script>
App.js
App.Router.map(function(){
this.resource('modals', { path:'/modals' }, function(){
this.route('layout');
this.route('visuals');
this.route('finish');
this.route('preview');
});
});
App.LayoutRoute = Em.Route.extend({
renderTemplate: function() {
this.render('modals.layout',{
into: 'modals',
});
}
This is not the full code but I thought it would be all that pertains to this topic. Please feel free to ask me if you need the full file contents.
the renderTemplate method you are using is completely overriding the template for modals that is why, get rid of it. Ember handles the rendering of templates for you, there's no need for it with the basic functionality you are trying to achive.
So I used ember-cli and finally figured out a way to get this working. I used the ember-cli g route to generate my routes. I ran it once for each of my routes except for application. After that things just worked properly. Here is a git repository that someone created for me that will help anyone with this same problem : https://github.com/mooror/mooror. Thanks everyone
If you want nested templates you need nested routes.
this.resource('parent', function() {
this.route('child');
});
See: http://guides.emberjs.com/v1.10.0/routing/defining-your-routes/#toc_nested-resources
Related
My question comes from the need to lazy load different/separate ngApps on one page(bootstrapping them with angular.bootstrap), each of them using its own ngRoute definitions(defined in such a way that they do not overlap each other).
Now I have a working plunkr example and all seems to function well, but somehow I have the feeling that this is not the correct approach so that's why Im asking here for advice.
<!DOCTYPE html>
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div class="container">
<div class="col-xs-6 well" id="app1">
<h3>App1</h3>
Route 1
Route 2
Route 3
<div class="well" ng-view></div>
</div>
<div class="col-xs-6 well" id="app2">
<h3>App2</h3>
Route 1
Route 2
Route 3
<div class="well" ng-view></div>
</div>
</div>
<script type="text/javascript" src="https://code.angularjs.org/1.2.21/angular.min.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.2.21/angular-route.min.js"></script>
<script>
var app1 = angular.module('app1', ['ngRoute']);
app1
.config(function($routeProvider) {
$routeProvider
.when('/:app/:param', {
template: 'app = {{app}}, param = {{param}}',
controller: 'Ctrl1'
})
})
.controller("Ctrl1",['$scope','$routeParams', function($scope,$routeParams) {
$scope.app = $routeParams.app;
$scope.param = $routeParams.param;
}]);
var app2 = angular.module('app2', ['ngRoute']);
app2
.config(function($routeProvider) {
$routeProvider
.when('/:app/:param', {
template: 'app = {{app}}, param = {{param}}',
controller: 'Ctrl2'
})
})
.controller("Ctrl2", ['$scope','$routeParams', function($scope,$routeParams) {
$scope.app = $routeParams.app;
$scope.param = $routeParams.param;
}]);
angular.bootstrap(document.getElementById("app1"), ['app1']);
angular.bootstrap(document.getElementById("app2"), ['app2']);
</script>
</body>
</html>
http://plnkr.co/edit/Y7A9bc3bDUyAXIS13JMZ?p=preview
FYI: I have already checked out ui-router to load separate states on one page, but it all requires for the source code to be loaded upfront(and have a single app.config with all routes in it) which is something I do not want as the Project is quite huge. So for the sake of keeping it simple and modular Im looking for something like the above example.
UPDATE to answer HockeyJ question:
OK I will try to keep this as short as possible. Very basically put - as I said the project could potentially become quite huge, from source code and module dependency perspective. This is why I want to make it in such a way, that each module can be totally separate App, able to be injected at any place and tested separately(thats not really an issue with AnJS). In the same time however the whole project, should be a single page app. As such there should not be screen reloads(to load an App jscript files) etc. Hence comes the lazy loading of scripts on demand and bootstrapping apps to DOM elems. The only possible intersection point of all apps is the URL routing which has strict naming convention for routes i.e. /reports/whatever ; /transactions/whatever etc. and should be managed by ngRoute.
UPDATED:
Checkout overmind project for angular and the demo here
Shows a project broken up into several apps: nav, home, profile, and admin. The nav app is always on the page and is the only app bootstrapped on pageload.
Just take routing out of angular and use something like sammy.js , history.js etc for client side routing.
Use
angular.bootstrap(module, node)
in the routes of your sammy handlers
Also you might want to take a look at react.js (with or without flux) and tide it up with sammy the same way. Consider this example from todombc : https://github.com/tastejs/todomvc/tree/gh-pages/architecture-examples/react
Trying to get gist-embed (https://github.com/blairvanderhoof/gist-embed) working within my Angular app but with no luck.
It works no problem on my homepage (which is not part of the Angular app) but when I use something like:
<code data-gist-id="<gist-id>"></code>
within the app it won't show. There are no messages in the console to explain why though.
Could someone explain why and offer a solution?
(function($) {
$(function() {
// find all elements containing "data-gist-id" attribute.
$('[data-gist-id]').each(function() {
var $elem = $(this),
id,
that lib is coded in such a way one cant really use it in angular,you'll have to look for a fork that offers a proper jquery plugin architecture you can use into a directive.That lib doesnt respect basic jQuery plugin architecture.
And no Error will show up because it's likely the .each will execute before your angular app runs.
As of June (version 1.8), the gist-embed project is now a jQuery plugin.
var $code = $('<code data-gist-id="474f6d7839fccffc4b2a"/>');
$code.appendTo('body').gist();
Basically you have to trigger "gist()" on the dom elements.
Try this, it worked for me very well.
// register trigger gist on every template include
$rootScope.$on('$includeContentLoaded', function() {
// initialize gist on new elements
angular.element(document).ready(function() {
if (typeof(angular.element(document).gist) === 'function') {
angular.element('[data-gist-id]').gist();
}
});
});
I put together a small library hoping to solve the problem.
Check it out : https://github.com/pasupulaphani/angular-gist-embed
Chekout this angular module : https://github.com/kiran3807/another-angular-gist-embed
This allows you to include the gists in your angular project in the form of a directive, one of the attributes for which is the gist-id :
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="path/to/another-angular-gist-embed.js"></script>
<script>
angular.module('example',['another-angular-gist-embed']);
angular.module.controller('exampleCtrl',['$scope',function($scope){
$scope.gistId = 'a85770344febb8e30935';
}]);
</script>
</head>
</head>
<body ng-app="example">
<div ng-controller="exampleCtrl">
<!-- This loads a gist with a specific id-->
<gist-embed data-gist-id="gistId"></gist-embed>
</div>
</body>
</html>
Declaration : I am the author of this module
So I'm checking out Emberjs.
Scroll down a little on the homepage to "GETTING STARTED WITH EMBER.JS IS EASY."
Great, that looks simple, I'll give it a go.
Create a new boilerplate HTML5 file.
Paste their template code into my :
<body></body>
Include emberjs:
<script src="ember.js" type="text/javascript"></script>
Include the JS code they provided into a:
<script type="text/javascript"></script>
Within my head tags. Great, let's see what happens.
Load the page, Console tells me it requires jquery. So no problem I include jQuery. Try again, another error, I need to include handlebars. No problem, I include handlebars. Try again, App is not defined... right... so I include
window.App = Ember.Application.create();
Above the snippet they provided. Try again, DS is not defined. At this point I have no idea where to go next. I took a look at the emberjs guides section as I assume I have to define a DS model somewhere, or something. But the guides were no use.
Am I missing something blatantly obvious, or is this in fact not 'easy' as they put it? What do I have to do to make this basic example work (and why the hell have they given 'basic' code that doesn't work out the box as an example)?
Edit:
My full code thus far:
<!DOCTYPE html>
<html>
<head>
<script src="jquery.js" type="text/javascript"></script>
<script src="handlebars.js" type="text/javascript"></script>
<script src="ember.js" type="text/javascript"></script>
<script type="text/javascript">
window.App = Ember.Application.create();
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
fullName: function() {
return this.get('firstName') +
" " + this.get('lastName');
}.property('firstName', 'lastName')
});
App.peopleController = Em.ArrayController.create({
content: App.Person.findAll()
});
</script>
</head>
<body>
<h1>People</h1>
<ul>
{{#each peopleController}}
<li>Hello, <b>{{fullName}}</b>!</li>
{{/each}}
</ul>
</body>
</html>
The problem isn't you, it's that the documentation neglects to list the required dependencies, the naming convention of Handlebars, and suddenly starts talking about a Post controller without providing the code. There's also a couple of places where there's a little bit of cognitive forward referencing going on, so if you know about EmberJs, the guide makes sense, but if you're trying to learning it fresh, you have to hop around, make assumptions, and conduct some tests.
While this isn't the minimal code needed for an EmberJS application, it should be enough to get you started to plug in about 80% of the Getting Started Guide. Hope this helps.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Demo</title>
<script src="jquery-1.9.1.js"></script>
<script src="handlebars.js"></script>
<script src="ember-1.0.0-rc.1.js"></script>
</head>
<body>
<script language="javascript" type="text/javascript">
// Make a global variable -- http://emberjs.com/guides/application/
App = Ember.Application.create({
VERSION: '1.0.0',
// store: Ember.Store.create().from(Ember.fixtures)
});
// http://emberjs.com/api/classes/Ember.Application.html
App.ready = function() {
// alert("App initialized!");
};
// Application Controller, extended with custom properties
App.ApplicationController = Ember.Controller.extend({
appName: 'My First Example',
firstName: "Charlie",
lastName: "Brown",
// initial value
isExpanded: false,
expand: function() {
this.set('isExpanded', true);
},
contract: function() {
this.set('isExpanded', false);
}
});
// A router
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
// `controller` is the instance of ApplicationController
controller.set('title', "Hello world!");
}
});
</script>
<script type="text/x-handlebars" data-template-name="application">
<header>
<h1>A Header - {{appName}}</h1>
</header>
<div>
Hello, {{firstName}} {{lastName}}.
<p/>
{{#if isExpanded}}
<div class='body'>{{body}}</div>
<button {{action "contract"}}>Contract</button>
{{else}}
<button {{action "expand"}}>Show More...</button>
{{/if}}
<hr/>
{{outlet}}
<hr/>
</div>
<footer>
<H1>A Footer</H1>
</footer>
</script>
</body>
</html>
You do not need to define a DS store unless you have included Ember Data. I have the most basic Ember starting template available on jsfiddle. You can view the source of the rendered frame to get an idea that you only need 3 JS files (which you already have included) in order for the application to work.
From there it is up to you, but yes I admit that the documentation is lacking in regards to starting off from scratch.
From your edit, you have a DS object referenced, but you have not referenced the Ember Data script. It's currently an add-on to the default EmberJS script due to the fact that it is not API locked, whereas the main stream is locked.
I am trying to learn Ember using the docs here: http://emberjs.com/guides/templates/handlebars-basics/
I am following the example given on that link and accordingly have the following two files and contents and nothing else. However, when I go the browser and open index.html I do not see Hello World. The World part does not show up. However, according to what is explained near the bottom of that page, World should show up on the page.
Any help in understanding why this is not working would be great. Thanks!
index.html
<h2>First Ember Application</h2>
<script type="text/x-handlebars">
Hello {{name}}.
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.rc.1/handlebars.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/ember.js/1.0.0-pre.2/ember-1.0.0-pre.2.min.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
window.App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({
name: "World"
});
you would need an Ember.View to pass variables into and connect with the template. You could set the Context of the controller and have the view use this as its contents, but for your simple example:
window.App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({});
App.ApplicationView = Ember.View.extend({
templateName : 'application',
name: 'World'
})
Its worth noting that Ember will look for a set of View, Controller and template with the 'application' naming convention.
Here is a link to the full fiddle http://jsfiddle.net/gNKJj/1/
I just started this HTML5 project where we decided to make it a single page architecture by leveraging jQuery $.load() method. Unfortunately, as soon as the JS started to grow, we quickly started running into issues where the modules loaded into the master dashboard have no knowledge of their parent.
The architecture looks like this:
dashboard.html (master file)
moduleA.html
moduleA.js
moduleB.html
moduleB.js
moduleC.html
moduleC.js
Since we decided to also keep the JS as separate files, we are having to load all JS files through dashboard.html in order to invoke them individually when modulex is loaded.
So when loading moduleA.html into the dashboard we have to call its corresponding JS. To do this we simply wrote the JS using a Module Pattern so we can easily invoke it by doing a function call, like:
<script>
moduleA
</script>
or this if we want to access a specific property of this member.
<script>
moduleA.someMethod();
</script>
Now, I know there are is gotta be a nicer way of doing this, right? I hate having to have script tags in the HTML modules in order to load its corresponding JS file.
Another limitation of this is the fact that we no longer can work on modules individually, since the scripts and CSS invocation happens on the parent (dashboard.html) so certainly when moduleA.html is loaded directly, it is pure HTML with no script or CSS.
I looked through the other questions but I didn't see anyone with the same problem.
I looked at AngularJS, EmberJS, KO.JS and BoilerPlateJS but none of them addresses what we are trying to accomplish. The only one that has a similar single page concept is jQuery Mobile but I don't know if you can switch from jQuery to jQuery Mobile and everything remains working.
Has anyone face this issue yet? Is there a solution or would I have to go with a custom solution.
Thanks!
I could argue about AngularJS with you. It is exactly what you need
dashboard.html is layout with some directives attached, but power lies in AngularJs if you use ng-view directive
here is example:
dashboard.js
var app = angular.module("modularApp",[]);
app.config(['$routeProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
controller:'HomeCtrl',
templateUrl:'templates/home.html'
});
$routeProvider.when('/moduleA', {
controller:'ModuleACtrl',
templateUrl:'templates/moduleA.html'
});
$routeProvider.when('/moduleB', {
controller:'ModuleBCtrl',
templateUrl:'templates/moduleB.html'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
templates/dashboard.html
<html ng-app="modularApp">
<head>
<!--.... include angular minified js file and what else you need...-->
<script type="text/javascript" src="dashboard.js"></script>
<script type="text/javascript" src="moduleACtrl.js"></script>
<script type="text/javascript" src="moduleBCtrl.js"></script>
</head>
<body>
<a ng-href="#/moduleA">Open Module A View</a>
<a ng-href="#/moduleB">Open Module B View</a>
<!-- Add widgets header menus .... -->
<ng-view></ng-view>
</body>
</html>
moduleACtrl.js
var app=angular.module("modularApp");
app.controller("ModuleACtrl",function($scope){
$scope.scopeValue="Hellow from view";
});
moduleBCtrl.js
var app=angular.module("modularApp");
app.controller("ModuleBCtrl",function($scope){
$scope.scopeValue="Hellow from another view";
});
templates/moduleA.html
<div>{{scopeValue}} in module A</div>
templates/moduleB.html
<div>{{scopeValue}} in module B</div>
You can do more complex things with angular then just this. All depends on your needs. Do you have any special requirements :)
Also, you could create your own directive, like ng-view and use your own $route service and $routeProvider so you can add css and javascript you want to dynamically load when some rute match url.
so instead of above routing table, you could have
app.config(['$myRouteProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
javascript:'javascript/dashboard.js',
templateUrl:'templates/dashboard.html',
cssfile: 'css/dashboard.css'
});
$routeProvider.when('/moduleA', {
javascript:'javascript/moduleA.js',
templateUrl:'templates/moduleA.html',
cssfile: 'css/moduleA.css'
});
$routeProvider.when('/moduleB', {
javascript:'javascript/moduleB.js',
templateUrl:'templates/moduleB.html',
cssfile: 'css/moduleB.css'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
But that is, pardon on my French, stup. There are couple libs I tried in ruby on rails to acheive similar, but backend is rendering content, or just part of content. But I'm not sure which backend you are using and are you interested to switch to rails anyway.
DomController in BoilerplateJS does what you need, without using any custom HTML attributes. Your dashboard.html can just have place holders where you want to inject your components. I'm just pulling out some html below from BoilerplateJS index.html to show how it works:
<body>
<section id="page-content">
<header>
<section class="theme"></section>
<section class="language"></section>
</header>
<aside>
<section class="main-menu"></section>
</aside>
</section>
</body>
theme, language and main-menu sections above are just place holders in to which relavant components would be injected by the DomController. The DomController can be now used to register the components with appropriate selectors as below:
//scoped DomController that will be effective only on $('#page-content')
var controller = new Boiler.DomController($('#page-content'));
//add routes with DOM node selector queries and relavant components
controller.addRoutes({
".main-menu" : new MainMenuRouteHandler(context),
".language" : new LanguageRouteHandler(context),
".theme" : new ThemeRouteHandler(context)
});
controller.start();
Above code is extracted from "/boilerplatejs/src/modules/baseModule/module.js"