Just trying to figure things out in Meteor JS. I am stuck trying to get a Meteor.call result object passed back into a template. Here is my code:
HTML Template
<template name="showTotals">
<div class="container">
{{#each recordCnt}}
{{/each}}
</div>
</template>
Client JS
//get a count per item
Template.showTotals.recordCnt = function(){
Meteor.call('getCounts', function(err, result) {
if (result) {
//console.log(result); <-this shows object in javascript console
return result;
}
else {
console.log("blaaaa");
}
});
}
Server JS
Meteor.startup(function () {
Meteor.methods({
getCounts: function() {
var pipeline = [{$group: {_id: null, count: {$sum : 1}}}];
count_results = MyCollection.aggregate(pipeline);
return count_results;
}
});
});
Global JS
MyCollection = new Meteor.Collection('folks');
I have tried adding a console.log(result) for the results of the call, and it displays in the javascript console with the expected results. I cannot get this to populate recordCnt however.
Any ideas?
You can't do it the way you want because the callback from the Meteor.call runs asynchronously.
You can, however, use the callback to set a Session value and have your helper read Session.get() instead.
Template.showTotals.rcdCount = function() {
return Session.get('rcdCount');
}
Template.showTotals.rendered = function() {
Meteor.call('getCounts', function(err, result) {
if (result) {
Session.set('rcdCount', result);
}
else {
console.log("blaaaa");
}
});
}
See here for a similar answer that includes an example of a "hacky" way of doing it.
Related
I'm using Telerik for MVC and trying to get the multi-select to populate with the initial values in an Edit scenario.
<script>
function filterProducts() {
return {
manufacturerId: $("#ServiceBulletinItem_ManufacturerId").val()
};
}
function onManufacturerChange(e) {
var v = e.sender.dataItem().Value;
$.post("#Url.Action("GetCascadeProducts", "Components")", { manufacturerId: v }, function (result) {
var grid = $("#ServiceBulletinItem_ApplicableProducts").data("kendoMultiSelect")
grid.setDataSource(result)
});
}
function InitialPopulate(manId) {
$.post("#Url.Action("GetCascadeProducts", "Components")", { manufacturerId: manId }, function (result) {
var grid = $("#ServiceBulletinItem_ApplicableProducts").data("kendoMultiSelect")
grid.setDataSource(result)
});
}
$(document).ready(function () {
$('.control-datepicker').Zebra_DatePicker();
var m = $("#ServiceBulletinItem_ManufacturerId").val();
InitialPopulate(m);
});
</script>
<div class="form-group">
#Html.LabelFor(m => m.ManufacturerList, "Manufacturer", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#(Html.Kendo().DropDownListFor(m => m.ServiceBulletinItem.ManufacturerId)
.HtmlAttributes(new { #class = "col-md-6 form-control" })
.Filter("contains")
.DataValueField("Value")
.DataTextField("Text")
.BindTo((IEnumerable<SelectListItem>)Model.ManufacturerSelectList)
.HtmlAttributes(new { style = "width:70%;" }).Events(e =>
{
e.Change("onManufacturerChange");
})
)
</div >
</div >
<div class="form-group">
#Html.LabelFor(m => m.ProductList, "Product", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#(Html.Kendo().MultiSelectFor(m => m.ServiceBulletinItem.ApplicableProducts)
.AutoClose(false)
.DataTextField("ProductName")
.DataValueField("ProductId")
.Placeholder("Select products...")
)
</div>
</div>
I'm trying to populate the manufacturer drop down and the Product multiSelect. The ApplicableProducts item is an IEnumerable representing the ProductId's of all those previously selected and I know that when I select the manufacturer and it calls the GetCascadeProducts controller method it will return back a collection of ProductId and ProductName for all the manufacturers products of which those productId is the ApplicableProducts property should exist.
On document.ready I can call the InitialPopulate method with the manufacturerID which will populate the multiSelect items but can't seem to populate the initial values.
I couldnt get the binding working correctly so ended up using
#(Html.Kendo().MultiSelect()
.Name("ServiceBulletinItem_ApplicableProducts")
.AutoClose(false)
.DataTextField("ProductName")
.DataValueField("ProductId")
.Placeholder("Select products 2...")
.AutoBind(false)
)
and then on the using the following code on document ready to make an ajax call to populate the manufacturer and product controls
function PopulateProductsInitial(manId) {
$.post("#Url.Action("GetCascadeProducts", "Components")", { manufacturerId: manId }, function (result) {
var grid = $("#ServiceBulletinItem_ApplicableProducts").data("kendoMultiSelect")
grid.setDataSource(result);
var s = $("#ServiceBulletinItem_Id").val();
$.post("#Url.Action("GetSBProducts", "ServiceBulletins")", { Id: s}, function (result) {
var arr = [];
result.forEach(function (element) {
arr.push(element.ProductId);
});
var grid = $("#ServiceBulletinItem_ApplicableProducts").data("kendoMultiSelect")
grid.value(arr);
});
});
}
}
$(document).ready(function () {
//Populate Initial Values
PopulateProductsInitial($("#ServiceBulletinItem_ManufacturerId").val());
$('#YourButton').click(SendForm);
});
The problem then became sending the selected items back to the controller when the edit was complete which again seemed convoluted because the control was not bound and therefore I had to make an Ajax call to submit the data.
function SendForm() {
var items = $("#ServiceBulletinItem_ApplicableProducts").data("kendoMultiSelect").value();
//Manipulate into ServiceBulletinViewModel for the save
var data = {
Id: $("#ServiceBulletinItem_Id").val(),
ServiceBulletinItem: {
Id: $("#ServiceBulletinItem_Id").val(),
ManufacturerId: $("#ServiceBulletinItem_ManufacturerId").val(),
IssueDate: $('#ServiceBulletinItem_IssueDate').val(),
Heading: $('#ServiceBulletinItem_Heading').val(),
Details: $('#ServiceBulletinItem_Details').val(),
Url: $('#ServiceBulletinItem_Url').val(),
SelectedProducts: items
}
}
$.ajax({
type: 'POST',
url: '/ServiceBulletins/Edit',
contentType: 'application/json',
data: JSON.stringify(data),
success: function (result) {
//Your success code here..
if (result.redirectUrl != null) {
window.location = result.redirectUrl;
}
},
error: function (jqXHR) {
if (jqXHR.status === 200) {
alert("Value Not found");
}
}
});
}
It all seemed a lot more convoluted than any of the demo's that teleriks and I couldnt find any good examples of binding from remote sources which looked similar.
As the binding is convention based I'm wondering if its possible to simplify the ajax calling for the post functionality with the correct naming of the controls so that I can simply get the selected items on the multiselect control or if the ajax post is the way to go.
I have some issue with Ember that I don't really understand. I have an array like this :
var device.images = [
0: {
url: "url",
key: "key"
},
1: "url2",
2: "url3",
3: "url4"
];'
And in my controller, inside a map I try to modify every item from the array doing this :
device.images[idx] = image;
image is a new object that look like the first item of my array. But I always get the assertion failed :
Assertion Failed: You must use Ember.set() to access this property (of [object Object]...
How can I fix that ? because that should normally works no ?
[edit] Here is the complete code :
loadGallery: function( device ) {
var that = this;
var images = device.images.map( function( item, idx ) {
return new Ember.RSVP.Promise(function( resolve ) {
if ( item.url ) {
resolve( item );
}
else {
resolve( that.imageStore.readImage( that, item ) );
}
})
.then( function( image ) {
device.images.set( idx, image );
that.set( 'selectedItem.images', device.images );
return image;
});
});
return Ember.RSVP.Promise.all( images );
},
[Edit: first attempt was wrong]
It seems you should be able to use item[index] = value construct. Example usage:
App.IndexRoute = Ember.Route.extend({
resolveImage: function (that, item) {
return {
url: item,
key: "resolved"
};
},
model: function() {
var device = {
images: [
{url: "a", key: "existing"},
"b",
"c"
]
};
var that = this;
var images = device.images.map(function(item, idx) {
return new Ember.RSVP.Promise(function(resolve) {
if (item.url) {
resolve(item);
}
else {
resolve(that.resolveImage(that, item));
}
})
.then(function(image) {
device.images[idx] = image;
return image;
});
});
return Ember.RSVP.hash({
model: Ember.RSVP.Promise.all(images),
device: device
});
},
setupController: function (c, m) {
c.set("model", m.model);
c.set("device", m.device);
}
});
<script type="text/x-handlebars" data-template-name="index">
<h2>From model</h2>
<ul>
{{#each item in model}}
<li>{{item.url}} - {{item.key}}</li>
{{/each}}
</ul>
<h2>From device</h2>
<ul>
{{#each item in device.images}}
<li>{{item.url}} - {{item.key}}</li>
{{/each}}
</ul>
</script>
JSBin here.
Barring some bug elsewhere in your code, the only remaining solution is that you are using Ember.Data and images isn't really an array but a custom class that merely implements one or several of Ember's array/enumerable mixins. [...] construct is a syntactic sugar that is hardcoded into javascript and not available to library authors to override.
I can't help you there much, as I don't use Ember.Data. You can try using explicit array mutation methods from Ember.MutableArray interface. Eg:
device.images.replace(idx, 1, [image]);
Alternatively, use object.set(key, value) construct (my first suggestion), with caveat that key value must be a string. Eg:
device.images.set(String(idx), image);
If neither of these work, you can just try to build up a new array instead of replacing members of the existing one.
Sorry for the long title. Its quite hard to put into words.
Ember version: 1.2.0
here goes:
My components:
App.AutocompleteComponent = Ember.Component.extend({
searchResults: function() {
var returnValue
var service = new google.maps.places.AutocompleteService();
service.getPlacePredictions({options},callback);
function callback(results){
returnValue = results;
}
return returnValue;
}.property('searchText')
My Templates:
{{input type="text" value=searchText placeholder="Search..."}}
<ul >
{{#each itemResults}}
<li>{{this.name}}</li>
{{/each}}
</ul>
When i debug using ember chrome debug tool, i can see the component holding the searchResults values correctly. But it is not being updated accordingly in the template.
Any ideas?
if this way of handling/using computed property is not suggested, can suggest any other ways?
Thank you in advance.
You probably want to debounce this (and I don't know what options is, is it a global var?). And the template is itemResults instead of searchResults. http://emberjs.com/api/classes/Ember.run.html#method_debounce
watchSearchResults: function() {
var self = this;
var service = new google.maps.places.AutocompleteService();
var callback= function(results){
self.set('searchResults', results);
}
service.getPlacePredictions({options},callback);
}.observes('searchText')
Thank you kingpin2k for your response,
I have found other way of dealing with returns that obviously didn't work on callbacks and since computed property which in a way requires 'returns' making it not feasible on this use case.
Instead i have opt to using Observers.
By the way, this code is was meant to be dealing with auto complete.
here is the final code:
WebClient.AutocompleteFromComponent = Ember.Component.extend({
searchTextChanged: Em.observer('searchText',function(){
var service = new google.maps.places.AutocompleteService();
service.getPlacePredictions({
input: this.get('searchText'),
types: ['(regions)'],
componentRestrictions: {
country: 'my'
}
}, function(predictions, status) {
//Start callback function
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status);
return;
}
for (var i = 0, prediction; prediction = predictions[i]; i++) {
console.log(prediction.description);
mapItem = {};
mapItem.name = prediction.description;
mapItem.type = 'map'
mapItem.reference = prediction.reference;
itemResults.push(mapItem);
}
//console.log(itemResults)
self.set('itemResults', itemResults)
});
})
The template code is still the same.
Please look at this code...
```
App.BooksRoute = Ember.Route.extend({
model: return function () {
return this.store.find('books');
}
});
App.BooksController = Ember.ArrayController.extend({
actions: {
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
}
});
```
I want to call the updateData action on BooksController from the outside.
I tried this code.
App.__container__.lookup("controller:books").send('updateData');
It works actually. But, in the updateData action, the this is different from the one in which updateData was called by clicking {{action 'updateData'}} on books template.
In the case of clicking {{action 'updateData'}}, the this.filter() method in updateData action will return books models.
But, In the case of calling App.__container__.lookup("controller:books").send('updateData');, the this.filter() method in updateData action will return nothing.
How do I call the updateData action on BooksController from the outside, with the same behavior by clicking {{action 'updateData'}}.
I would appreciate knowing about it.
(I'm using Ember.js 1.0.0)
You can use either bind or jQuery.proxy. bind is provided in JS since version 1.8.5, so it's pretty safe to use unless you need to support very old browsers. http://kangax.github.io/es5-compat-table/
Either way, you're basically manually scoping the this object.
So, if you have this IndexController, and you wanted to trigger raiseAlert from outside the app.
App.IndexController = Ember.ArrayController.extend({
testValue : "fooBar!",
actions : {
raiseAlert : function(source){
alert( source + " " + this.get('testValue') );
}
}
});
With bind :
function externalAlertBind(){
var controller = App.__container__.lookup("controller:index");
var boundSend = controller.send.bind(controller);
boundSend('raiseAlert','External Bind');
}
With jQuery.proxy
function externalAlertProxy(){
var controller = App.__container__.lookup("controller:index");
var proxySend = jQuery.proxy(controller.send,controller);
proxySend('raiseAlert','External Proxy');
}
Interestingly this seems to be OK without using either bind or proxy in this JSBin.
function externalAlert(){
var controller = App.__container__.lookup("controller:index");
controller.send('raiseAlert','External');
}
Here's a JSBin showing all of these: http://jsbin.com/ucanam/1080/edit
[UPDATE] : Another JSBin that calls filter in the action : http://jsbin.com/ucanam/1082/edit
[UPDATE 2] : I got things to work by looking up "controller:booksIndex" instead of "controller:books-index".
Here's a JSBin : http://jsbin.com/ICaMimo/1/edit
And the way to see it work (since the routes are weird) : http://jsbin.com/ICaMimo/1#/index
This solved my similar issue
Read more about action boubling here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
SpeedMind.ApplicationRoute = Ember.Route.extend({
actions: {
// This makes sure that all calls to the {{action 'goBack'}}
// in the end is run by the application-controllers implementation
// using the boubling action system. (controller->route->parentroutes)
goBack: function() {
this.controllerFor('application').send('goBack');
}
},
};
SpeedMind.ApplicationController = Ember.Controller.extend({
actions: {
goBack: function(){
console.log("This is the real goBack method definition!");
}
},
});
You could just have the ember action call your method rather than handling it inside of the action itself.
App.BooksController = Ember.ArrayController.extend({
actions: {
fireUpdateData: function(){
App.BooksController.updateData();
}
},
// This is outside of the action
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
});
Now whenever you want to call updateData(), just use
App.BooksController.updateData();
Or in the case of a handlebars file
{{action "fireUpdateData"}}
I'm making an adapter for the Web SQL storage for use in a Cordova application. So far it's going pretty well except for some reason the deleteRecord method isn't getting called at all. Here's an excerpt of my code:
DS.CordovaStorageAdapter = DS.Adapter.extend({
// Other methods...
deleteRecord: function(store, type, record) {
console.info('Deleting!');
var adapter = this;
var qr = new QueryRapper({id: record.get('id')}).tableName(this.tableName(type));
var query = qr.deleteQuery();
console.info(query);
function deleteSuccess(tx, results) {
adapter.didDeleteRecord(store,type,record);
}
return this.db.transaction(
function(tx) { tx.executeSql(query, [], deleteSuccess); },
function(err) { this.dbError(query, err, type, record); }
);
},
// Support methods...
});
Here's my Jasmine test:
describe('EmberCordovaStorageAdapter', function() {
var m;
beforeEach(function() {
waitForDbInit();
runs(function() {
m = App.TestModel.createRecord({
string: 'String!',
number: 1234,
date: new Date(),
boolean: true
});
m.save();
waitForReady();
});
});
// Other tests...
it('deletes a record', function() {
waitForReady();
runs(function() { console.info('Deleting in test!'); m.deleteRecord(); waitForDeleted(); });
runs(function() { expect(m.get('isDeleted')).toBe(true); });
});
function waitForDeleted(model) {
model = model || m;
waitForMessage(model, 'rootState.deleted.committed');
}
function waitForReady(model) {
model = model || m;
waitForMessage(model, 'rootState.loaded.saved');
}
function waitForMessage(model, msg) {
waitsFor(function() {
console.info(msg, model.get('stateManager.currentPath'));
return model.get('stateManager.currentPath') == msg;
}, 'model message: ' + msg, 1500);
}
});
I'm seeing in my JS console the message "Deleting in test!" but not the message "Deleting!". The github repo can be found here: https://github.com/eltiare/ember-cordova-storage
I don't have experience writing custom adapters, but a quick glance at your test I see your calling deleteRecord but I don't see a call to commit
In order for ember-data to actually attempt to delete the record you need to call commit. To commit the defaultTransaction you can do something like this:
m.get('store').commit();