I have multiple models each of which has its own template. I want to share a footer of buttons accross the different templates so when the same button is pressed different functions are executed according to the selected template. The HTML looks like this:
<script type="text/x-handlebars" id="application" >
<menu>
<nav>
<ul>
<li>{{#link-to 'model1'}}Model 1{{/link-to}}</li>
<li>{{#link-to 'model2'}}Model 2{{/link-to}}</li>
<li>{{#link-to 'model3'}}Model 3{{/link-to}}</li>
</ul>
</nav>
</menu>
<div class="content">
<section>
{{outlet}}
</section>
</div>
<footer>
<ul>
<li><button class="action-undo" {{action undo}}>Undo</button></li>
<li><button class="action-save" {{action save this}}>Save</button></li>
<li><button class="action-saveContinue" {{action saveAndContinue this}}>Save & Continue</button></li>
</ul>
</footer>
</script>
You probably want to use the {{partial}} helper. It will inline a template (with the same context) when it is called.
To use the partial helper just create a template with a leading underscore, like :_buttons. To include that template you can now use {{partial "buttons"}}.
See: http://emberjs.com/guides/templates/rendering-with-helpers/
You can keep the application template with the footer so all templates inserted in the outlet will have the footer.
To get the actions triggering to some corresponding template, just override the route of that template, for example in the model1 template, you can create a App.Model1Route implementing the actions:
App.Model1Route = Ember.Route.extend({
actions: {
undo: function() {
// perform undo
},
save: function() {
// perform save
},
saveAndContinue: function() {
// perform save and continue
}
}
});
And for model2 template just override the App.Model2Route and add the actions to be triggered in that template, and so on.
Give a look in that fiddle to see this working http://jsfiddle.net/marciojunior/d3xA6/
Be careful because if some route don't implement some triggered action, an error is raised. In that case you can extract your footer to a partial and just add where is used.
I hope it helps
Related
I have found https://sean-hunter.io/2016/10/23/inter-component-communication-with-aurelia/ where it is explained how simple communication of values can be made between a parent and a child template.
Now, I'm trying to apply that to the contact manager tutorial:
http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/contact-manager-tutorial/1
... also on https://github.com/aurelia/app-contacts/tree/master/src - in particular to the looped contact items, but I cannot get it to work.
I have made gists as an example, which can run on gist.run (note, gist.run seems to only work in Chrome, not in Firefox 50 yet - but you can replace gist.run/?id= with gist.github.com/ and look at the code):
Copy of original contacts manager app (works) - https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8
My changes in the app (do not work) - https://gist.run/?id=47c4f1c053adbdf46f6a33413dd12d3d
This is what I'm trying to do: in the original contacts app, there is this in src/contact-list.html which works fine:
<template>
<div class="contact-list">
<ul class="list-group">
<li repeat.for="contact of contacts" class="list-group-item ${contact.id === $parent.selectedId ? 'active' : ''}">
<a route-href="route: contacts; params.bind: {id:contact.id}" click.delegate="$parent.select(contact)">
<h4 class="list-group-item-heading">${contact.firstName} ${contact.lastName}</h4>
<p class="list-group-item-text">${contact.email}</p>
</a>
</li>
</ul>
</div>
</template>
Now, I'd like to replace the inner elements of the "looped" li - that is, the a with the h4 and the p - with a new template.
So, I made src/contact-list-item.html:
<template>
<a route-href="route: contacts; params.bind: {id:theContact.id}" click.delegate="$parent.$parent.select(theContact)">
<h4 class="list-group-item-heading">${theContact.firstName} ${theContact.lastName}</h4>
<p class="list-group-item-text">${theContact.email}</p>
</a>
</template>
... and src/contact-list-item.js:
import {bindable} from 'aurelia-framework';
export class ContactListItem {
#bindable theContact;
}
... and changed src/contact-list.html as:
<template>
<require from="./contact-list-item"></require>
<div class="contact-list">
<ul class="list-group">
<li repeat.for="contact of contacts" class="list-group-item ${contact.id === $parent.selectedId ? 'active' : ''}">
<contact-list-item theContact.bind="contact"></contact-list-item>
</li>
</ul>
</div>
</template>
Basically, I've made a property in the contact-list-item class called theContact, and I'd like to bind it to the contact which is the looper variable in the repeat.for="contact of contacts" -- unfortunately, this doesn't work, as the data is not propagated (and so the contact names are empty). Also, the clicks on contact fiels do not propagate to show the details, even if I've changed click.delegate="$parent.select(contact)" to click.delegate="$parent.$parent.select(theContact)" in the new template.
What do I have to do, in order to have the data propagate form the li repeate.for loop, down to the new replacement template - and to have the app react on clicks on the new template?
Quick answer:
In contact-list.html, change:
<contact-list-item theContact.bind="contact"></contact-list-item>
to:
<contact-list-item the-contact.bind="contact"></contact-list-item>
Explanation:
HTML is not case-sensitive, but the convention is to use only lower-case letters. To respect that, Aurelia converts all camelCase variables to dash-case parameters and element names when interfacing with your HTML code.
More Information:
Dwayne Charrington wrote a good blog about this topic here:
http://ilikekillnerds.com/2016/06/dont-get-skewered-kebab-case-aurelia/
I have 5 buttons using the same ng-click function. Basically each of the buttons operate similarly to a tabbed navigation, where you click one of the buttons and it takes you to that tab's pane. Each of these buttons can be repeatable and are housed in a template. The tab panes are also all in a template but aren't all active until a user clicks one of the buttons and creates a page. So basically there are multiple click functions nested within click functions that do different things depending on what user has activated.
In jQuery, I could just use "this" and select the object that was clicked and do all my manipulations to that object easily; however, it doesn't appear there's a way to do that using just angular. Currently, when you click one of these buttons it does the same thing to all of them. I figure I could create 5 separate functions, but I don't want to do that for scalability reasons.
So to summaraize:
Is there a way to select "this" in Angular?
I'd like a solution that is just using Angular and no jQuery
Is there an efficient way of dealing with click functions within click functions?
<nav class="block--menu">
<section class="content--menu" ng-controller="ActiveCtrl">
<div class="menu" >
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
</div>
</section>
You can access jQuery event object using $event in angular events check the documentation for details but if you are sending that to your controller it most likely means you are not doing it in angular way.
the usage is
<button class="menu__item" ng-click="showConfirm($event)"></button>
and in the controller
$scope.showConfirm = function($event){
//$event.target should be your link
};
You should stop thinking in a jQuery way and don't try to manipulate the DOM directly. In your controller you should only manipulate the data, which is then reflected in the view. When you think Angular-way, your code usually looks as follows:
HTML
<section ng-controller="ActiveCtrl as ctrl">
<div class="menu" >
<button ng-repeat="button in ctrl.buttons track by $index"
ng-click="ctrl.showConfirm(button)"
ng-class="{'menu__item_active':button.active, 'menu__item':true}"
>{{button.name}}</button>
</div>
</section>
JavaScript
angular.module('app',[]).
controller('ActiveCtrl', ['$window', function($window) {
this.buttons = [{
name: 'First'
}, {
name: 'Second'
}, {
name: 'Third'
}];
this.showConfirm = function(button) {
button.active = !button.active;
$window.alert(button.name);
}
}]);
Plunker
http://plnkr.co/edit/Dg10cXqFxEKgEt7jWQ7Z?p=preview
Please bear with me, this question is very crude. I'm new to Ember js, and have got a lotta confusion between views controllers templates routes. I have this basic requirement, to have a menu on the left, with item users, organizations. and a div in the center, As of now, when i click users, i have given a link to /users route. for organizations /organizations route, which is a different template. I want to display them in the centered div. Not sure how to do that. I think it has got something to do with child views, but find myself numb in beginning. I just need help in the workflow of how to achieve this.
EDIT:
Here is the basic code, I have this page called home. Template home.hbs:
<ul class="nav nav-list">
<li>
{{#link-to 'users'}} Users{{/link-to}}
</li>
<li>
{{#link-to 'organizations'}} Organizations {{/link-to}}
</li>
</ul>
<div class="summary-width span10" id="home-container">
</div>
Similarly there is users.hbs and organization.hbs.
When a user clicks on users link, i want the users template to be displayed in home-container div. organizations template otherwise.
Generally you do what you are describing with routes. This is your application.hbs ie the template associated with the application route and controller.
<ul class="nav nav-list">
<li>
{{#link-to 'users'}} Users{{/link-to}}
</li>
<li>
{{#link-to 'organizations'}} Organizations {{/link-to}}
</li>
</ul>
<div class="summary-width span10" id="home-container">
{{outlet}}
</div>
you would then have a router:
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('organizations');
this.route('users');
});
export default Router;
{{link-to 'users'}} will enter the users route, call all the hooks, create the controller if it doesn't already exist, set the model on the controller, and render the users.hbs template into the application template's outlet. The home.hbs you are describing should be renamed to index.hbs because by default Ember renders your index.hbs template into the outlet when / is accessed (or whatever your base url is). Sure, its possible to make a different template be the default but that involves more code so just change your home.hbs to index.hbs
Edit:
add this to your router:
this.resource('home', function(){
this.route('organizations');
this.route('users');
});
if those routes are truly nested below your home route. Your link to's would need to change to {{#link-to 'home.organizations'}}
If you don't nest like this, then {{#link-to 'users'}} will render the users.hbs file into the application's outlet. That's just how Ember and the router work.
If you can't nest for some reason, then change the link-to to actions, make the {{outlet}} a named outlet like {{outlet 'named'}} and in your routes/home.js actions hash, make actions that this.renderTemplate() into the named outlet. Look at the api for exactly how to do this
Background
I am trying to make a breadcrumb trail as per this Evented Mind video (7:07).
The Problem:
I want the article title to show up next to a / divider. Here's a pic for reference:
When I navigate to an article page, the / divider dynamically shows up in the breadcrumb trail, but the title does not show.
The Question
What's weird is that the title shows up in the main region, just below. Shouldn't the data context for the template be available in the contentFor block?
The Code
Here is my layout template. See the yield regions at the bottom:
<template name='Layout'>
<div class="container">
<div id="new-article">
{{#linkTo route="blog.new"}}
New Article
{{/linkTo}}
</div>
</div>
<ul class="breadcrumb">
{{> yield "breadcrumbs"}}
</ul>
<div class="content">
{{> yield}}
</div>
</template>
And here is the template with the problematic breadcrumb trail (at the very top):
<template name='Article'>
{{#contentFor "breadcrumbs"}}
<li>
{{#linkTo route="home"}}
Home
{{/linkTo}}
</li>
<li>
<span class="divider">/</span>
</li>
<li>
{{title}}
</li>
{{/contentFor}}
<h1>
{{title}}
</h1>
<div>
{{author}}
</div>
<p>
{{body}}
</p>
</template>
The problem was not with the HTML at all. It had to do with the data option set on the router. For some reason that I don't entirely understand and would love to have explained to me, defining the data option on the "layout" function makes the data available to both the layout and to the template being rendered.
My hypothesis is that the two yield regions--breadcrumb and main--have different scopes that inherit from the layout. Even though the code for the content for block gets written in the Article Template.
Before:
In the code below, the data option is getting defined on the article template.
Router.route("/blog/:_id", function(){
this.layout("Layout");
this.render('Article', {
data: function() {
return Articles.findOne({_id: this.params._id});
}
});
}, {
name: 'article.show'
});
After: In the code below, the data option is getting defined on the layout.
Router.route("/blog/:_id", function(){
this.layout("Layout", {
data: function() {
return Articles.findOne({_id: this.params._id}); // good to make these functions, so that they stay reactive
}
}); // you can set data context here too, which is accessible to all children
this.render('Article', {});
}, {
name: 'article.show'
});
Ember Community Assemble!
I want to conditionally {{render ''}} small templates inside of the application.hbs sidebar but the content of that sidebar depends on which model's hbs we are routed to. For instance, the contents of the 'permit' sidebar would be different than that of the 'profile' sidebar.
Right now I am only able to render all of the sidebar contents at once regardless of what model.hbs is chosen.
<!-- Right Sidebar in application.hbs START -->
<div id="sidebar-wrapper" class="super-super-float-right-col">
<div id="sidebar-wrapper" class="super-float-right-col">
<div id="sidebar-wrapper" class="float-right-col">
{{render 'applicant'}} <!-- only available to '/person/#' -->
{{render 'location'}} <!-- only available to '/permit/#' -->
</div>
</div>
</div>
<!-- Right Sidebar END -->
<div class="The rest of the content">
{{outlet}} <!--inserts the rest of the html into the main content container -->
</div>
I don't want both 'applicant' and 'location' to be rendered at the same time as they are above, and I want to data inside of 'applicant' to change depending on the id # of 'person'. The same relationship applies to 'location' inside of 'permit.hbs'
VpcYeoman.PermitRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ render:'location'});
}
});
Application_route.js is blank for now
Although 1.2.0 introduced the ability to use properties for template name in {{view}} it does not work for {{render}} yet.
So your options are to use {{view}} if you can, or a series of {{#if}} in the template, or a component/view to wrap the choice of what to render (one way to do this would be to have a template for each render, and a choice view that binds templateName property to the parentController property that determines which should be displayed)
Here is a jsbin that I used to experiment.