I've got the following routes:
routes: {
"home": "login",
"login": "login",
"forum": "forum",
"group/:id/:group": "group",
"": "login",
}
if i'm on page forum the URL is url.com/#forum, but if I navigate from the forum view to the group view with:
router.navigate("group/:123/:nicegroup", {trigger: true});
problem: then the url changes to "url.com/group/123/nicegroup". In the meantime the "" route will be called because there is no "#" I think and the empty route "" fires. I end up on the login page.
problem: if I delete the "" route, then navigating from #forum to page group with:
router.navigate("group/:123/:nicegroup", {trigger: true});
the url changes to url.com/group/123/nicegroup without the # in front of group. It works fine, but if I go back pushing the browser's back button, the url changes to url.com/#forum again and nothing happens. I still stay on the group page and I get the error:
Uncaught TypeError: Cannot call method 'indexOf' of undefined jquery.mobile-1.1.0.js:3791
$.extend.resetUIKeys jquery.mobile-1.1.0.js:3791
$.extend.hashValueAfterReset jquery.mobile-1.1.0.js:3803
$.extend.onPopState jquery.mobile-1.1.0.js:3863
jQuery.event.dispatch require-jquery.js:5385
jQuery.event.add.elemData.handle.eventHandle
Again, in the second problem I already deleted the "" route, so that it doesn't bother me but it's still my 1st problem.
I disabled all jqm navigation.
$.mobile.ajaxEnabled = false;
$.mobile.linkBindingEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
This question was asked a while ago - but have you enabled Backbone.History when your application starts?
Backbone.history.start()
Related
I am using jquery mobile and I am using $.mobile.changePage( "#newpage"); option when the user authentication is done to move to next page. in the next page I have a logout button and when user clicks on that it has to logout and on success it has to come back to the login screen again.
WL.Client.logout('CustomAuthenticatorRealm',{onSuccess: WL.Client.reloadApp})
in this code onsuccess it is reloading the same url. i tried to change it like onSuccess: $.mobile.changePage( "#loginpage");
but it is not working. any suggestions please
Hi I got it working ..
Instead making a page change I used onSuccess: WL.Client.reloadApp and for all the pages where I am using the load page function i have added changeHash: false so that the same url follows till the end . now it works fine as I expected
I am not sure about that #loginpage bit, but that depends on your multiple pages implementation.
Anyway, try this instead as the onSuccess callback:
$.mobile.changePage("#loginpage", { changeHash: false });
I have a simple controller that I have responding to both html and json. I'm using the json response for a Backbone app. Everything works as expected, except that when I click a link that uses the show method, and then click the back button, the index method just prints a big string of JSON into the browser. If I refresh, it displays HTML as expected. Here's the controller.
class RecipesController < ApplicationController
def index
#user = User.find(params[:user_id])
#recipes = Recipe.all
respond_to do |format|
format.html
format.json { render json: Recipe.where(user_id: params[:user_id]).featured }
end
end
...
end
I tried adding a check for response.xhr?, and only rendering JSON if it was an AJAX request, but that didn't work.
Edit
This is a Rails 3 app not utilizing turbolinks.
Edit 2
Here is the relevant Backbone code.
# app/assets/javascripts/collections/recipe_list_.js.cofee
#App.Collections.RecipeList = Backbone.Collection.extend
url: ->
"/users/#{#userId}/recipes"
model: window.App.Models.Recipe
initialize: (opts) ->
#userId = opts.userId
# app/assets/javascripts/app.js.coffee
$ ->
urlAry = window.location.href.split('/')
userId = urlAry[urlAry.length - 2]
App = window.App
App.recipeList = new App.Collections.RecipeList(userId: userId)
App.recipeListView = new App.Views.RecipeListView
If you're referring to a chrome and turbolinks issue, then an easy fix is to disable caching on ajax requests:
$.ajaxSetup({cache: false})
you could try using /recipes.html
and /recipes.json
and /recipes/1.html and /recipes/1.json
instead of relying on backbone and history to always send the correct headers
I bet it's due to turbolink, or ajax based page rendering (backbone, remote=true, ...)
I always disable turbolink and keep control over which links are remote=true, and for all ajax response I insert this javascript line at the end
history.pushState(null, '', '/the/requested/url' );
If you don't want to manually implement this line for each of your link responses, you can wrap it in an ajax:complete event (more info), and I assume turbolink has an event you can use as well.
Second part of the trick is to bind popstate so when your users click on the "back" button the page will be refreshed from the server (through the url that was pushState-ed earlier) and the ajax/js/json/whatever response won't be displayed anymore.
setTimeout( function () {
$(window).bind('popstate', function () {
window.location = location.href;
});
}, 500);
As you see I wrap the popstate event binding in a setTimeout, because if you don't do that you may have trouble with some browser that would infinitely refresh the page.
Are you using Chrome? if so this is a known issue. When you hit the back button chromes serves the page from cache since what was returned was json that is what it dumps on the screen. This post has some suggested workarounds
https://code.google.com/p/chromium/issues/detail?id=108766
I'm building an app which has layout like below.
I want to create a new post so I pressed 'new post button' and it took me to 'posts/new' route.
My PostsNewRoute is like below (I followed the method described here)
App.PostsNewRoute = Ember.Route.extend({
model: function() {
// create a separate transaction,
var transaction = this.get('store').transaction();
// create a record using that transaction
var post = transaction.createRecord(App.Post, {
title: 'default placeholder title',
body: 'default placeholder body'
});
return post;
}
});
It immediately create a new record, updates the list of the posts, and displays forms for new post.
(source: sunshineunderground.kr)
Now I have two problems.
One is the order of post list.
I expected new post will be on top of the list, but It's on the bottom of the list.
I'm using rails as my backend and I set the Post model order to
default_scope order('created_at DESC')
so old Post sits below within existing posts. but newly created one is not. (which isn't commited to backend yet)
Another is When I click created post in the list
I can click my newly created post in the posts list. and It took me to a post page with URL
/posts/null
and It's very weird behavior that I must prevent.
I think there will be two solutions.
When I click 'new post button' create a record and commit to server immediately and when server successfully saved my new record then refresh the posts list and enter the edit mode of newly created post.
or initially set Route's model to null and create a record when I click 'submit' button in a PostsNewView.
or show show only posts whose attribute is
'isNew' = false, 'isDirty' = false,
in the list..
But sadly, I don't know where to start either way...
for solution 1, I totally get lost.
for solution 2, I don't know how to bind datas in input forms with not yet existing model.
for solution 3, I totally get lost.
Please help me! which will be ember's intended way?
(I heared that Ember is intended to use same solution for every developer)
Update
now I'm using solution 3 and still having ordering issue. Here is my Posts template code.
<div class="tools">
{{#linkTo posts.new }}new post button{{/linkTo}}
</div>
<ul class="post-list">
{{#each post in filteredContent}}
<li>
{{#linkTo post post }}
<h3>{{ post.title }}</h3>
<p class="date">2013/01/13</p>
<div class="arrow"></div>
{{/linkTo}}
</li>
{{/each}}
</ul>
{{outlet}}
Update
I solved this problem by filtering 'arrangedContent' ,not 'content'
App.PostsController = Ember.ArrayController.extend({
sortProperties: ['id'],
sortAscending: false,
filteredContent: (function() {
var content = this.get('arrangedContent');
return content.filter(function(item, index) {
return !(item.get('isDirty'));
});
}).property('content.#each')
});
We use a variation of solution 3 in several places on our app. IMHO it's the cleanest of the 3 as you don't have to worry about setup/teardown on the server side This is how we implement it:
App.PostsController = Ember.ArrayController.extend({
sortProperties: ['id'],
sortAscending: true,
filteredContent: (function() {
return this.get('content').filter(function(item, index) {
return !(item.get('isDirty'));
});
}).property('content.#each')
});
Then in your Posts template you loop through controller.filteredContent instead of controller.content.
For solution 1, there are many possibilities. You could define the following event:
createPost: function() {
var post,
_this = this;
post = App.Post.createRecord({});
post.one('didCreate', function() {
return Ember.run.next(_this, function() {
return this.transitionTo("posts.edit", post);
});
});
return post.get("store").commit();
}
It creates the post then sets up a promise that will be executed once "didCreate" fires on the post. This promise transitions to the post's route only after it has come back from the server, so it will have the correct ID.
Indeed, very nice write up. Thx for that.
Doesn't your filteredContent have to use the isNew state i.o. isDirty, otherwise the Post that is being edited will not be visible.
In either case, the filteredContent property does not work in my case. I also noticed that, since I use an image as part of every element, all images will be refreshed when filteredContent is changed. This means that I see a request for every image.
I use a slightly different approach. I loop through the content and decide whether or not to display the Post in the template:
# posts.handlebars
<ul class='posts'>
{{#each controller}}
{{#unless isNew}}
<li>
<h3>{{#linkTo post this}}{{title}}{{/linkTo}}</h3>
<img {{bindAttr src="imageUrl"}}/>
<a {{action deletePost}} class="delete-post tiny button">Delete</a>
</li>
{{/unless}}
{{/each}}
</ul>
This will only show the Post object after it is saved. The url in the H3-tag also contain the id of the newly created object i.o. posts/null.
One other thing I noticed in your question: instead of passing default values to createRecord, you can use the defaultValues property on the model itself:
So, instead of:
# App.PostsNewRoute
var post = transaction.createRecord(App.Post, {
title: 'default placeholder title',
body: 'default placeholder body'
});
you can do this:
# App.Post
App.Post = DS.Model.extend({
title: DS.attr('string', {
defaultValue: "default placeholder title"
}),
body: DS.attr('string', {
defaultValue: "default placeholder body"
})
});
# App.PostsNewRoute
var post = transaction.createRecord(App.Post);
I actually wrote a function to reverse the content array a while back:
http://andymatthews.net/read/2012/03/20/Reversing-the-output-of-an-Ember.js-content-array
That's a pretty old article, almost a year old, so it probably won't work as is, but the framework is there...
You can see it in action in this mini-app I wrote:
http://andymatthews.net/code/emberTweets/
Search for users in the input field at the top and watch the left side as they appear in order from newest to oldest (instead of oldest to newest).
I have a Rails app (using Authlogic for authentication) with a simple jquery animation that I only want to run once upon first page load. I know that the key is to check cookies or something to see if the user has visited the page before. Please forgive my n00bness, I know little to nothing about HTTP cookies or sessions.
So, what's the best way to see if a visiting user (even if they haven't logged in) is viewing a page for the first time?
EDIT: Ok, so I realize I wasn't being entirely clear.
I've spent hours looking at similar questions and reading the Rails API Docs for cookies and sessions and I still can't visualize how to implement a visited? function for each page in my site that will only be set to "true" after the user has visited the page the first time. I looked at the supposed "duplicate" question Rails Detect If User's Very First Visit and the respective answers and still can't figure it out.
Here's my "Pages" controller:
def home
#title = "Home"
end
def contact
#title = "Contact Us"
end
dd
And my jquery javascript that does a simple animation:
$(document).ready(function()
{
if (!$.cookie('visited')) {
$('.title .flying-text').css({opacity:0});
$('.title .active-text').animate({
opacity:1,
marginTop: "-150px",
}, 5000);
}
});
I only want it to show if the user HAS NOT visited the page before. I have no idea how to properly set a cookie in Rails, nor where to put it, nor how to make sure that the animation script can access that exact same cookie value. Can someone give me a hand?
~Dan
You can set a cookie like this:
cookies[:welcomed] = {:value => true, :expires => Time.now + 6.months}
and from jquery make a wee function
function readCookie(name) {
var nameEQ = name + "=", ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i += 1) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
and call it #in js
if (readCookie('welcomed') == null){}
Ignore the cookie from Javascript.
Using the question referenced as a duplicate, use a permanent cooke and on the first run, set a class on your body from Rails (let's say first-run). Then, have your javascript check and see if body has class first-run. At that point, you can have javascript execute it's first run code.
I am looking to only show a form if it is pulled on a page within an iframe. How do I do that? Is there a server side solution?
If you are using JQuery... (installation instructions here: http://jquery.com/ )
$(document).ready(function(){
if( window == window.top) { $('form#myform').hide(); }
});
Which just hides the form with id "myform" if the window is not the topmost window.
I can't think of purely serverside way, but you could use a bit of hybrid javascript/rails.
assuming that you have a dedicated iframe layout template e.g. 'layouts/iframe.erb'
you could put some javascript in the head to check if it is being loaded as an iframe, and if it is not, redirect to an action and maybe display a flash msg "can only load this page inside application"
The javascript/rails for the head
<script type="text/javascript">
function parentExists()
{
return (parent.location == window.location)? true : false;
};
function check_modal(){
if (parentExists()) {
window.location = '<%= url_for( :controller => "home", :action => 'iframe_action', :iframe_fail => 'true')%>'}
}
check_modal()
</script>
notice the param :iframe_fail which you could check for in a controller and do whatever you please if that param is present e.g. display flash msg or redirect
example controller
def iframe_action
if params[:iframe_fail]
flash[:notice] = 'can only load inside app'
else
#do something else
end
end
Not real pretty but might help you get the job done.
My iframe tag was like
%iframe{:height => "98%", :width => "98%",:"id" => "profileIframe"}
I wanted to hide header of my webpage within this iframe hence I used code as:
var $frame = $(window.parent.frames["profileIframe"]).contents();
$frame.find('.header-ui').hide();
If you observe then contents() returns a element as "#document", which is html of iframe, hence calling a javascript without this will try to access your actual webpage rendered in background of iframe.
You can only check it on the client side via JavaScript.
However: DO NOT DO THAT. There are plenty of legitimate uses of putting a site in a (i)frame. Breaking out of such iframe or changing your site in any way in such circumstances them will only make your users pissed unhappy.