Ember Query Parameter cant send value in hash - ember.js

I cant send the query param value to call set pageno value
template code :
{{#each #model.PageNo as |item|}}
<LinkTo #route="getServices" #query={{hash pageno="{{item.pgno}}" }} >{{item.pgno}}</LinkTo>
{{/each}}
controller :
export default class GetServicesController extends Controller {
queryParams = ['pageno'];
#tracked pageno = "1";
}

{{#each #model.PageNo as |item|}}
<LinkTo #route="getServices" #query={{hash pageno=item.pgno}} >{{item.pgno}}</LinkTo>
{{/each}}
this should work

Related

How do you render and filter over a collection of data from an #argument?

This question comes from our Ember Discord
I have something like this
{{#each #data as |project|}}
<ItemList #categories={{project.category}}/>
{{/each}}
I am calling action somewhere else, then filtering the #data and I would like to reset this each #data.
I want to assign the filtered result to #data
data passed from parent component to this
At the same component I have button
<li
class="inline hover:underline cursor-pointer ml-4"
{{on "click" (fn this.changeProject project.name #data)}}
>
{{project.name}}
</li>
and changeProject action filters the #data that I would like to re-assign to #each
for example - this.data = filteredModel; does not work.
Assuming your action, changeProject looks something like this:
#action
changeProject(projectName, data) {
this.data = data.filter(project => project.name.includes(projectName));
}
the observation that this.data doesn't work is correct and intended, as #data is available at this.args.data, but every key on this.args is immutable.
so, in your component, in order to reference filtered data, maybe you want to create an alias:
get filteredData() {
return this.args.data;
}
and in your template:
{{#each this.filteredData as |project|}}
...
and then your action would need to be updated as well because we still can't set filteredData or this.args.data
import Component from '#glimmer/component';
import { tracked } from '#glimmer/tracking';
import { action } from '#ember/object';
export default class Demo extends Component {
#tracked filtered;
get filteredData() {
return this.filtered ?? this.args.data;
}
#action
changeProject(projectName) {
this.filtered = this.args.data.filter(project => project.name.includes(projectName));
}
}
{{#each this.filteredData as |project|}}
<ItemList #categories={{project.category}}/>
{{/each}}
...
<ul>
{{#each #data as |project}}
<li
class="inline hover:underline cursor-pointer ml-4"
{{on "click" (fn this.changeProject project.name)}}
>
{{project.name}}
</li>
{{/each}}
</ul>

How do I render a collection of models each with variable view and controller?

I have a collection of models in my Ember.js app, which I would like to render. The catch is that I want to be able to specify a specialized view and controller for each of the models.
The controller part seems to be easy: I would just wrap the array in an ArrayController and implement itemController method. The view part is where it gets tricky. I don't see an obvious idiomatic way of doing this.
The best way we came up with is the combination of ArrayController and CollectionView with an overridden createChildView. For instance:
createChildView: function(viewClass, attrs) {
var viewInstance,
widgetType = attrs.content.get('type');
// lookup view, if found, use it, if not, pass empty view
var viewDefined = this.container.lookup('view:' + widgetType);
var createWidgetType = viewDefined ? widgetType : 'empty';
// create view instance from widgetType name
// it causes lookup in controller
viewInstance = this._super(createWidgetType, attrs);
// if `attrs.content` is controller (item of `ComponentsController`)
// set it as controller of newly created view
if(attrs.content.get('isController')) {
viewInstance.set('controller', attrs.content);
}
return viewInstance;
}
This feels unnecessarily convoluted, I don't like that I have to connect the view with the controller manually like that. Is there a cleaner way?
You can create a component, which will act as controller and have a view associated with it:
App.XItemComponent = Ember.Component.extend({
controllerProperty: '!',
tagName: 'li'
});
Then, you can just do:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model }}
{{ x-item item=this }}
{{/each}}
</ul>
</script>
http://emberjs.jsbin.com/wehevixolu/1/edit?html,js,output
I'd use the {{render}} helper. It'll create a view and controller for each instance.
{{#each item in model}}
{{render "item" item}}
{{/each}}
Example: http://emberjs.jsbin.com/vuwimu/2/edit?html,js,output
Render helper guide: http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper
Additionally:
In your comment you mentioned you want different controller/view types for particular model types. This could be done like this:
{{#each item in model}}
{{#if item.typeX}}
{{render "itemX" item}}
{{/if}}
{{#if item.typeY}}
{{render "itemY" item}}
{{/if}}
{{/each}}
or if you'd choose to go with components:
{{#each item in model}}
{{#if item.typeX}}
{{component-x item=item}}
{{/if}}
{{#if item.typeY}}
{{component-y item=item}}
{{/if}}
{{/each}}
Without knowing what you are trying to accomplish in more detail it’s hard to tell what the best solution is.

How can I render a block only if a specific route is active?

I wanna render a block in Ember Handlebars only, if a specific route is active.
So, how can I create a 'ifRoute' helper, with the same conditons then the 'active' class on the 'linkTo' helper?
I want this, because I've a two layer navigation. So, I want to show the sub-navigation only, if the head navigation point is active. I dont wanna use the 'active' class, because I use lazy loading and I only want to load the sub navigation when the head navigation point is active.
So, what I want to do is:
<ul>
{{#each assortmentGroups}}
<li>
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#ifRoute "assortmentGroup" this}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/ifRoute}}
</li>
{{/each}}
<ul>
How can I do this or is there a better solution?
Thanks
Just add to the controller:
needs: ['application'],
isCorrectRouteActive: Ember.computed.equal('controllers.application.currentRouteName', 'correctRoute')
Similarly:
isCorrectPathActive: Ember.computed.equal('controllers.application.currentPath', 'correct.path')
isCorrectURLActive: Ember.computed.equal('controllers.application.currentURL', 'correctURL')
I am quite sure latest Ember does the rest
Here are two possible options, although for both you first have to save the currentPath in your ApplicationController to have access to it whenever you need it:
var App = Ember.Application.create({
currentPath: ''
});
App.ApplicationController = Ember.ObjectController.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
}.observes('currentPath')
});
Using a computed property
Then in the controller backing up the template, let's say you have a NavigationController you create the computed property and define also the dependency to the ApplicationController with the needs API to gather access, then in the CP you check if the currentPath is the one you want:
App.NavigationController = Ember.Controller.extend({
needs: 'application',
showSubMenu: function(){
var currentPath = this.get('controllers.application.currentPath');
return (currentPath === "assortmentGroup");
}.property('controllers.application.currentPath')
});
So you can use a simple {{#if}} helper in your template:
...
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#if showSubMenu}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/if}}
</li>
...
Using a custom '{{#ifRoute}}' helper
But if your really want a custom helper to deal with your condition then this is how you could do it, note that the currentPath stored on your application is still needed since we need a way to get the value of the current route:
Ember.Handlebars.registerHelper('ifRoute', function(value, options) {
if (value === App.get('currentPath')) {
return options.fn(this);
}
else {
return options.inverse(this);
}
});
And then you could use it like this:
...
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#ifRoute "assortmentGroup"}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/ifRoute}}
</li>
...
See here also a simple Demo of the "custom helper" solution: http://jsbin.com/izurix/7/edit
Note: with the second solution there is a catch! Since bound helpers do not support blocks (in embers handlebars customization) I used a simple helper that does not reevaluate the condition depending on bindings which is may not what you want.
Hope it helps.
After investigating the ember code for the linkTo and if helpers, the answer from intuitivepixel and a blog post about writing my own bound block helper, I've found a solution:
var resolveParams = Ember.Router.resolveParams;
var resolvedPaths = function(options) {
var types = options.options.types.slice(1),
data = options.options.data;
return resolveParams(options.context, options.params, { types: types, data: data });
};
Ember.Handlebars.registerHelper('ifRoute', function(name) {
var options = [].slice.call(arguments, -1)[0];
var params = [].slice.call(arguments, 1, -1);
var theResolvedPaths = resolvedPaths({ context: this, options: options, params: params });
var router = options.data.keywords.controller.container.lookup('router:main');
var self = this;
var evaluateIsCurrentRoute = function() {
self.set('current_route_is_active_bool_for_ifroute', (function() {
return router.isActive.apply(router, [name].concat(theResolvedPaths)) ||
router.isActive.apply(router, [(name + '.index')].concat(theResolvedPaths));
})());
};
evaluateIsCurrentRoute();
router.addObserver('url', evaluateIsCurrentRoute);
options.contexts = null;
return Ember.Handlebars.helpers.boundIf.call(this, 'current_route_is_active_bool_for_ifroute', options);
});
I found an easy way to check if a route is active, but to get this into a computed property may not be so easy.
// Test if you are currently in a route by it's lowercase name
App.isInRoute = function(name) {
return App.Router.router.currentHandlerInfos.mapProperty('name').contains(name);
}
To use:
App.isInRoute('posts.show'); // true if in the route

How to access controller property in template?

Here,I am trying to access property of controller but it is throwing an exception
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Template Code :
{{#each itemController="index"}}
<div class="row" {{bindAttr class="item.isWithBorder:border"}}>
{{#each item in model}}
{{#each item in item.home_products}}
{{#each item in item.contents}}
<li>{{item.product_name}}</li>
{{/each}}
{{/each}}
{{/each}}
</div>
{{/each}}
I want to show border property only for first iteration i.e. first product.
Hence, one property is added in controller which i am accessing in template.
Controller Code :
Astcart.IndexController = Ember.ObjectController.extend({
init: function() {
console.log("Item controller initialized");
this._super();
},
isWithBorder : function(){
return this.get("model.id") == 1;
}.property("model.id")
});
I have updated my code Here.
Here working jsfiddle: http://jsfiddle.net/fQNRk/2/
You where using the name index as your itemController index is already a reserved name so I just changed it to item now it works.
Astcart.ItemController = Ember.ObjectController.extend({
...
{{#each itemController="item"}}
...
Hope it helps.

Emberjs bindAttr id-prefix

I am loading multiple models (using an ArrayController) with ember-data that obviously returns and id for each model and I am displaying some dynamic map content based on that, which is initialized via JS in the didInsertElement function.
So my hbs code looks like this:
{{#each controller}}
....
<div class="map" {{bindAttr id="id"}}>
....
{{/each}}
That works fine, but my problem is, I don't just want the id-number for the div id, but I want it with a static prefix: E.g. user-{{id}}. Or at a different route I would want e.g. news-{{id}}
Is anything like that possible?
Assuming you can store the prefix in you model data, you could create a computed property for this use case:
App.User = DS.Model.extend({
...
prefixedId: function() {
return "user-" + this.get('id');
}.property('id')
});
App.News = DS.Model.extend({
...
prefixedId: function() {
return "news-" + this.get('id');
}.property('id')
});
And then use the computed property instead:
{{#each controller}}
....
<div class="map" {{bindAttr id="prefixedId"}}>
....
{{/each}}