How to Register custom handelbars helper in assemble 0.17.1 - assemble

In my assemblefile.js I try to register a custom helper. The helper itself does work since i have it in use in a grunt project with assemble.
assemble: {
options: {
helpers: ['./src/helper/custom-helper.js' ]
}
}
In assemble 0.17.1 I tried it like this but it doesn´t work. Does anyone know how to do this?
app.helpers('./src/helper/custom-helper.js');
custom-helper.js:
module.exports.register = function (Handlebars, options, params) {
Handlebars.registerHelper('section', function(name, options) {
if (!this.sections) {
this.sections = {};
}
this.sections[name] = options.fn(this);
return null;;
});
};

assemble is built on top of the templates module now, so you can use the .helper and .helpers methods for registering helpers with assemble, which will register them with Handlebars. This link has more information on registering the helpers.
Since the templates api is used, you don't have to wrap the helpers with the .register method in your example. You can just export the helper function, then name it when registering with assemble like this:
// custom-helper.js
module.exports = function(name, options) {
if (!this.sections) {
this.sections = {};
}
this.sections[name] = options.fn(this);
return null;
};
// register with assemble
var app = assemble();
app.helper('section', require('./custom-helper.js'));
You may also export an object with helpers and register them all at once using the .helpers method:
// my-helpers.js
module.exports = {
foo: function(str) { return 'FOO: ' + str; },
bar: function(str) { return 'BAR: ' + str; },
baz: function(str) { return 'BAZ: ' + str; }
};
// register with assemble
var app = assemble();
app.helpers(require('./my-helpers.js'));
When registering the object with the .helpers method, the property keys are used for the helper names

Related

Add new data from restful api to angularjs scope

I'm trying to create a list with endless scroll in angularjs. For this I need to fetch new data from an api and then append it to the existing results of a scope in angularjs. I have tried several methods, but none of them worked so far.
Currently this is my controller:
userControllers.controller('userListCtrl', ['$scope', 'User',
function($scope, User) {
$scope.users = User.query();
$scope.$watch('users');
$scope.orderProp = 'name';
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
var promise = user.query();
$scope.users = $scope.users.concat(promise);
}
}, false);
}
]);
And this is my service:
userServices.factory('User', ['$resource',
function($resource) {
return $resource('api/users', {}, {
query: {
method: 'GET',
isArray: true
}
});
}
]);
How do I append new results to the scope instead of replacing the old ones?
I think you may need to use $scope.apply()
When the promise returns, because it isnt
Part of the angular execution loop.
Try something like:
User.query().then(function(){
$scope.apply(function(result){
// concat new users
});
});
The following code did the trick:
$scope.fetch = function() {
// Use User.query().$promise.then(...) to parse the results
User.query().$promise.then(function(result) {
for(var i in result) {
// There is more data in the result than just the users, so check types.
if(result[i] instanceof User) {
// Never concat and set the results, just append them.
$scope.users.push(result[i]);
}
}
});
};
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
$scope.fetch();
}
}, false);

Non-Singleton Services in AngularJS

AngularJS clearly states in its documentation that Services are Singletons:
AngularJS services are singletons
Counterintuitively, module.factory also returns a Singleton instance.
Given that there are plenty of use-cases for non-singleton services, what is the best way to implement the factory method to return instances of a Service, so that each time an ExampleService dependency is declared, it is satisfied by a different instance of ExampleService?
I'm not entirely sure what use case you are trying to satisfy. But it is possible to have a factory return instances of an object. You should be able to modify this to suit your needs.
var ExampleApplication = angular.module('ExampleApplication', []);
ExampleApplication.factory('InstancedService', function(){
function Instance(name, type){
this.name = name;
this.type = type;
}
return {
Instance: Instance
}
});
ExampleApplication.controller('InstanceController', function($scope, InstancedService){
var instanceA = new InstancedService.Instance('A','string'),
instanceB = new InstancedService.Instance('B','object');
console.log(angular.equals(instanceA, instanceB));
});
JsFiddle
Updated
Consider the following request for non-singleton services. In which Brian Ford notes:
The idea that all services are singletons does not stop you from
writing singleton factories that can instantiate new objects.
and his example of returning instances from factories:
myApp.factory('myService', function () {
var MyThing = function () {};
MyThing.prototype.foo = function () {};
return {
getInstance: function () {
return new MyThing();
}
};
});
I would also argue his example is superior due to the fact that you do not have to use the new keyword in your controller. It is encapsulated within the getInstance method of the service.
I don't think we should ever have a factory return a newable function as this begins to break down dependency injection and the library will behave awkwardly, especially for third parties. In short, I am not sure there are any legitimate use cases for non-singleton sevices.
A better way to accomplish the same thing is to use the factory as an API to return a collection of objects with getter and setter methods attached to them. Here is some pseudo-code showing how using that kind of service might work:
.controller( 'MainCtrl', function ( $scope, widgetService ) {
$scope.onSearchFormSubmission = function () {
widgetService.findById( $scope.searchById ).then(function ( widget ) {
// this is a returned object, complete with all the getter/setters
$scope.widget = widget;
});
};
$scope.onWidgetSave = function () {
// this method persists the widget object
$scope.widget.$save();
};
});
This is just pseudo-code for looking up a widget by ID and then being able to save changes made to the record.
Here's some pseudo-code for the service:
.factory( 'widgetService', function ( $http ) {
function Widget( json ) {
angular.extend( this, json );
}
Widget.prototype = {
$save: function () {
// TODO: strip irrelevant fields
var scrubbedObject = //...
return $http.put( '/widgets/'+this.id, scrubbedObject );
}
};
function getWidgetById ( id ) {
return $http( '/widgets/'+id ).then(function ( json ) {
return new Widget( json );
});
}
// the public widget API
return {
// ...
findById: getWidgetById
// ...
};
});
Though not included in this example, these kinds of flexible services could also easily manage state.
I don't have time right now, but if it will be helpful I can put together a simple Plunker later to demonstrate.
Another way is to copy service object with angular.extend().
app.factory('Person', function(){
return {
greet: function() { return "Hello, I'm " + this.name; },
copy: function(name) { return angular.extend({name: name}, this); }
};
});
and then, for example, in your controller
app.controller('MainCtrl', function ($scope, Person) {
michael = Person.copy('Michael');
peter = Person.copy('Peter');
michael.greet(); // Hello I'm Michael
peter.greet(); // Hello I'm Peter
});
Here is a plunk.
I know this post has already been answered but I still think there would be some legitimate scenarios that you need to have non-singleton service. Let's say there are some reusable business logic which can be shared between several controllers. In this scenario the best place to put the logic would be a service, but what if we need to keep some state in our reusable logic? Then we need non-singleton service so can be shared across different controllers in app. This is how I would implement these services:
angular.module('app', [])
.factory('nonSingletonService', function(){
var instance = function (name, type){
this.name = name;
this.type = type;
return this;
}
return instance;
})
.controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){
var instanceA = new nonSingletonService('A','string');
var instanceB = new nonSingletonService('B','object');
console.log(angular.equals(instanceA, instanceB));
}]);
Here's my example of a non singleton service, It's from a ORM im working on. In the example I show a Base Model (ModelFactory) which I want services('users','documents') to inherit and potential extend.
In my ORM ModelFactory injects other services to provide extra functionality(query,persistence,schema mapping) which is sandboxed using the module system.
In the example both user and document service have the same functionality but have their own independent scopes.
/*
A class which which we want to have multiple instances of,
it has two attrs schema, and classname
*/
var ModelFactory;
ModelFactory = function($injector) {
this.schema = {};
this.className = "";
};
Model.prototype.klass = function() {
return {
className: this.className,
schema: this.schema
};
};
Model.prototype.register = function(className, schema) {
this.className = className;
this.schema = schema;
};
angular.module('model', []).factory('ModelFactory', [
'$injector', function($injector) {
return function() {
return $injector.instantiate(ModelFactory);
};
}
]);
/*
Creating multiple instances of ModelFactory
*/
angular.module('models', []).service('userService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("User", {
name: 'String',
username: 'String',
password: 'String',
email: 'String'
});
return instance;
}
]).service('documentService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("Document", {
name: 'String',
format: 'String',
fileSize: 'String'
});
return instance;
}
]);
/*
Example Usage
*/
angular.module('controllers', []).controller('exampleController', [
'$scope', 'userService', 'documentService', function($scope, userService, documentService) {
userService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
username : 'String'
password: 'String'
email: 'String'
}
}
*/
return documentService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
format : 'String'
formatileSize: 'String'
}
}
*/
}
]);
angular only gives a singleton service/factory option.
one way around it is to have a factory service that will build a new instance for you inside your controller or other consumer instances.
the only thing that is injected is the class that creates new instances.
this is a good place to inject other dependencies or to initialize your new object to the specification of the user (adding services or config)
namespace admin.factories {
'use strict';
export interface IModelFactory {
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel;
}
class ModelFactory implements IModelFactory {
// any injection of services can happen here on the factory constructor...
// I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion.
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel {
return new Model($log, connection, collection, service);
}
}
export interface IModel {
// query(connection: string, collection: string): ng.IPromise<any>;
}
class Model implements IModel {
constructor(
private $log: ng.ILogService,
private connection: string,
private collection: string,
service: admin.services.ICollectionService) {
};
}
angular.module('admin')
.service('admin.services.ModelFactory', ModelFactory);
}
then in your consumer instance you need the factory service and call the build method on the factory to get a new instance when you need it
class CollectionController {
public model: admin.factories.IModel;
static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory'];
constructor(
private $log: ng.ILogService,
$routeParams: ICollectionParams,
private service: admin.services.ICollectionService,
factory: admin.factories.IModelFactory) {
this.connection = $routeParams.connection;
this.collection = $routeParams.collection;
this.model = factory.build(this.$log, this.connection, this.collection, this.service);
}
}
you can see it provides opperatiunity to inject some specific services that are not available in the factory step.
you can always have injection happen on the factory instance to be used by all Model instances.
Note I had to strip off some code so I might made some context errors...
if you need a code sample that works let me know.
I believe that NG2 will have the option to inject a new instance of your service in the right place in your DOM so you dont need to build your own factory implementation. will have to wait and see :)
I believe there is good reason to create a new instance of an object within a service. We should keep an open mind as well rather than just say we ought never do such a thing, but the singleton was made that way for a reason. Controllers are created and destroyed often within the lifecycle of the app, but the services must be persistent.
I can think of a use case where you have a work flow of some kind, like accepting a payment and you have multiple properties set, but must now change their payment type because the customer's credit card failed and they need to provide a different form of payment. Of course, this does have a lot to do with the way you create your app. You could reset all properties for the payment object, or you could create a new instance of an object within the service. But, you would not want a new instance of the service, nor would you want to refresh the page.
I believe a solution is providing an object within the service that you can create a new instance of and set. But, just to be clear, the single instance of the service is important because a controller may be created and destroyed many times, but the services need persistence. What you are looking for may not be a direct method within Angular, but an object pattern that you can manage inside your service.
As an example, I have a made a reset button. (This is not tested, its really just a quick idea of a use case for creating a new object within a service.
app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) {
$scope.utility = {
reset: PaymentService.payment.reset()
};
}]);
app.factory("PaymentService", ['$http', function ($http) {
var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/"
function PaymentObject(){
// this.user = new User();
/** Credit Card*/
// this.paymentMethod = "";
//...
}
var payment = {
options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"],
paymentMethod: new PaymentObject(),
getService: function(success, fail){
var request = $http({
method: "get",
url: paymentURL
}
);
return ( request.then(success, fail) );
}
//...
}
return {
payment: {
reset: function(){
payment.paymentMethod = new PaymentObject();
},
request: function(success, fail){
return payment.getService(success, fail)
}
}
}
}]);
Here's another approach to the problem that I was quite satisfied with, specifically when used in combination with Closure Compiler with advanced optimizations enabled:
var MyFactory = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
MyFactory.prototype.foo = function() {
console.log(this.arg1, this.arg2);
// You have static access to other injected services/factories.
console.log(MyFactory.OtherService1.foo());
console.log(MyFactory.OtherService2.foo());
};
MyFactory.factory = function(OtherService1, OtherService2) {
MyFactory.OtherService1_ = OtherService1;
MyFactory.OtherService2_ = OtherService2;
return MyFactory;
};
MyFactory.create = function(arg1, arg2) {
return new MyFactory(arg1, arg2);
};
// Using MyFactory.
MyCtrl = function(MyFactory) {
var instance = MyFactory.create('bar1', 'bar2');
instance.foo();
// Outputs "bar1", "bar2" to console, plus whatever static services do.
};
angular.module('app', [])
.factory('MyFactory', MyFactory)
.controller('MyCtrl', MyCtrl);

Creating new Meteor collections on the fly

Is it possible to create new Meteor collections on-the-fly? I'd like to create foo_bar or bar_bar depending on some pathname which should be a global variable I suppose (so I can access it throughout my whole application).
Something like:
var prefix = window.location.pathname.replace(/^\/([^\/]*).*$/, '$1');
var Bar = new Meteor.Collection(prefix+'_bar');
The thing here is that I should get my prefix variable from URL, so if i declare it outside of if (Meteor.isClient) I get an error: ReferenceError: window is not defined. Is it possible to do something like that at all?
Edit : Using the first iteration of Akshats answer my project js : http://pastie.org/6411287
I'm not entirely certain this will work:
You need it in two pieces, the first to load collections you've set up before (on both the client and server)
var collections = {};
var mysettings = new Meteor.Collection('settings') //use your settings
//Startup
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
collections[doc.name] = new Meteor.Collection(doc.name);
})'
And you need a bit to add the collections on the server:
Meteor.methods({
'create_server_col' : function(collectionname) {
mysettings.insert({type:'collection', name: collectionname});
newcollections[collectionname] = new Collection(collectionname);
return true;
}
});
And you need to create them on the client:
//Create the collection:
Meteor.call('create_server_col', 'My New Collection Name', function(err,result) {
if(result) {
alert("Collection made");
}
else
{
console.log(err);
}
}
Again, this is all untested so I'm just giving it a shot hopefully it works.
EDIT
Perhaps the below should work, I've added a couple of checks to see if the collection exists first. Please could you run meteor reset before you use it to sort bugs from the code above:
var collections = {};
var mysettings = new Meteor.Collection('settings')
if (Meteor.isClient) {
Meteor.startup(function() {
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
eval("var "+doc.name+" = new Meteor.Collection("+doc.name+"));
});
});
Template.hello.greeting = function () {
return "Welcome to testColl.";
};
var collectionname=prompt("Enter a collection name to create:","collection name")
create_collection(collectionname);
function create_collection(name) {
Meteor.call('create_server_col', 'tempcoll', function(err,result) {
if(!err) {
if(result) {
//make sure name is safe
eval("var "+name+" = new Meteor.Collection('"+name+"'));
alert("Collection made");
console.log(result);
console.log(collections);
} else {
alert("This collection already exists");
}
}
else
{
alert("Error see console");
console.log(err);
}
});
}
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Collectionlist = mysettings.find({type:'collection'});
Collectionlist.forEach(function(doc) {
collections[doc.name] = new Meteor.Collection(doc.name);
});
});
Meteor.methods({
'create_server_col' : function(collectionname) {
if(!mysettings.findOne({type:'collection', name: collectionname})) {
mysettings.insert({type:'collection', name: collectionname});
collections[collectionname] = new Meteor.Collection(collectionname);
return true;
}
else
{
return false; //Collection already exists
}
}
});
}
Also make sure your names are javascript escaped.
Things got much easier:
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
db.createCollection("COLLECTION_NAME", (err, res) => {
console.log(res);
});
Run this in your server method.

Was using .bind but now haved to use .delegate... have tried .undelegate?

Heres the jsfiddle, jsfiddle.net/kqreJ
So I was using .bind no problem for this function but then I loaded more updates to the page and found out that .bind doesn't work for content imported to the page but just for content already on the page! Great!
So I switched it up to .delegate which is pretty cool but now I can't figure out how to .bind .unbind my function the way it was???
Function using .bind which worked perfect... except didn't work on ajax content.. :(
$('.open').bind("mouseup",function(event) {
var $this = $(this), handler = arguments.callee;
$this.unbind('mouseup', handler);
var id = $(this).attr("id");
var create = 'nope';
var regex = /\d+$/,
statusId = $('#maindiv .open').toArray().map(function(e){
return parseInt(e.id.match(regex));
});
var divsToCreate = [ parseInt(id) ];
$.each(divsToCreate, function(i,e)
{
if ( $.inArray(e, statusId) == -1 ) {
create = 'yup';
}
});
if( create == 'yup' ) {
if(id) {
$.ajax({
type: "POST",
url: "../includes/open.php",
data: "post="+ id,
cache: false,
success: function(html) {
$('.open').html(html);
$this.click(handler);
}
});
}
}
});
New function using .delegate that is not binded and creates multiple instances?
$('#maindiv').delegate("span.open", "mouseup",function(event) {
var $this = $(this), handler = arguments.callee;
$this.unbind('mouseup', handler);
var id = $(this).attr("id");
var create = 'nope';
var regex = /\d+$/,
statusId = $('#maindiv .open').toArray().map(function(e){
return parseInt(e.id.match(regex));
});
var divsToCreate = [ parseInt(id) ];
$.each(divsToCreate, function(i,e)
{
if ( $.inArray(e, statusId) == -1 ) {
create = 'yup';
}
});
if( create == 'yup' ) {
if(id) {
$.ajax({
type: "POST",
url: "../includes/open.php",
data: "post="+ id,
cache: false,
success: function(html) {
$('.open').html(html);
$this.click(handler);
}
});
}
}
});
I've spent hours trying to figure this out because I like learning how to do it myself but I had to break down and ask for help... getting frustrated!
I also read that when your binding and unbinding .delegate you have to put it above the ajax content? I've tried using .die() and .undelegate()... Maybe I just don't know where to place it?
Take a look at undelegate
It does to delegate what unbind does to bind.
In your case, I think it'd be something like:
$('#maindiv').undelegate("span.open", "mouseup").delegate("span.open", "mouseup" ...
Then you can drop the $this.unbind('mouseup', handler); within the function.

Javascript Plugin Development , Extending Libraries

i got two javascript objects api and myApi :
var api = {};
api.foo = function(){ ... };
api.foo2 = function(){ ... };
var myApi = {};
myApi.foo = function(){ ...};
myApi.myFoo = function(){ ...};
myApi.myFoo2 = function(){ ...};
i want to add all myApi.* functions to api object without overriding api.foo.
Indeed, i want learn how to extend APIs to each other and developing plugins with javascript.
What are the best practices in this subject?
To answer the first part of your question, you can modify the merge method I use in some of my code:
if (typeof Object.merge !== 'function') {
Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
for(var i in o2) { o1[i] = o2[i]; }
return o1;
};
}
To not overwrite preexisting objects, you would have to add a check:
if (typeof Object.merge !== 'function') {
Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
for(var i in o2) { if(!(i in o1)) o1[i] = o2[i]; }
return o1;
};
}