I have a polymer element in which I can declare an array and use it as below.
...
<template repeat="{{chapters as chapter}}">
<core-submenu id='{{chapter.id}}-core-submenu' label="{{chapter.label}}">
</core-submenu>
</template>
...
Polymer('app-element', {
chapters: [{id: one, label: one}, {id: two, label: two}],
...
somethingChanged : function(){
this.$[this.chapter.id + '-core-submenu']
}
...
}
It works as expected if I fill the chapters object as above.
I want to will this chapters from outside. I then do the following:
var app = document.querySelector('#app');
window.addEventListener('polymer-ready', function() {
app.chapters = [{id: one, label: one}, {id: two, label: two}];
});
If I do that, this.$[this.chapter.id + '-core-submenu'] is now null. I guess it happens because the this.$ is filled before the polymer-ready event is fired.
Does it make sense? Which would be the best practice to now access this element? I was hoping to still be able to access it through this.$.
Best
Nicolas
I would suggest to expose this property as polymer attribute and then access that from outside using setAttribute
window.addEventListener('polymer-ready', function() {
var app = document.querySelector('#app');
app.setAttribute('chapters',[{id: one, label: one}, {id: two, label: two}]);
});
Related
Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');
What I am trying to do is to get data from the server and then putting it all in an observable and then make all the properties observable. The issue I am facing is that it does not make all my properties observable and I need them all to be observable as sometimes depending on the data it makes some properties observable and sometimes it doesn't.
var viewModel = this;
viewModel.Model = ko.observable();
viewModel.SetModel = function (data) {
viewModel.Model(ko.mapping.fromJS(data));
}
The data that I am receiving from the server is like this for example: normaldata,items(this is an array with unknown number of elements).
so if i try to access data like viewModel.Model().Items[0]().Layer() i sometimes have Layer as a function and sometimes it is a normal element with observable elements.I want all my objects inside Items to have Layer as a function.
Server data example:
Name: "test"
Items: [Layer[ID: 132]]
In this example Name,Items and ID are observable but Layer is not.
Fiddle example:
jsfiddle.net/98dv11yz/3
So the problem is that sometimes the layer is null resulting in ko making the property observable but sometimes that property has id and ko makes only the child elements observable. The problem is that i have if's in the code and i want it to be a function so i can always reffer to it as layer() because now it is sometimes layer or layer()
An explenation for what's happening:
When the ko.mapping plugin encounters an object in your input, it will make the object's properties observable, not the property itself.
For example:
var myVM = ko.mapping.fromJS({
name: "Foo",
myObject: {
bar: "Baz"
}
});
Will boil down to:
var myVM = {
name: ko.observable("Foo"),
myObject: {
bar: ko.observable("Baz")
}
}
and not to:
var myVM = {
name: ko.observable("Foo"),
myObject: ko.observable({
bar: ko.observable("Baz")
})
}
The issue with your data structure is that myObject will sometimes be null, and sometimes be an object. The first will be treated just as the name property in this example, the latter will be treated as the myObject prop.
My suggestion:
Firstly: I'd suggest to only use the ko.mapping.fromJS method if you have a well documented and uniform data structure, and not on large data sets that have many levels and complexity. Sometimes, it's easier to create slim viewmodels that have their own mapping logic in their constructor.
If you do not wish to alter your data structure and want to keep using ko.mapping, this part will have to be changed client-side:
Items: [
{ layer: {id: "0.2"} },
{ layer: null}
]
You'll have to decide what you want to achieve. Should the viewmodel strip out the item with a null layer? Or do you want to render it and be able to update it? Here's an example of how to "correct" your data before creating a view model:
var serverData = {
Name: "Example Name",
Id: "0",
Items: [
{layer: {id: "0.2"} },
{layer: null}
]
};
var correctedData = (function() {
var copy = JSON.parse(JSON.stringify(serverData));
// If you want to be able to render the null item:
copy.Items = copy.Items.map(function(item) {
return item.layer ? item : { layer: { id: "unknown" } };
});
// If you don't want it in there:
copy.Items = copy.Items.filter(function(item) {
return item.layer;
});
return copy;
}());
Whether this solution is acceptable kind of relies on how much more complicated your real-life use will be. If there's more complexity and interactivity to the data, I'd suggest mapping the items to their own viewmodels that deal with missing properties and what not...
I have the below JS code in my Ember app that gets called;
myPanels.accordionPanels = [];
myPanels.accordionPanels.push({
panel: {
name: "my-grid",
type: 'comp',
props: [{
key: 'elementId',
value: "myCustomId"
}]
}
});
So as you can see, I start by setting myPanels.accordionPanels = [] every time and then push the object.
However, I got the following error
Assertion Failed: Attempted to register a view with an id already in
use: myCustomId
So I am assuming that the object inside is not getting reset & it is able to find the earlier created "myCustomId".
Am I resetting the array (or rather the object inside it) correctly ?
Since I am able to push values using:
accordionPanels = [];
accordionPanels.push({
panel: {
name: "my-grid",
type: 'comp',
props: [{
key: 'elementId',
value: "myCustomId"
}]
}
});
make sure myPanels.accordionPanels doesn't have any prototype associated with it.
Try to inspect its value as:
myPanels.accordionPanels = [];
console.log(myPanels.accordionPanels); // see if it has values.
You can delete value using :
delete myPanels.accordionPanels PROTOTYPE
I extended the multiselect widget with nothing special. The issue is the binding of values no longer work. In a first sample, I'm using the native widget and binds values fine. The second is where I use an extended multiselect which fails on the value binding and is blank.
HTML:
<selectdata-role="multiselect"data-bind="source: selectData, value: selectedIDs"data-text-field="Name"data-value-field="ID"></select>
<selectdata-role="multiselectcustom"data-bind="source: selectData, value: selectedIDs"data-text-field="Name"data-value-field="ID"></select>
Javascript:
//EXTEND MULTISELECT WITH NOTHING MUCH
kendo.ui.plugin(kendo.ui.MultiSelect.extend({
init: function(element, options) {
kendo.ui.MultiSelect.fn.init.call(this, element, options);
},
options: {
name: 'MultiSelectCustom'
}
}));
varviewModel = kendo.observable({
selectedIDs: [ 1, 3 ],
selectData: [{
Name: 'Bill Smith',
ID: 1
}, {
Name: 'Jennifer Jones',
ID: 2
}, {
Name: 'Tim Philips',
ID: 3
}]
});
kendo.bind('body', viewModel);
I guess I can re-create the binder for "value" again, but is this indeed a bug? I have a jsFiddle that demonstrates this: http://jsfiddle.net/basememara/2Dacw/9/
this isn't a bug so much as that multiselect has custom binders set for it. You can try duplicating the binders for the multiselect for your new extended role.
try this:
kendo.data.binders.widget.multiselectcustom = kendo.data.binders.widget.multiselect;
you can place it before/after your widget extension code, but this should tell the bind function how to properly bind to your widget.
I would also take a look at the kendo docs for custom binders, be warned though it isn't very thourough
When building up a list of options in a select list using Javascript I'd like to attach a Javascript object to that option that I could easily retrieve in the change event.
So, while writing this question I wanted to provide some sample code and found a way to make this work; so I have a slight addition to the question. Is it bad to attach javascript object to DOM elements? Back in the day I remember there being issues with memory leaks in certain circumstances.
Here's a working example:
http://jsbin.com/afolo3/edit
var objs = [
{ id: 1, value: "One", type: "number" },
{ id: 2, value: "Two", type: "number" },
{ id: 3, value: "Three", type: "number" },
{ id: "A", value: "A", type: "char" },
{ id: "B", value: "B", type: "char" },
{ id: "C", value: "C", type: "char" },
];
var options = $.map(objs, function(item, idx) {
var opt = $("<option/>").val(item.id).text(item.value);
opt[0]["obj"] = item;
return opt;
});
$.fn.append.apply($("#select"), options)
.change(function() {
$("#select option:selected")
.each(function(){
alert(this.obj.type);
});
});
Use jQuery's .data() function instead.
Updated example: http://jsbin.com/afolo3/2
You can certainly attach objects to element instances the way you have; in fact, that's how jQuery does its data magic behind-the-scenes in the current version.
That said, since you're using jQuery already, I'd probably use the jQuery API for this (data) instead, just in case it turns out that at some stage, a browser comes on the scene where a workaround is required — you'll get the benefit of the jQuery maintainers doing the workaround for you, rather than having to do it yourself.
Here's what it would look like using data. Basically to set the data:
opt.data("obj", item);
...and to retrieve and show its type property:
alert(opt.data("obj").type);
...where in each case, opt is a jQuery object for the option element in question.
One slight "gotcha" with jQuery's data function: If you retrieve a data object you've never set, you'll get null back (rather than undefined, which would be the usual JavaScript convention).