Polymer 1.*
!editState(uploadState.*, index, 'edited') is true but yet prevent-load is not working and the request still fires. It's not a issue with a simple boolean variable binding, only with data path. Why isn't prevent-load working off of this data path binding?
<iron-image
hidden$="[[!editState(uploadState.*, index, 'edited')]]"
prevent-load="[[!editState(uploadState.*, index, 'edited')]]"
src="[[getImage(uploadState.*, index, 'value')]]"
sizing="cover"
class="image-show">
</iron-image>
properties: {
uploadState: {
type: Array,
value: function() {
var arr = Array.apply(null, Array(5));
var newArray = arr.map(()=> {
return {
value: false,
main: false,
edited: false,
loading: false
};
});
return newArray;
},
notify: true
},
`
The issue you are referring to was an outstanding issue in the iron-image 1.x element. You can update to the latest 2.1.1 version for the fix. This element​ is hybrid so it will work with Polymer 1.x and 2.0.
Related
I have:
<template is="dom-repeat"
initial-count="1"
index-as="index"
items="{{uploadState}}">
<div hidden="[[getState(index)]]" class="layout vertical center-center">
and I have:
properties: {
uploadState: {
type: Array,
value: function() {
var arr = Array.apply(null, Array(1));
var newArray = arr.map(()=> {
return { value: false };
});
return newArray;
},
notify: true
},
getState: function(index) {
var uploaded = this.get(['uploadState', index]);
// use this.linksPath??
return uploaded.value;
}
And:
changeState: function(index) {
this.uploadState[index].value = true;
}
When I change this.uploadState[0].value to false the hidden="[[getState(index)]]" does not detect the change.
How would I make hidden detect the change? I read this similar issue but not sure how I would use linkPath on this.
You can't work with native arrays in Polymer with data bindings. Polymer has it's own way of mapping the array elements in memory and it won't be able to detect a change to a array of elements.
Instead, I had to use the Polymer api's to mutate the arrays..this.get and this.set. This takes some getting use to since you will need to treat array elements as paths as I did with uploadState.*.
Documentation is a little rough since since there is no api documentation for array.base()(at least that I found ....not Polymer.base()).
Solution:
See Bind to an array item
<div hidden$="{{getState(uploadState.*, index, 'value')}}">
...
uploadState: {
type: Array,
value: function() {
var arr = Array.apply(null, Array(1));
var newArray = arr.map(()=> {
return { value: false };
});
return newArray;
},
notify: true
}
},
getState: function(change, index, path) {
var uploaded = this.get(path, change.base[index]);
return uploaded;
},
changeState: function() {
this.set('uploadState.0.value', true);
}
I have defined a polymer element
Polymer({
is: 'disco-ccontrol',
properties: {
midiValue: {
type: Number,
value: 0,
observer: '_valueChanged',
notify: true
},
channel: {
type: Number,
value: 0
},
channelNumber: {
type: Number,
value: 0
},
ref: {
type: Object,
computed: '_computeRef(channel, channelNumber)'
}
},
_computeRef: function(channel, channelNumber) {
var ref = new Firebase("https://incandescent-inferno-8405.firebaseio.com/user/"+channel+"/"+channelNumber);
ref.on("value", function(data) {
this.midiValue = data.val().value;
});
return ref;
},
_valueChanged: function() {
var message = { value: this.midiValue, channel: this.channel, channelNumber: this.channelNumber };
if (this.ref) {
this.ref.set(message);
}
}
});
I use this element in another element (parent element)
<disco-ccontrol midi-value="{{value}}" channel="{{channel}}" cn="{{channelNumber}}"></disco-ccontrol>
When I adapt the value property in the parent it propagates to the child. When I change the value property in the child (i.e in disco-ccontrol) it doesn't propagate up. What am I doing wrong to establish a two way binding?
In this function
ref.on("value", function(data) {
this.midiValue = data.val().value;
});
the this keyword is is not bound to the Polymer element. Thus your are not setting the midiValue on the correct object. You can bind this to the Polymer element using bind.
ref.on("value", function(data) {
this.midiValue = data.val().value;
}.bind(this);
Trying to load a plan model, embedded, into my app model.
I keep getting the following error when loading (it saves just fine):
Cannot read property 'typeKey' of undefined TypeError: Cannot read property 'typeKey' of undefined
at Ember.Object.extend.modelFor (http://localhost:4200/assets/vendor.js:71051:22)
at Ember.Object.extend.recordForId (http://localhost:4200/assets/vendor.js:70496:21)
at deserializeRecordId (http://localhost:4200/assets/vendor.js:71500:27)
at http://localhost:4200/assets/vendor.js:71477:11
at http://localhost:4200/assets/vendor.js:69701:20
at http://localhost:4200/assets/vendor.js:17687:20
at Object.OrderedSet.forEach (http://localhost:4200/assets/vendor.js:17530:14)
at Object.Map.forEach (http://localhost:4200/assets/vendor.js:17685:14)
at Function.Model.reopenClass.eachRelationship (http://localhost:4200/assets/vendor.js:69700:42)
at normalizeRelationships (http://localhost:4200/assets/vendor.js:71463:12) vendor.js:17062logToConsole
With that said I have the following models,
app/models/app.js
export default DS.Model.extend({
name: attribute('string'),
domain: attribute('string'),
plan: DS.belongsTo('plan', { embedded: 'load' }),
creator: DS.belongsTo('user', { async: true }),
time_stamp: attribute('string', {
defaultValue: function () {
return moment().format("YYYY/MM/DD HH:mm:ss");
}
})
});
app/models/plan.js
export default DS.Model.extend({
price: attribute('number'),
description: attribute('string'),
tagline: attribute('string'),
title: attribute('string'),
features: attribute('array') // Array is defined in a transform, don't worry.
});
Plan being kind of a static document.
Here's my server response when calling store.get('creator.apps');
{
"apps":[
{
"_id":"53da9994b2878d0000a2e68f",
"name":"Myapp",
"domain":"http://myapp.com",
"creator":"53d9598bb25244e9b1a72e53",
"plan":{
"_id":"53d93c44b760612f9d07c921",
"price":0,
"description":"Free plan",
"tagline":"Great for testing",
"title":"Developer",
"features":["5,000 Requests","API/Plugin Access"],
"__v":0
},
"time_stamp":"2014/07/31 13:31:32",
"__v":0
}
]
}
I realize that the typeKey error is due to Ember not finding a model for the response. I can confirm that it finds the app type, firing a hook under normalizeHash.apps.
Sorry this is such a long post, just can't wrap my head around the cause of the issue!
App.Thing = DS.Model.extend(
{
name: attr('string'),
children: DS.hasMany('child', {inverse:null})
}
);
App.ThingSerializer = DS.RESTSerializer.extend(
DS.EmbeddedRecordsMixin, {
attrs: {
children: { embedded: 'always' }
}
}
);
DS.EmbeddedRecordsMixin must be in your model and you must have `embedded:'always' for the correct attribute.
If you have a Thing model then you can make Ember Data load the embedded children (here and array of nested objects) by using the model specific serializer.
Resources:
http://emberjs.com/guides/models/customizing-adapters/
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
Ember doesn't want to have the record embedded in the JSON of the parent record.
Do what you need to, to get your json like this. With just the plan id.
{
"apps":[
{
"_id":"53da9994b2878d0000a2e68f",
"name":"Myapp",
"domain":"http://myapp.com",
"creator":"53d9598bb25244e9b1a72e53",
"plan_id":"53d93c44b760612f9d07c921", // just output id only not embedded record
"time_stamp":"2014/07/31 13:31:32",
"__v":0
}
]
}
This then lets ember look up the related model itself, using the async: true
export default DS.Model.extend({
name: attribute('string'),
domain: attribute('string'),
plan: DS.belongsTo('plan', { async: true }), //changed
creator: DS.belongsTo('user', { async: true }),
time_stamp: attribute('string', {
defaultValue: function () {
return moment().format("YYYY/MM/DD HH:mm:ss");
}
})
});
I've just gone through the pain of this myself and with some help found an answer.
For anyone else who's come here and still is has issues, read my answer to my own question for a detailed rundown of what the typeKey error means and further steps I used to resolve the issue myself.
Deploying ember-rails to Heroku - TypeError: Cannot read property 'typeKey' of undefined
I have an element with a model object that I want to observe like so:
<polymer-element name="note-editor" attributes="noteTitle noteText noteSlug">
<template>
<input type="text" value="{{ model.title }}">
<textarea value="{{ model.text }}"></textarea>
<note-ajax-button url="/api/notes/" method="POST" model="{{model}}">Create</note-ajax-button>
</template>
<script>
Polymer('note-editor', {
attached: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug
}
},
});
</script>
</polymer-element>
I want to observe changes in the model but apparently it's not possible to use modelChanged callback in the element and neither in the note-ajax-button element. What is wrong? How can I do that?
I've tried observing the fields separately, but it's not clean at all. The state of the button element you see there should change depending on the model state, so I need to watch changes for the object, not the properties.
Thanks!
To observe paths in an object, you need to use an observe block:
Polymer('x-element', {
observe: {
'model.title': 'modelUpdated',
'model.text': 'modelUpdated',
'model.slug': 'modelUpdated'
},
ready: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug
};
},
modelUpdated: function(oldValue, newValue) {
var value = Path.get('model.title').getValueFrom(this);
// newValue == value == this.model.title
}
});
http://www.polymer-project.org/docs/polymer/polymer.html#observeblock
Or you can add an extra attribute to your model called for example 'refresh' (boolean) and each time you modify some of the internal values also modify it simply by setting refresh = !refresh, then you can observe just one attribute instead of many. This is a good case when your model include multiple nested attributes.
Polymer('x-element', {
observe: {
'model.refresh': 'modelUpdated'
},
ready: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug,
refresh: false
};
},
modelUpdated: function(oldValue, newValue) {
var value = Path.get('model.title').getValueFrom(this);
},
buttonClicked: function(e) {
this.model.title = 'Title';
this.model.text = 'Text';
this.model.slug = 'Slug';
this.model.refresh = !this.model.refresh;
}
});
what I do in this situation is use the * char to observe any property change in my array, here an example of my JSON object:
{
"config": {
"myProperty":"configuraiont1",
"options": [{"image": "" }, { "image": ""}]
}
};
I create a method _myFunctionChanged and I pass as parameter config.options.* then every property inside the array options is observed inside the function _myFunctionChanged
Polymer({
observers: ['_myFunctionChanged(config.options.*)']
});
You can use the same pattern with a object, instead to use an array like config.options. you can just observe config.
I have a form:
And implements KnockoutJS Validation. Everything is applied by following the documentation but it always return that is valid even when I leave empty fields, etc.
This is the code:
ko.validation.rules.pattern.message = 'Invalid.';
ko.validation.configure({
registerExtenders: true,
messagesOnModified: true,
insertMessages: true,
parseInputAttributes: true,
messageTemplate: null
});
var initialData = [
{ firstName: "John", fathersLast: "Smith", country : ""
}
];
var Contact = function (contact) {
var self = this;
self.firstName = ko.observable(contact.firstName ).extend({ required: true, message: '* required' });;
self.fathersLast = ko.observable(contact.fathersLast ).extend({ required: true, message: '* required' });;
self.country = ko.observable(contact.country ).extend({ required: true, minLength: 2, message: '* required' });
};
var ContactsModel = function(contacts) {
var self = this;
self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function(i) {
return new Contact(i);
}));
self.errors = ko.validation.group(self.contacts);
self.addContact = function() {
if (self.errors().length == 0) {
alert('Thank you.');
} else {
alert('Please check your submission.');
self.errors.showAllMessages();
}
self.contacts.push({
firstName: "",
fathersLast: "",
country: ""
});
};
self.removeContact = function(contact) {
self.contacts.remove(contact);
};
};
ko.applyBindings(new ContactsModel(initialData));
Here is the working example: http://jsfiddle.net/8Hude/3/
Any clue why It's not working right?
Thanks.
As stated already, the validation plugin will be the most elegant, less re-inventive solution.
Edit: After commentary implementation utilizing validation plugin
With that aside, you have a couple options.
If you are confident the contact object will always contain only required fields, a not very robust implementation would be iterate over the properties of the contact ensuring each has some value.
A little more robust, but still lacking the elegance of the plugin, implementation would be to maintain an array of required fields and use that array for validation. You can reference my example for this setup. Essentially, each required property is mapped to observables. Changes made to the value of any observable property triggers (via a subscription) a mutation call for a dummy observable that is used in a computed. This is required since a computed can't call valueHasMutated. The mutation call triggers the computed to reevaluate, thus updating the UI.