Knockoutjs: bind dynamic iframes in foreach to parent - iframe

I am trying to bind iframe and parent window so that I can change/update an observable in either the iframe or parent window and both views will update with new value.
Here is working sample: http://jsfiddle.net/NnT78/26/
I have tweaked some sample code that I have found and have it working great as follows;
HTML:
<iframe src="http://fiddle.jshell.net/zVPF8/11/show/" data-bind="bindIframe: $data"></iframe>
But when I put the same html in a foreach bind it get an error;
HTML:
<ul data-bind="foreach: iframes">
<li>
<iframe data-bind="attr: {src: src}, bindIframe: $data"></iframe>
</li>
</ul>
Error:
Uncaught ReferenceError: Unable to parse bindings.
Bindings value: text: someProperty
Message: someProperty is not defined
Here is my Knockoutjs ViewModel code;
ko.bindingHandlers.bindIframe = {
init: function(element, valueAccessor) {
function bindIframe() {
try {
var iframeInit = element.contentWindow.initChildFrame,
iframedoc = element.contentDocument.body;
} catch(e) {
// ignored
}
if (iframeInit)
iframeInit(ko, valueAccessor());
else if (iframedoc){
ko.applyBindings(valueAccessor(), iframedoc);
}
};
bindIframe();
ko.utils.registerEventHandler(element, 'load', bindIframe);
}
};
function ViewModel() {
var self = this;
self.someProperty = ko.observable(123);
self.clickMe = function(data, event) {
self.someProperty(self.someProperty() + 1);
}
self.anotherObservableArray = ko.observableArray([
{ name: "Bungle", type: "Bear" },
{ name: "George", type: "Hippo" },
{ name: "Zippy", type: "Unknown" }
]);
self.iframes = ko.observableArray([
{ src: "http://fiddle.jshell.net/zVPF8/6/show/", type: "Bear" },
{ src: "http://fiddle.jshell.net/zVPF8/6/show/", type: "Hippo" },
{ src: "http://fiddle.jshell.net/zVPF8/6/show/", type: "Unknown" }
]);
};
// Bind outer doc
ko.applyBindings(new ViewModel());
See http://jsfiddle.net/NnT78/26/ for sample of single iframe working and dynamic iframes in foreach bind not working.
Thanks in advance!

When in a foreach binding, $data is different; it's the current item in the array. You can fix your example by changing the iframe to bind to $root instead.
<iframe data-bind="attr: {src: src}, bindIframe: $root"></iframe>
http://jsfiddle.net/mbest/NnT78/29/

Related

font-awesome icon in display of combobox extjs 6

I tried several ways to set an icon, in the displayfield, when an item of the combo is selected with not luck, this is the fiddle for anyone to want try to help with this. very much appreciated any light.
fiddle example
The only solution is to transform the input type combo in a div with this:
fieldSubTpl: [
'<div class="{hiddenDataCls}" role="presentation"></div>',
'<div id="{id}" type="{type}" style="background-color:white; font-size:1.1em; line-height: 2.1em;" ',
'<tpl if="size">size="{size}" </tpl>',
'<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
'class="{fieldCls} {typeCls}" autocomplete="off"></div>',
'<div id="{cmpId}-triggerWrap" class="{triggerWrapCls}" role="presentation">',
'{triggerEl}',
'<div class="{clearCls}" role="presentation"></div>',
'</div>', {
compiled: true,
disableFormats: true
}
],
Override the setRawValue method of the combo like this:
setRawValue: function (value) {
var me = this;
me.rawValue = value;
// Some Field subclasses may not render an inputEl
if (me.inputEl) {
// me.inputEl.dom.value = value;
// use innerHTML
me.inputEl.dom.innerHTML = value;
}
return value;
},
and style your fake combo div like you want.
Thats because an input on HTML can't have HTML like value inside it.
Keep attenction, the get Value method will return you the HTML inside the div, and maybe you should also override it, but thats the only one method.
You will be able to get the selected value with this method:
Ext.fly(combo.getId()+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
If I were you I would like to do something like this:
combo.getMyValue();
So add this property to your combo:
getMyValue:function(){
var combo=this;
if(Ext.fly(combo.id+'-inputEl'))
return Ext.fly(combo.id+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
},
Here is a working fiddle
Perhaps my solution is similar to a hack, but it works in 6.7.0 and is a bit simpler.
Tested in Chrome. Theme - Material. For another theme will require minor improvements.
Sencha Fiddle live example
Ext.application({
name: 'Fiddle',
launch: function () {
var store = new Ext.data.Store({
fields: [{
name: 'class',
convert: function (value, model) {
if (value && model) {
var name = value
.replace(/(-o-)|(-o$)/g, '-outlined-')
.replace(/-/g, ' ')
.slice(3)
.trim();
model.data.name = name.charAt(0).toUpperCase() + name.slice(1);
return value;
}
}
}, {
name: 'name'
}],
data: [{
class: 'fa-address-book'
}, {
class: 'fa-address-book-o'
}, {
class: 'fa-address-card'
}]
});
var form = Ext.create('Ext.form.Panel', {
fullscreen: true,
referenceHolder: true,
items: [{
xtype: 'combobox',
id: 'iconcombo',
queryMode: 'local',
editable: false,
width: 300,
valueField: 'class',
displayField: 'name',
store: store,
itemTpl: '<div><i class="fa {class}"></i> {name}</div>',
afterRender: () => {
var component = Ext.getCmp('iconcombo');
var element = document.createElement('div');
element.className = 'x-input-el';
element.addEventListener('click', () => component.expand());
component.inputElement.parent().dom.prepend(element);
component.inputElement.hide();
component.addListener(
'change', (me, newValue, oldValue) => {
component.updateInputValue.call(me, newValue, oldValue);
},
component
);
var method = component.updateInputValue;
component.updateInputValue = (value, oldValue) => {
method.call(component, value, oldValue);
var selection = component.getSelection();
if (selection) {
element.innerHTML =
'<div><i class="fa ' + selection.get('class') + '"></i> ' + selection.get('name') + '</div>';
}
};
}
}, {
xtype: 'button',
text: 'getValue',
margin: '30 0 0 0',
handler: function (component) {
var combo = Ext.getCmp('iconcombo');
alert(combo.getValue());
}
}]
});
form.show();
}
});

(Meteor + React) Cannot access props from within subcomponent

I am passing data from one component to another (MyApplicants) via my router (FlowRouter):
FlowRouter.route('/applicants', {
name: 'Applicants',
action: function () {
var currentUser = Meteor.user();
ReactLayout.render(App, {
content: <MyApplicants institutionID={Meteor.user().profile.institutionID} />,
nav: <Nav />,
header: <Header />
});
}
});
As you can see I'm passing institutionID to the new component via a prop in the router. I know that the institutionID is being passed because I can see it in the render of the MyApplicants component.
Here is the MyApplicants component:
MyApplicants = React.createClass({
mixins: [ReactMeteorData],
pagination: new Meteor.Pagination(Applicants, {
perPage: 25,
filters: {institution_id: this.props.institutionID },
sort: {institution_id: 1, "last_name":1, "first_name":1}
}),
getMeteorData() {
return {
currentUser: Meteor.user(),
applicants: this.pagination.getPage(),
ready: this.pagination.ready()
}
},
RenderApplicantRow(applicant, key) {
return (
<div key={key}>
<p>[{applicant.institution_id}] {applicant.last_name}, {applicant.first_name}</p>
</div>
)
},
render : function () {
return (
<div>
<section className="content">
{this.data.applicants.map(this.RenderApplicantRow)}
{console.log(this.data.applicants)}
<DefaultBootstrapPaginator
pagination={this.pagination}
limit={6}
containerClass="text-center"/>
</section>
</div>
)
}
});
(FYI, I'm using krounin:pagination.) The problem is that I cannot access this.props.institutionID inside of the pagination component. I know the value is getting passed (I can see it if I'm just testing output in the render) but can't figure out why it doesn't pass into the pagination call. And I know the pagination works because I do not get an error if I hard code in a value.
Thanks for the help.
This is a simple scope problem I think, you need to bind it to the right context
Try something like this:
pagination: function() {
var self= this;
new Meteor.Pagination(Applicants, {
perPage: 25,
filters: {institution_id: self.props.institutionID },
sort: {institution_id: 1, "last_name":1, "first_name":1}
})
}
,

RactiveJS: Partials with (dynamic) local data

I'm trying to separate data per partials so that the data doesn't mix/overwrite globally.
The problem is that the main template does not re-render when partial's data changes.
The best I've accomplished so far is to get the partial to re-render in the main container, but not within the main template itself.
Am I missing something?
Here's the code (also on JS Bin):
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
</head>
<body>
<div id="main"></div>
</body>
</html>
JS:
var partial = new Ractive({
template: '{{name.first}} {{name.last}}',
oninit: function () {
this.observe( function ( newValue, oldValue, keyPath ) {
console.info( 'newValue = %o, oldValue = %o, keyPath = %s', newValue, oldValue, keyPath );
});
},
data: {
name: {
first: 'John',
last : 'Doe'
}
}
}),
main = new Ractive({
el: '#main',
// template: 'Hello {{name}}!',
template: 'Hello {{>partial}}!',
partials: {
partial: function ( p ) {
// 1) Not working...
// return '{{name.first}} {{name.last}}';
// 2) ...also not working...
// return partial.template;
// 3) ...still not working...
// return ( p.isParsed( partial.template ) ) ? partial.template : p.parse( partial.template );
// 4) Kind of working... (static - does not re-render)
// return partial.toHTML();
// 5) Kind of working... (returning Promise!)
return partial.render( this.el );
}
},
data: {
name: 'John Doe'
}
});
// partial.set( 'name.first', 'Jane' );
You have two ways to do this. First, you can still use partials, but you force the context of the partial. Note, however, that the data will still reside in the same instance. In the following example, the partial picks up "John Doe" as data. But in the second partial usage, we force the jane object to it, making it use first and last from jane.
var main = new Ractive({
el: '#main',
template: 'Hello {{>personPartial john}} and {{>personPartial jane}}!',
partials: {
personPartial: '{{first}} {{last}}'
},
data: {
first: 'John',
last : 'Doe',
jane: {
first: 'Jane',
last : 'Dee'
}
}
});
main.set( 'jane.name.first', 'Jen' );
If you really total separation of the data, consider using components instead. In this case John and Jane are their own components. Note however, that if a component is missing data (like say John didn't have first), it will look into the parent (main) for that data. If it happens to be there, it will use that (in this case foo). You can use isolated:true to prevent this behavior, like in Jane. You can also explicitly pass data from the parent to the child by way of attributes.
var John = Ractive.extend({
template: 'Hello {{first}} {{last}}!',
data: {
last: 'Doe'
}
});
var Jane = Ractive.extend({
isolated: true,
template: 'Hello {{first}} {{last}}!',
data: {
first: 'Jane',
}
});
var Jay = Ractive.extend({
isolated: true,
template: 'Hello {{first}} {{last}}!'
});
var main = new Ractive({
el: '#main',
template: 'Hello {{>person}}! <john /> <jane /> <jay first="{{first}}" last="{{last}}" />',
partials: {
person: '{{first}} {{last}}'
},
components: {
john: John,
jane: Jane,
jay: Jay
},
data: {
first: 'foo',
last: 'bar'
}
});

Meteor when many subscriptions are ready

I'm creating a chat app. I hope i can add a new "hello" message if i check the messages count of current chat is equal to 0 (Problem #1). Also i have a dictionary as a collection for translation. But t() returns EN variant (Problem #2)
t = function(text) {
var res = Dictionary.findOne({o:text});
return res && res.t || text;
}
Meteor.startup(function () {
Deps.autorun(function () {
Meteor.subscribe('dictionary', Session.get('lang'), function(){
Session.set('dictionaryReady', true);
});
Meteor.subscribe('chats', Session.get('domain'), function(){
if (chatCurrent(Meteor.userId(), Session.get('domain')).count()===0 //true, even is not actually [problem_#1]
&& Session.get('dictionaryReady') //true, but next function t() doesn't work properly [problem #2]
) {
var mudata = Session.get('my_manager') ? udata(Session.get('my_manager'), Session.get('domain')) : null,
hello = mudata && mudata.hello || t('Hello! How I can help you?'),
name = mudata && mudata.name || t('Anna');
Meteor.call('create_message', {chat: Meteor.userId(), to: Meteor.userId(), text: hello, name: name, from: Session.get('my_manager'), domain: Session.get('domain'), last_manager: Session.get('my_manager')});
});
});
});
Problem #1 and Problem #2 everytime when page just loaded. So when i refresh the page i get another "hello message" on default EN locale.
Here is how you can render your template only once your subscriptions are ready. This is a solution taken from meteor kitchen generated code.
first you create a "loading" template
<template name="loading">
<div class="loading">
<i class="fa fa-circle-o-notch fa-4x fa-spin"></i>
</div>
</template>
Second, attach to your template a route controller. Here is a simplified version of it (but it should work):
this.myTemplateController = RouteController.extend({
template: "myTemplate",
onBeforeAction: function() {
this.next();
},
action: function() {
if(this.isReady()) { this.render(); } else { this.render("loading"); }
},
isReady: function() {
var subs = [
Meteor.subscribe("sub1", this.params.yourParam),
Meteor.subscribe("sub2", this.params.yourParam),
Meteor.subscribe("sub3", this.params.yourParam)
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
params: this.params || {},
yourParamWhatever: Chat.findOne({_id:this.params.yourParam}, {})
};
},
});
Now you should have all your subscriptions ready when your template is loaded.
Concerning the translation, you could have a look at TAPi18n package that I highly recommend. It is quite easy to implement.

preload the selected values of a kendo multiselect that using a server bound data source and templated tags

I have a kendo multiselect as follows.
$("#tags").kendoMultiSelect({
change: onChange,
dataSource: {
transport: {
prefix: "",
read: {
url: "/OpsManager/Api/Activity/SearchResourcesTagged",
data: getSubmitData
}
},
serverFiltering: true,
filter: [],
schema: { errors: "Errors" }
},
itemTemplate: $('#resourceItemTemplate').html(),
tagTemplate: $('#resourceTagTemplate').html(),
dataValueField: "k",
value: [{"k":"[109]","n":"All Open Alerts","icon":"!","all":105}]
});
with the following templates:
<script id="resourceItemTemplate" type="text/x-kendo-template">
<span data-icon="#:data.icon#" class="#: data.s || '' #"> #:data.n #</span>
# if (data.d) { #
<div class="details">#: data.d #</div>
# } #
# if (data.details) { #
<div class="details k-state-disabled">
# for (var v in data.details) {
var t = typeof data.details[v];
if (t != "object" && t != "function" && v != "uid") { #
<div class="k-button">#: v #: #: data.details[v] #</div>
# } } #
</div>
# } #
</script>
<script id="resourceTagTemplate" type="text/x-kendo-template">
<span data-icon="#:data.icon#" class="tag-content #: data.s || '' #"> #:data.n #</span>
</script>
<select id="tags" multiple="multiple" name="tags"></select>
I'm trying to preload a specific selection and I can't seem to get it to work.
selection:
[{"k":"[109]","n":"All Open Alerts","icon":"!","all":105}]
I've put the initialized value in place according to their documentation and looking the multiselect object up inside the browser I see the passed in object inside _initialValues but I don't see anything inside _dataItems or in the tag list on the ui.
Any clues how to get this working?
Thanks to #OnaBai,
The problem is a difference in expected content of the datasources. The datasource originally loaded is not looking for "[109]" but rather "some string query" and then provides a specific list around that search. Thus I need to initialize a fake datasource for the control and then immediately switch out the data source for the dynamic one.
$("#tags").kendoMultiSelect({
change: onChange,
dataSource: {
transport : {
read: function (op) {
op.success([{"k":"[109]","n":"All Open Alerts","icon":"!","all":105}]);
}
}
},
itemTemplate: $('#resourceItemTemplate').html(),
tagTemplate: $('#resourceTagTemplate').html(),
dataValueField: "k",
value: ["[109]"]
});
$("#tags").data("kendoMultiSelect").setDataSource({
transport: {
read: {
url: "OpsManager/Api/Activity/SearchResourcesTagged",
data: getSubmitData
}
},
serverFiltering: true,
filter: [],
schema: { errors: "Errors" }
});
after that it works exactly like expected.
Two problems:
Define value in multiselect and not in dataSource.
Set value to an array with the list of key values that you want to initially load (in your case just "[109]").
Something like:
$("#tags").kendoMultiSelect({
change: onChange,
dataSource: {
transport: {
prefix: "",
read: {
url: "/OpsManager/Api/Activity/SearchResourcesTagged",
data: getSubmitData
}
},
serverFiltering: true,
filter: [],
schema: { errors: "Errors" }
},
itemTemplate: $('#resourceItemTemplate').html(),
tagTemplate: $('#resourceTagTemplate').html(),
dataValueField: "k",
value: ["[109]"]
});
Example in here

Resources