how to share behaviour between ractive components - ractivejs

Perhaps this is more of a basic javascript question than particular to Ractivejs.
How can I add shared behaviour to Ractive components... for example, if several components have a strip_whitspace() method, how can I avoid duplicating it in each component?
thanks in advance for any javascript or Ractivejs insight you can offer.

The usual way to do it is to use extend, which pretty much looks like how you'd inherit in traditional OOP. Under the hood, it uses prototypal inheritance.
var CommonClass = Ractive.extend({
// define common stuff
strip_whitspace : function(){...}
});
var FirstClass = CommonClass.extend({
// FirstClass stuff
});
var SecondClass = CommonClass.extend({
// SecondClass stuff
});
If you prefer a more compositional approach where you build the object on the fly rather than impose inheritance, you can always use an extend function from other libs, like lodash's defaults. I prefer this method because I can inherit from more than one common object.
var UtilsMixin = {
strip_whitspace : function(){...}
};
var AnotherMixin = {...};
var FirstClass = Ractive.extend(_.defaults({
// FirstClass stuff
}, UtilsMixin));
var SecondClass = Ractive.extend(_.defaults({
// SecondClass stuff
}, UtilsMixin, AnotherMixin));

You can also look at Frequently Used Expressions in RactiveJS. If you use a particular expression frequently, you can add that expression to Ractive's default data, like so:
var helpers = Ractive.defaults.data;
helpers.fromNow = function(timeString){
return moment(timeString).fromNow()
}
helpers.formatTime = function(timeString){
return moment(timeString).format("ddd, h:mmA");
}
helpers.humanizeTime = function(timeString){
return moment.duration(timeString).humanize();
}

Related

How to access child context in parent templates?

If child template defined something in onCreated,
Template.test.onCreated(function() {
this.xxx = 'test';
});
And I want to access child context in parent. How can I do that?
You can use Session or ReactiveVar for this operation. But if you need to save it on the child template I think you could use:
var childView = Blaze.getView(currentTemplate.find('#your-child-theme-id'));
and then get template instance by:
var childTemplateInstance = childView._templateInstance;
and then
var myXXXvar = childTemplateInstance.xxx;
But this is a little bit dirty :/ I don't think there is a Meteor API for this particular use case. I think you should play with reactive vars or client only collections etc. I don't know exactly what you want to achieve.

How can I access DOM Elements declared within a template outside Template.myTemplate.events, Template.myTemplate.rendered etc

I have a template with few elements (input, radioButton etc). If I want to access to these DOM elements within mytemplate I can either access them within events
Template.myForm.events({
'click #submitButton' : function (event, template) {
//template variable here gives me access to the
//current template instance, so I can get to any
//DOM element within this template.
}
})
OR within
Template.myForm.rendered = function () {
//within this function I have access to "this" which points to template instance
}
I was wondering if there is a way to access the DOM Elements that a declared within a template outside of these event functions and rendered callback?
Thanks in advance
You can but you need to reference the template instance.
The reason for this is a single template can be used multiple times. In this case a single easy to use way to access the template would not know which instance it would belong to. This is why you need to use a reference, such as done in the example below.
You have to store the instance somewhere when it is rendered:
TheTemplateInstance = null;
Template.myForm.rendered = function() {
TheTemplateInstance = this;
}
Then you can use TheTemplateInstance anywhere you want, provided the template is on the DOM.
If you use myForm many times then it will only have access to the one created last.
Also You did not give a use case for your intentions. But there are several better ways to do most things with a template:
JQuery modding something when some variable changes (the most common use case where helpers aren't useful)
Template.myForm.rendered = function() {
var self = this;
this.autorun(function() {
var xx = something.findOne();
self.$("something").autoform() //Some jquery call
});
}
and helpers:
Template.myForm.helpers({
someName: function() {
return Session.get("name");
}
});
You can then use {{someName}} in your template's html where it can change when you use Session.set("name", "a new value");

Iron-Router RouteController Inheritance: Why does the Parent Controller's Hook run before the Child's?

I've been working with the fantastic Iron-Router package (0.7.1) for Meteor (0.8.1.3) and have run into something that seems somewhat counter-intuitive. I have provided an example below.
The following code was written in context of the Iron-Router's provided Tinytests.
https://github.com/EventedMind/iron-router/blob/devel/test/both/route_controller.js
var Parent = RouteController.extend({
onBeforeAction: function(pause) {
console.log('I\'m in the parent!');
pause();
}
});
var Child = Parent.extend({
onBeforeAction: function(pause) {
console.log('I\'m in the child!');
pause();
}
});
var inst = new Child(Router, route, {});
inst.runHooks('onBeforeAction');
The test resulted in the Child printing out "I'm in the parent"
I had expected for the Child to print out "I'm in the child"
I feel like with Object Oriented Programming, it would be more natural for the Child's onBeforeAction to override the Parent's.
That being said, if that is intentional, how can I subvert the order of the hooks and have only the Child's onBeforeAction run?
It looks like it is intentional:
https://github.com/EventedMind/iron-router/blob/devel/lib/route_controller.js#L97
// concatenate together hook arrays from the inheritance
// heirarchy, starting at the top parent down to the child.
var collectInheritedHooks = function (ctor) {
var hooks = [];
if (ctor.__super__)
hooks = hooks.concat(collectInheritedHooks(ctor.__super__.constructor));
return Utils.hasOwnProperty(ctor.prototype, hookName) ?
hooks.concat(ctor.prototype[hookName]) : hooks;
};
If you don't want the parent hook to run, it looks like you'll have to skip using inheritance and do something like mixin common functionality into the various controllers.

Render a Backbone.js collection

I am a Backbone.js n00b and trying to get my head around it. I know how to render a model using a view and the built-in underscore.js templating engine. Now I'm trying to render a collection and that's where I get stuck. There is no server here, so I'm not fetching anything remotely, just a simple HTML page with some JavaScript.
ContinentModel = Backbone.Model.extend({});
ContinentsCollection = Backbone.Collection.extend({
model: ContinentModel,
initialize: function () {
this.continentsView = new ContinentsView;
this.bind("reset", this.continentsView.render);
}
});
ContinentsView = Backbone.View.extend({
el: '#continents',
template: _.template($('#continents-template').html()),
render: function() {
var renderedContent = this.template(this.collection.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
$(function() {
var continentsCollection = new ContinentsCollection();
continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
});
It breaks on the template attribute line in the view but I'm not sure that's where I need to look. Am I supposed to render a collection or do I miss the point completely here (maybe collections are just grouping objects and I shouldn't look at it as a list I can render)?
Thanks for helping...
The problem is that when you define ContinentsView, the template is evaluated and it uses $('#continents-template') - but the DOM is not ready yet, so it does not find the template.
To solve it, simply move the template assignment in the initialize function:
ContinentsView = Backbone.View.extend({
el: '#continents',
initialize: function() {
this.template = _.template($('#continents-template').html());
}
...
Regarding collections, yes, they are grouping objects, specifically sets of models.
You should make the code so the models (and collections) do NOT know about the views, only the views know about models.
ContinentModel = Backbone.Model.extend({});
ContinentsCollection = Backbone.Collection.extend({
model: ContinentModel,
// no reference to any view here
});
ContinentsView = Backbone.View.extend({
el: '#continents',
initialize: function() {
this.template = _.template($('#continents-template').html());
// in the view, listen for events on the model / collection
this.collection.bind("reset", this.render, this);
},
render: function() {
var renderedContent = this.template(this.collection.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
$(function() {
var continentsCollection = new ContinentsCollection();
continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
// initialize the view and pass the collection
var continentsView = new ContinentsView({collection: continentsCollection});
});
It is also worth noting there are additional complexities that quickly rear their heads when rendering a collection in a view. For instance, the view generally needs to be re-rendered when models are added or removed from the collection. It isn't rocket science to implement your own solution, but it is probably worth looking into existing solutions since there are quite a few tried and tested ones out there.
Backbone.CollectionView is a robust collection view class that handles selecting models in response to mouse clicks, reordering the collection based on drag and drop, filtering visible models, etc.
Several popular frameworks built on top of backbone also provide simple collection view classes, like Backbone.Marionette, Chaplin, and Layout Manager.
Even though Backbone itself does not provide any structure for rendering a collection, it is a non-trivial problem and lots of people have different opinions on how it should be done. Luckily it is such a common need that there are quite a few good options already in the eco system.

Looking for CSS parser written in AS3

I need to load and apply CSS at runtime in my Flex app. I know that the adobe docs say that you need to compile the CSS before loading it but I would like to find a work around.
I know that you can set individual styles like this:
cssStyle = new CSSStyleDeclaration();
cssStyle.setStyle("color", "<valid color>);
FlexGlobals.topLevelApplication.styleManager.setStyleDeclaration("Button", cssStyle, true);
I was planning on parsing a CSS file and appling each attribute as above.
I was wondering if:
Adobe had a CSS parser library that I could use
Someone else had a CSS parser that I could use
If I write my own CSS parser what I should watch out for
I know that the adobe flex.text.StyleSheet class has a CSS parser but I could not find a way to harness that. (Is there a way to get that source code?)
Edit: This solution does not work. All selectors that are taken out of the parser are converted to lowercase. This may work for your application but it will probably not...
I am leaving this answer here because it may help some people looking for a solution and warn others of the limitations of this method.
Although it was not intended for this it is possible to use the StyleSheet class to parse the CSS. I am currently investigating how robust this is currently but for the most part it appears to be working.
public function extractFromStyleSheet(css:String):void {
// Create a StyleSheet Object
var styleSheet:StyleSheet = new StyleSheet();
styleSheet.parseCSS(css);
// Iterate through the selector objects
var selectorNames:Array = styleSheet.styleNames;
for(var i:int=0; i<selectorNames.length; i++){
// Do something with each selector
trace("Selector: "+selelectorNames[i];
var properties:Object = styleSheet.getStyle(selectorNames[i]);
for (var property:String in properties){
// Do something with each property in the selector
trace("\t"+property+" -> "+properties[property]+"\n");
}
}
}
I had similar problem but more precisely i want the completely avoid the compilation because my application is wrapper by ActiveX used by a custom exe file and i let the software distributor to customize their skin.
In practice we put the <fx:Style> outside the application. To avoid low level parsing on the string we had transformed the Style Sheet in an XML:
<styles>
<namespace name="myNs" value="com.myComponent">
<declaration selector="myNS|Button#myselector:over #mysubselector">
color:#ffffff;
font-size:bold
</declaration>
... other styles
</styles>
Beside the security considerations about let the user know your components you can load the XML and create a CSSStydeclaration.
Splitting and parsing only the selector let you create a series of CSSCondition and CSSSelector to add to your CSSStyleDeclaration. To parse the selector we use a little loop which search "#",":" and "." and split the string mantaining the sequence of the found CSS conditions.
var selectors:Array = [];
// first selector
var conditions:Array = [
new CSSCondition(CSSConditionKind.ID, 'myselector');
new CSSCondition(CSSConditionKind.PSEUDO, 'over');
];
// here you have to find and expand the namespace
ancestor:CSSSelector = new CSSSelector('com.myComponent.Button', conditions);
selectors.push(selector);
// second selector
var conditions:Array = [
new CSSCondition(CSSConditionKind.ID, 'mysubselector');
];
selector:CSSSelector = new CSSSelector('', conditions, ancestor);
selectors.push(selector);
// Empty style declaration
new CSSStyleDeclaration(selectors, styleManager, false);
Then you can parse CSS properties by parseCSS() with the function created by #sixtyfootersdude, but using a fake selector:
var myCSS:String = "#fake " + "{" + cssTextReadedFromXML + "}";
var style:StyleSheet = new StyleSheet();
sheet.parseCSS(myCSS);
// here you have your parsed properties
var list:Object = sheet.getStyle('#fake');
Then you can add the properties to the CSSStyleDeclaration and apply them by the setStyle method and apply the declaration as in your example.
Less or more is how I've tryed to resolve this.

Resources