Writing custom controls in Sproutcore 2 - sproutcore

I'm fairly new to Sproutcore, but I am familiar with Handlebars. I have walked through the Todo tutorial and checked out a few other samples as well.
I love everything about it and would like to use it over Backbone, but I am having a hard time understanding how to wire up custom controls. I can see where some of the data will play into the bindings, but triggering events I get lost in.
As an example, if I had a link list that I would like to use to filter data below it, how to do I tie into the events? I know in backbone you would use the event and selector: "click .link"
Any help would be greatly appreciated!

It sounds like you want to loop through a list of objects and create links that, when clicked, calls some JavaScript code that has access to the original objects.
At the moment, the easiest way to do that is to bind the template context to a new custom view. You can see everything in action at this JSFiddle: http://jsfiddle.net/67GQb/
Template:
{{#each App.people}}
{{#view App.PersonView contentBinding="this"}}
{{content.fullName}}
{{/view}}
{{/each}}
App Code:
App = SC.Application.create();
App.Person = SC.Object.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.people = [
App.Person.create({ firstName: "Yehuda", lastName: "Katz" }),
App.Person.create({ firstName: "Tom", lastName: "Dale" })
];
App.PersonView = SC.View.extend({
mouseDown: function() {
// Note that content is bound to the current template
// context in the template above.
var person = this.get('content');
alert(person.get('firstName'));
}
});
That said, we understand that this is a bit cumbersome, and have some ideas for further streamlining the process that we will be working on in the upcoming weeks.

Here's are list of demo apps.
http://blog.sproutcore.com/announcing-the-winners-of-the-demo-apps-contest/
Here's a plug for my own open source demo app that I entered - chililog. I'm blogging about how I've used sproutcore at the blog.chililog.org.
Hope this helps.

An alternative way to achieve what Yehuda is doing above is to use the #collection directive:
Template code:
{{#collection App.PeopleView contentBinding="App.people"}}
{{content.fullName}}
{{/collection}}
App Code:
App = SC.Application.create();
App.Person = SC.Object.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.people = [
App.Person.create({ firstName: "Yehuda", lastName: "Katz" }),
App.Person.create({ firstName: "Tom", lastName: "Dale" })
];
App.PeopleView = SC.CollectionView.extend({
itemViewClass: SC.View.extend({
mouseDown: function() {
var person = this.get('content');
alert(person.get('firstName'));
}
})
});

Related

How do I tie an image stored in Firebase Storage to a record in Firebase Database? *SWIFT/iOS* [duplicate]

I have a marketplace web app, where users can upload items, and of course they can also see images associated with these items. The problem is organizing the storage buckets, I was thinking to make the path itemImages/itemUID/image1.jpg. The problem is getting the itemUID, which is automatically generated, after an item is added to the database.
Here's a code snippet I have for adding items to the db:
itemsRef.push({
title: title,
description: description,
tags: tags,
price: price,
});
and here's a simplified function I use to store an image:
var uploadTask = imageNewItemRef.child('fakeUID' + '/' + imageNames[x]).putString(images[x], 'base64');
uploadTask.on('state_changed', function(snapshot) {
}, function(error) {
console.log("error uploading image");
}, function() {
var downloadURL = uploadTask.snapshot.downloadURL;
console.log(downloadURL);
});
as you can see, I'm using a hardcoded fakeUID link for testing purposes, but I have no clue (and searching hasn't helped), on how to have a uniqueUID instead of a fake one, that is linked to the item :/
Any help is appreciated!
Apologies for poorly written (and untested) JS, but would something like this work?
// create a new push ID and update the DB with some information
var currentItemRef = itemsRef.push({
title: title,
description: description,
tags: tags,
price: price,
}).then(function() {
var storageRef = firebase.storage().ref();
// currentItemRef.name is the unique key from the DB
return storageRef.child(currentItemRef.name + '/' + imageNames[x]).putString(images[x], 'base64');
}).then(function(snapshot) {
// update the DB with the download URL
return currentItemRef.update({
url: snapshot.metadata.downloadURLs[0]
});
}).catch(function(error) {
console.error(error);
});

How to export selected data with form post

I'm trying to export selected data from my ui-grid into another web application I'm building which accepts data via a form post. So, I've added a custom menu item with a function...
gridMenuCustomItems: [
{
title: 'Export to Texter',
action: function($event) {
console.log($scope.gridApi);
alert('do the export here');
},
order: 210
}
],
This works so far, but can someone point me in the direction of how to get the selected rows, then export all the values of one column (RegID, in my case) via a form post?
Here's my plunker: https://plnkr.co/edit/qsWac1FtIiblBKL3qALM?p=preview
I've got something working. I'm sure someone can improve it (I had to resort to jQuery for the form submit because I'm familiar with it and it's available), but this is working for me.
I should note that I needed the whole page to redirect to the new app, so AJAX handling wasn't enough.
gridMenuCustomItems: [
{
title: 'Export selected to Some App',
action: function($event) {
var currentSelection = $scope.gridApi.selection.getSelectedRows();
var currentSelectionUserIds = [];
currentSelection.forEach(function(entry) {
currentSelectionUserIds.push(entry.userId);
});
$myExportForm = $('<form id="ExportForm" action="/Link/To/My/App/" method="post"><input type="hidden" name="UserIDs" value="' + currentSelectionUserIds + '"></form>');
$('body').append($myExportForm);
$myExportForm.submit();
},
order: 210
}
],

Select2 AJAX doesn't update when changed programatically

I have a Select2 that fetches its data remotely, but I would also like to set its value programatically. When trying to change it programatically, it updates the value of the select, and Select2 notices the change, but it doesn't update its label.
https://jsfiddle.net/Glutnix/ut6xLnuq/
$('#set-email-manually').click(function(e) {
e.preventDefault();
// THIS DOESN'T WORK PROPERLY!?
$('#user-email-address') // Select2 select box
.empty()
.append('<option selected value="test#test.com">test#test.com</option>');
$('#user-email-address').trigger('change');
});
I've tried a lot of different things, but I can't get it going. I suspect it might be a bug, so have filed an issue on the project.
reading the docs I think maybe you are setting the options in the wrong way, you may use
data: {}
instead of
data, {}
and set the options included inside {} separated by "," like this:
{
option1: value1,
option2: value2
}
so I have changed this part of your code:
$('#user-email-address').select2('data', {
id: 'test#test.com',
label: 'test#test.com'
});
to:
$('#user-email-address').select2({'data': {
id: 'test#test.com',
label: 'test#test.com'
}
});
and the label is updating now.
updated fiddle
hope it helps.
Edit:
I correct myself, it seems like you can pass the data the way you were doing data,{}
the problem is with the data template..
reading the docs again it seems that the data template should be {id, text} while your ajax result is {id, email}, the set manual section does not work since it tries to return the email from an object of {id, text} with no email. so you either need to change your format selection function to return the text as well instead of email only or remap the ajax result.
I prefer remapping the ajax results and go the standard way since this will make your placeholder work as well which is not working at the moment because the placeholder template is {id,text} also it seems.
so I have changed this part of your code:
processResults: function(data, params) {
var payload = {
results: $.map(data, function(item) {
return { id: item.email, text: item.email };
})
};
return payload;
}
and removed these since they are not needed anymore:
templateResult: function(result) {
return result.email;
},
templateSelection: function(selection) {
return selection.email;
}
updated fiddle: updated fiddle
For me, without AJAX worked like this:
var select = $('.user-email-address');
var option = $('<option></option>').
attr('selected', true).
text(event.target.value).
val(event.target.id);
/* insert the option (which is already 'selected'!) into the select */
option.appendTo(select);
/* Let select2 do whatever it likes with this */
select.trigger('change');
Kevin-Brown on GitHub replied and said:
The issue is that your templating methods are not falling back to text if email is not specified. The data objects being passed in should have the text of the <option> tag in the text property.
It turns out the result parameter to these two methods have more data in them than just the AJAX response!
templateResult: function(result) {
console.log('templateResult', result);
return result.email || result.text;
},
templateSelection: function(selection) {
console.log('templateSelection', selection);
return selection.email || selection.id;
},
Here's the fully functional updated fiddle.

Sorting an array and saving to Mongo doesn't seem to trigger reactivity on my page

I have a template that I am loading from a route like so:
this.route('formEdit', {
path: '/admin/form/:_id',
data: function() { return Forms.findOne({_id: this.params._id}); },
onBeforeAction: function() { AccountUtils.authenticationRequired(this, ['ADMIN']); }
});
In which I have a template defined like:
<template name="formEdit">
<div id="formContainer">
...
{{#each header_fields}}
<div class="sortable">
{{> headerFieldViewRow }}
</div>
{{/each}}
</div>
</template>
And:
<template name="headerFieldViewRow">
{{#with header_field}}
...
{{/with}}
</template>
I then make the container around all the header fields sortable using jQuery UI Sortable:
Template.formEdit.rendered = function() {
$('.sortable').sortable({
axis: "y",
stop: function(event, ui) {
var form = Blaze.getData($('#formContainer')[0]);
var newFormHeaders = [];
$('#headerFieldsTable div.headerField').each(function(idx, headerFieldDiv) {
var header = Blaze.getData(headerFieldDiv);
header.sequence = idx;
Meteor.call('saveHeaderField', header);
newFormHeaders.push({header_field_id: header._id});
});
form.header_fields = newFormHeaders;
Meteor.call('saveForm', form);
}
});
}
Basically, when sorting stops, loop through all the headers, getting the data for each and updating the sequence number, then re-build the array in Forms and save them back. In the server code I have printouts for the two save calls, and the do properly print out the correct order of both the headers and the form.
The problem I am running into is that, after sorting, the visual display of the form and it's headers "snaps" back to the pre-sorted state, even though the data in the DB is correct. If I simply reload the form, either by hitting enter in the Address bar or by simply re-loading it from the menu, everything is displayed correctly. It's as if the reactive piece isn't working.
I have noted that I am getting an error when I update the client code in my server log that reads:
=> Client modified -- refreshing
I20141010-18:25:47.017(-4)? Failed to receive keepalive! Exiting.
=> Exited with code: 1
I don't think this is related as I was getting that error prior to adding this sorting code.
Update: Adding code for saveForm and saveHeader
saveForm:
// Saves the Form to the DB
saveForm: function(params) {
// Structure the data to send
var formEntry = {
title: params.title,
client_id: params.client_id,
header_fields: params.header_fields,
form_fields: params.form_fields,
created_by: Meteor.userId(),
created_on: new Date()
};
if (params._id == null) {
console.log("Saving new Form entry: %j", formEntry);
formEntry._id = Forms.insert(formEntry);
} else {
formEntry._id = params._id;
console.log("Updating Form entry: %j", formEntry);
Forms.update({_id: formEntry._id}, formEntry);
}
return formEntry;
}
saveHeader:
// Saves the HeaderField to the DB
saveHeaderField: function(params) {
// Structure the data to send
var headerFieldEntry = {
label: params.label,
field_type: params.field_type,
field_options: params.field_options,
form_id: params.form_id,
required: params.required,
allows_pictures: params.allows_pictures,
record_geo: params.record_geo
};
if (params._id == null) {
console.log("Saving new HeaderField entry: %j", headerFieldEntry);
headerFieldEntry._id = HeaderFields.insert(headerFieldEntry);
} else {
headerFieldEntry._id = params._id;
console.log("Updating HeaderField entry: %j", headerFieldEntry);
HeaderFields.update({_id: headerFieldEntry._id}, headerFieldEntry);
}
return headerFieldEntry;
}
I think the issue here is that Meteor.call will run on the server - you either need to use a callback or invalidate your template, if you want to return a value Meteor.call. From the docs:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
There is more info in this answer and this answer and the Meteor.call docs.
Hope that helps!

ASP.NET MVC + jqGrid without AJAX

I have an ASP.NET MVC application which is executing a search against a products database. I want to display the results in a jqGrid using the TreeGrid module. I don't really need the grid to be AJAX-y because the data is static and it is small enough that it can all be sent to the client at once.
First question: how do I set up jqGrid so that instead of pulling the JSON data from a URL it just looks in a JS variable or something?
Secondly, what is the most appropriate way to get ASP.NET MVC to put JSON data into a JavaScript variable? I already have the List in my controller and just want to somehow get it out into a JS variable after JSON-ing it.
Or am I fighting against the current too much and just accept the AJAX-y way that jqGrid seems to want to work?
Thanks,
~ Justin
Here is how to display a jqGrid tree using a JavaScript function.
$(document).ready(function() {
TreeDemo.setupGrid($("#tree"));
});
TreeDemo = {
data: { A: ["A1", "A2"], B: ["B1", "B2"] },
setupGrid: function(grid) {
grid.jqGrid({
colNames: ['Name'],
colModel: [
{ name: 'Name', index: 'Name', width: "250em" }
],
datatype: TreeDemo.treeData,
loadui: "none",
sortname: 'Number',
treeGrid: true,
treeGridModel: "adjacency",
sortorder: "asc"
})
},
treeData: function(postdata) {
var items = postdata.nodeid ? TreeDemo.data[postdata.nodeid] : TreeDemo.data;
var i = 0;
var rows = new Array();
for (val in items) {
var isLeaf = postdata.nodeid != undefined;
rows[i] = {
Name: val,
Id: val,
level: postdata.nodeid ? 1 : 0,
parent: postdata.nodeid || null,
isLeaf: isLeaf ? "true" : "false",
expanded: "false"
}
i++;
}
$("#tree")[0].addJSONData({
Total: 1,
Page: 1,
Records: 2,
Rows: rows
});
}
};
Note that there are lots of options for how you do this and my example is only one.
The way I would get the JSON into a JS var is to either:
Write a HTML Helper which emits a short script to the page.
Write an action which returns a JavaScriptResult to get the data in a file, if, for some reason, you can't have the data inline.
You create the JSON using the .NET JavaScript serializer. Look at the JsonResult.ExecuteResult in the MVC source code for an example.
See the Data Manipulation page in the jqGrid documentation wiki. There you'll find many ways to feed the data to the grid.
There is also a Table_to_jqGrid plugin that may be an useful option.

Resources