EmberJS re-use same template, but displays the template twice - javascript

I am playing with Ember, and building a basic contact management app to learn Ember. I am following the Emberjs getting started guide. Only instead of doing a "to-do" app, Im doing my own thing in hopes of picking it up better.
My Router, and Routes:
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' });
this.route('motoDigitalTrue');
});
this.resource('about');
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UsersMotoDigitalTrueRoute = Ember.Route.extend({
model: function(){
return App.User.filter(function(user) {
if (user.get('motoDigital')) {
return true;
}
});
},
renderTemplate: function(controller) {
this.render('users', {
controller:controller
});
}
});
Essentially, I have a template named 'users' that I want to reuse. This template lists all the users. I have a sorting button that when clicked, will only display the users who have the motoDigitalTrue property set to true. The sorting is correct, but it just displays another Users template, rather than re-populating the original.
My Users template:
<script type="text/x-handlebars" id="users">
<div class="span10 tableContainer">
<button class="btn btn-primary createUser" {{action createUser}}><i class="icon-plus icon-white"></i> Add a Contact</button>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Sort<span class="caret"></span></a>
<ul class="dropdown-menu">
{{#linkTo 'users.motoDigitalTrue' activeClass="selected"}}Receiving MOTO Digital{{/linkTo}}
</ul>
</div>
<div class="tableScrollable">
<table class="table table-striped">
<thead>
<tr>
<th class="nameHead">Name</th>
<th class="companyHead">Company</th>
<th class="emailHead">Email</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name">&nbsp</td>
<td class="company">&nbsp</td>
<td class="email">&nbsp</td>
</tr>
{{#each model}}
<tr>
<td class="name"><i class="icon-user"></i> <strong>{{#linkTo 'user' this }}{{firstName}} {{lastName}}{{/linkTo}}</strong></td>
<td class="company">{{company}}</td>
<td class="email"><i class="icon-envelope"></i> <a {{bindAttr mailto="email"}}>{{email}}</a></td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="span3">
{{#if isCreateUser}}
<div class="well">
{{partial 'users/createUser'}}
<button {{action 'saveUser'}} class="btn btn-primary"><i class="icon-ok icon-white"></i> Save</button>
</div>
{{else}}
{{outlet}}
{{/if}}
</div>
</script>
I have been unable to find an answer, and any help would be appreciated!

I guess in your case to reuse templates, you should try using a partial, have a look here.
For example, rename your users template to _users
<script type="text/x-handlebars" data-template-name='_users'>
...
</script>
and then use the partial helper to render it
{{partial users}}
Note that {{partial}} takes the template to be rendered as an argument, and renders that template in place. This means that it does not change context or scope. It simply renders the given template with the current scope.
Hope it helps.

Related

Pass multiple params handlebars each block

So I'm trying to render two form information within a view I can get the one forms information to render however I can't get the second form to render the necessary information.
Here is what I have so far.
<tbody>
{{#each poemRegistrations}}
<tr>
<td>{{schoolName}}</td>
<td>{{competitionResults.winnersName}}</td>
<td>{{poem1AuthorName}}</td>
<td>{{poem1Title}}</td>
<td>{{poem1Url}}</td>
<td>{{poem2AuthorName}}</td>
<td>{{poem2Title}}</td>
<td>{{poem2Url}}</td>
<td>{{poem3AuthorName}}</td>
<td>{{poem3Title}}</td>
<td>{{poem3Url}}</td>
<td>
<div class="btn-group">
<a href="/dashboard/users/forms/poem-registrations/{{_id}}">
<button class="btn">Show</button>
</a>
<a href="/dashboard/users/forms/poem-registrations/edit/{{_id}}">
<button class="btn">Edit</button>
</a>
<a href="/dashboard/users/forms/poem-registrations/delete/{{_id}}">
<button class="btn user-btn-danger">Delete</button>
</a>
</div>
</td>
</tr>
{{/each}}
router.get('/dashboard/all-poems', ensureAuthenticated, (req, res) => {
PoemRegistrations.find({}, function(err, poemRegistrations, competitionResults) {
res.render('dashboard/all-poems.hbs', {
pageTitle: 'All Poems',
poemRegistrations: poemRegistrations,
competitionResults: competitionResults
});
});
});
The PoemRegistration form information is rendering however I just want to get the winners name from another form.
How would I go about doing this?
You may use the {{#root}} helper in Handlebars V2.0.0 :{{#root.competitionResults.winnersName}}
Or you could also include ../ segments to change the context to the root : {{../competitionResults.winnersName}}
Hope this helps.

Inheritance scope using ui.router to filter data in ng-repeat [Angular1.5]

I have a problem probably with inheritance scope using ui.router. I'm creating a small application something like addressbook. I want to have an input in navbar which allow me to filter addresses on other page (so when i'm writing there, the parial view should be changed by ui-router). I have one view named index.html and other tamplets for each of sites using ui-ruter.
When i'm writing in input, view is changing, but the data is not filtered. I didn't receive any errors. Thanks for answers.
(Sorry for complicated description, its my first post)
index.html (without header with links)
<body ng-app="appModule" ng-controller="applicationController">
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#nav-toggle">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand">Contact Manger</a>
</div>
<div class="collapse navbar-collapse" id="nav-toggle">
<ul class="nav navbar-nav">
<li><a ui-sref="index">View</a></li>
<li><a ui-sref="add">Add Contact</a></li>
</ul>
<form class="navbar-form navbar-right" role="search">
<input type="text" class="form-control" placeholder="Search" ng-model="search" ng-keyup="startSearch()">
</form>
</div>
</nav>
<div class="container">
<ui-view>
</ui-view>
</div>
appModule.js
angular.module("appModule",[
"ui.router",
"AddModule",
"DetailsModule",
"MainModule",
"ngSanitize"])
appController.js
angular.module("appModule")
.controller('applicationController', function($scope, $location){
$scope.startSearch = function(){
$location.path('index');
};
});
router.js
angular.module('appModule')
.config(function($stateProvider){
$stateProvider.state('index',{
url: '/index',
template: "<main-component></main-component>"
})
.state('add',{
url: '/add',
template: "<add-component></add-component>"
})
.state('contact-details',{
url: '/details/:id',
template: "<details-component></details-component>",
})
})
mainController.js (its in other module)
(function () {
function MainController($scope, contactFactory) {
$scope.contacts = contactFactory.get();
}
angular.module("MainModule")
.component("mainComponent", {
templateUrl: "/assets/partials/main.html",
controller: ["$scope", "contactFactory", MainController],
})
})()
And half of code in ui-router template:
<tbody>
<tr ng-repeat="contact in contacts | filter:search">
<td>{{contact.name}}</td>
<td>{{contact.email}}</td>
<td>{{contact.phone}}</td>
<td><a ui-sref="contact-details({id:$index})" class="btn btn-default btn-xs">More..</a></td>
</tr>
</tbody>
As you might know, $scope has prototype inheritance you can change your code to something like this:
just assign search to a property of an object on scope like this;
$scope.data = {};
$scope.data.search = '';
your input changes to this:
<input type="text" class="form-control" placeholder="Search" ng-model="data.search" ng-keyup="startSearch()">
and finally your inner component becomes like this:
<tr ng-repeat="contact in contacts | filter:data.search">
<td>{{contact.name}}</td>
<td>{{contact.email}}</td>
<td>{{contact.phone}}</td>
<td><a ui-sref="contact-details({id:$index})" class="btn btn-default btn-xs">More..</a></td>
</tr>
Here Misko discusses this issue more deeply.

AngularJS dynamic routing from index to detailed page

I'm trying to route from an index list of items to a page that will display a detailed view of that item.
In my index view I have a table that iterates through all the items that are saved in the database.
There is a button under the actions column that will take me to events/show route using ng-click="go('events/show')"
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Title</th>
<th class="col-md-2">Actions</th>
</tr>
</thead>
<tbody>
<tr scope="row" ng-repeat="event in events | reverse | filter:filterByUID">
<td>{{event.title}}</td>
<td class="col-md-2">
<div class="btn-group" role="group" aria-label="actions">
<button class="btn btn-primary" ng-click="go('events/show')">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
</button>
<button class="btn btn-primary" ng-click="events.$remove(event)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
The table looks like this:
In my controller I have:
$scope.go = function ( path ) {
$location.path( path );
};
in my routes.js I have:
.whenAuthenticated('/events/show', {
templateUrl: 'views/eventShow.html',
controller: 'eventShowCtrl'
})
Everything works so far.
However, what is unclear to me is how do I pass the event id to the eventShow.html page, so I know which item was clicked from the index list, so I can display the detailed information?
My firebase database looks like this:
Check out ui-router, it makes dynamic routing much easier
https://github.com/angular-ui/ui-router
But if you want to keep what you have, you should pass the event id into your path, like such
$scope.go = function ( path, event ) {
$location.path( path + "/" + event.id );
};
.whenAuthenticated('/events/show/:eventId', {
templateUrl: 'views/eventShow.html',
controller: 'eventShowCtrl'
})
and in your controller, access $stateParams.eventId to load that event.
You should use a variable in your router:
.whenAuthenticated('/events/:id', {
templateUrl: 'views/eventShow.html',
controller: 'eventShowCtrl'
})
Then you can simply use the ID in your function call:
go('events/:id')
Here's a great tutorial (and I highly recommend watching all of both parts).
And you'll have nicer URLs that can be bookmarked.
One you could pass the UID(uid is just an example for user id) onClick
<tr scope="row" ng-repeat="event in events | reverse | filter:filterByUID">
<td>{{event.title}}</td>
<td class="col-md-2">
<div class="btn-group" role="group" aria-label="actions">
<button class="btn btn-primary" ng-click="go('events/show', event.UID)">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
</button>
<button class="btn btn-primary" ng-click="events.$remove(event)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button>
</div>
</td>
</tr>
Then in your js file
$scope.go = function ( path, uid ) {
$location.path( path + "/" + uid );
};
.whenAuthenticated('/events/show/:eventId', {
templateUrl: 'views/eventShow.html',
controller: 'eventShowCtrl'
})
Then to query firebase, say you have a field in your objects called uid, you can use startAT and endAT methods.
See here for example
And here to read more on filtering

Using jquery with dynamically created elements from angular doesnt work

Im trying to use jquery to manipulate elements created by angular, but I am not able to get it to work. I was wondering if anyone could help me out. Thanks
Here is the HTML
<div class="patients">
<tbody ng-repeat="patient in patients">
<tr>
<td>{{patient.name}}</td>
<td>{{patient.number}}</td>
<td>{{patient.date}}</td>
<td id="item-{{$index}}">{{patient.reminded}}</td>
<div class="sendreminder">
<td>
<a href="" class="btn btn-info btn-sm sendreminder" style=" background-color: #00e699; border-color:#00e699; " ng-click="post($index) " "$parent.selected = $index" id="button-{{$index}}">
<span class="glyphicon glyphicon-send"></span> Request Payment
</a>
</td>
</div>
<td>
<a href="" style="text-decoration:none; color:inherit; scale: 4" class="pe-7s-info">
</a>
</td>
</tr>
</tbody>
</div>
Here is the jquery
$(function() {
$('.patients').on('click', ".sendreminder",function(e){
alert('worked');
});
});
ng-repeat recreates DOM everytime it detects changes(and hence, all the attached events will be gone). So to reattach the events after ng-repeat finishes, you can do
<tbody ng-repeat="patient in patients" ng-init="$last && ngRepeatFinish()">
$last will be set to true if its the last item for ng-repeat
and in you controller, create ngRepeatFinish() function
$scope.ngRepeatFinish = function(){
$('.sendreminder').click(function(e){
alert('worked');
});
}
you can also make custom directives for this which is better than this, but this will suffice for a quick solution.
See this for a solution with custom directives
You should call that code immediately after you dynamically create the new element since that code sets the handler for the actual elements (when you call the function) that have class .patients, not the new ones...
i recommend you to use Angular instead of Jquery
added both methods below
//using Jquery
$('.patients').on('click', ".sendreminder", function(e) {
alert('from JQuery');
});
function TestCtrl($scope) {
$scope.patients = [{
name: 'one',
number: 1,
date: '2016-08-16',
reminded: true
}, {
name: 'two',
number: 2,
date: '2016-08-16',
reminded: true
}, {
name: 'three',
number: 3,
date: '2016-08-16',
reminded: false
}];
//using angular
$scope.post = function(i) {
alert('from Angular');
var selectedPatient = $scope.patients[i];
console.log(selectedPatient);
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app>
<div class="patients" ng-controller="TestCtrl">
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
<th>Date</th>
<th>Reminded</th>
<th>Request</th>
<th>Info</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="patient in patients">
<td>{{patient.name}}</td>
<td>{{patient.number}}</td>
<td>{{patient.date}}</td>
<td id="item-{{$index}}">{{patient.reminded}}</td>
<td>
<a href="" class="btn btn-info btn-sm sendreminder" style="background-color: #00e699; border-color:#00e699;" ng-click="post($index)" id="button-{{$index}}">
<span class="glyphicon glyphicon-send"></span> Request Payment
</a>
</td>
<td>
<a href="" style="text-decoration:none; color:inherit; scale: 4" class="pe-7s-info">test
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>

How to access current row data in angular-datatables using templates?

I am using angular-datatables. There is a column Actions which I render using an inline template. I want to access current row data in that template. How do that?
controller
$scope.dtOptions = DTOptionsBuilder.newOptions().withOption('ajax', {
url: '/api/department',
type: 'GET'
})
.withDataProp('data')
.withOption('processing', true)
.withOption('serverSide', true)
.withPaginationType('full_numbers')
.withOption('createdRow', function (row, data, dataIndex) {
return $timeout(function() {
// Recompiling so we can bind Angular directive to the DT
return $scope.$apply($compile(angular.element(row).contents())($scope));
});
})
.withBootstrap();
$scope.dtColumns = [
DTColumnBuilder.newColumn('id').withTitle('ID'),
DTColumnBuilder.newColumn('name').withTitle('Name'),
DTColumnBuilder.newColumn('actions').withTitle('Actions').withOption("searchable", false)
];
view
<div class="hbox hbox-auto-xs hbox-auto-sm" ng-controller="DepartmentsController">
<!-- Inline Template -->
<script type="text/ng-template" id="actions.html">
<button class="btn btn-primary btn-xs"
ng-click="edit(/** CURRENT ROW ELEMENT ID */)"><i class="fa fa-edit"></i> Edit</button>
<button class="btn btn-danger btn-xs"
ng-click="delete()"><i class="fa fa-trash"></i> Delete</button>
</script>
<div class="bg-light lter b-b wrapper-md">
<h1 class="m-n font-thin h3">Departments</h1>
</div>
<div class="wrapper-md">
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-xs-6">
<button class="btn m-b-md btn-md btn-primary " ui-sref="manager.departments.create">
<i class="fa fa-plus"></i> <span class="hidden-sm hidden-xs">Add Department</span></button>
</div>
</div>
<div class="row">
<div class="col-sm-12 m-b-xs">
<table datatable="" dt-options="dtOptions" dt-columns="dtColumns" class="table table-striped b-t b-b">
<thead>
<tr>
<th style="width:20%">ID</th>
<th style="width:60%">Name</th>
<th style="width:20%">Actions</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
Here is plunkr to help you: http://plnkr.co/edit/iAZBof7g6cp68RnM0X8H?p=preview
After hours of struggle, I found the solution. It's quite obvious when you see it.
I created a new scope and added data to it before passing it to $compile in createRow callback. Creating a new scope is necessary to pass unique data to each row. If you simply passed by $scope.row then each row will have the same row equal to the last row processed.
controller
.withOption('createdRow', function (row, data, dataIndex) {
// Create a new scope for each row, otherwise, data will
// not be unique for each row becuase of data bindings
var $newScope = $scope.$new(true);
$newScope.row = data;
// Pass any methods you are using in current scope
$newScope.delete = $scope.delete;
return $timeout(function() {
// Recompiling so we can bind Angular directive to the DT
return $scope.$apply($compile(angular.element(row).contents())($newScope));
});
});
view
<script type="text/ng-template" id="actions.html">
<button class="btn btn-primary btn-xs" ui-sref="manager.departments.edit({id: {{ row.id }} } )"><i class="fa fa-edit"></i> Edit</button>
<button class="btn btn-danger btn-xs" ng-bootbox-confirm="Are you sure you want to delete this department?" ng-bootbox-confirm-action="delete(row.id)"><i class="fa fa-trash"></i> Delete</button>
</script>
I used the above accepted answer, and it worked fine. However, later when we moved to production and the rows per page changed from 20 to 500, I saw significant performance issues through chrome developer tools (Most time spent on hundreds of setTimer and listener events)
I found the official document here which gives us an example as below:
.withOption('createdRow', createdRow);
// ...
function createdRow(row, data, dataIndex) {
// Recompiling so we can bind Angular directive to the DT
$compile(angular.element(row).contents())($scope);
}
This piece of code does not use the $timeout or $apply functions, but still works well. If you run into performance issues as I did, this may help.

Categories

Resources