I'm making a live search on ember.js. This is the code
App.Router.map ->
#resource "index", {path : "/"}
#resource "index", {path : "/:query"}
App.Torrents =
findByQuery : (query) ->
url = "/api/find/#{query}"
$.getJSON(url)
App.IndexRoute = Ember.Route.extend
model : (params) ->
App.Torrents.findByQuery(params.query)
App.IndexController = Ember.ArrayController.extend
onChangeQuery : _.debounce(->
query = #get("query")
#transitionToRoute("index", {query : query})
, 500).observes("query")
I have a query property binded to an input. When the input change I want to transition to the route passing the new query parameter, but the IndexRoute.model method is not being called.
The reason IndexRoute.model method not being called. is
A route with a dynamic segment will only have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper), then a model context is already provided and the hook is not executed. Routes without dynamic segments will always execute the model hook.
explained here.
So as discussed in this issue, use the setupController hook, to fetch your model, in these cases.
Working bin of your code, with setupController
Sorry I'm late and this might not be of any use for you. I just wanted to post it over here, if in case it might be of any use for others.
This link helped me, clear my problem.
Approach 1: We could supply a model for the route. The model will be serialized into the URL using the serialize hook of the route:
var model = self.store.find( 'campaign', { fb_id: fb_id } );
self.transitionToRoute( 'campaign', model);
This will work fine for routing, but the URL might be tampered. For this case, we need to add extra logic to serialize the object passed into the new route and to correct the URL.
Approach 2: If a literal is passed (such as a number or a string), it will be treated as an identifier instead. In this case, the model hook of the route will be triggered:
self.transitionToRoute( 'campaign', fb_id);
This would invoke the model() and would correctly display the required URL on routing. setupController() will be invoked immediately after the model().
2nd one worked fine for me fine. Hope it's useful and answered the above question.
Related
Example
http://localhost:4200/login?aUasas129198
resolves to
http://localhost:4200/login
What should I do if I want the value after '?'
I tried doing
{ path: 'login/:id', component: LoginComponent },
But it did not work
I also tried
console.log(this.route.snapshot.queryParams);
console.log(this.route.snapshot.params);
But they both return empty object. What should I do now please help
If it’s unavoidable that Angular redirects you immediately and loses the query parameters, you could subscribe to router events and on each NavigationStart for login route get a hold of route’s queryParamMap or snapshot.paramMap before they’re lost in redirection, then you can e.g. pass it to a service and do whatever you wanted to do with it.
Or, as another alternative, you could look into configuring your router with queryParamsHandling: 'preserve', in which case it should pass the query params to the next route (see the section in Angular docs linked below for more on this).
I worked with a project that made use of query params in Angular 5, IIRC I don’t think it would redirect on its own so I’d recommend to look elsewhere in your project but I may be wrong.
See also
Routing & Navigation → Query parameters and fragments in Angular docs
Angular Route Start and Route End Events on StackOverflow
Actually, You are not passing the value in any key:
http://localhost:4200/login?aUasas129198
The proper way should be:
http://localhost:4200/login?anykey=aUasas129198
// get it as
// this._ActivatedRoute.queryParams.subscribe()
If you are using the URI as you shown in your question as:
{ path: 'login/:id', component: LoginComponent }
Then you should pass the value to id as:
http://localhost:4200/login/aUasas129198
// remember the '/' after the login that you didn't use.
// get it as
// this._ActivatedRoute.snapshot.params.id
I'm using Ember.js 2.3. I have a parent Route that looks like this:
App.AppRoute = Ember.Route.extend
model: ->
Ember.RSVP.hash
projects: #store.findAll "project"
clients: #store.findAll "client"
consultants: #store.findAll "consultant"
practiceAreas: #store.findAll("practice_area").then (practice_areas) ->
practice_areas.sortBy "displayName"
I have a child Route looking like:
App.AppProjectRoute = Ember.Route.extend
model: (params) ->
hash = #modelFor "app"
hash["project"] = #store.findRecord "project", params.project_id
.then (p) -> p
hash["workWeeks"] = #store.query "work-week", project_id: params.project_id
.then (weeks) -> weeks
console.log _.keys hash
Ember.RSVP.hash hash
The template for the child Route contains:
<hr/>
{{add-consultant project=model.project consultants=model.consultants addConsultant="didAddConsultant"}}
</div>
Then the add-consultant component contains a computed property:
remainingConsultants: Ember.computed "project", "project.consultants.[]", "consultants.[]", ->
already_involved = #get "project.consultants"
remaining = #get "consultants"
already_involved.forEach (ai) ->
remaining = remaining.reject (r) -> r.get("id") is ai.get("id")
remaining.sortBy "displayName"
Case 1
When I navigate directly to a project, such as http://localhost/#/app/project/27, the Routes model hook correctly queries the server for data and renders the template and components.
Case 2
When I navigate directly to a project, then manually change the project ID in the URL (say from http://localhost/#/app/project/27 to http://localhost/#/app/project/28 and press Enter, the Route model hooks update, and the template and components are correctly rerendered.
Case 3
However - and this is my confusion - navigating to a project by clicking a link (say, from a list of Projects using the {#link-to} helper) - even if I first visit the page sucessfully as in Case 1 or Case 2 and then immediately navigate back to the same Project, I get an error:
TypeError: already_involved is undefined
Looking deeper, it turns out that #get "project" itself is undefined.
What is different about using {#link-to} over "direct" navigation that causes this? Is it an error I made in defining my Route model heirarchy? Am I missing some kind of event hook?
EDIT 1: If there's a smarter or more idiomatic way to define these types of hierarchical model hooks maybe that would obviate this issue?
EDIT 2: The Router:
this.route "app", ->
this.route "projects"
this.route "project", path: "/project/:project_id"
Transitions, triggered by either the link-to helper or transitionTo/transitionToRoute have two "modes". The transition can either trigger the model hooks (beforeModel, model, afterModel), or not.
To skip the model hooks, you can pass an object to the transition, e.g. {{link-to "Profile" user}}. Ember.js interprets this as you already having the necessary information, and thusly skips the hooks that would load said information.
To force the model hooks though, you can pass a string or an integer to the transition, e.g. {{link-to "Profile" 1}} or {{link-to "Profile" user.id}}. This will use the value passed to the transition to fill out the dynamic segment of the route, and Ember.js interprets this as the data necessary not being loaded yet, and thusly triggers the hooks.
Since your model hook returns a hash, it is advantageous to force the hooks so everything loads appropriately.
You can consult the documentation for the model hook for further information.
I have a Backbone js app that runs when I go to the URL domain.com/item/1 or domain/item/2` etc. When the app starts I create a new instance of my model and pass it an id which needs to be the last part of the URL. Is there a way to access this in Backbone?
I know it's easy to build a router that can access parameters after a hash so I am better of changing my URL to be something like domain.com/item/1#1?
I don't know you have a backbone router or not.But that's easily achievable by one of the basic use of Backbone.router.
and you do not have to use # or anything.You can access anything between slashes.
routes: {
"item/:page": function(page){
//page holds the query parameter.
}
}
The routes hash maps URLs with parameters to functions on your router (or just direct function definitions, if you prefer), similar to the View's events hash. Routes can contain parameter parts, :param, which match a single URL component between slashes; and splat parts *splat, which can match any number of URL components. Part of a route can be made optional by surrounding it in parentheses (/:optional).
Please read the section of Backbone.router in the documentation for detail.
http://backbonejs.org/#Router
FYI, passing the query parameter to your model should not be executed when a user start app but when routes is called.otherwise everytime you want to change page,You need to change url and reload the whole page.
and usually Controller makes model instances which means,You'd better create controller instance with parameters in router and then create a model in the controller.something like this
routes: {
"item/:page": function(page){
var page = new YourNameSpace.Controller.Foo({pageId : page});
page.render();
}
}
//inside of Itempage Controller
initialize : function(){
this.model = new YourNameSpace.Model.Foo({pageId : this.pageId});
}
Is there any reason why setupController would not get called when using {{linkTo}}? I have two instances in my app where linkTo is being used, and in the second case. It doesn't work. The only difference that I can see is that in the first case linkTo is being used in a loop, and in the second it's not. Below is relevant code for the non-working one:
App.Router.map(function() {
this.resource("search", { path: "/search/:args" });
});
App.SearchCriteria = Ember.Object.extend({ });
App.SearchRoute = Ember.Route.extend({
serialize: function(model, params) {
// .. some code that converts model to a string called args
return {'args': args}
},
model: function(params) {
// convert args, which is query string-formatted, to an object
// and then make a App.SearchCriteria object out of it.
return App.SearchCriteria.create($.deparam(params.args));
},
setupController: function(controller, model) {
controller.set("searchCriteria", model);
}
});
In the search template:
{{view Ember.Checkbox checkedBinding="searchCriteria.music"}} Music
{{#linkTo search searchCriteria}}Search{{/linkTo}}
The last thing I see in the logs is:
Transitioned into 'search'
Normally, I'd see the setupController being called at some point, but it's not happening or some reason. I even tried using the {{action}} method to call a handler and then use transtionTo, but that had the same results.
UPDATE 1: Adding more details
The only difference between the working and non-working cases is that in the working case, the {{linkTo}} is being called from the same template as that of the controller and router (i.e., the linkTo is in the search template and it's invoking the SearchRoute). In the working case, the linkTo is being called on the SearchRoute but from a different template belonging to a different router).
After some Chrome debugging of Ember code, I found out that the router isn't being called is because partitioned.entered is empty. In the working case, it is non-empty.
var aborted = false;
eachHandler(partition.entered, function(handler, context) {
if (aborted) { return; }
if (handler.enter) { handler.enter(); }
setContext(handler, context);
if (handler.setup) {
if (false === handler.setup(context)) {
aborted = true;
}
}
});
UPDATE 2: Root issue found - bug?
I think I understand the root cause of why the handler isn't being triggered, and I think it's because the partitionHandlers(oldHandlers, newHandlers) method doesn't think that the model has changed, thus doesn't fire the handler.
To be specific, this is the relevant part of the view:
{{view Ember.Checkbox checkedBinding="searchCriteria.music"}} Music
{{#linkTo search searchCriteria}}Search{{/linkTo}}
Although the user checks off the checkbox (thus changing the state of searchCriteria), Ember doesn't think that searchCriteria is any different, thus doesn't do anything.
Is this a bug?
I'm not sure what your problem is, but this may help.
setupController is called every time the route is entered. But model hook may not be called every time.
See Ember guide: http://emberjs.com/guides/routing/specifying-a-routes-model/
Note: A route with a dynamic segment will only have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper), then a model context is already provided and the hook is not executed. Routes without dynamic segments will always execute the model hook.
Genrally speaking, if you click the link generated by link-to to enter the route, Ember will not call model hook for that route. Instead it passes the model (link-to parameter) to that route.
The philosophy here is since the client already has the model context, Ember think there is no need to get it again from server (that's model hook's job).
Say I have a route setup:
'photos/:id' : 'showPhoto'
and somebody shares the url: www.mysite.com/photos/12345 with a friend.
When their friend clicks on the shared link, showPhoto gets called back with 12345 passed as the id. I cant figure out how to fetch the model from the server, because even when setting its id property and calling fetch(), backbone thinks that the model isNew and so the ajax request url is just /photos instead of /photos/12345:
showPhoto: (id) ->
photo = new models.Photo _id:id
photo.fetch #does a GET to /photos, I would have expected it to request /photos/12345
success: () ->
render photo view etc...
Photo = Backbone.Model.extend
idAttribute: '_id'
urlRoot: '/photos'
The model Photo is usually part of a collection, but in this scenario someone is visiting the site directly and only expects to see data for one photo, so the collection is not instantiated in this state of the app.
Is the solution to load the entire collection of photos and then use collection.getById(id)? This just seems way too inefficient when I just want to load the properties for one model.
if you don't have the model as part of a collection, you have to tell the model the full url manually. it won't auto-append the id to the urlRoot that you've specified. you can specify a function as the urlRoot to do this:
Photo = Backbone.Model.extend({
urlRoot: function(){
if (this.isNew()){
return "/photos";
} else {
return "/photos/" + this.id;
}
}
});
Backbone uses the id of the model to determine if it's new or not, so once you set that, this code should work correctly. if it doesn't, you could always check for an id in the if-statement instead of checking isNew.
You do not need to tell backbone whether or not to append the id the url. Per the documentation: http://backbonejs.org/#Model-fetch, you may simply set the urlRoot to the equivalent of the url in a collection.
Backbone will automatically append the desired id to the url, provided you use one of the following methods:
model.set("id", 5); //After initialized
model = new Backbone.Model({id: 5}); //New model
If you manually set the id in the attributes hash or directly on the model, backbone won't be aware of it.
model.id = 5; //Don't do this!
there's already a similar question: "How do I fetch a single model in Backbone?"
my answer there should work for you (and it's in coffeescript)
also remember to check Backbone Model#url documentation, it's all explained there
I would bootstrap the collection (by rendering the following to the page) with just one model in it like this:
photos = new PhotoCollection();
photos.reset([ #Html.ToJson(Model) ]);
Note that the server side code that I use is ASP.Net MVC so use something specific to your server side architecture. Also note that the square brackets are important as they take your singular model and wrap it in an array.
Hope that's helpful.