Emberjs access a nested property - ember.js

This question is the following of this one emberjs display an object return from an ajax call
To resume a bit, I have a dynamic list generated with some button for each item of that list. I catch the event of any button with this class :
App.EnquiriesView = Ember.View.extend({
didInsertElement: function() {
var that = this;
this.$().on('click', '.view-btn', function(){
var id = $(this).attr('data-value');
that.get('controller').send('clickBtn', id);
});
}
});
And it goes to my controller here :
App.EnquiriesController = Ember.ObjectController.extend({
actions: {
clickBtn: function( id ) {
console.log('DEBUG: ClickBtn OK id = ' + id);
//console.log(App.Enquiries.findOne(id));
this.transitionToRoute('enquiry', /*App.Enquiries.findOne(id)*/id);
}
}
});
The router related :
App.EnquiryRoute = Ember.Route.extend({
model: function( param ) {
console.log('Enquiry id = ' + param.enquiry_id);
return App.Enquiries.findOne(param.enquiry_id);
}
});
and my map :
App.Router.map(function() {
this.resource('login', { path: '/' });
this.resource('home');
this.resource('enquiries', function (){
this.route('create');
});
this.resource('enquiry', { path: 'enquiry/:enquiry_id' }, function(){
this.route('update');
});
});
So far for now when the user click on the button its redirect correctly to the enquiry with the good URL (e.g : /#/enquiry/1)
But the problem is coming from my update class now. I've just create a button with the action helper to display the update form :
App.EnquiryController = Ember.ObjectController.extend({
actions: {
update: function() {
console.log('DEBUG: in EnquiryController update');
console.log(this.get('model'));
this.transitionToRoute('enquiry.update');
}
}
});
So when you click on the update button you are redirected to this kind of URL : /#/enquiry/undefined/update instead of /#/enquiry/1/update ...
I don't know how this can happen and how I can loose my id during the process...
Thanks for your help.
[edit] If you need to know what is my findOne function :
findOne: function(id) {
return $.ajax({
url: host + 'mdf/enquiry/' + id,
type: 'GET',
accepts: 'application/json',
success: function (data) {
console.log('DEBUG: GET Enquiry ' + id + ' OK');
},
error: function() {
console.log('DEBUG: GET Enquiry ' + id + ' Failed');
}
});
}
Its fetching the data from the server for every item after you've click on the related button in the list.
Here is the object I've got back :
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
abort: function ( statusText ) {
always: function () {
complete: function () {
done: function () {
error: function () {
fail: function () {
getAllResponseHeaders: function () {
getResponseHeader: function ( key ) {
overrideMimeType: function ( type ) {
pipe: function ( /* fnDone, fnFail, fnProgress */ ) {
progress: function () {
promise: function ( obj ) {
readyState: 4
responseJSON: Object
responseText: "{"ok":true,"enquiry":{"id":1,"domainid":"domain","userid":"userid","status":null,"type":"new","customerName":"Marco","customerEmail":"Marco#email.com",...}"
setRequestHeader: function ( name, value ) {
state: function () {
status: 200
statusCode: function ( map ) {
statusText: "OK"
success: function () {
then: function ( /* fnDone, fnFail, fnProgress */ ) {
__proto__: Object

There's a number of issues going on here. A couple of things you're doing are very "non-ember" idiomatic, and I wouldn't be surprised if further issues pop up as a result. But I'll focus on the question asked, and if you want more advice on adjusting further segments I'm happy to provide it.
In short, you have the following code:
this.resource('enquiry', { path: 'enquiry/:enquiry_id' });
in your map, but an enquiry object that looks something like:
{"ok":true,
"enquiry":{
"id":1,
"domainid":"motorpark",
"userid":"motorpark/mpuser"‌​
...
}
}
And these don't match. Your map defines that it is serialized by a field enquiry_id *which does not exist on your model. To fix this you can do one of these solutions:
Solution 1: Adjust your model
If you want to keep your map as is, you'll need to adjust your model to have an enquiry_id field, such as:
{"ok":true,
"enquiry_id":1,
"enquiry":{
"id":1,
"domainid":"motorpark",
"userid":"motorpark/mpuser"‌​
...
}
}
Solution 2: Adjust your Map (recommended)
It's easier to just change your map though. To do this, replace your enquiry resource on the map with:
this.resource('enquiry', {path: 'enquiry/:enquiry.id'});
The . tells ember that the desired element is the id field within the enquiry object.
You'll also need to modify how you access the param value. Because you're naming an element of the param as enquiry.id you need to specify this as a variable name and not a path when retrieving the value. On your route, change all instances of:
param.enquiry_id
To:
param['enquiry.id']

Related

How do I send a object into the Jquery $('calendar') selector from outside

In the below code, in the didInsertElement, I want to send myController object in to the JQuery Selector ('#calendar').How can I do this. Is it possible to do this. This might be easy. Any one please
(function () {
App.EventDisplayCalendarDisplayView = Ember.View.extend({
didInsertElement: function () {
//I got the controller object here, now I want to pass this in to the JQuery calendar selector below.
// How to do this
var myController = this.get('controller');
$('#calendar').fullCalendar({
weekends: true,
events: this.get('controller'),
dayClick: function(start, allDay, jsEvent, view) {
//I want to access the controller here how to do this.
var controller = myController; //This does not work.
},
eventLimit: true, // for all non-agenda views
views: {
agenda: {
eventLimit: 6 // adjust to 6 only for agendaWeek/agendaDay
}
},
eventClick: function(xEvent, jsEvent, view) {
debugger;
alert("Title: " + xEvent.title //Get the event title
+ "\n StartTime: " + xEvent.start //Get the event start date
+ "\n EndTime: " + xEvent.end //Get the event end date
);
console.log(xEvent); //See your console for all event properties/objects
}
});
},
willDestroyElement: function () {
}
});
}());
Look into calling the function with .bind(variable): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

ember.js sending actions to controllers from views

I've created a typeahead view and i'm trying to send an action to the current controller to set a property. Here is my typeahead view
App.Typeahead = Ember.TextField.extend({
dataset_name: undefined, //The string used to identify the dataset. Used by typeahead.js to cache intelligently.
dataset_limit: 5, //The max number of suggestions from the dataset to display for a given query. Defaults to 5.
dataset_template: undefined, //The template used to render suggestions. Can be a string or a precompiled template. If not provided, suggestions will render as their value contained in a <p> element (i.e. <p>value</p>).
dataset_engine: undefined, //The template engine used to compile/render template if it is a string. Any engine can use used as long as it adheres to the expected API. Required if template is a string.
dataset_local: undefined, //An array of datums.
dataset_prefetch: undefined, //Can be a URL to a JSON file containing an array of datums or, if more configurability is needed, a prefetch options object.
dataset_remote: undefined, //Can be a URL to fetch suggestions from when the data provided by local and prefetch is insufficient or, if more configurability is needed, a remote options object.
ctrl_action: undefined,
didInsertElement: function () {
this._super();
var self = this;
Ember.run.schedule('actions', this, function () {
self.$().typeahead({
name: self.get('dataset_name'),
limit: self.get('dataset_limit'),
template: self.get('dataset_template'),
engine: self.get('dataset_engine'),
local: self.get('dataset_local'),
prefetch: self.get('dataset_prefetch'),
remote: self.get('dataset_remote')
}).on('typeahead:selected', function (ev, datum) {
self.selected(datum);
});
});
},
willDestroyElement: function () {
this._super();
this.$().typeahead('destroy');
},
selected: function(datum) {
this.get('controller').send(this.get('ctrl_action'), datum);
}
});
Here's an implementation
App.CompanyTA = App.Typeahead.extend({
dataset_limit: 10,
dataset_engine: Hogan,
dataset_template: '<p><strong>{{value}}</strong> - {{year}}</p>',
dataset_prefetch: '../js/stubs/post_1960.json',
ctrl_action: 'setCompanyDatum',
selected: function (datum) {
this._super(datum);
this.set('value', datum.value);
}
});
and here's my controller
App.PeopleNewController = Ember.ObjectController.extend({
//content: Ember.Object.create(),
firstName: '',
lastName: '',
city: '',
state: '',
ta_datum: undefined,
actions: {
doneEditing: function () {
var firstName = this.get('firstName');
if (!firstName.trim()) { return; }
var lastName = this.get('lastName');
if (!lastName.trim()) { return; }
var city = this.get('city');
if (!city.trim()) { return; }
var state = this.get('state');
if (!state.trim()) { return; }
var test = this.get('ta_datum');
// Create the new person model
var person = this.store.createRecord('person', {
firstName: firstName,
lastName: lastName,
city: city,
state: state
});
// Clear the fields
this.set('firstName', '');
this.set('lastName', '');
this.set('city', '');
this.set('state', '');
// Save the new model
person.save();
},
setCompanyDatum: function(datum) {
this.set('ta_datum', datum);
}
}
});
I'm expecting the setCompanyDatum controller action to be called, but it's not. Everything else is working as expected. The App.Typeahead.selected method is being called with the right action name, but it doesn't actually call the action method.
the controller inside your App.Typeahead points to the instance of the App.Typeahead, not the controller from the route where you are creating the view.
You should just be using sendAction
http://emberjs.jsbin.com/EduDitE/2/edit
{{view App.Typeahead}}
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
},
actions:{
externalAction:function(item){
console.log('helllllo' + item);
}
}
});
App.Typeahead = Ember.TextField.extend({
internalAction: 'externalAction',
didInsertElement: function () {
this.sendAction('internalAction', " I'm a sent action");
this._super();
}
});

call an ember component action from within the component

I am creating a component to wrap the select2 select box. The code is below:
App.FixedSelectComponent = Ember.Component.extend({
actions: {
change: function(value) {
this.set('selectedValue',value);
}
},
didInsertElement : function(){
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});
however, what I am stuck at is how to send the data that I've got in the on("change") event to the action:change that I've defined , or if I can set the selectedValue property itself in the on("change") event
"this" isn't the component at the "// send to change" lines - how / where do I get the reference to the component itself at this point ?
basically what I am trying to achieve is to get the data passed to the "change" event of select2 into my selectedValue property
thanks
You can use Component.send('actionName').
I found it in Ember's documentation.
this context will not refer to FixedSelectComponent context in $.each, and also use send method which will call FixedSelectComponent change method..
refer : http://emberjs.com/api/classes/Ember.Component.html#method_send
didInsertElement : function(){
var _this = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
_this.send('change',value.split('>')[2].split('<')[0]); // send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
_this.send('change',e.val.split('>')[2].split('<')[0]); // send to change
}
});
}
this.get('actions').change.call(this, value);
Check http://emberjs.com/api/classes/Ember.Component.html#property_actions -- 'actions' is simply another property on your Component.
Try this:
App.FixedSelectComponent = Ember.Component.extend({
change: function(value) {
this.set('selectedValue',value);
}
didInsertElement : function(){
var self = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});
this._actions['change'].apply(this, value);

Ajax without ember data - Uncaught TypeError: Object #<Object> has no method 'forEach'

I'm attempting to build a non blocking async call in an Ember.js app without using Ember Data.
I have the following Ember.js model:
App.Player = Ember.Object.extend({
id: '',
alias: '',
name: '',
twitterUserName: '',
isFeatured: ''
});
App.Player.reopenClass({
getPlayers: function () {
var players = Ember.ArrayProxy.create({ content: [] });
$.getJSON("/api/players").then(function (response) {
response.forEach(function (p) {
players.pushObject(App.Player.create(p));
});
});
return players;
}
});
And I am calling it as follows in my route:
App.IndexRoute = Ember.Route.extend({
model: function (params) {
return App.Player.getPlayers();
}
});
For some reason I am getting the following javascript error:
Uncaught TypeError: Object # has no method 'forEach'
I've tried a few variants I have seen around but nothing seems to work. Any help would be appreciated...
EDIT - Found the solution with some help from Darshan, here's the working code:
App.Player.reopenClass({
getPlayers: function () {
var players = [];
$.ajax({
url: "/api/players",
}).then(function (response) {
response.players.forEach(function (player) {
var model = App.Player.create(player);
players.addObject(model);
});
});
return players;
}
});
Your response.forEach suggests that you are expecting the json response body to be an array. It is probably wrapped in some root element like players or data like so.
{
"players": [...]
}
If that is the case you need to use forEach on that root element like response.players.forEach.
You also want to restructure that code to return a promise directly. The Ember router will then pause until your json is loaded and only proceed after it finishes. Something like this,
getPlayers: function () {
return $.getJSON("/api/players").then(function (response) {
var players = Ember.ArrayProxy.create({ content: [] });
response.players.forEach(function (p) {
players.pushObject(App.Player.create(p));
});
return players;
});
}
Returning players resolve the promise. And Ember understands that when a promise resolves that result is the model.

Ember.js pass params object to linkTo or action helper

Is there a way to pass a params object to a linTo or action helper?
I have an object and it needs compound keys. I am using Ember Model and had to modify the adapter. In the Router that gets the initial object i pass a params object with the necessary keys. My problem is trying to do the same thing when using either a linkTo or action with a transitionToRoute. Neither hit the router again as far as I can tell.
Im coming back to this questions. Im still not sure the proper way to handle this.
App.Router.map(function () {
this.resource("clients", { path: 'clients' }, function () {
this.resource("client", { path: ':client_id' }, function () {
this.resource("claims", function () {
this.resource('claim', { path: ':claim_id/:claim_sub' }, function () {
this.resource('lines', { path: 'lines' }, function () {
this.resource('line', { path: ':line_id' }, function () {
this.resource('flags', function () {
this.resource('flag', { path: ':flag_id' });
});
});
this.route('claim_lines');
});
});
});
});
});
this.route("errors", { path: '/errors/:error_id' });
});
When i link to anything under the claim, where the compound slugs are, i get those set to undefined.
UPDATE
The serialize was just what i needed.
App.ClaimRoute = Nucleus.Route.extend({
model: function (params) {
params.client_id = this.modelFor('client').get('client_id');
return App.Claim.find('claim', params);
},
serialize: function (model) {
return { claim_id: model.get('cla_seq'), claim_sub: model.get('cla_sub') };
}
});
I think a code example will be most helpful, but to try to answer your question, yes you can pass an object to linkTo with something like the following:
{{#linkTo routename params}}Text for Link{{/linkTo}}
Then for the route code matching the route name, you can take params as a parameter when you define the serialize function and use them to build the URL to match the routing segments defined in your router:
serialize: function(params) {
// params.property1 and params.property2
return { segment_1: params.property1, segment_2: params.property2 };
}