i'm developing an app for Blackberry OS5 using Phonegap + Zepto + Backbone. In order to do so, I've been using Backbone-forms to handle submition but since OS5 has problems with jQuery version higher than 1.4.4, i moved to Zepto, but the render method on BackboneForms stop working, could anyone help me with it?
Here it's the section where it breaks, for full code please check here https://github.com/powmedia/backbone-forms:
render: function() {
var self = this,
options = this.options,
template = Form.templates[options.template];
//Create el from template - HERE IT BREAKS
var $form = $(template({
fieldsets: '<b class="bbf-tmp"></b>'
}));
//Render fieldsets
var $fieldsetContainer = $('.bbf-tmp', $form);
_.each(options.fieldsets, function(fieldset) {
$fieldsetContainer.append(self.renderFieldset(fieldset));
});
$fieldsetContainer.children().unwrap();
//Set the template contents as the main element; removes the wrapper element
this.setElement($form);
if (this.hasFocus) this.trigger('blur', this);
return this;
},
Thanks in advance!
Zepto, backbone and underscore don't officially support the BB5.0 browser- BB5.0 has mango under the hood unlike BB6.0 upwards which has webkit. In addition to this I can say from my experience that the javascript support in BB5.0 is not great to say the least. So working this one out is a bit like finding a needle in a haystack.
A likely culprit is underscore which is what I see you are using for your templating. Underscore does not seem to handle templates with any kind of logic on BB5.0 (Any loops or if statements). So if you want to do anything more complex than basic data binding in the templates you are going to need to modify underscore or do your own templating solution.
Not sure if this is the kind of answer you wanted but hopefully it helps . . .
EDIT:
Re the underscore templating issue: there seems to be an issue with the native foreach implementation on BB OS5.0. Underscore calls this native foreach if available. Commenting out the following lines in underscore fixed our collection rendering in OS5.0:
/* if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else */
Hope that helps.
Related
This is a generic question don't need the "ready to use" code , but an idea where should invest my time in. I set mustache tag because may be similar.
This is an express (4) application using .doT template where i want to expand with some angular (1.4.3) features.
Doesn't seem possible to me to work with .doT template and angularjs , I'm i right here? .I'm trying get it to work a .html template file and his angular application without success( return a 404 cause don't find a template, but when i use with ejs, it does work ( because it read .html files)
2 . STILL , would like to know in case i'm wrong above. considering this anwer that state is possible to use multiple view engine with consolidate. is possible and how would it be done with angular? (just a hint, doens't need to be the whole implementation)
I know by this answer would be possible to use this below, but also says is better to use a helper, is possible to use helpers in.doT template ? , how?
appModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
considering the above. Which one of 2 and 3 whould you say would be faster?
it would considerably be faster if I change my app to ejs view engine ?
Point by point :
This is incorrect , .doT template works well with Angular, I have use it both, as long as you render a route with dot.
router.get("/blog/new", function(req, res) {
res.render("owner/blog/create");
});
and set a state in your angular provider like
$stateProvider
...
.state("owner-new-blog", {
url: "/blog/new",
templateUrl: "/owner/blog/new",
controller: "CreateUpdateCtrl"
})
There is no point to use consolidate middleware, .doT template works with angular template out of the box, unless you change .doT default parameters.
Yes ... that would be like this , like state in this post
var dT = require('doT');
var tpl = {
tmpl:dT.template('{{=this.helper()}}'),
helper: function(){
/* do what you want to do */
}
}
tpl.tmpl(); // render template
as you can see in this benchmark , .doT is pretty fast if you set double engine in you app , is a bit slowe since you are expanded is functionallity, if you set a helper in doT is less painfull than having two engines .. so , is faster adding a Helper to dot.
there is no point in changing to ejs , dot is pretty fast and an advance template engines which suport layouts and partials. and still , in my opinion, is faster.
I'm doing Angular JS tutorials. I've no clue why this notation have been used. Can anyone explain what is happening here?
Code in the controller.js file:
var vm = this;
vm.openSideBar = openSideBar; // Did not understand this line
function openSideBar() {
$mdSidenav('left').open();
};
The code that you post is purely stylistic and takes advantage of hoisting to enable the bindable functions to be displayed at the top of the file, and the details (implementations) for those to be kept separate and specified further down the file.
Benefits
The benefits of this approach are more apparent as the number of bindable functions in the controller increases:
Easier to read (what's important). Since the purpose of the controller is to support the view and supply it with bindings, the what is more important than the how i.e. What the view can bind to is more important than how the controller goes about it's work. You can see immediately on opening the file what the controller can do for the view.
Easier to find (the details). Having the bindable functions at the top of the file acts as like a contents page for the file. Most IDE's will highlight matching variables if you place the cursor over them and that helps find the implementation details further down the file.
Encourages single responsibility. Your controller should be supplying bindings for a view, and only one view. You can see at the top of your file if you start to build up lots of unrelated bindable functions that serve different purposes that you might be trying to do too much in one controller.
Encourages named functions. This is particularly useful for debugging as it means that you will have less anonymous functions in your application, which in turn makes reading call stacks in developer tools easier.
Further Reading
Check out John Papa's Angular 1 Style Guide which has been approved by the Angular team. You can read all about the thought behind this approach under the heading Bindable Members Up Top in the Controllers section (although it can be applied to factories/services/directives also).
openSideBar was add to view model. it was aviable to run this func from view like <someElement ng-click="$ctrl.openSideBar()"></someElement>
The VM model or way of doing is quite old... before ES6 came along.
Nowadays, I would suggest you learn ES6, take a look at Todd Motto's AngularJS style guide https://github.com/toddmotto/angularjs-styleguide.
But here an answer to your question:
// OLD WAY
function MyAppController() {
var vm = this;
vm.colors = ['red', 'blue', 'green'];
vm.getBlue() {
return vm.colors[1];
}
vm.handleClickEvent(e) {
// this keyword is now the dom element, not your controller
// this is why the VM pattern came to life.
this.classList.add(vm.getBlue());
}
}
// NEW WAY
class MyAppController {
$onInit() {
this.colors = ['red', 'blue', 'green'];
}
getBlue() {
return this.colors[1];
}
handleClickEvent(e) {
// Here this keyword is still your controller, NOT the dom
// element (provided you used angular's ng-click directive
const elem = e.target;
elem.classList.add(this.getBlue);
}
}
With ES6, it is rare that you have to same a reference to this since you can use arrow functions. You should also get familiar with bind(), call(), and apply() methods to control which context you want to apply to functions.
I am using Chartist JS for my charts in my Angular JS app. The issue is I am seeing this here. There is a JS bin that highlights the issue. The author gives a solution for it. The solution is doing DOM manipulations in Jquery which is easy to do. However with AngularJS the way you manipulate the DOM is via Directives. I have created a plunker here which highlights the same issue in Angular JS but I am confused as to how to put the solution provided by author into my Angular code.
Here is the solution
$('[data-tab]').on('toggled', function (event, tab) {
tab.find('.ct-chart').each(function(i, e) {
e.__chartist__.update();
});
});
Edit: As requested the JSFiddle is updated, so what I am trying to do is. I have three different tabs and three different graphs, whenever I click on them I should see the respective graph. To make the tab behavior possible I have written a basic code using scope and model. which facilitates the changing of tabs. The issue is that the chart is getting created for first or default tab but not for the second and third tab. There is a solution given by the author but I don't know how to implement that in AngualrJS
the jQuery solution that you post is basically finding all the chart references and then doing DOM manipulation and call the update() function.
The key is how to find the chart to update in Angular.
In this case, you can assign a variable when you create a chart. For example:
var chart4 = new Chartist.Bar('#chart4', data1);
var chart5 = new Chartist.Bar('#chart5', data2);
Now you have the reference of the chart. All you have to do is to call update() function to render the chart again.
if (value === "allDrivers") {
$scope.tab = "All";
chart4.update();
}
Here is the working plunker
One thing I like to point out is: right now you need to double click the tab in order to see the chart is being rendered or you resize the browser window. I am still trying to find a way to fix this. But at least this approach gives you an idea how to convert the jQuery solution to Angular solution.
I was able to solve this using angular.element() method. So if you wish you use jquery in your angular code. You have to do this via angular.element method. But make sure to include jquery before angular in your index.html
If jQuery is available, angular.element is an alias for the jQuery
function. If jQuery is not available, angular.element delegates to
Angular's built-in subset of jQuery, called "jQuery lite" or jqLite.
I did not know this. From here it was learning for me. Following advice of #pieterjandesmedt from this post. I was able to do this. For other people who want to learn how this works. I have created a GitHub repo which gives a solution to this issue. The link for problem is given in the question. Hope that helps
Are there any inherent problems with using CoffeeScript to code backbone.js apps? Did you run into some problems that you couldn't fix or had to use some particularly clunky workarounds?
No problems, really. At least, none that aren't easily worked around.
The problems with using CS are the same problems you might get using CS anywhere:
Debugging is still done in the generated JS
CS requires a pre-processing step that can be awkward at times
The rest of your team might not know CS
There are some odd things about CS (they introduce "classes" but they are not real classes)
In addition, since Backbone dev with Coffeescript is "class" based, you find yourself wanting to separate your classes into separate files, in separate folders. Because of this, you can get into a situation where the classes are defined out of order. For instance, your collection might get defined before your model, which can't happen. For this, I recommend using something that can manage dependencies (imports). I use coffee-toaster but there are several other options (Rails has dependency management built in to the asset pipeline, for instance)
It is my preferred way to write Backbone code. In my opinion, Backbone.js development is actually better in CoffeeScript than in Javascript. To me, they go together like chocolate and peanut butter. (Not everyone likes chocolate/peanut butter... not everyone likes BB/CS)
Class semantics
Backbone development relies heavily on extending prototypes and this is something that is built-in with CoffeeScript. So, where you would extend a View in JS:
App.Models.MyModel = Backbone.View.extend({
render: function() {
...
}
});
The CS alternative is a native experience:
class App.Models.MyModel extends Backbone.Model
render: ->
...
Overriding Functions
Some things you do often in Backbone, like overriding functions becomes much more simple. In Javascript:
constructor: function ( attributes, options ) {
this.constructor.__super__.constructor.apply( this, arguments );
...
}
Becomes:
constructor: (attributes, options) ->
super
"this" Context Binding
The "fat arrow" in CS is really useful when you need to declare the context of a function is "this"
Javascript sets 'this' differently when functions get called back. There are several ways to solve it, but out of the box, it is awkward:
initialize: function() {
this.model.bind('reset', this.render);
},
render: function () {
this.$el.html("<ul></ul>");
this.model.each(this.renderItem); // <--- Fails on "reset" because 'this' is wrong
return this;
}
CoffeeScript has the => token that will automatically bind 'this' to the function when it gets called by anybody:
initialize: ->
#model.bind 'reset', #render
render: =>
#$el.html '<ul></ul>'
#model.each #renderItem # The fat arrow fixed it for you
#
When all is said and done, Backbone.js code is easier to write and is much easier to read when written in CS. At least, that is my opinion.
Good luck!
Both CoffeeScript and Backbone.js were written by the same author (Jeremy Ashkenas). The backbone-on-rails gem generates CoffeeScript by default. Though certain plugins (such as Backbone-relational, which you mentioned) may require additional setup, Backbone itself plays very nicely with CoffeeScript.
CoffeeScript is just a syntax layer on top of JavaScript. It essentially is JavaScript. Anything you can do in JavaScript you can reproduce in CoffeeScript.
I'm attempting to learn backbone.js and (by extension) underscore.js, and I'm having some difficulty understanding some of the conventions. While writing a simpel search filter, I thought that something like below would work:
var search_string = new RegExp(query, "i");
var results = _.filter(this, function(data){
return search_string.test(data.get("title"));
}));
But, in fact, for this to work I need to change my filter function to the following:
var search_string = new RegExp(query, "i");
var results = _(this.filter(function(data){
return search_string.test(data.get("title"));
}));
Basically, I want to understand why the second example works, while the first doesn't. Based on the documentation (http://documentcloud.github.com/underscore/#filter) I thought that the former would have worked. Or maybe this just reflects some old jQuery habits of mine... Can anyone explain this for me?
I'd guess that you're using a browser with a native Array#filter implementation. Try these in your console and see what happens:
[].filter.call({ a: 'b' }, function(x) { console.log(x) });
[].filter.call([1, 2], function(x) { console.log(x) });
The first one won't do anything, the second will produce 1 and 2 as output (http://jsfiddle.net/ambiguous/tkRQ3/). The problem isn't that data is empty, the problem is that the native Array#filter doesn't know what to do when applied to non-Array object.
All of Underscore's methods (including filter) use the native implementations if available:
Delegates to the native filter method, if it exists.
So the Array-ish Underscore methods generally won't work as _.m(collection, ...) unless you're using a browser that doesn't provide native implementations.
A Backbone collection is a wrapper for an array of models, the models array is in c.models so you'd want to:
_.filter(this.models, function(data) { ... });
Backbone collections have several Underscore methods mixed in:
Backbone proxies to Underscore.js to provide 28 iteration functions on Backbone.Collection.
and one of those is filter. These proxies apply the Underscore method to the collection's model array so c.filter(...) is the same as _.filter(c.models, ...).
This mixing-in is probably what's confusing the "should I use the native method" checks that Underscore is doing:
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
You can use _.filter on a plain old object (_.filter({a:'b'}, ...)) and get sensible results but it fails when you _.filter(backbone_collection, ...) because collections already have Underscore methods.
Here's a simple demo to hopefully clarify things: http://jsfiddle.net/ambiguous/FHd3Y/1/
For the same reason that $('#element') works and $#element doesn't. _ is the global variable for the underscore object just like $ is the global variable for the jQuery object.
_() says look in the _ object. _filter says look for a method named _filter.