Ember: Setting a component object value within a promise - ember.js

I have component with a couple of properties, using a promise in the willRender hook to try and create a (pagination) object:
export default Ember.Component.extend({
pagination:null,
testing:null, // to check if this.set is ok within the promise!
willRender() {
let page = {};
let model = this.get('data');
model.get('products').then(relatedItems => {
let maxRecords = relatedItems.get('length');
relatedItems.forEach(function(item,index) {
if (item.get('slug') === itemModel.get('id')) {
if (index === 0) {
page.Prev = null;
page.Next = relatedItems.objectAt(index+1).get('slug');
}
else if (index+1 === maxRecords) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
page.Next = null;
}
else {
page.Prev = relatedItems.objectAt(index-1).get('slug');
page.Next = relatedItems.objectAt(index+1).get('slug');
}
}
});
this.set('testing','hello world');
console.log(this.get('testing')); // hello world
this.set('pagination',page);
console.log(this.get('pagination')); // Object {Prev: "product-1", Next: "product-2"}
},reject => {
console.log('error '+reject);
});
}
})
In my template
{{testing}} // prints hello world
However, if I try and access {{pagination}} eg {{log pagination}}, the browser crashes with a loop printing out the object to the console.
I don't know where I'm going wrong here - any help much appreciated!

It's likely you are triggering the template to rerender causing willRender to fire over and over which causes an infinite loop in your code.
willRender is a non-standard place to do this code, init would be more standard since it only fires on initialization of the component. Even better would be to use
myInit: Ember.on('init', function(){
....
})`
instead of overriding willRender on the object.

try to check whether Object is present at specific position. i think its going undefined during iteration of for loop. try to ensure that ObjectAt is not returning undefined or null value during running of for loop.
relatedItems.forEach(function(item,index) {
if (item.get('slug') === itemModel.get('id')) {
if (index === 0) {
page.Prev = null;
if(relatedItems.objectAt(index+1) ! = undefined) {
page.Next = relatedItems.objectAt(index+1).get('slug');
}else{
page.Next == null;
}
}
else if (index+1 === maxRecords) {
if(relatedItems.objectAt(index-1) ! = undefined) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
}else{
page.Prev = null;
}
page.Next = null;
}
else {
if(relatedItems.objectAt(index-1) ! = undefined) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
}else{
page.Prev = null;
}
if(relatedItems.objectAt(index+1) ! = undefined) {
page.Next = relatedItems.objectAt(index+1).get('slug');
}else{
page.Next = null;
}
}
}
Please ensure that Object at is returning object.

There seems to be a few problems here, would be interested to know what your console errors are.
You don't seem to have defined itemModel so don't know how you're referencing that.
Also you can't access this from within a .then. You need to do something like this to set a component variable.
var _this = this;
promise().then(function(results) {
_this.set('testing', 'hello world');
});

you are not using , after testing:null
there should be , after testing property like that
pagination:null,
testing:null, // i use ',' after testing: null property

try to use your pagination code under init hook rather than willrender hook
init() {
//you code
},

Related

Accessing controller properties within controller function in ember

I'm unable to access the controller property within a controller function:
App.ViewRController = Ember.Controller.extend({
datas:null,
actions:{
viewfile:function() {
let filename = this.get('filename');
let count=0;
let data;
let self = this;
if (filename != null)
{
filename = filename.trim();
if(filename.length > 0)
{
try {
let obj= new XMLHttpRequest();
obj.onreadystatechange = function() {
if(obj.readyState==4 && obj.status ==200)
{
let json = obj.responseText;
console.log(self);
data = JSON.parse(json);
self.set(datas, data);
}
}
let params = "filename=" + filename;
obj.open("POST","view");
obj.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
obj.send(params);
//this.set('datas',data);
}
catch(e)
{
alert(e);
}
}
else
alert("enter filename");
}
else
alert("enter filename");
}
}
The template should iterate over the data object with properties. But I couldn't set the data property within the function in the controller.
You trying to set a property with
self.set(datas,data);
But you need to be setting the "datas" property as a string:
self.set("datas", data);

IE9: store.find is failing

I can't seem to fetch new data in Internet Explorer 9. For the purpose of an example I test the store this way:
App.__container__.lookup('store:main').find('style')
The only error I receive is the following:
SCRIPT5022: Error: Assertion Failed: [object Object]
Does Ember-data works out of the box (without polyfills, ...) in Internet Explorer 9?
versions:
Ember: 1.9.1
Ember-data: 1.0.0-beta.12
Problem solved. When doing an AJAX request with jQuery, this normally happens through the XMLHttpRequest object.
On IE8-9, this object is not present, instead it uses XDomainRequest. The simplest fix for this is adding: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest.
ember-data works out of the box with IE8+. According to this issue:
We've been supporting IE8 with our platform (built on Ember) for a
while now. Things I know:
shim/sham is not needed, it's polyfilled by Ember and Ember-Data.
You will need it if you want additional things like .bind() on a function, then you must prepend it to the vendor file (using Brocfile)
and we only include the shim for that purpose, not the sham
Solution Synthesis
Reason :
On IE8-9, this object is not present, instead it uses XDomainRequest.
Solution :
The issue is solved. When using an AJAX request with jQuery. Normally this is done through the XMLHttpRequest object. A simple fix would be using the Open-Source jQuery-ajaxTransport-XDomainRequest
Code : Adding :
jQuery-ajaxTransport-XDomainRequest.js
/*!
* jQuery-ajaxTransport-XDomainRequest - v1.0.4 - 2015-03-05
* https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
* Copyright (c) 2015 Jason Moon (#JSONMOON)
* Licensed MIT (/blob/master/LICENSE.txt)
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals.
factory(jQuery);
}
}(function($) {
// Only continue if we're on IE8/IE9 with jQuery 1.5+ (contains the ajaxTransport function)
if ($.support.cors || !$.ajaxTransport || !window.XDomainRequest) {
return $;
}
var httpRegEx = /^(https?:)?\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^(\/\/|' + location.protocol + ')', 'i');
// ajaxTransport exists in jQuery 1.5+
$.ajaxTransport('* text html xml json', function(options, userOptions, jqXHR) {
// Only continue if the request is: asynchronous, uses GET or POST method, has HTTP or HTTPS protocol, and has the same scheme as the calling page
if (!options.crossDomain || !options.async || !getOrPostRegEx.test(options.type) || !httpRegEx.test(options.url) || !sameSchemeRegEx.test(options.url)) {
return;
}
var xdr = null;
return {
send: function(headers, complete) {
var postData = '';
var userType = (userOptions.dataType || '').toLowerCase();
xdr = new XDomainRequest();
if (/^\d+$/.test(userOptions.timeout)) {
xdr.timeout = userOptions.timeout;
}
xdr.ontimeout = function() {
complete(500, 'timeout');
};
xdr.onload = function() {
var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
var status = {
code: 200,
message: 'success'
};
var responses = {
text: xdr.responseText
};
try {
if (userType === 'html' || /text\/html/i.test(xdr.contentType)) {
responses.html = xdr.responseText;
} else if (userType === 'json' || (userType !== 'text' && /\/json/i.test(xdr.contentType))) {
try {
responses.json = $.parseJSON(xdr.responseText);
} catch(e) {
status.code = 500;
status.message = 'parseerror';
//throw 'Invalid JSON: ' + xdr.responseText;
}
} else if (userType === 'xml' || (userType !== 'text' && /\/xml/i.test(xdr.contentType))) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = false;
try {
doc.loadXML(xdr.responseText);
} catch(e) {
doc = undefined;
}
if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
status.code = 500;
status.message = 'parseerror';
throw 'Invalid XML: ' + xdr.responseText;
}
responses.xml = doc;
}
} catch(parseMessage) {
throw parseMessage;
} finally {
complete(status.code, status.message, responses, allResponseHeaders);
}
};
// set an empty handler for 'onprogress' so requests don't get aborted
xdr.onprogress = function(){};
xdr.onerror = function() {
complete(500, 'error', {
text: xdr.responseText
});
};
if (userOptions.data) {
postData = ($.type(userOptions.data) === 'string') ? userOptions.data : $.param(userOptions.data);
}
xdr.open(options.type, options.url);
xdr.send(postData);
},
abort: function() {
if (xdr) {
xdr.abort();
}
}
};
});
return $;
}));

cannot call method 'set' of undefined ember

I get cannot call method 'set' of undefined object when I remove item from ArrayController.
start: function () {
this.registerModel('listController', Ember.ArrayController.create());
this._super();
},
My view looks like,
{{#each item in marketingListCriteriaList}}
{{view Select valueBinding="item.entity" contentBinding="controller.allEntities" optionLabelPath="content.Name" optionValuePath="content.Name" }}
{{/each}}
I have a observer method which observes
.observes('listController.#each.entity')
The above observer gets called when i remove object from array controller using removeObject() method.
Are there any other ways to remove objects from array?
entityChangeObserver: function (thisModule) {
var thisModule = this;
var criteria = thisModule.get('listController.content');
if (criteria != undefined && criteria.length > 0 && criteria[criteria.length - 1].entity != undefined) {
var presentObject = criteria[criteria.length - 1];
$.each(thisModule.get('allEntities'), function (index, item) {
if (presentObject.entity === item.Name) {
presentObject.set('allAttributes', item.Attributes);
}
});
}
}.observes('listController.#each.entity'),
attributeChangeObserver: function (thisModule) {
var thisModule = this;
var criteria = thisModule.get('listController.content');
if (criteria != undefined && criteria.length > 0 && criteria[criteria.length - 1].attribute != undefined) {
var presentObject = criteria[criteria.length - 1];
$.each(presentObject.get('allAttributes'), function (index, item) {
if (presentObject.attribute === item.Name) {
thisModule.setDefaulsVisibility(presentObject);
if (item.Type === '1') {
presentObject.set('textVisible', true);
}
else if (item.Type === '2') {
presentObject.set('selectVisible', true);
presentObject.set('allValues', item.Values);
}
else if (item.Type === '3') {
presentObject.set('multiSelectVisible', true);
presentObject.set('allValues', item.Values);
}
else if (item.Type === '4') {
presentObject.set('dateVisible', true);
}
}
});
}
}.observes('listController.#each.attribute'),
You can also remove array elements using "remove" instead of "removeObject" however, you might want to double check your logic in your observer which gives undefined error when you remove an object. I would recommend sticking to remove object and just fixing the error within the observer. Also, do note that using "remove" will not instantly update handlebar templates if you are looping over the array.
Firstly sorry for the late post.
I figured the solution for the problem "calling set on destroyed object".
In my control definition of didInsertElement I made a check for if (!me.isDestroyed) for every set operation.

nokia here maps geocoding and display map

please, could someone explain me how can I manage, in Here Maps code, nokia.maps.map.Display listener and nokia.places.search.manager.geocode method?
I have markers to be geocoded, in geocode "oncomplete" it waits for the request to be completed, after that it listens when map display is ready, so as it happens asynchronously, it sometimes displays on browser a map not finished yet, this is because map.zoomTo(bbox, false) was not executed.
How can I manage these two events?
<script type="text/javascript">
function goPageOnLoad() {
container = new nokia.maps.map.Container();
map = new nokia.maps.map.Display(document.getElementById('gmapcanvas'),
{ components:[ infoBubbles, new nokia.maps.map.component.Behavior(), new
nokia.maps.map.component.ZoomBar(), new
nokia.maps.map.component.Overview(), new
nokia.maps.map.component.TypeSelector(), new
nokia.maps.map.component.ScaleBar() ] });
addMarkersGeoLoc(map,container);
}
function addMarkersGeoLoc(map,container) {
countMarkerGeoLoc=1; coordinate = new
nokia.maps.geo.Coordinate(0, 0); startGeoCode('Via Roma 2, 16038 Santa
Margherita Ligure GE ');
}
function startGeoCode(addressStringt) {
nokia.places.search.manager.geoCode({
searchTerm : addressString,
onComplete: function(data, requestStatus){
if(data != null){
coordinate =
new nokia.maps.geo.Coordinate(data.location.position.latitude,
data.location.position.longitude);
var marker = new
nokia.maps.map.StandardMarker(coordinate, {brush: {color: "#FF0000"}});
marker.addListener( CLICK, function (evt) {
infoBubbles.openBubble(content, marker.coordinate); } );
container.objects.add(marker);
managersFinished++;
}
else {
managersFinished++; alert('Address: '+addressString+', is not
localizable.');
}
if(managersFinished === countMarkerGeoLoc) {
map.objects.add(container);
map.set('zoomLevel', 14);
map.addListener("displayready", function () {
map.set('center',
[40.645304, 14.874063]);
bbox = container.getBoundingBox();
if(bbox !=null){
map.zoomTo(bbox, false);
}
});
}
}
});
}
</script>
The simplest method would be to wait for the displayready event before starting your geocoding.
function goPageOnLoad() {
container = new nokia.maps.map.Container();
map = new nokia.maps.map.Display
.. etc...
map.addListener('displayready', function () {
addMarkersGeoLoc(map,container);
}, false);
The other alternative would be to have a global bbox variable and use zoomTo() twice - either on displayready or on managersFinished === countMarkerGeoLoc i.e.
var bbox;
...
function goPageOnLoad() {
container = new nokia.maps.map.Container();
map = new nokia.maps.map.Display
.. etc...
map.addListener("displayready", function () {
if(bbox){map.zoomTo(bbox, false);}
});
...
if(managersFinished === countMarkerGeoLoc) {
map.objects.add(container);
bbox = container.getBoundingBox();
map.zoomTo(bbox, false);
Either the first or the second of the zoomTo() functions must fire to move the map.

how to return values from actions in emberjs

how to return some value from actions??
I tried this:
var t = this.send("someAction", params);
...
actions:{
someAction: function(){
return "someValue";
}
}
actions don't return values, only true/false/undefined to allow bubbling. define a function.
Ember code:
send: function(actionName) {
var args = [].slice.call(arguments, 1), target;
if (this._actions && this._actions[actionName]) {
if (this._actions[actionName].apply(this, args) === true) {
// handler returned true, so this action will bubble
} else {
return;
}
} else if (this.deprecatedSend && this.deprecatedSendHandles && this.deprecatedSendHandles(actionName)) {
if (this.deprecatedSend.apply(this, [].slice.call(arguments)) === true) {
// handler return true, so this action will bubble
} else {
return;
}
}
if (target = get(this, 'target')) {
Ember.assert("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function');
target.send.apply(target, arguments);
}
}
I had the same question. My first solution was to have the action put the return value in a certain property, and then get the property value from the calling function.
Now, when I need a return value from an action, I define the function that should be able to return a value seperately, and use it in an action if needed.
App.Controller = Ember.Controller.extend({
functionToReturnValue: function(param1, param2) {
// do some calculation
return value;
},
});
If you need the value from the same controller:
var value = this.get("functionToReturnValue").call(this, param1, param2);
From another controller:
var controller = this.get("controller"); // from view, [needs] or whatever
var value = controller.get("functionToReturnValue").call(controller, param1, param2); // from other controller
The first argument of the call() method needs to be the same object that you are running the return function of; it sets the context for the this reference. Otherwise the function will be retrieved from the object and ran from the current this context. By defining value-returning functions like so, you can make models do nice stuff.
Update I just found this function in the API that seems to do exactly this: http://emberjs.com/api/#method_tryInvoke
Look this example:
let t = this.actions.someAction.call(this, params);
Try
var t = this.send("someAction", params);
instead of
vat r = this.send("someAction", params);
Just use #set for set value which you want to return
actions:{
someAction: function(){
// return "someValue";
this.set('var', someValue);
}
}