polymer share component property structure - javascript

If I have the following polymer element, how do I let the users of this element know about the structure of the object in dataSource. i.e If I have to declare and share Employee function, so that they can new Employee() and create an array. Whats the polymer way of sharing common data objects? Some seem to suggest behavior, but this isn't a behavior of the component, this is a data object with properties and no behavior.
<dom-module id="employee-list">
<template>
<ul>
<template is="dom-repeat" items="{{ dataSource }}">
<li>
<span>{{item.firstName}}</span>
</li>
</template>
</ul>
</template>
<script>
Polymer({
is: 'employee-list',
properties: {
dataSource: {
type: Array,
value: [{ firstName: "First 1", lastName: "Last 1" }]
}
}
});
</script>
</dom-module>

Use behaviors! Although the name is misleading, you can use behaviors to share properties.
A behavior is an object that looks similar to a typical Polymer
prototype. A behavior can define lifecycle callbacks, declared
properties, default attributes, observers, and listeners.
https://www.polymer-project.org/1.0/docs/devguide/behaviors.html
JS Bin example: http://jsbin.com/vuqumo/edit?html,output

Your users are going to use your element like this
<employee-list datasource="[[usersEmployeeList]]"></employee-list>
You need to tell them the format of the datasource in a jsdoc comment on the property. I don't know the format of the jsdoc comments properly, but look at the way the existing polymer elements do it.

Related

How do I pass an item in a Vuex list to a component?

So, I have a Vuex state that looks like so:
state: {
keylist: ['key1', 'key2', 'key3'],
items: {
key1: {title: "First Item"},
key2: {title: "Second Item"},
key3: {title: "Third Item"}
}
}
And I have a list component, referenced from root like so:
<event-list :list="this.$store.state.keylist"></event-list>
The components is defined like so:
Vue.component('event-list', {
template: "<ul><li v-for='key in list'>{{ key }}</li></ul>",
props: {
list: {
type: Array,
required: true
}
}
})
Now, this all displays the key just fine.
But, of course, what I really want to do is use a component on each item, found by it's key. And that's where I am stuck. I have an item component like so:
Vue.component('event-list-item', {
template: "<h4>{{ item.title }}</h4>",
props: {
item: {
type: Object,
required: true
}
}
})
But I cannot figure out how to translate the key in the parent component into an item in the child component. This template barfs on the first curly brace:
<ul><li v-for='key in list'><event-list-item :item="this.$store.state.items.{{key}}"</li></ul>
And in any case, that doesn't look like the right solution! so What is the right one?
To me the first thing that comes to mind is that's your nested item has no place to be inserted anyway. You might want to have a look at slots for nesting components like that:
https://v2.vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots
Anyway, a few considerations:
In your use case you are better off having the list in the event list item and repeat just the element that you actually need, rather than retrieving it in a useless component that only wraps around another component. I mean:
Vue.component('event-list-item', { template: "<li v-for="item in list"><h4>{{ item.title }}</h4></li>
The store is considered the single source of truth also because it should be easier to access it from the directly impacted components, sparing you from having to hand down multiple props for several layers. Like you are doing. This base is kinda brittle for deeply nested components.
Keep in mind that each component has a different scope and they don't share a thing that's not explicitly passed.
You should access the state items fields like key :
<ul><li v-for='key in list'><event-list-item :item='key' /></li></ul>
and in tht child component :
template: "<h4>{{ store.state.items[item].title }}</h4>"
There is no problem of iterating through properties of items object. It will work the same way as iterating through an array.
<ul>
<li
v-for="item in items"
:key="item.title"
>
<event-list-item :item="item" />
</li>
</ul>
For better practice, you can format data inside a getter, to assign keys and return a list that is ready for rendering so no additional logic is delegated to the component.
Note that key used in code example is for internal use of Vue, as it is a reserved special attribute and is suggested to be present when using v-for loops.

Access array and object created by meteor autoform

I'm trying to access info in an array created by meteor auto-form. The set in question is below.
PlayersSchema = new SimpleSchema({
contacts: {
type: Array,
optional: true
},
"contacts.$": {
type: Object
},
"contacts.$.comments": {
type: String,
label: "Comments From Salesmen"
}
});
I need to make it to the comments in the last block but I'm not having any luck.
When you access the first part contacts it returns object Object which is to be expected. But when I try to go deeper and reach the comments section I have no luck. I've tried contacts.comments - and contacts.comments[0] and nothing happens.
Here's a screenshot of my console showing the placement of the comment i need to reach
Console Array Comment Placement
Once I reach the comments I want to place them in the template I have below. Like I said I can get object Object to show in this form, but nothing else.
<template name="showPerson">
{{#with person}}
<h1>Show Person Details: {{name}}</h1>
<div class="row">
Comments: {{contacts.comments}}
</div>
{{/with}}
{{> update}}
</template>
New to javascript and meteor so please forgive my ignorance!!
You can also loop through contacts:
{{#each contacts}}
{{comments}} <!-- this might also need a loop -->
{{/each}}
If Contacts is a different collection, make sure you have a helper to return a cursor in order to loop

Make a child component call his parent API in Polymer 1.0.0

I have two web components defined with Polymer 1.0.0 and my question is about accessing the parent public API
<dom-module id="x-gallery">
<template is="dom-repeat" items="{{photos}}">
<x-photo photo="{{item}}"></x-photo>
</template>
</dom-module>
<script>
Polymer({
is: 'x-gallery',
...
getAll: function() {
return this.photos;
}
});
</script>
<dom-module id="x-photo">
<template>
<img src="{{photo.src}}">
</template>
</dom-module>
<script>
Polymer({
is: 'x-photo',
properties: {
photo: Object
},
ready: function() {
// HERE ---
// How could I access the x-gallery.getAll public method?
// ...
}
});
</script>
As you can see I wonder how you could easily access the getAll public method from the children?
I've seen some documentation referring to event based solutions (listening to the child events) but that doesn't really fit with my need. Unless you told me that the only solution available..
Any ideas?
ready: function() {
this.domHost.getAll()
}
from documentation:
http://polymer.github.io/polymer/
"domHost" is the
"element whose local dom within which this element is contained"
In this way you can access the "parent" and its functions.
In my opinion it is not the right approach in Polymer framework.
I have used it, in my project, only to define callback functions to the parent.
(sorry for my bad English)
Confirmed..I have used both methods - 1. parent explicitly setting a child's property to point back to parent, and 2. the domHost. domHost is easier and better as it is built-in.
In method 1, you have to make sure that the child is ready before setting the property.

What is the difference between Node.bind() and Template Binding

I am reading the docs of Google Polymer,there are two types of data binding,Node.bind() and Template Binding,so, what is the difference between Node.bind() and Template Binding
They're both ways of achieving data-binding. One in the context of DOM nodes and the other in the context of templates.
Node.bind()
Node.bind() allows you to tell DOM nodes to bind a named property to some data that you provide. So below, we're binding the property value (object.path.to.value) to the .textContent in our textNode.
var obj = {
path: {
to: {
value: 'hi'
}
}
};
var textNode = document.createTextNode('mytext');
textNode.bind('textContent', obj, 'path.to.value');
This is neat because anytime value changes, Node.bind() keeps your .textContent updated.
TemplateBinding
The Polymer docs state that this extends what you can do with the HTML <template> tag - specifically, giving you access to four new attributes: bind, repeat, if and ref.
So let's say that we wanted to pass a propery foo to a <template> which we would like to use in our template content, but want to keep in sync so that anytime foo changes, the template also gets updated. With bind that's as straight-forward as:
<template bind="{{ foo }}">
Creates a single instance with {{ foo }} when singleton model data is provided.
</template>
repeat is really useful for when you're working with lists or collections of data - like users:
<template repeat="{{ user in users }}">
<li>{{user.name}}</li>
</template>
if is really handy for conditional values in templates:
<template if="{{ conditionalValue }}">
Binds if and only if conditionalValue is truthy. (same as *bind if*)
</template>
and so on. You can find more examples of what can be done with these attributes in the TemplateBinding docs.

How can I get an instance of a model's controller inside a handlebars template?

I've got a sticky situation that I keep on running into: The need for a new instance of a controller inside a handlebars template.
Here is a brief example of my situation. (Please excuse my use of coffeecript)
In Ember, I have a model:
App.Foo = DS.Model.extend
attr: DS.attr()
...
Which I load from an endpoint etc.. And place into an array controller:
App.FooArray = Ember.ArrayController.extend
###*
* Array of App.Foo
* #type {Array}
*/
content:
method: ->
...
Finally, I have an 'instance' controller for this model, which implements further methods (i.e. this is not a singleton controller as would be found at the router level, but a decorator (or proxy) that augments the model with added methods and event handlers):
App.FooController = Ember.ObjectController.extend
###*
* Content
* #type {App.Foo}
*/
content: null
action: ->
...
In handlebars, I want to iterate over items in an App.FooArray:
{{#each myFooArray}}
Hi! My attr is {{attr}}
{{/each}}
etc.. This works splendidly for parameters and such.
However, the trouble starts when I want to use actions (or other properties which would belong to a FooController)
{{#each myFooArray}}
Hi! My attr is {{attr}} <a {{action 'action'}}>Action me!</a>
{{/each}}
Suddenly my actions are not working. That's because the action helper doesn't apply the action to 'this' but rather to a controller higher up, possibly even at the Route level!
So to work around this, I need to pass a target (i.e. a controller):
{{action 'action' target=**********}}
Well, the controller I want is an instance of App.FooController.
Up until now, I've been instantiating controllers inside the model (yuck!):
App.Foo = DS.Model.extend
attr: DS.attr()
...
attrn: DS.attr()
myController: Ember.computed (->
App.FooController.create
content: this
)
and thus iterating as follows:
{{#each myFooArray}}
Hi! My attr is {{attr}} <a {{action 'action' target=myController}}>Action me!</a>
{{/each}}
I know this is bad, but I can't think of a better way. Somebody, please help me see the light!
You can explicitly set the itemController in your each loop.
{{#each myFooArray itemController="foo"}}
Hi! My attr is {{attr}}
{{/each}}
This question poses an important and longstanding question about ArrayControllers, CollectionViews, Models and ObjectControllers.
At the time of writing, my knowledge of the inner workings of Ember was limited. However, I can rephrase my question more concisely as follows:
Given an ArrayController, CollectionView and instance controllers for a model, how can one leverage the itemControllerClass property of the ArrayController to iterate over its content and wrap each item in a unique (i.e. non-singleton) instance of itemController?
Turns out this problem is longstanding and the solution echoes #jeremy-green but I will expand on things here.
First off though: the Ember support thread that encapsulates the problem: https://github.com/emberjs/ember.js/issues/1637
I think the discussion there points very clearly to the need for non-singleton controllers in certain situations.
As well, here is the documentation for ArrayController that indicates the presence of an 'itemController' property on the ArrayController: http://emberjs.com/api/classes/Ember.ArrayController.html#property_itemController
Looking further into the docs, you will also note the presence of an 'lookupItemController' function: http://emberjs.com/api/classes/Ember.ArrayController.html#method_lookupItemController
These functions are for the express purpose of returning the content as an array of Controllers but how?
Well the first requirement is to use the ArrayController directly as the content in a loop. Unfortunately this is where things start to fall apart.
You may think it would be simply the case that a CollectionView can be used:
{{view myCollectionView controllerBinding=myArrayController}}
or
{{view myCollectionView contentBinding=myArrayController}}
But unfortunately this is not the case. More so in the situation where you are rendering a Controller on another Controller's route. CollectionView does not preserve the relationship between the ArrayController and its 'itemControllerClass`
The only way to make this work, as #jeremy-green points out:
{{#each myFooArray itemController="foo"}}
Hi! My attr is {{attr}}
{{/each}}
A more complete example would be:
<ol class="foo-item-list">
{{#each controllers.foo_items}}
<li>{{ view "foo" }}</li>
{{/each}}
</ol>
Wherein App.FooItemsController has either the property itemController or lookupItemController defined.
Unfortunately in this situation we lose the benefits of using the tagName or emptyView properties of CollectionView. Hopefully if an 'ArrayView' is ever created, it will bring the best of both worlds to this situation!

Categories

Resources