Using KoGrid in HotTowel template - knockout-mvc

I am trying to use KoGrid in a HTML view within the HotTowel SPA template. I created a simple view:
<section>
<h2 class="page-title" data-bind="text: title"></h2>
<div class="gridStyle" data-bind="koGrid: gridOptions"></div>
</section>
and added the model data in the JS:
define(['services/logger'], function (logger) {
var vm = {
activate: activate,
title: 'My Grid'
};
return vm;
//#region Internal Methods
function activate() {
var self = this;
this.myData = ko.observableArray([{ name: "Moroni", age: 50 },
{ name: "Tiancum", age: 43 },
{ name: "Jacob", age: 27 },
{ name: "Nephi", age: 29 },
{ name: "Enos", age: 34 }]);
this.gridOptions = { data: self.myData };
return true;
}
//#endregion
});
The grid is on the page, but the styling seems to be rendering widths and positions completely wrong so that columns are on top of each other and most data is not visibly correct. The KoGrid.css file is being referenced correctly.
Thanks for any help.

The core of the problem is that "when KOGrid does its binding in Durandal/HotTowel, the KOGrid element is not yet part of the DOM". You need to ensure that KOGrid does not do its binding until after the view is attached. This can be achieved as follows:
1) Add a new observable to the viewmodel to hold a boolean value for whether the view has been attached or not by durandal:
isAttachedToView = ko.observable(false)
and expose it
isAttachedToView: isAttachedToView
2) Up date it to be true when the viewAttached function callback is invoked:
function viewAttached() {
isAttachedToView(true);
return true;
}
3) Surround your HTML with a ko if statement to ensure that bit of HTML is not evaluated (i.e. kogrid does not do its binding) until after the view is attached:
<!-- ko if: isAttachedToView() -->
<div data-bind="koGrid: { data: ...
<!-- /ko -->
4) Reset isAttachedToView to be false on deactivating view
function deactivate() {
isAttachedToView(false);
}
And expose this:
deactivate: deactivate

You have probably already figured this one out but was also faced with the same problem today. A quick look at the chrome inspector told me that koGrid dimensional properties have not registered correctly and this tells me its a timing issue. I found an answered question relating to the same problem here.
I did try this solution but there is still some work to do to make it play ball nicely. I have decided to give koGrid a miss since I don't really want all it's functionality anywayz :)

Related

EmberJS: Observer Not Being Triggered on Computed Property

I am building a handelbars helper that renders a checkbox group. My goal is to display a checkbox group with something like this and get two-way binding on selectedOptions:
{{form-checkboxGroup options=allOptions selectedOptions=selectedOptions}}
I've used this pattern successfully with other form components and it's a big win. I'm able to render my allOptions and selectedOptions values as a checkbox group, but it's the two-way binding that's tripping me up. Any idea what I'm missing?
By the way, I'm using ember-cli, but that doesn't affect anything relating to this issue.
Here's my setup:
Handlebars Helper: helpers/form-checkbox-group.js
The sole purpose of this file is to link the Handelbars expression {{form-checkboxGroup}} to the view and template below.
import FormCheckboxGroupView from 'my-app/views/util/form/form-checkbox-group';
export default Ember.Handlebars.makeBoundHelper(function( options ) {
return Ember.Handlebars.helpers.view.call(this, FormCheckboxGroupView, options);
});
CheckboxGroup Handlebars Template: templates/util/form/form-checkbox-group.hbs
...
{{#each user in view.combinedOptions}}
{{input type="checkbox" name="view.fieldName" checked=user.checked }} {{user.name}}
{{/each}}
...
CheckboxGroup View: views/util/form/form-checkbox-group.js
...
export default FormCheckboxGroupView = Ember.View.extend( FormFieldMixin, {
templateName: 'util/form/form-checkbox-group',
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('view.combinedOptions.#each.checked'),
// combines the "options" and "selected options" into a single array of "combinedOptions" explicitly indicating what's checked
combinedOptions: function() {
...
// sample result of combinedOptions:
// { name: "Johnny Five", id: "12", checked: true }
return combinedOptions;
}.property('view.options', 'view.selectedOptions')
});
And finally, to actually use my Handlebars helper, here's the consuming page's template and corresponding controller:
Consuming Page: templates/my-page.hbs
{{form-checkboxGroup options=allUsersArray selectedOptions=selectedUsersArray fieldName="selectedProvidersArray" }}
Backing Controller for Consuming Page: controllers/my-page.js
export default MyPageController = Ember.Controller.extend( FormMixin, {
allUsersArray: [
{ name: 'Bill Huxtable', id: 'billy' },
{ name: 'Samantha Jones', id: 'jones' },
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
selectedUsersArray: [
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
...
});
So, all of this successfully renders the checkbox group nicely, but my efforts to capture the fact that a checkbox has been newly selected by using observes("view.combinedOptions.#each.checked') is not working.
Any idea on how I can this up for two-way binding? Thanks in advance for assistance!
No jsbin so I'm flying blind, but try this:
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('combinedOptions.#each.checked')
view.property is how you access view from template. You don't need that from the view itself (unless you have view property on your view).

AngularJs Directive: Using TemplateURL. Replace element. Add form input. Getting form.input.$error object

Not sure if this is possible but I'm trying, and keep coming up short.
http://plnkr.co/edit/Gcvm0X?p=info
I want a 'E' (element) directive that is replaced with a more complex nested HTML node using the 'templateUrl' feature of directives.
HTML defining the directive (form tag included for complete mental image):
<form id="frm" name="frm">
<ds-frm-input-container
class="col-md-1"
frm-Name="frm"
frm-obj="frm"
input-name="txtFName"
ds-model="user.firstName"></ds-frm-input-container>
</form>
TemplateUrl contents which 'replaces' the above directive 'ds-frm-input-container' HTML element:
<div>
<input
required
ng-minlength=0
ng-maxlength=50
class="form-control"
ng-model="dsModel"
placeholder="{{dsPlaceHolder}}" />
<span ng-if="showErrs" class="label label-danger">FFFFF: {{dsModel}}</span>
</div>
Controller and Directive:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = "Nacho";
$scope.user = {};
$scope.user.firstName = "";
})
.directive('dsFrmInputContainer', function(){
var ddo = {
priority: 0,
restrict: 'AE',
scope:
{
frmName: '#',
inputName: '#',
dsPlaceHolder: '#',
dsModel: '=',
frmObj: '='
},
templateUrl: 'template1.html',
replace: true,
controller: function($scope)
{
$scope.showErrs = true;
},
compile: function compile(ele, attr) {
return {
pre: function preLink(scope, ele, attr, controller)
{
},
post: function postLink(scope, ele, attr, controller)
{
var txt = ele.find('input');
txt.attr('id', scope.inputName);
txt.attr('name', scope.inputName);
//BLUR
txt.bind('blur', function () {
console.log("BLUR BLUR BLUR");
angular.forEach(scope.frmObj.$error, function(value, key){
var type = scope.frmObj.$error[key];
for(var x=0; x < type.length; x++){
console.log(type[x]);
}
});
event.stopPropagation();
event.preventDefault();
});
}
};
},
};
return ddo;
});
The directive replaces just fine and the input element is named just fine. The form object however doesn't include the input element name in the error information. This makes it impossible for me to single out the input element during a 'blur' event that is setup in the directive.
I am doing this trying to reduce the show/hide logic 'noise' in the html for error messages (spans) and it should be reusable.
UPDATE (2014.01.28):
2014.01.28:
Added promises. There is a service that allows validation on button clicks. NOT USING built in angular validation anymore found some compatibility issues with another library (or viceversa).
ORIGINAL:
Here is my form validation directive vision completed (plnkr link below). Completed in concert with the help of the stack overflow community. It may not be perfect but neither are butterfingers but they taste good.
http://plnkr.co/edit/bek8WR?p=info
So here is a link that has the name variables set as expected on the given input form error object. http://plnkr.co/edit/MruulPncY8Nja1BUfohp?p=preview
The only difference is that the inputName is read from the attrs object and is not part of the scope. This is then read before the link function is returned, in the compile phase, to set the template DOM correctly.
I have just spent quite a while trying to sort this problem out, and while this is not exactly what you were looking for, his is my attempt. It uses bootstrap for all the styling, and allows for required and blur validation, but its definitely not finished yet. Any thoughts or advice much appreciated.
https://github.com/mylescc/angular-super-input

Class prefix as selector for each function

I am able to do this using an ID prefix as the selector, but I need to be able to do it with classes instead. It's an each function for opening up different modal windows on the same page. I need to avoid using ID names because I have some modal windows that will have multiple links on the same page, and when using IDs, only the first link will work.
So here's the function as it works with IDs:
$('div[id^=ssfamodal-help-]').each(function() {
var sfx = this.id,
mdl = $(this),
lnk = $('.link-' + sfx),
cls = $('.ssfamodal-close'),
con = $('.ssfamodal-content');
lnk.click(function(){
mdl.show();
});
cls.click(function(){
mdl.hide();
});
mdl.click(function() {
mdl.hide();
});
con.click(function() {
return false;
});
});
and I'm trying to change it to classes instead, like:
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = this.attr('class'),
etc.
But I cannot get it to work without using IDs. Is it possible?
EDIT Fixed error with semi-colon at end of Vars, and updated Fiddle with the fix. Still not working though.
Here's a Fiddle
** UPDATE **
To be clearer, I need to be able to refer to the same modal more than once on the same page. E.g.:
MODAL 1
MODAL 2
MODAL 3
MODAL 4
LINK TO MODAL 1
LINK TO MODAL 2
LINK TO MODAL 3
LINK TO MODAL 4
OTHER STUFF
LINK TO MODAL 1
LINK TO MODAL 4
LINK TO MODAL 3
OTHER STUFF
LINK TO MODAL 2
ETC.
When using classes get rid of the ID habit :
className1, className2, className3 ... etc
simply use
className
HTML:
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy
</div>
</div>
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy Ho
</div>
</div>
<span class="link-ssfamodal-help-base">One</span>
<span class="link-ssfamodal-help-base">Two</span>
LIVE DEMO
var $btn = $('.link-ssfamodal-help-base'),
$mod = $('.ssfamodal-help-base'),
$X = $('.ssfamodal-close');
$btn.click(function(i) {
var i = $('[class^="link-"]').index(this); // all .link-** but get the index of this!
// Why that?! cause if you only do:
// var i = $('.link-ssfamodal-help-base').index();
// you'll get // 2
// cause that element, inside a parent is the 3rd element
// but retargeting it's index using $('className').index(this);
// you'll get the correct index for that class name!
$('.ssfamodal-help-base').eq(i).show() // Show the referenced element by .eq()
.siblings('.ssfamodal-help-base').hide(); // hide all other elements (with same class)
});
$X.click(function(){
$(this).closest('.ssfamodal-help-base').hide();
});
From the DOCS:
http://api.jquery.com/eq/
http://api.jquery.com/index/
http://api.jquery.com/closest/
Here I created a quite basic example on how you can create a jQuery plugin of your own to handle modals: http://jsbin.com/ulUPIje/1/edit
feel free to use and abuse.
The problem is that class attributes can consist of many classes, rather than IDs which only have one value. One solution, which isn't exactly clean, but seems to work is the following.
$('div').filter(function () {
var classes = $(this).attr('class').split(/\s+/);
for (var i = 0; i < classes.length; i++)
if (classes[i].indexOf('ssfamodal-help-') == 0)
return true;
return false;
}).each(function() {
// code
});
jsFiddle
Or, equivalently
$('div').filter(function () {
return $(this).attr('class').split(/\s+/).some(function (e) {
return e.indexOf('ssfamodal-help-') == 0;
});
}).each(function() {
// code
});
jsFiddle
If there is one-to-one relationship between the modal helps and the modal links which it appears there is...can simplfy needing to match class values by using indexing.
For this reason you don't need unique class names, rather they just overcomplicate things. Following assumes classes stay unique however
var $helps=$('div[id^=ssfamodal-help-]');
var $help_links=$('div[id^=link-ssfamodal-help-]');
$help_links.click(function(){
var linkIndex= $help_links.index(this);
$helps.hide().eq( linkIndex ).show();
});
/* not sure if this is what's wanted, but appeared original code had it*/
$helps.click(function(){
$(this).hide()
})
/* close buttons using traverse*/
$('.ssfamodal-close').click(function(){
$(this).closest('div[id^=ssfamodal-help-]' ).hide();
});
Also believe that this code is a little more readable than original apporach
DEMO
Can you try this,
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = $(this).attr('class');
console.log(sfx);
/*console log:
ssfamodal-help-base ssfamodal-backdrop
ssfamodal-help-base2 ssfamodal-backdrop
*/
});
Demo: http://jsfiddle.net/xAssR/51/
why don't you write like
$('div.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
or
$('.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
where classname is the name of the class used for the div in html
Thanks.

Dynamic Carousel Content does not show

I have been working on this for a number of days now, but my limited JS knowledge seems to hurt me.
I am creating a dynamic Ext.Carousel component in my ST2 application, which is based on the contents of a Store file.
That all works fine, but I will show the code anyway, so that nothing is left to imagination:
Ext.getStore('DeviceStore').load(
function(i) {
Ext.each(i, function(i) {
if (i._data.name == 'Audio Ring') {
var carousel = Ext.ComponentManager.get('speakerCarousel');
var items = [];
Ext.each(i.raw.speakers, function(speaker) {
items.push({
sci: Ext.create('SmartCore.view.SpeakerCarouselItem', {
speakerId: speaker.speakerid,
speakerName: speaker.speakername,
speakerEnabled: speaker.speakerenabled
})
});
});
carousel.setItems(items);
}
});
})
Now, this adds me the appropriate number of items to the carousel. They display, but without the content I specified:
This is the Carousel itself:
Ext.define('SmartCore.view.SpeakerCarousel', {
extend: 'Ext.Carousel',
xtype: 'speakerCarousel',
config: {
id: 'speakerCarousel',
layout: 'fit',
listeners: {
activeitemchange: function(carousel, item) {
console.log(item);
}
}
}
});
This is the item class, that I want to fill the data from the store into:
Ext.define("SmartCore.view.SpeakerCarouselItem", {
extend: Ext.Panel,
xtype: 'speakerCarouselItem',
config: {
title:'SpeakerCarouselItem',
styleHtmlContent: true,
layout: 'fit'
},
constructor : function(param) {
this.callParent(param);
this.add(
{
layout: 'panel',
style: 'background-color: #759E60;',
html: 'hello'
}
)
}
});
Again, the right number of items shows in the carousel (11), but the content is not visible, nor is the background colour changed.
When I check the console.log(item) in the browser, the items show as innerItems inside the carousel object.
Any help is greatly appreciated!!
Well, I fixed it myself, or better, I found a workaround that seems to be what I want.
I ended up ditching the constructor all together.
Instead I overwrote the apply method for the 'speakerName' key-value pair.
From there, I can use:
this._items.items[0]._items.items[i].setWhatever(...)
to set the content inside the item.
If anyone knows the "real" way to do this, I would still greatly appreciate input!

Conflict with use of rel attribute in mootools

Looking for some insight into this problem.
I have dynamically generated links on a page that launch a lightbox ie they use a rel="lightbox[...]" I'm also putting a class on the hyperlink to make a tooltip work.
<a id="a_-1_6" class="Tips2" href="/media/63/forest_150.jpg" rel="lightbox[examples]" data-title="Tractor" data-desc="description..." data-rel="std" title="" style="opacity: 1; visibility: visible;">
And in the dom ready event
var Tips2 = new Tips($$('.Tips2'), {
initialize: function() { this.tip.fade('hide'); },
onShow: function(tip) { tip.fade('in'); },
onHide: function(tip) { tip.fade('out'); }
});
This all works fine except the tip uses the rel attribute to store data, i'm presuming as its a pre-html5 - so my question is would this mean I need to make my own version of the Tips class in mootools to work off the data.* attributes? I'd like to see I'm not barking up the wrong tree before I try that.
Thanks
Could you make another element inside the Ahref, like:
<a id="a_-1_6" href="/media/63/forest_150.jpg" rel="lightbox[examples]" data-title="Tractor" data-desc="description..." data-rel="std" title="" style="opacity: 1; visibility: visible;">
<span class="Tips2">blah</span>
</a>
This way, you can avoid the conflict.
Tips' documentation states that you can change which property is checked for the tip text. By default, it is rel or href, but you can change it when initializing the new Tip:
var Tips2 = new Tips($$('.Tips2'), {
initialize: function() { this.tip.fade('hide'); },
onShow: function(tip) { tip.fade('in'); },
onHide: function(tip) { tip.fade('out'); },
text: 'data-text' // Will now check the data-text property for tooltip text
});