I am trying to use Ember.select to create a drop down list, my question is when it renders first time, I would like to set a default value. And once I change the selection, it won't check for default value until refresh the page.
Here is the code:
ET.selectedAppYearController = Ember.Object.create({
appYear: null,
isChanged: function () {
if (this.appYear != null) {
LoadETByAppYearETTypeID(this.appYear.text, ET.selectedEmailTypesController.emailType.email_template_type_id);
}
} .observes('appYear'),
setDefault: function() {
if (this.appYear.text == 2012) {
this.set('selection',2012);
}
} .observes('appYear')
});
The View:
{{view Ember.Select
contentBinding = "ET.appYearController.content"
selectionBinding="ET.selectedAppYearController.isDefault"
optionLabelPath="content.text"
optionValuePath="content.value"
}}
I guess i need to set something on selectionBinding...but what kind of value I should bind to?
1. the drop down list values are JSON type.
I was going to make a jsfiddle for this but it seems down at the moment. Will edit one in if it comes back up later as they usually make things clearer.
If you set the view to
{{view Ember.Select
contentBinding = "ET.appYearController"
selectionBinding="ET.selectedAppYearController.appYear"
optionLabelPath="content.text"
optionValuePath="content.value"
}}
and then set up the selectedAppYearController with something like:
ET.selectedAppYearController = Ember.Object.create({
appYear: ET.appYearController.get('lastObject')
}
then you should have the last element in your appYearController set as default, and when you change the selection ET.selectedAppYearController.appYear will reflect this.
Obviously if you want something other than the last element of your appYearController then you can set the value of appYear to whatever you want.
I'm way late to this party, but in the latest version of ember 0.9.7.1 there is a "prompt" attribute you can set in your Ember.Select. It looks something like,
{{view Ember.Select
prompt="This is selected by default"}}
Hope that helps some one.
It's hard to tell from your code example. One thing you could do is extract all your domain specific requirements and set up a live jsfiddle example that tries to accomplish what you are trying to do in a more generic way. http://jsfiddle.net/ You can see examples of using jsfiddle in other emberjs posts here.
But one thing I remarked is that your selectionBinding appears to be wrong. This should bind to the value you are trying to set, not a default. You could set a default in the controller if you like (just by assigning it to the value bound by selectionBinding). So I think your selectionBinding should be "ET.selectedAppYearController.appYear" if I understand your example correctly.
Not sure if anyone still needs that, but the way I did it is binding to the value with something like
{{view Ember.Select content=templates optionValuePath="content.id" optionLabelPath="content.name" value=selectedTemplateId class="form-control"}}
then in controller implement selectedTemplateId as computed property, working both as setter and getter:
selectedTemplateId: ( (key, value)->
# setter code:
if arguments.length > 1
#set('selTemplateId', value)
return value
# getter code
else
if #get('selTemplateId')?
return #get('selTemplateId')
else
return #get('templates')?.objectAt(0).id
).property()
Sorry for CoffeeScript instead of js. JS version at: http://goo.gl/5FZHFh
A bit of docs for computed properties: http://emberjs.com/api/classes/Ember.ComputedProperty.html
Related
I am currently working on a Vue-Website that supports multiple languages. The selected Language is stored in a vuex-store and i have the the computed property lang like this
lang(){
return this.$store.state.lang
}
Now i use this property in v-ifs, shows, in other computed properties and so and it works pretty well. Theres only one thing where i run into problems and that's the multiselect (https://vue-multiselect.js.org/). the computed options property looks somthing like this
mappedOptions(){
return this.options.map(
(o, ind) => {
return {name: o.title[this.lang], code: ind}
}
);
}
Now the problem is, that this does not work. Probably the options are being initialized in the created lifecycle hook and therefore are not beeing updated later on. Do you have any clever ideas how I could use multiple languages with this vue-multiselect? I pretty much like the component apart from this issue so I would not like to switch.
Put a v-if="computedOption" on the multiselect, should solve the issue
I am new to ember and I am building a DnD character sheet to learn and practice. Right now I am having a lot of trouble getting access to model data in a controller. After hours and hours of reading related posts it is just not clicking and I think I am misunderstanding what I am passing to the controller.
Basically what I am trying to do is grab data from a model so I can do calculations on them and then display the results of those calculations on the page.
Here is my transforms/router.js:
Router.map(function() {
this.route('characters', { path: '/'})
this.route('new', {path: 'new'});
this.route('view', {path: '/:character_id'});
});
So here I am trying to pull up the view page which has the URL of the character id. Next here is my route for the view page:
export default Route.extend({
character: function () {
return this.store.findRecord('character', id)
}
});
So this is finding the record of the character with the id I pass. My link looks like this and is coming from a component:
<h5 class="card-title">{{#link-to 'view' id}}{{name}}{{/link-to}}</h5>
Now I have my controller for the view page, which looks like this:
init(id){
let character = this.get('character')
}
When I try to log character, it is still undefined. When looking ember information in dev tools it seems the page is getting the information from the model after I refresh the page, but I just can't seem to be figure out how to grab that info in the controller itself to manipulate it.
I've been trying to figure this out for quite a while now, and its getting pretty frustrating. I currently have a work around where I do the calculations beforehand and just store all the calculated results in the model as well, but while I am learning I would like to understand how this works. Thanks is advance.
Edit: As pointed out in comments below I was missing let when defining character.
Your model hook seems wrong. You're using id but never define it. Probably what you want is more like this:
character(params) {
return this.store.findRecord('character', params.character_id);
}
Next your init hook makes no sense:
init(id){
let character = this.get('character')
}
First there is no id passed to the init hook. Second you're missing this._super(...arguments) which should always be called when you override init.
Last is that your controller is first created and later populated with the model. Also the model is populated as model property, not character.
So you could place this in your routes template and it will work:
This is character {{model.id}}
Or if you want to change something before you pass it to the template you should use a computed property in your controller:
foo: computed('model.id', function() {
return this.get('model.id') + ' is the id of the character';
}),
However for this code to run you need to use. The easiest way to use it is to put it into your template:
{{foo}}
I created this in a the company controller.
$scope.$watch('companyName', function () {
console.log($scope.company.name);
});
I wanted to get the company name, how do I call it where the ng-controller matches the controller name in the view?
Would it be something along the lines of this?
<span ng-init="companyName"> {{ companyName() }} </span>
I'm not sure I understand how to get the information from that function in the view. I think I've confused myself.
Yes, your sample seems a bit strange.
Anyway, to bind a $scope property in the view, you use its name.
So in the controller:
$scope.company = { name: "Acme" };
And in the view:
<span> {{ company.name }} </span>
There is no need to create a $watch manually. Angular will create a watch for any property used in the view (for you).
You also, don't have to use ng-init, unless you want to initialize something in the view.
To see the watch in action, simple add a text input next to the span:
<input type="text" ng-model="company.name"/>
If you change the value in the text box, you will see the changes reflected in the span.
Here is a plunker to play with that demonstrates.
I think you have confused yourself!
In the company controller all you need to do is:
$scope.companyName = "ACME";
To access it in the view:
{{ companyName }}
When binding to values, it is assumed to be relative to $scope. In this case (assuming the value you want is the same as what you specify in your $watch), you want to init an item to an object, and it should look something like this:
<span ng-init="company = {name: 'whatever' }">{{company.name}}</span>
For various reasons, it's not recommended to use ng-init except in things like ng-repeat. If it works, it works, but I'd recommend getting used to initializing things in your controller (just add the statement, such as $scope.company = {name: 'whatever'}; somewhere before your controller function ends). One reason is to make it easier to test your controller in unit tests, because it's annoying to make your tests dependent on a particular view).
The simplest was to do it:
controller:
$scope.companyName = "My company";
view:
<span>{{companyName}}</span>
$scope.$watch is just a function to monitor changes in the value of CompanyName, I don't think you need it.
In my ember application I have defined a select element like this in my template:
{{view Ember.Select
content=greetings
optionValuePath="content.id"
optionLabelPath="content.code"
value=selectedGreeting
selection=selectedGreeting
prompt="Please choose"}}
The controller for this page (shared form and controller for create and edit form) looks like this:
greetings: [
{code: "Mr.", id: 1},
{code: "Mrs.", id: 2}
],
selectedGreeting: null,
actions: {
save: function(){
var person = this.get('model');
if (person == null || person.id == undefined)
{
// create mode
var greeting = this.selectedGreeting.id;
// ....
var newPerson = this.get('store').createRecord('person',{
greeting: greeting,
// ..
});
newPerson.save();
this.transitionToRoute('index');
}
else
{
// edit mode
person.set('greeting', this.selectedGreeting.id);
person.save();
}
}
}
This works perfect when creating a new person, but when opening an existing one the select box shows the promt instead of the saved value (do I really have to do this with jQuery?).
Besides I suppose there must be an easier way to update the existing model with the selected values in the else branch, as the other properties get modified automagically.
Any suggestions would be appreciated, the documentation for Ember unfortunately does not help a lot in this case.
The Ember.Select view has quite a few quirks. In fact, if I remember correctly, I saw an issue on Github to just have the whole thing rewritten. For now, I'm assuming that the prompt attribute takes precedence over the selection attribute. To get around that, I would just use a conditional in the template.
{{#if model}}
{{view Ember.Select ... value=selectedGreeting}}
{{else}}
{{view Ember.Select ... prompt='Please Choose'}}
{{/if}}
Also, your second question, if I understand you correctly, you should be able to just use selection=model.greeting to bind the selection value to the model property. And if you're using the template above, you can make it so it only applies to the former and not the latter.
I got it fixed using:
{{view Ember.Select
content=greetings
optionValuePath="content.id"
optionLabelPath="content.code"
value=model.greeting
prompt="Please select"
}}
This works in both ways - loading the data from model and saving the data to the model. The prompt does not interfere with the value from what I experienced.
I have the following html that is bound to an object containing id and status. I want to translate status values into a specific color (hence the converter function convertStatus). I can see the converter work on the first binding, but if I change status in the binding list I do not see any UI update nor do I see convertStatus being subsequently called. My other issue is trying to bind the id property of the first span does not seem to work as expected (perhaps it is not possible to set this value via binding...)
HTML:
<span data-win-bind="id: id">person</span>
<span data-win-bind="textContent: status converter.convertStatus"></span>
Javascript (I have tried using to modify the status value):
// persons === WinJS.Binding.List
// updateStatus is a function that is called as a result of status changing in the system
function updateStatus(data) {
persons.forEach(function(value, index, array) {
if(value.id === data.id) {
value.status = data.status;
persons.notifyMutated(index);
}
}, this);
}
I have seen notifyMutated(index) work for values that are not using a converter.
Updating with github project
Public repo for sample (not-working) - this is a really basic app that has a listview with a set of default data and a function that is executed when the item is clicked. The function attempts to randomize one of the bound fields of the item and call notifyMutated(...) on the list to trigger a visual updated. Even with defining the WinJS.Binding.List({ binding: true }); I do not see updates unless I force it via notifyReload(), which produces a reload-flicker on the listview element.
To answer your two questions:
1) Why can't I set id through binding?
This is deliberately prevented. The WinJS binding system uses the ID to track the element that it's binding to (to avoid leaking DOM elements through dangling bindings). As such, it has to be able to control the id for bound templates.
2) Why isn't the converter firing more than once?
The Binding.List will tell the listview about changes in the contents of the list (items added, removed, or moved around) but it's the responsibility of the individual items to notify the listview about changes in their contents.
You need to have a data object that's bindable. There are a couple of options:
Call WinJS.Binding.as on the elements as you add them to the collection
Turn on binding mode on the Binding.List
The latter is probably easier. Basically, when you create your Binding.List, do this:
var list = new WinJS.Binding.List({binding: true});
That way the List will call binding.as on everything in the list, and things should start updating.
I've found that if I doing the following, I will see updates to the UI post-binding:
var list = new WinJS.Binding.List({binding: true});
var item = WinJS.Binding.as({
firstName: "Billy",
lastName: "Bob"
});
list.push(item);
Later in the application, you can change some values like so:
item.firstName = "Bobby";
item.lastName = "Joe";
...and you will see the changes in the UI
Here's a link on MSDN for more information:
MSDN - WinJS.Binding.as
Regarding setting the value of id.
I found that I was able to set the value of the name attribute, for a <button>.
I had been trying to set id, but that wouldn't work.
HTH
optimizeBindingReferences property
Determines whether or not binding should automatically set the ID of an element. This property should be set to true in apps that use Windows Library for JavaScript (WinJS) binding.
WinJS.Binding.optimizeBindingReferences = true;
source: http://msdn.microsoft.com/en-us/library/windows/apps/jj215606.aspx