Using the jQuery appear plugin with Knockout - jquery-plugins

I'm having some trouble integrating the appear-plugin for jQuery with my Knockout viewmodel. My first attempt only involved a single appear-item and a single global action but now I have multiple number of items to appear on and multiple action to trigger. I was thinking of making a binding-handler for this purpuse which could be used like this:
<div class="loadMoreTrigger" data-bind="appear: loadMore" data-appear-top-offset="200" />
My skills with custom bindinghandlers is very limited, and maybee there is existing code for this already? Haven't found any...
Would this be possible at all? And the right way of doing it? (I'm trying to lazy load items)
This is what I have now:
ko.bindingHandlers.appear = {
init: function (element, valueAccessor) {
$(element).appear();
var action = valueAccessor();
$(element).on("appear", action);
}
};
But it doesn't work...

Related

An aesthetic or correct way of invoking an action that requires DOM manipulation as well as controller related tasks in EmberJS

My EmberJS app has a couple of actions (triggered by buttons) that require a view/DOM manipulation as well as setting a state in the controller, followed by a model update. The way I do this, it does not appeal to my programming aesthetics. It gets the job done, but it doesn't look good :(
Here is a gist of how I do things :
<button {{action 'whatever' target='view'}}></button>
App.MyView = Ember.View.extend({
actions:{
whatever:function(){
var ctrl = this.get('controller');
ctrl.set('property',value); // arbitrary example of setting a controller property through it's view
ctrl.controllerMethod(); // invoking a controller method through the view
**// do some DOM manipulation**
}
}
});
Naturally, I can wrap whatever controller related steps I am performing in the view in a controller method and invoke that method through the view, but IMO that's just equally ugly. The view shouldn't really be invoking a controller method like how I have done. Unfortunately, this specific action requires a DOM manipulation as well as setting some state and performing an action in the controller.
I am not sure what is the recommended way of performing this. Can anyone enlighten ?
I suggest you handle the action from the controller. I noticed you're setting a property. You can use that to signal something to the view and then do the DOM manipulation within the view.
App.MyView = Ember.View.extend({
function () {
**// do some DOM manipulation**
}.observe('controller.property');
});
The way I think about it is that UI 'actions' are mapped to a business event (e.g. addClient instead of click), then as a result of that something happens that could change properties of the model, controller. As a result of those changes the view might need to update directly, ideally through a binding, but sometimes is needed to modify the DOM manually.
as #LukeMelia said in his comment you should really handle the changes in your controller and update your view (if you need to?) via databinding.
so, you would just omitt the target="view" argument from your view helper and Ember will look for the proper action in the nearest controller, bubbling all the way up to the route, and so on.
a simple code snippet (with what you provided in your first post) would look like:
Handlebars Template:
<button {{action someAction}}>Fire!</button>
Ember.Controller:
App.MyController = Ember.ObjectController.extend({
myProperty: 'cool',
printMyCoolness: function () {
console.log("I'm using Ember.js!");
},
actions: {
someAction: function () {
this.set('myProperty', 'set on fire!');
this.printMyCoolness();
}
}
});

Ember.js template binding performance

We have a page containing a table with 26 rows. Each row will contain either an <input> or <select> element, depending on the data we're binding to. When binding to elements that contain between 5-30 options, it takes a page about 5 seconds to render. If I remove the binding, the page renders in under a second.
Is there a known performance issue when binding to Ember.Select views? Or, could I be doing it incorrectly? I'm using Firefox 22. IE9 is about twice as slow. The CPU is not pegged during this time. I'm using ember 1.0rc6.
Template snippet:
{{#if pa.isPickList}}
{{view Ember.Select viewName="select" contentBinding="pa.options" selectionBinding="pa.selected"}}
{{else}}
{{input valueBinding="pa.selected"}}
{{/if}}
I worry that the async nature of how I'm fetching the model could be causing inefficiencies. Perhaps the binding and async events are interacting inefficiently.
Salesforce.com is the backend. From what little I know about promises, I'm wondering if I need to fetch the server data in a promise. I'm not sure how to do this.
Here's how I'm currently fetching the data in my Route:
App.IndexRoute = Ember.Route.extend({
model: function(params){
var otherController = this.controllerFor('selectedProducts');
var ar = Ember.A(); //create an array
var arg = '0067000000PNWrV';
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.ProductPricingLookupController.loadOpportunityProducts}',
arg,
function myHandler(result, event) {
console.info('got results!!! ' + result.length);
for(var i = 0; i < result.length; i++)
{
var p = result[i];
var sfProd = App.ProductResult.create({content: p});
ar.pushObject(sfProd);
}
},
{escape: false} //some of the names have ampersands!!
);
return ar;
}
}
Thanks in advance for helping a newbie learn the ways of javascript and Ember.
Update
Here is working example of this issue. I have 5 picklists each with 60 options. This take 2-3 seconds to render on my machine. I realize these are decently large numbers but hopefully not unreasonable. Increase the number of picklist elements or options and you can easily hit 5 seconds.
Also, moving my server-model-fetching to a promise did not affect performance.
Andrew
It's a little hard to guess at performance problems like this without looking at it in a profiler. You can try creating a profile in Chrome dev tools to see what method is taking the most amount of time. Or create a jsbin which has the same issue.
One potential issue is that the array that you bind to is being built at the same time when the bindings are connected. This shouldn't be an issue with rc.6. What version of Ember are you on?
Regards to promises, your model hook should return a promise that wraps your async call, like so.
model: function(params) {
var promise = Ember.Deferred.create();
var myHandler = function(result, event) {
var model = Ember.A();
// populate the model
promise.resolve(model)
}
var arg = '0067000000PNWrV';
Visualforce.remoting.Manager.invokeAction(..., myHandler);
return promise;
}
If the bindings were firing too early/often, loading the model in a promise like this would help.
Finally try setting Ember.STRUCTURED_PROFILE to true. This will show you exactly how long the templates are taking to render.
Edit: After the the jsfiddle
I dug into this a little more. This is a known issue with Ember.Select. The default implementation creates SelectOption views for each option inside the select to allow databinding of the option items itself. Creating those many views is what takes the time.
However the typical usage will rarely need binding to the option items only to the whole list itself. And this appears to be the common way to bridge the performance gap.
I found a gist that uses option tags instead of SelectOption views.
Here's your updated jsfiddle. I upped the lists to 10 with 100 items each. The list renders in about 150ms for me now.

Pass username from ViewBag to knockout viewmodel

Trying to put some pieces together here.
Piece 1: I know that I can get the current username in MVC by using:
#HttpContext.Current.User.Identity.Name
in my razor view.
Piece 2: I have some MVVM-style code in a separate JS file, relevant parts shown below:
var FeedbackViewModel = function () {
var self = this;
self.username = ko.observable("");
self.feedbackText = ko.observable("");
self.userNameCaptured = ko.computed(function () { return self.username().length > 3; }, self);
};
var feedbackViewModel = new FeedbackViewModel();
ko.applyBindings(feedbackViewModel, document.getElementById("feedbackModal"));
Question: How do I pass the current username from MVC3 to the Knockout viewModel so that I can observe and take action based upon it?
I assume feedbackViewModel is defined as a global variable, so you could simply add some code to the CSHTML file to populate it inline:
<script type="text/javascript">
feedbackViewmodel.username("#HttpContext.Current.User.Identity.Name");
</script>
Another option would be to pass the variable into the ViewModel function as a constructor element.
(I'd rather post this as comment than answer but somehow don't have option here)
I've faced similar problems in the past (handoff between razor and javascript). The best answer I've found is to create a hidden control (#hiddenval or #username) which can then be "read" by a JQuery selector.
This approach works for me every time. It's inelegant and may potentially expose info to a user sourcing the html...
if it's not obvious, #HttpContext.Current.User.Identity.Name should be populated as part of the markup

MVC2 - Submit form with AJAX and non-AJAX

Using MVC2 I have created a form using the Ajax helper in a view. The form posts to a controller which binds to a model object. A PartialViewResult is returned by the controller and the HTML gets updated into a div. So far, so good.
I now need to submit the same form and return the results in a generated file for the user to download. Obviously I don't want the file contents going into my div.
Is there an elegant way to handle this situation without having to hack it to bits? I'm fairly new to MVC / AJAX and it's still a point of confusion for me.
You may not use ajax call to download files. Following links may help you to do what you are trying to do
JQuery Ajax call for PDF file download
http://forums.asp.net/t/1683990.aspx/1
OK, so I couldn't find any simple solutions anywhere so I came up with my own. I remove the Ajax event handlers from the form when I want the download, put them back when I want the Ajax. I'm guessing there's a more elegant way to do this, as this feels like a 'clever trick'. I'm open to better suggestions but so far this is my preferred method.
Reference ToggleAjax.js on my page:
var ToggleAjax = function ($, form) {
var onclick = form.onclick,
onsubmit = form.onsubmit;
$('input[class*="ajax-enabled"]').click(function () {
form.onclick = onclick;
form.onsubmit = onsubmit;
});
$('input[class*="ajax-disabled"]').click(function () {
form.onclick = function () { };
form.onsubmit = function () { };
});
};
Then I call ToggleAjax on my page and pass in the form:
$(function () {
ToggleAjax($, $('form')[0]);
});
And of course I add the class ajax-enabled or ajax-disabled to the input controls.

Titanium Mobile: reference UI elements with an ID?

How do you keep track of your UI elements in Titanium? Say you have a window with a TableView that has some Switches (on/off) in it and you'd like to reference the changed switch onchange with a generic event listener. There's the property event.source, but you still don't really know what field of a form was just toggled, you just have a reference to the element. Is there a way to give the element an ID, as you would with a radiobutton in JavaScript?
Up to now, registered each form UI element in a dictionary, and saved all the values at once, looping through the dictionary and getting each object value. But now I'd like to do this onchange, and I can't find any other way to do it than create a specific callback function for each element (which I'd really rather not).
just assign and id to the element... all of these other solution CAN work, but they seem to be over kill for what you are asking for.
// create switch with id
var switcher0 = Ti.Ui.createSwitch({id:"switch1"});
then inside your event listener
myform.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "switch1" ) {
// do some magic!!
}
});
A simple solution is to use a framework that helps you keep track of all your elements, which speeds up development quite a bit, as the project and app grows. I've built a framework of my own called Adamantium.js, which lets you use a syntax like jQuery to deal with your elements, based on ID and type selectors. In a coming release, it will also support for something like classes, that can be arbitrarily added or removed from an element, tracking of master/slave relationships and basic filter methods, to help you narrow your query. Most methods are chainable, so building apps with rich interaction is quick and simple.
A quick demo:
// Type selector, selects all switches
$(':Switch')
// Bind a callback to the change event on all switches
// This callback is also inherited by all new switch elements
$(':Switch').bind('change', function (e) {
alert(e.type + ' fired on ' + e.source.id + ', value = ' + e.value);
});
// Select by ID and trigger an event
$('#MyCustomSwitch').trigger('change', {
foo: 'bar'
});
Then there's a lot of other cool methods in the framework, that are all designed to speed up development and modeled after the familiar ways of jQuery, more about that in the original blog post.
I completely understand not wanting to write a listener to each one because that is very time consuming. I had the same problem that you did and solved it like so.
var switches = [];
function createSwitch(i) {
switches[i] = Ti.UI.createSwitch();
switches[i].addEventListener('change', function(e) {
Ti.API.info('switch '+i+' = '+e.value);
});
return switches[i];
}
for(i=0;i<rows.length;i++) {
row = Ti.UI.createTableViewRow();
row.add(createSwitch(i));
}
However keep in mind that this solution may not fit your needs as it did mine. For me it was good because each time I created a switch it added a listener to it dynamically then I could simply get the e.source.parent of the switch to interact with whatever I needed.
module Id just for the hold it's ID. When we have use id the call any another space just use . and use easily.
Try This
var but1 = Ti.Ui.createButton({title : 'Button', id:"1"});
window.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "1" ) {
// do some magic!!
}
});
window.add(but1);
I, think this is supported for you.
how do you create your tableview and your switcher? usually i would define a eventListener function while creating the switcher.
// first switch
var switcher0 = Ti.Ui.createSwitch();
switch0.addEventListener('change',function(e){});
myTableViewRow.add(switch0);
myTableView.add(myTableViewRow);
// second switch
var switch1 = ..
so no generic event listener is needed.

Resources