Ember 2. I have a template where I display my model data. And I have a JS script that makes some changes to HTML (inits some JQuery plugins etc), and I need to run it every time I render the view.
I trigger it in the didRender hook of my view. It works fine on the first load. But when I visit the page second time, I can see that plugins are initializing, but in the next moment all changes disappear and the page is as it was initially in the template.
I guess that there is something that looks for changes in the model and re-render the page after it was rendered in the second time, but I'm not sure about it. I tried to listen for other hooks, like didUpdate, but they are not triggering.
What could be a reason of such strange behaviour?
A simple example:
Js:
App.ResumeView = Ember.Component.extend({
didRender: function () {
$('.event h6').text('Hi!');
},
didUpdate: function () {
$('.event h6').text('Hi!');
}
});
Hbs:
{{#each model.work as |work|}}
<div class="event">
<h4>{{work.position}}</h4>
<h5>{{work.name}}</h5>
<h6></h6>
<span class="location">{{work.location}}</span>
<p>{{work.description}}</p>
</div>
{{/each}}
Result: on the first load all H6s say 'hi', then if I go to another page and return to this, it shows 'hi' for a second and then it disappears.
I'm going to bet the problem is because the plugins have initialized already, they aren't initializing again. Try tearing down your jQuery plugins in the willDestroyElement event.
Related
I'm working on a single-page app with Vue.js and its official router.
I have a menu and a component (.vue file) per every section which I load using the router. In every component I have some code similar to this:
<template>
<div> <!-- MY DOM --> </div>
</template>
<script>
export default {
data () {},
methods: {},
route: {
activate() {},
},
ready: function(){}
}
</script>
I want to execute a piece of code (init a jQuery plugin) once a component has finished transitioning in. If I add my code in the ready event, it gets fired only the first time the component is loaded. If I add my code in the route.activate it runs every time, which is good, but the DOM is not loaded yet, so is not possible to init my jQuery plugin.
How can I run my code every time a component has finished transitioning in and its DOM is ready?
As you are using Vue.js Router, it means that each time you will transition to a new route, Vue.js will need to update the DOM. And by default, Vue.js performs DOM updates asynchronously.
In order to wait until Vue.js has finished updating the DOM, you can use Vue.nextTick(callback). The callback will be called after the DOM has been updated.
In your case, you can try:
route: {
activate() {
this.$nextTick(function () {
// => 'DOM loaded and ready'
})
}
}
For further information:
https://vuejs.org/api/#Vue-nextTick
https://vuejs.org/guide/reactivity.html#Async-Update-Queue
You can use
mounted(){
// jquery code
}
Though this may be a bit late... If I guess correctly, the component having problem stays on the page when you navigate between routes. This way, Vue reuses the component, changing what's inside it that needs to change, rather than destroy and recreate it. Vue provides a key attribute to Properly trigger lifecycle hooks of a component. By changing a component's key, we can indicate Vue to rerender it. See key in guide for details and key in api for code sample.
In a Meteor app, a large collection containing 1000 records is published to the client. However users loading the {{loginButtons} will experience a 3-5 second lag as it fully renders only after all the large collection loads.
It appears that the div #login-buttons rendered by {{ loginButtons }} is rendered instantly on page load, but the div #login-dropdown-list is what's taking some time to start rendering. #login-dropodown-list template
The site is using Meteor 0.7.0.1 with Iron Router.
Update
Here's the template code for the accounts-ui-bootstrap-3 dropdown menu that take a few seconds to load after the rest of the page renders. It's just the basic template from the Meteor package, nothing special.
<ul class="nav navbar-nav navbar-right">
{{ loginButtons }}
</ul>
I used to think the problem is due to that this dropdown menu using the Meteor.users collection, so here's my Fast Render route.
FastRender.onAllRoutes(function(urlPath) {
this.subscribe(Meteor.users);
this.subscribe(users);
})
This does not seem to help with the problem. I also found out that Meteor.userId() is already defined when the dropdown menu is still not rendered. The dropdown menu only appears/renders at the point in time pointed to by the red arrow, which is the point where all the collections have loaded.
Furthermore, the div #login-buttons rendered by {{ loginButtons }} is rendered instantly on page load, but the div #login-dropdown-list is what's taking some time to start rendering.
Maybe it's how accounts-ui-bootstrap-3 handles the rendering?
You can use iron-router's waiton on specific routes and loadingTemplate properties to show the user a progress indicator while the subscription gets ready. As seen on https://github.com/EventedMind/iron-router#waiting-on-subscriptions-waiton
Router.configure({
layoutTemplate: 'layout',
notFoundTemplate: 'notFound',
loadingTemplate: 'loading'
});
Router.map(function () {
this.route('postShow', {
path: '/posts/:_id',
waitOn: function () {
return Meteor.subscribe('posts');
}
});
});
Also, https://atmosphere.meteor.com/package/fast-render is a third party package which integates into iron-router and sends down the initial data along with the template therefore making the page appear loaded with data instantly.
There is a good tutorial about this at http://meteorhacks.com/integrating-iron-router-based-apps-with-fast-render.html
I see two other packages that could help you :
Paginated subscription : https://atmosphere.meteor.com/package/paginated-subscription
The idea here would be to set a small limit for the initial load, and when the rest is loaded, change the limit to load the rest
Lazy subscription : https://atmosphere.meteor.com/package/lazy-subscription
The idea here is not to subscribe at all to your large collection in the first time ; only init it like that :
Post = new Meteor.Lazy('post', function() {
Meteor.subscribe('posts');
});
(it does not subscribe to anything)
And then when ready :
Template.some_template.get_posts = function() {
return Post().find({}); //Note that Post is now a function.
};
=> it doe subscribe
The second solution may seem more straight forward, but you can manage it way better with the first one.
First, I'm sure I'm missing a best-practice way to navigate to and load partial views. So, if I'm going about this all the wrong way, please let me know. I have a feeling I'm making this more difficult than it needs to be.
What I'm trying to accomplish is having my web site behave like a single page app. In order to do that, I have many partial views within divs. I hide all divs but the active one. I'm not sure how to run code in a partial view only when it is shown.
Here is the div and partial view that I'm concerned with in this scenario (this is in Index.cshtml):
<div id="app">
#Html.Partial("~/Views/PartialViews/App.cshtml")
</div>
And this is the JavaScript that shows that partial view (also in Index.cshtml):
function showApp(id) {
hideAll();
$('#txtAppId').val(id); // This is how I'm passing data to the partial view
$('#app').show();
}
This works well (at least as far as showing the partial view), but how can I get code in App.cshtml to run only when it is shown like this?
In App.cshtml (the partial view):
<script>
$(function() {
// Note: This will execute as soon as the main page is loaded. I don't want
// code to execute right away, but only when this view is shown.
});
function doSomething() {
// Only when this view is shown do I want code to execute here.
}
</script>
How can I have doSomething() run when the partial view is shown?
Invoke the function after you show the partial view, in index.cshtml
function showApp(id) {
hideAll();
$('#txtAppId').val(id); // This is how I'm passing data to the partial view
$('#app').show();
doSomething(); //Here invoke it.
}
You cannot directly detect when an element changes its visibility and run some script, here you have the control of when it becomes visible so invoke the method once you make it visible.
Recently I have come into a problem where I need to update the page using angular, update the url, but not reload the page. Confused? Let me further explain. I have a table of data (uses ng-grid) and I want it so when I click row 1 angular prints out a one and changes the url to 'currentUrl/1' without reloading the page. Then I click row 4, a 4 prints out and changes the url to 'currentUrl/4'. Is this possible with angular to do this. To make my page not reload every time I clicked a row, I put the following
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
Which it does everything I want, changes url, page doesn't reload, but now angular is not updating that number. What am I doing wrong?
It would help to see the part of the code that prints out the number, but your code for changing the URL without refreshing the route looks fine.
Without more information, a shot in the dark is that your controller is updating a $scope variable but this update isn't getting applied by Angular.
Try wrapping the variable update like so:
$scope.$apply(function() {
$scope.numberToPrint = newNumber;
});
The basic idea behind calling $scope.$apply is that you have to manually coax Angular into updating any bindings listening to the variable. Although this should normally be taken care of, what is possibly happening is that by preventing a route change from occurring you are not actually having Angular digest the new variable.
Here is an article which can explain $apply better.
After a lot of google'ing and reading forums I've not found a suitable answer.
So far all I have found is something like below:
show loading message
call change page
hide loading message
This would work but I would have to do this every time I call load/change page (which is a lot).
Which would leave me either to make a middle man function like below:
function customLoader(url){
showLoader();
$.mobile.changePage(url);
hideLoader();
}
Is there anyway of binding it to the change page event?
So that it shows from the second changePage is called but hides once changePage is away...
I know the above middle man method would work but would like something more tidy and nicer to implement as there's a lot of html/js files.
Something like this:
$('#index').live('pagebeforeshow',function(e,data){
$('#test-button').live('click', function(e) {
$.mobile.showPageLoadingMsg(true);
setTimeout(function () {
$.mobile.changePage('#second');
}, 1000);
});
});
$("#second").live('pageshow', function () {
$.mobile.hidePageLoadingMsg();
});
Timeout is here only so you can see it's working successfully. This is a light example so transition is fired quickly. Remove it in your real code.
And here's an example: http://jsfiddle.net/Gajotres/arrHd/
Every change page event cycle has a order of events occuring when a page A is transiting to a page B. No matter which action is used to trigger a change page you can always disable it when page B i successfully loaded. If you want to find more about page load order take a look at this link: https://stackoverflow.com/a/14010308/1848600. There you will find a lot about jQM page dynamics.
In case you want to implement this into every page transition use this:
$('[data-role="page"]').live('pageshow', function () {
$.mobile.hidePageLoadingMsg();
});
This will hide a ajax loader (if it is open) every time a different page is successfully loaded and shown.
Maybe it's too much for many, but I found a solution different than the written in the comments of this question.
I use the jquery mobile router and in the 'show' event of a page, I do $.mobile.loading("show");, so when the page appears it does with the loading spinner showing.
I use Jquery Mobile Router for a lot more, but it solved this issue.
Though to hide the spinner, I had to use $('.ui-loader').hide();, which is weird, I know...
(Maybe just listening to the proper event and triggering the spinner would also work, as this is what JQMR does...)
I'm using JQM 1.4.2...