i'm trying to implement the very common list/detail pattern, like the tables in the peepcode example or the blog posts in the recent tom dale screencast. Only in my case the first item should be selected and the details shown when you enter the common /items route. So when you go to /items, it should automatically change the url to /items/1 and display the list as well as the details.
This is what i tried:
App.ItemsRoute = Ember.Route.extend({
model: function () {
return App.Item.find();
},
redirect: function () {
if(Ember.isEmpty(this.modelFor('item'))) {
firstItem = this.model().get('firstObject');
this.replaceWith('item', firstItem);
}
}
});
Complete example:
http://jsfiddle.net/ralph/zeKH9/5/
Problem is, when you add the redirect to the items route, the list of items is not displayed any more, only the item details (try removing the redirect part in the ItemsRoute to see what I mean).
So, what's the best way to achieve this?
When you redirect, you need to redirect from one route to a different route.
item is nested inside items. So I guess Ember.js is getting confused, because transitioning to item does not mean leaving items.
Instead of redirecting from items to item, you should redirect from items.index to item.
item is included in items which means items -> item does not make much sense.
item is not included in items.index which means items.index -> item is a different route, and so should work.
Updated fiddle
Related
I can't really understand this. I have a course factory that I use to create two objects. When I visit the page for the Accounting course (as seen below) it displays the Marketing course page. However, if I use factory girl to create the accounting course first then it visits the correct page and the test passes.
describe "Course pages" do
subject { page }
let!(:published_course) { FactoryGirl.create(:course, title: 'Marketing') }
let!(:unpublished_course) { FactoryGirl.create(:course, title: 'Accounting') }
describe "displaying the right page" do
it "should display the accounting course page" do
visit course_path(unpublished_course)
expect(page).to have_content('Accounting')
end
end
end
It obviously visits the page of the object that is created first, but I don't know why or how to fix this.
Thanks,
Matt
course_path will generate the correct URL (i.e. containing the correct id param), but it's up to your controller to find the course for that id and pass it to your view. Instead, your controller/view must be conspiring to display the first course created.
I want to document once and for all for myself (and keep a permanent record!) of the relationship between parents and children and the ideal json structure, along with basic examples of routes and controllers.
As I (hopefully) get answers and comments I will update the question to reflect best practice.
So, I have my ember models :
App.Customer = DS.Model.extend({
name: DS.attr('string' ),
orders: DS.hasMany("order")
});
App.Order = DS.Model.extend({
carrier: DS.attr('string' ),
customer: DS.belongsTo("customer")
orderlines: DS.hasMany("orderline")
});
App.Orderline = DS.Model.extend({
order: DS.belongsTo("order"),
item: DS.belongsTo("item"),
price: DS.attr('number'),
qty: DS.attr('number'),
});
App.Item = DS.Model.extend({
name: DS.attr('string'),
orderlines: DS.hasMany("orderline")
});
Question 1: are these model definitions correct ?
There are several approaches that I could take to viewing this data:
Customer Tab | Orders Tab | Order lines tab
Customer page -> Orders Page -> Order lines page
Treeview
Can anyone suggest any other JS widgets that could display this hierarchical data ?
Question 2:
what are the router / controllers that are required for each of these ?
I have option 2) working as far as displaying the customer, but when I click on the orders link for the customer I am getting all orders. It could be that my json is wrong .. or more likely I don't know how to display all the orders of the selected customer. Or both :(
I currently have :
{"customers":[
{"name":"foobar inc","id":"0x181","orders":["0x386","0x3a4"]},
{"name":"barfoo ltd","id":"0x182","orders":["0x3de","0x3fd"]} ],
"orders":[
{"carrier":"Standard Mail","id":"0x386","customer_id":"0x181"},
{"carrier":"FlyByNight Courier","id":"0x3a4","customer_id":"0x181"},
{"carrier":"Standard Mail","id":"0x3de","customer_id":"0x182"},
{"carrier":"FlyByNight Courier","id":"0x3fd","customer_id":"0x182"} ]}
Question 3: is this correct ? (I can make the json come out in any format, so it's probably best to create the json structure that best suits ember-data).
Question 4: should I be including related data at this point (so all customers, all orders, all orderlines)? Or would it be better not to include the child data, but get these on demand from the server ?
I'll leave it for now - hopefully I can start to make sense of the nested data soon ! thanks.
Question 1 Those model definitions look good to me.
Question 2 You may want to end up with a mixture of your options #1 and #2. Tabs to let you see the entire list of each model, but with the ability to drill down hierarchically, on the /customers page, for instance. The exact routes that you need depend on the exact URLs that you want to have available in your app which will correspond to the screens/views that you want to show.
Let's say that you wanted these URLs/screens
/customers - The list of all customers
/customers/1 - The details about customer #1
/customers/1/orders - All orders for customer #1
/customers/1/orders/1 - The details for order #1 (including OrderLines)
Then your routes would be:
App.Router.map(function() {
this.resource('customers');
this.resource('customer', { path: '/customers/:customer_id' }, function(){
this.resource('orders');
this.resource('order', { path: '/orders/:order_id'});
});
});
Question 3 Yes, that JSON looks correct.
Question 4 That depends on the needs of your app. Probably you don't want to include the entire tree of data in a single request (Customers -> Orders -> OrderLines -> Items). You probably want to progressively load things as the user goes down a tree.
For instance you'd want to load just list list of customers at first, and then when the user clicks on a customer, you'd want to fire a request to get all of the orders for that customer. And so on, down the tree.
Here's a JSBin showing the general idea : http://jsbin.com/ucanam/1074/edit
Note that the hasMany relationships are defined with {async:true}. This allows them to be looked up on demand, instead of loaded with the parent model.
If you switched to the RESTAdapter, when it tries to load the list of orders for a customer it would make a request like :
/orders?ids[]=0x3de&ids[]=0x3fd
[UPDATE] : In response to the comments.
So, for the url structure that you requested : > >List of customers > -> customer details > -> list of orders > -> order details > -> list of order lines > -> order line details
You are very close with your JSBin. The thing that's tripping you up is the way that nesting of templates works.
The 'order' template, if it exists, is rendered for any and all routes that match /orders/xxx or /orders/xxx/*. If there are other parts of the route like /orderlines those templates get rendered into the 'order' template. But, since your 'order' template doesn't have an {{outlet}} there is nothing for the 'orderlines' template to render into.
Here's a slightly modified JSBin : http://jsbin.com/iKIsisO/3/edit
The only change there is the addition of the {{outlet}} to the bottom of the 'order' template.
Now, rendering orderlines below, or otherwise inside of the main order detail may not be what you want. Most likely you want the orderlines to replace the other order info. In this case you can rename the 'order' template to be 'order/index'. (You can also remove the OrderRoute and the needs from the OrderController).
Another JSBin, with the template renamed : http://jsbin.com/iDiMOCO/1/edit
So, what's happening here?
Using the Order model as an example, when you visit the /orders route, and any other routes that apply to the collection (/orders/new, /orders/some_custom_batch_thing), the main 'orders' template is rendered if it exists, then the sub path templates are rendered into 'orders'. If the 'orders' template does not exist, then the sub-path templates render into the {{outlet}} that is immediately up the chain. The /orders route is kind of a special case in that the sub-template for it is implicitly assumed to be orders/index. Similarly, with /orders/xxx and any other routes that apply to a single Order, the 'order' template is rendered first (if it exists), then the sub-path templates are rendered, either into 'order', or into the most immediate parent {{outlet}}. Also with /orders/xxx the 'order/index' template is the implicit sub-path template.
/orders :: 'orders' -> 'orders/index'
/orders/new :: 'orders' -> 'orders/new'
/order/xxx :: 'order' -> 'order/index'
/orders/xxx/edit :: 'order' -> 'order/edit'
So, the 'orders' and 'order' templates are really kind of like per-model-layout templates that can be used to decorate the sub-paths in a consistent way.
Final JSBin with template names rendered in the template and a few extra templates added to act as "model layouts": http://jsbin.com/iKIsisO/2/edit
I hope that all makes sense.
Learning emberjs
I am not sure if this is a stackoverflow question or git issue. So I decided to put it on stackoverflow first.
Here is my Jsbin (Open in firefox ..not in chrome as raw.github file is used)
When I click on "<- All Department" in department template which I reached after creating a new department it does navigate back to departments template
but the #each does not display the newly added department name in list.
It does show the newly added department on refreshing the browser on /departments
UPDATE
It seems that the .set() method is working but for some reason the new object created is returning the name and ID as undefined. Might be a bug with ember-model perhaps.
The best solution for the moment would be to have 2 save methods, one on the edit controller as you currently do and then adding a different save method for creating a new department.
App.NewController = Ember.ObjectController.extend({
save:function(){
var newDep = App.Department.create({name: this.get('name')});
newDep.save();
this.get('target').transitionTo('department', this.get('model'));
}
});
Here is a jsbin with the New controller added - http://jsbin.com/EVUlOyo/1/edit
End Update
It looks like when you are creating the record it is not setting the name value correctly on the object.
I changed the following -
newDepartment = self.get('model');
newDepartment.set('name',this.get('name'));
newDepartment.save();
to -
var newDep = App.Department.create({name: this.get('name')});
newDep.save();
Here is an updated jsbin also http://jsbin.com/EkEXInO/1/edit
Hope that helps and works for you.
I'm exploring the possibility of using MVC for my next e-commerce site. One thing I can't seem to figure out is whether or not I can use the same URL convention I normally use. Currently, the URL for any product could be one of the following:
Category/SubCategory/Product1.html
Category/SubCategory/SubSubCategory/Product2.html
Category/SubCategory/SubSubCategory/Product3.html
Category/SubCategory/SubSubCategory/SubSubSubCategory/Product4.html
etc.
The issue I'm having is with the nested category structure. So far the only thing I've come up with is as follows:
routes.MapRoute(
"Products",
"{categories}/{productname}",
new { controller = "Product", action = "Details", productname = UrlParameter.Optional },
new { categories = #"\w+/\w+" }
);
I was hoping that {categories} could be matched with any one of the following which I could process to identify the right category that the product belongs to:
Sport/Tennis/Rackets/ProductA
Sport/Badminton/Rackets/ProductB
But the route shown above doesn't work correctly.
Does anyone know how this can be achieved, or if it can't be done?
The routing system allows you to define catchall parameters, which ignore slashes and capture
everything up to the end of a URL. Designate a parameter as being catchall by prefixing it with an
asterisk (*).
routes.MapRoute(null, "Articles/{*articlePath}",
new { controller = "Articles", action = "Show" }
);
You can only have one catchall parameter in a URL pattern, and it must be the last (i.e.,
rightmost) thing in the URL, since it captures the entire URL path from that point onward.
One Caveat though, it doesn’t capture anything from the query string as route objects only look at the
path portion of a URL.
Catchall parameters are useful if you’re letting visitors navigate through some kind of arbitrary
depth hierarchy, such as in a content management system (CMS).
You can use the RouteData object to extract information about the route. For your needs, you would probably create a custom route handler that parses the route data and calls the correct controller methods.
You need access to the individual segments of the URL so you need to divide the category segment into two segments. That would make it much easier.
Let's say we call Tennis and Badminton categories and Rackets within those categories as a product class
You need a way to access the category, productClass and productName parameters. Supposing that "Sport" is fixed in this case, I will do it like this:
routes.MapRoute(
"Products",
"sport/{category}/{productClass}/{productName}",
new { controller = "Product", action = "Details", productClass = UrlParameter.Optional, productName = UrlParameter.Optional }
);
Your action method will be something like this
public ActionResult Details(string category, string productClass, string productName){
//Do whatever you need to do in order to get the specified product
}
You could use Areas in MVC2
So it would read:
Area/Controller/View/id
So in your case it would end up being:
Sport being the area,
Tennis The controller,
Rackets the view,
ProductA being an ID or querystring,
http://www.asp.net/mvc/videos/aspnet-mvc-2-areas
Hope this makes sense.
I want to implement search functionality, So my requirement is I want to search some keyword in body tag in all content page. I do not know how I can search keyword in body tag in Sitecore. Please guide me?
As Anton outlined, the concept of searching the Body tag is wrong for Sitecore. You want to think in terms of Content in fields of Items. Sitecore's ContentSearch is how you can achieve this.
Sitecore comes with default indexes out-of-the-box that you should use for the search. You should rebuild these via the Index Manager in the Content Editor and then base your search on the basic example I've outlined for you below.
public IEnumerable<Item> Search(string searchterm)
{
string indexName = "sitecore_web_index";
using (var index = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
IQueryable<SearchResultItem> query = index.GetQueryable<SearchResultItem>().Where(i => i.Content.Contains(searchterm)).Filter(predicate);
var searchResults = query.GetResults();
foreach (var hit in searchResults.Hits)
{
yield return hit.Document.GetItem();
}
}
}
jRobbins's answer is sensible (he get's my upvote). However, it is technically possible to index the content of the body tag. I would be cautious with this. I've seen it working well, but I've also seen it completely destroy the performance of a site.
The approach involves the creating a computed field in your index. You populate the computed field by making a web request to your newly published page and scraping the response body tag.
Here's are a couple of module that more or less does that:
https://github.com/efocus-nl/sitecorewebsearch
https://github.com/hermanussen/sitecore-html-crawler
If you can accept something a little less accurate, then you could loop through each of the components on your page and extract content from their datasources. That approach is discussed in this video:
http://www.techphoria414.com/Blog/2012/May/Sitecore_Page_Editor_Unleashed