I'm just getting started with backbone, and I have run into a very confusing wall.
What I am doing:
Create a model instance of type A that calls fetch during initialization.
Create a model instance of type B that calls fetch during initialization.
Create a view that uses data from both.
Bind the sync event from both of the models to view.render(), and create a table once both AJAX calls have returned (render will be called twice, I know - not a big deal).
What I am expecting is that when the second sync event hits, the table is rendered using data from both.
What is actually happening is that the sync event is firing, but the model data is completely empty when the view tries to use it (I can check the model.cid and see that it is the same model that I created initially, it just contains absolutely nothing). I have logging so that I can tell which event is firing from which model, and I can see render being called twice as expected.
I can add a debug button to my page that calls the same render manually (once both AJAX calls have completed), and everything renders just fine, so I know both my AJAX calls were ultimately successful and did eventually result in a fully populated model.
What am I doing wrong? Am I fundamentally misunderstanding what an event is supposed to do here? Why would my model be completely empty after a sync event?
Dave
Apologies for the rather poor question, replete with its lack of actual code. I went through and re-wrote everything from scratch, made it work, then went back through my original code line by line to figure out why it didn't work.
Let's just say that there is a big difference between this:
this.listenTo(user, 'sync', this.render("userchange"));
and this:
this.listenTo(user, 'sync', this.render);
I need to read up on on "passing a function" vs "running a function".
Dave
Related
We have a Jura plugin written by ourselves, and in it's vm template at the very end there's the following code:
AJS.$(window).ready(function(){
doSomeThing();
});
Inside of this method we are loading some server side data and initializing internal js objects. For some strange reason this specific method doSomeThing is being called twice. Moreover, vm template is also being called twice, owerwriting first template initialization state (but template may be already initialized and contain some data at this point). I don't get it why it's made this way and how to work around this. If someone faced similar thing before and knows what to deal with it - please respond. Much appreciated.
We've found a reason of such bahavior - it's Backbone. Jira creates an element using Backbone View, which calls AJS.$().ready second time during initialization. We stopped usage of this element after our investigation
Here is the code:
var view = Backbone.View.extend({
el:'#comment',
template:'',
initialize:function(){
this._getTemplate();
this.render();
},
event:{
'click #submit_btn' : 'submit'
},
_getTemplate:function(){
$.ajax({
...
});
},
render:function(){
this.$el.html(this.template);
},
submit:function(event){
alert('click');
}
});
I use ajax to get html template from server and it works well. I have no problem in loading template.
Here is the div which I wanna insert the template into.
<div id="comment">...</div>
Here is the template. I just show the simple structure.
<div>...</div>
<button id="submit_btn">submit</button>
<div>...</div>
Can someone help me to solve it?
The event not firing because when the event is attempted to bind to the DOM element (i.e. at the time of the View construction), the DOM element does not exist. From the Backbone docs:
Backbone will automatically attach the event listeners at instantiation time, right before invoking initialize.
Since the #submit_btn DOM element matcher returns nothing, no event is bound.
Your technique of fetching the template within the initialize() method will be problematic. In most OO-based development patterns, the Constructor of the object should not do a lot of work, and the returned instance should be fully initialized when the Constructor completes. Backbone Views are no different in this respect. Your Constructor as written here attempts to do quite a bit of work, but there is no benefit to fetching the template as a part of the object creation here. In fact there is some harm too: what if you end up creating more than one instance of your View? You would end up fetching the template more than once, even though the contents would be the exact same.
Forcing an XHR of any kind to be synchronous is also going to be problematic. During a synchronous request the browser's main thread is busy waiting for the response. In this example if the template ever takes longer than a few hundred milliseconds to arrive, your application will appear "frozen" to the user. This provides a very poor user experience. Asynchronous code may feel like it's more complex, but if you are building a JS-based application of even minimal complexity you will be required to deal with it. Since Javascript is (mostly) single-threaded, asynchronous execution becomes an essential tool of any application.
A better solution is to either embed the template into the hosting HTML page content, or use a JS modularization technique (RequireJS, CommonJS, others) to effectively package the template up with the module that needs it.
It looks like the event string is set up correctly and the render function is doing the right thing. The one issue I see is that the 'initialized' function should be 'initialize' as per the Backbone View Extend documentation.
Reference: http://backbonejs.org/#View-extend
I am learning AngularJS and having a weird problem whereby sometimes if I make a change in my JS files, the change doesn't apply.
I can see the GET request to the file through the console however it still contains old content. I can even remove everything inside of the file and the application still shows old content. It's only when I delete the file that it recognizes that something is wrong.
Is there some sort of caching going on that I need to know about?
I am using Laravel 5.1 however at this point, Laravel is really only handling the routing at this point.
Eventually, the changes come through. Am I going crazy or is this one of the gotcha's I should know about with AngulasJS?
Try:
$scope.$apply()
The way angularjs works is when one of the binded fields (ng-model) is changed in the browser, angularjs updateds the data model in the scope (controller or directive).
If you have code with callbacks when the new data is to be assigned to the data model, you'll need to run $scope.$apply(). This statement tells anguarljs to rebind the data model to the view (html).
Depends on where you have the $apply statement, you might want to test for $scope.$$phase because if the digest/apply is already in process and you run $apply, you'll get a Javascript run time error.
I am an Angular newbie and I'm stuck. I have some code on jsbin.com --->(http://jsbin.com/ratageza/7/edit?html,js,output)
It's not pretty but it's showing essentially what I am doing.
In my real code I am using $http.get calls a RESTful backend to load data from a database, and $http.post calls to add to the database. All works fine as far as the database fetches and updates. But after I create a new object using the "Create" form, if I click "List All" my tournaments object is not updated.
I'm confused about whether I would need $apply after the $http.post call? I've tried using it but am either using it wrong or that's not my problem here. I've looked all around stackoverflow and can't seem to find the answer to my problem.
If anybody can see what I'm doing wrong I would really appreciate the help.
Thanks!
Check out the slight changes I made here: http://jsbin.com/ratageza/8/edit?html,js,output
The change is how you define your controller usage. You use TournamentController in two different places which actually gives you two instances of that controller with two separate, isolated scopes. Your List All view was bound to one instance while the Create view was bound to a completely different instance.
Also, your question about $apply. As a general rule, you should very seldom have to use $apply and you should NEVER use it in a controller as it's not safe to use there. Directives are really the only safe place to use $apply and even there it should only be used if data is being modified outside the scope of angular. The order in which things are processed, you find that if you use $scope.$apply() in a controller, you will frequently get exceptions about already being in a digest cycle.
http://jsbin.com/ratageza/10/edit this works.
It's not a $apply or $diget issue. Because you put TournamentController into to part of views. One is create and the other one is to show. That makes a different copy of $scope.tournament;
So I add a div to wrapper the Form and the table. Put the controller to it. Remove the form and table controller.
Hope this can solve your problem. :)
I'm working on a webpage based on AngularJS and sometimes I need to change the path (the shebang if you prefer it). The thing is that sometimes $location.path("/my_path_here") works, but sometimes I need to call $scope.$apply() after calling $location.path to make the webbrowser switch to the new path.
Why is this happening?
EDIT:
Example http://pastebin.com/d1SjfCHd
Take a look at this question and Misko's answer: How does data binding work in AngularJS?
That answer explains the process in technical great detail. So, I'm gonna tell it in layman's terms.
AngularJS makes itself work by using dirty checking, there are sets of values that angular is watching. Everytime something big happens, (a click, a function call in controller), angular runs a digest cycle, comparing the watched values to see if there is any change. If there is a change, depending on the watcher, angular will take necessary action (update view, fire a callback, update route).
When you use default directives and no jquery event handling in controller, you will be fine.
However, if you do, you need to know that you are NOT in the angular's digest cycle. Which means the watchers will not fire until a digest occurs.
You need to know when are you updating a variable that is being watched. Mostly it is custom DOM event (or jquery events). When it is the case, you need to let angular know that something is changed and it needs to re-check what happened (ie. update watchers).
This is by doing scope.$apply() after you have changed something.
Bear in mind that you cannot run an $apply() if you are already in the angular's digest cycle. It will throw an error like $digest already in progress.