I am new to ember and I am trying to figure how to show different model hooks on a component. I have two models that I want to show in a table. Each model should show when the link-to route is clicked, but I want to use one component to build the table like so
let newCol = [];
export default Component.extend({
table:null,
model:null,
columns:computed(()=>{
return newCol;
}),
init(){
this._super(...arguments)
let model = this.get('model')
for(const key of Object.keys(model[0])){
newCol.push({'label': key});
newCol.push({'valuePath': key});
//console.log(key)
}
let table = new Table(this.get('columns'),this.get('model'));
//console.log('table = ', table);
this.set('table',table);
}
});
I am passing all my model hook from my routes like this
{{client-main-table model=model}}
and my menu is created like this
{{#each menu as |menu|}}
<li>{{#link-to menu}}{{capitalize menu}}{{/link-to}}</li>
{{/each}}
this creates a menu like user, files
when I click the user menu it display the users data on the table but if when I click the files menu then the table doesn't reset to show just the files data. It shows the user and the files data.
Related
I'm using the structure of the Ember version of the TodoMVC app for an Ember app by which I mean that, after setting the application route to products
export default Ember.Route.extend({
redirect: function(){
this.transitionTo('products');
}
});
I then use the ul structure of the todo app, with each product occupying a new list position (as each todo does in the TodoMVC). I also (as with the Todo app) bind some attributes that allow me to set css styles depending on state
<ul id="products">
{{#each product in arrangedContent }}
<li {{bind-attr class="isEditing:editing"}}>
Depending on state, an input box will be visible for a product once a user clicks a button. The problem is that once the user clicks the button, isEditing is set to true for all the products, so all the input boxes are visible i.e. the class for each list element is set to editing <li class='editing'>. The action (which makes the input box visible) is handled on the products controller
showInput: function(item){
this.set('isEditing', true);
},
I only want the input box made visible for the particular product (or list element) where the click event was triggered. In the Todo MVC app, the action to make the input field visible is handled on a (singular) Todo controller, not the TodosController, so in my app, I created a (singular) product controller and put the action there but when the user clicks the button on an individual product in the list, the action on the (single) product controller is not triggered. How can I get the product controller to handle the action, or alternatively, ensure that only one input field is made visible.
You can see how the functionality is supposed to work on the live demo the TodoMVC app by creating a few todo items and then clicking on one of them. Only one input field becomes visible.
Update
In the showInput function of the products controller, I tried to call the corresponding function in the product controller (after specifying the products needs the product controller) needs: ['product'],
showInput: function(){
this.get('controllers.product').send('showInput');
}
this call the function on the product controller but setting it to true does nothing, i.e the input field isn't made visible and none of the list element classes are set to editing <li class="">
showInput: function(item){
this.set('isEditing', true);
},
For what it's worth, the product controller isn't showing in the Ember inspector, although I am able to send the actions to it, just not able to call this.set('isEditing',true) on it.
Change
{{#each product in arrangedContent }}
<li {{bind-attr class="isEditing:editing"}}>
To
{{#each arrangedContent as |product|}}
{{to-do product=product}}
Generate a to-do component via ember g component to-do, and modify it (see below). Set it's template to be whatever went in the li before, accessing the appropriate property via product.<prop> in the template.
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['isEditing'],
isEditing: false,
product: null,
actions: {
showInput: function() {
this.setProperty('isEditing', true);
}
}
})
So I had a route: /tournaments/setup/:tournament_id in my ember app, and it displayed various fields from a model.
But then I find that I'll need other models on the same page. For example, suppose I want the list of all players, so that I can make a dropdown for selecting players to join a tournament.
I can pass an object:
this.transitionTo('tournaments.setup', {tournament: tournament, players: players});
and that works once I change the handlebars template to reflect the change of context.
But now the URL is wrong. The URL is /tournaments/setup/undefined. I'm sure there is a way to explicitly handle this also, but I don't know what it is ...
... and I feel like I'm losing the path. Is there a more conventional way to do what I'm doing?
Update: I've found that I can get the URL to work by adding an id:
this.transitionTo('tournaments.setup', {id: tournament.id, tournament: tournament, players: players});
but I'm still wondering if I'm doing this the right way.
What you want to do is load the models in your route and stick them in a controller to user them. I usually do this in the beforeModel hook. So in your App.TournamentsSetupRoute do something like this:
App.TournamentSetupRoute = Ember.Route.extend({
beforeModel: function() {
var self = this;
var players = this.store.find('player');
players.then(function() {
self.controllerFor('players').set('model',players);
});
}
});
So now, when ever this route is entered, the players will be fetched from your API and stored in a controller. In your controller, just add the players controller to needs so you can access it.
App.TournamentSetupController = Ember.ObjectController.extend({
needs: ['players']
});
You can access the players models anywhere in your controller by doing this.get('controllers.players.model); and iterate over it in your template like so:
<select>
{{#each player in controllers.players.model}}
<option>{{player.name}}</option>
{{/each}}
</select>
Hope that helps! I haven't tested the above code, so there may be a few typos, but it should help get ya there.
Hi I have a view call AutocompleteView its job is to put google places autocomplete on landing page.There is no map rendered on this page just a textbox.
what I am try to achieve is user should just use autocomplete textbox on landing page. On entering some place of choice. I will transition the user to a specific route where the same AutocompleteView will be rendered with map so tha user can change his choice of place.
When user chang the location on the page where map is rendered no transition is needed here
the approch I am trying is I check the parent view where
{{view AutocompleteView}}
has been rendered using this.get('ParentView") and based on the parent view
say if ParentView is Application(landing page) I will transition or else no transition will occur.
I have no good experienve of javascript mvc my doubt
Is it right to rely on ParentView for just transtions or is there a better way??
App.AutocompleteAddressView = Ember.View.extend({
tagName: 'input',
didInsertElement: function() {
var options = {
componentRestrictions: {country: "xx"}
};
var autocomplete = new google.maps.places.Autocomplete(this.$()[0], options);
console.log(this.get('parentView'));
}
});
It sounds like what you want is to handle the action of "entering text in the search box" differently based on which route is currently active -- the landing page or the results page.
The Ember way to do this is to trigger an action from your view using this.get('controller').send(#{actionName}). That action will bubble up from the view's controller to the current route. Then you can handle that action differently depending on which route is active, e.g.:
App.IndexRoute = Em.Route.extend({
...
actions: {
search: function() {
# transition to results page
}
}
});
App.ResultsRoute = Em.Route.extend({
...
actions: {
search: function() {
# update visible results
}
}
});
To make this easier, you might want to use a subclass of Ember.TextField for your AutocompleteAddressView. Then you can define an action property on your view that will automatically get triggered when the user types enter/return.
I have a scenario where I have list of items and each item has a create button. When I click on create, I wanted a component to be appended to the list item. This component uses model data as parameter and also accesses store from within. To access the store in the component I am using targetObject.store
The component works well if I add it to the template manually like:
{{#each}}
<div> blah blah {{my-component data=this.something action="doSomething"}} <button {{action 'add' this}}>Add</button></div>
{{/each}}
I can probably show/hide the component using a flag, and toggle it when we click on Add button, but I rather do it dynamically if possible.
I did try this but didn't work for me because I couldn't access store :
actions: {
add: function(obj){
var view = Ember.View.create({
template: Ember.Handlebars.compile('{{my-component action="addQuestion"}}')
});
view.set('data', obj.get('something'));
Ember.run(function() {
//prolly can get parent view rather than document.body
view.appendTo(document.body);
});
}
}
Thanks.
I think this example answers your question:
http://emberjs.jsbin.com/axUNIJE/1/edit
I'm new at using ember, but already familiar with it, basically following some tutorials here and there and reading the api docs. But tutorials don't go too deep into more complex topics.
These are the details: I already implemented a web page that shows a list of items. The following are the relevant code excerpts from different parts of the app.
// the data model, the view and the controller
App.Item = DS.Model.extend({
name: DS.attr('string')
});
App.ItemsController = Ember.ArrayController.extend();
App.ItemsView = Ember.View.extend({ templateName: 'items' })
// in the router's corresponding route
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('items', App.Item.find())
}
// in the handlebars template
<ul class="items">
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
The data for this list is loaded remotely via ember-data (notice the App.Item.find() call in the route's connectOutlet method above) and a handlebar template displays it, and dynamically updates the list as the data changes. Up to here this is basic ember.
Now I want to have a text field at the top of the list, and when the user types in this text field, the list should be updated, by filtering and showing only the items with a name that matches what the user is typing. The actual definition of what a matching name is, is irrelevant to this question. It could be those names that contain the typed string, or that start with it.
I know my next step is to include a textfield view on top of the list in the handlebars template:
<div class="search-bar">
{{view Ember.TextField}}
</div>
<ul class="items">
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
So my questions at this point are the following:
How do I refer to this text field in javascript code so I can attach a listener to it to detect when it changes?
And more importantly, what do I need to do inside this event listener so the list gets filtered?
I would like to know how to achieve it filtering data loaded locally, but also how to do it by loading the filtering data remotely everytime the user types.
I actually need to implement something slightly more complex than this, but knowing how to do this will help.
You can have a computed property on your controller that filters the content based on a text field.
App.ItemsController = Ember.ArrayController.extend({
// ...
searchedContent: function() {
var regexp = new RegExp(this.get('search'));
return this.get('content').filter(function(item) {
return regexp.test(item.get('name'));
});
}.property('search', 'content.#each.name')
});
Then you just bind your text field to controller.search. Example: http://www.emberplay.com#/workspace/2356272909
To filter data remotely you should have ember data load more items every time search changes. This can be done by sending an event to the router every time there is a change.