I have a property in the controller. I want to use a component to update that controller property. The usual component parameter passing works when I'm using a text field like the guides say, but in my case, I'm changing the value in code, not input fields. The binding seems to be broken.
What method similar to propertyDidChange() or notifyPropertyChange() should I use to accomplish this? Can you also provide a simple example so I know WHERE to make that method call?
personalities controller
`import Ember from 'ember'`
PersonalitiesController = Ember.ArrayController.extend
eiValue: ->
if params.name
params.name.charAt(0)
else
'e'
nsValue: ->
if params.name
params.name.charAt(1)
else
'n'
tfValue: ->
if params.name
params.name.charAt(2)
else
't'
pjValue: ->
if params.name
params.name.charAt(3)
else
'p'
type: (->
this.get('eiValue') + this.get('nsValue') + this.get('tfValue') + this.get('pjValue')
).property('eiValue', 'nsValue', 'tfValue', 'pjValue')
typeChanged: ((model, type)->
Ember.run.once(this, 'routeToPersonality')
).observes('type')
routeToPersonality: ->
this.get('controller').transitionToRoute('personality', this.get('type'))
`export default PersonalitiesController`
personalities template
%dichotomy-selector type=type
component
`import Ember from 'ember'`
DichotomySelectorComponent = Ember.Component.extend
eiValue: 'e'
nsValue: 'n'
tfValue: 't'
pjValue: 'p'
type: (->
newValue = this.get('eiValue') + this.get('nsValue') + this.get('tfValue') + this.get('pjValue')
this.set('controller.type', newValue)
).property('eiValue', 'nsValue', 'tfValue', 'pjValue')
actions:
toggleEI: ->
eiValue = this.get('eiValue')
if eiValue == 'e'
this.set('eiValue', 'i')
else if eiValue == 'i'
this.set('eiValue', 'e')
toggleNS: ->
nsValue = this.get('nsValue')
if nsValue == 'n'
this.set('nsValue', 's')
else if nsValue == 's'
this.set('nsValue', 'n')
toggleTF: ->
tfValue = this.get('tfValue')
if tfValue == 't'
this.set('tfValue', 'f')
else if tfValue == 'f'
this.set('tfValue', 't')
togglePJ: ->
pjValue = this.get('pjValue')
if pjValue == 'p'
this.set('pjValue', 'j')
else if pjValue == 'j'
this.set('pjValue', 'p')
`export default DichotomySelectorComponent`
You should be able to use this.set("controller.property", value) in a method inside the component, per the docs here: http://emberjs.com/api/classes/Ember.Component.html#property_controller
Edit September 2016
This answer has been the victim of the ongoing evolution of Ember, and the associated changes in best practices. It is amazing to see how different Ember is from just 1.5 years ago. The old answer has been totally rejected by the community as evidenced by the votes (negative as I type this), in fact I don't think it even works anymore. As of mid-2016 in Ember 2.x the generally accepted answer to this question is to create an action in the controller that changes the property, pass that action to the component, and call the action when the value changes on the component. But now you can use the action helper to pass a regular method instead of an action, and use component life cycle hooks to detect changes in the component instead of using an observer or computed property. You could also use a service instead. More changes on the way, I am sure.
For any future readers, any questions you need to research about Ember make sure that you filter results by time, you only want results within the last year.
Related
I have a route where I want to make a sibling route's link-to's active as well. I have tried using current-when in the link-to, but it's not working for me.
my routes are as follows
//projects
//projects/:project_id
//projects/:project_id/user/:user_id
When I navigate to //projects/:project_id route, the right link is set to active. I want the same link to be active on the //projects/:project_id/users/:user_id route.
My link-to in the parent //projects hbs template is
{{#link-to "projects.project" item.projectID current-when="projects.user" tagName="tr"}}
What am I doing wrong here?
UPDATE
I was able to get it to initially work when the route is rendered by using an edited version of #ykaragol's helper function and link-to...
{{#link-to "projects.project" item.projectName active=(calculate-active 'projects.user projects.project' item.projectName) tagName="tr"}}
compute(params, hash){
var pathname = window.location.pathname.split('/');
var pathProj = pathname[2];
var currRoute = this.get('currentRouteName');
var routes = params[0].split(' ');
if( ($.inArray( currRoute, routes) > -1) && (pathProj == params[1]) ){
return true;
}
return false;
}
But it's not updating when I click on a different project...
If routes don't have dynamic segments, it works as described in docs. I've tested it within this twiddle.
But I couldn't make it work while using dynamic segment. Please check this twiddle. I suspect this maybe a bug. You can ask this question in slack.
By the way, as a workaround, you can pass a boolean to the currentWhen property (as mentioned in docs). So you can write a helper or a computed property to calculate it with a regex.
Updated:
As a second workaround, you can handle active property of link-to by yourself. Try this twiddle.
I tried first something like this in my application controller:
init123: function() {
var locale;
if (this.session.get('lang') === null || this.session.get('lang') === undefined)
locale = ENV.i18n.defaultLocale;
else
locale = this.session.get('lang');
this.get('i18n').set('locale', locale);
}.on('init'),
but this only works when the user is logged in. If not, always the default is set.
Then I tried stuff with the initalizer like on this answer.
How to set the i18n.locale from within an initializer from Artych
But how do I remember the last choice done in the browser?
Thx
You need to persist the preference somewhere. Take a look at the ember-localforage-adapter or ember-local-storage packages.
If you are using ember-simple-auth you can save it into the session.
I have a property mood which is part of the interface for a component. Behind the scenes I have a computed property called _mood:
const { computed, typeOf } = Ember;
_mood: computed('mood','A','B','C' function() {
let mood = this.get('mood');
if (typeOf(mood) === 'function') {
mood = mood(this);
}
return !mood || mood === 'default' ? '' : `mood-${mood}`;
}).volatile(),
I have a situation where with the volatile() that hangs of of Ember's computed object resolves all non DOM unit tests successfully but for some reason it is not triggering the template to update the DOM under any circumstance. It should, at the very least, update if any of properties being watched change (in this case ['mood','A', 'B', 'C']) change. Because it is volatile (aka, doesn't cache results) the new results would show up in a template if the template knew to re-render the component but for some reason it doesn't.
If I remove the volatile() tag it works fine for static/scalar values but if mood is a function then the result is cached and therefore it only works once (not a working solution for this problem).
How do I get the best of both worlds?
I'm still not sure why the volatile() method is turning off updates to templates. This might be a real bug but in terms of solving my problem the important thing to recognise was that the volatile approach was never the best approach.
Instead the important thing to ensure is that when mood comes in as a function that the function's dependencies are included in the CP's dependencies. So, for instance, if mood is passed the following function:
mood: function(context) {
return context.title === 'Monkeys' ? 'happy' : 'sad';
}
For this function to evaluate effectively -- and more importantly to trigger a re-evaluation at the right time -- the title property must be part of the computed property. Hopefully that's straight forward as to why but here's how I thought I might accommodated this:
_moodDependencies: ['title','subHeading','style'],
_mood: computed('mood','size','_moodDependencies', function() {
let mood = this.get('mood');
console.log('mood is: %o', mood);
if (typeOf(mood) === 'function') {
run( ()=> {
mood = mood(this);
});
}
return !mood || mood === 'default' ? '' : `mood-${mood}`;
}),
That's better, as it allows at build time for a static set of properties to be defined per component (the _mood CP for me is part of a mixin used by a few components). Unfortunately this doesn't yet work completely as the it apparently doesn't unpack/destructure the _moodDependencies.
I'll get this sorted and update this unless someone else beats me to the punch.
i'm trying to implement infinite scroll in Orchard doing minimal changes.
The script I use need to perfectly identify with a jquery selector the Next page link of pager.
Currently a standard orchard pager renders this way:
<li>></li>
the desiderable rendering is:
<li class="next">></li>
I tried many ways to override the Pager_Next template but no joy.
The pager is a list and list is done by code. No easy way to override.
A great article the should explain how to do miss some basic part (as to override the whole list for example):
http://weblogs.asp.net/bleroy/overriding-the-pager-rendering-in-orchard
Right now my workaround was to change the Orchard source CoreShapes.cs for list rendering adding these two lines:
if (itemTag != null) {
if (index == 0)
itemTag.AddCssClass("first");
if (index == count - 1)
itemTag.AddCssClass("last");
//new lines
if (index == 1 && count > 2)
itemTag.AddCssClass("previous");
if (index == count - 2 && count > 2)
itemTag.AddCssClass("next");
So far it works BUT I do not like it
1) It changes orchard source, this is bad
2) It changes all the lists (and not just pager)
So "How may I override the list for JUST my theme and for JUST pager in a way that a class is added automatically at the Page_Next li tag?"
Thanks
Try something like this in your Pager.Next.cshtml alternate view:
#using System.Web.Routing;
#{
string text = Model.Value.ToString();
string action = Model.RouteValues["action"].ToString();
RouteValueDictionary routeValues = (RouteValueDictionary)Model.RouteValues;
}
<span><a class="next" href="#Url.Action(action, routeValues)">#text</a></span>
I'm not sure if the answer by joshb is close enough for your needs.
Otherwise I would say almost the same thing.
Change the Pager.Next.cshtml and Pager.Previous.cshtml in your theme, and simply add a way for you to identify the link.
The use jQuery to add the class you need to the parent.
This is by no means an elegant solution, but it will accomplish what you're asking for without changing the core and without affecting other lists.
My test looks like this, just for testing and only for next, but you get the idea.
(Don't use id obviously as it would give you duplicate id's, this is just a quick and dirty example)
Pager.Next.cshtml :
#{
var RouteValues = (object)Model.RouteValues;
RouteValueDictionary rvd;
if (RouteValues == null) {
rvd = new RouteValueDictionary();
}
else {
rvd = RouteValues is RouteValueDictionary ? (RouteValueDictionary)RouteValues : new RouteValueDictionary(RouteValues);
}
}
<a id="pagination-pager-next" href="#Url.Action((string)rvd["action"], rvd)"><i class="fa fa-angle-right"></i></a>
And added this at the end of Pager.cshtml, but you may have a better place for it:
<script>
$(document).ready(function () {
$("#pagination-pager-next").parent().addClass("next");
});
</script>
I am confused about the initialisation values of a new model.
When creating a new record, it seems that all properties have "null" as value.
As soon as you type something in the textfield (bound to the model), and then delete the typed value (thus again an empty textfield), the value of the properties becomes emptystring.
Is this the expected behaviour ?
There is a difference for REST API's whether they receive null or "" ...
if your atribute is DS.attr('string'), so this is correct.
Doesn't matter if the string is null or empty, this just represent that the text isn't present. Thinking in programming language you would be aware of the null, because you receive error when accessing methods. But in business rules, doesn't matter if the string is null or empty, so this is a common pattern, for example:
if (name == null || name.length == 0) {
// name is missing, prompt the user ...
}
And this is a rare situation:
if (name == null) {
// some logic here
} else if(name == "") {
// other logic here
}
About the record creating. When you create it, the values is null. But when binding to a text field, for example:
{{input type="text" valueBinding="field"}}
You have this relationship:
model.field <-> textfield.value
When you set something in model.field, it will be syncronized with the textfield and vice-versa. But in the case of editing the textfield, the value will always be a string, this is the reason that you receive a empty string, instead of null, when clearing the textfield.