Databind iron-ajax JSON response entry to nested Polymer custom elements - javascript

I've got a Polymer app running an iron-ajax call to a service that returns a json string:
{
"name": "John"
}
The Polymer code on the main page is:
<link rel="import" href="/elements/my-form.html">
<dom-module id="my-app">
<template>
...
<iron-ajax auto url="/grabData" handle-as="json" last-response="{{data}}"></iron-ajax>
<iron-label>
From iron-ajax = {{data.name}}
</iron-label>
<my-form></my-form>
...
"my-form" is:
<link rel="import" href="/my-name.html">
<dom-module id="my-form">
<template>
<my-name></my-name>
</template>
<script>
Polymer({
is: 'my-form'
});
</script>
</dom-module>
And "my-name" is:
<dom-module id="my-name">
<template>
<iron-label>
From my-name = {{data.name}}
</iron-label>
</template>
<script>
Polymer({
is: 'my-name'
});
</script>
</dom-module>
When run, "From iron-ajax = John", and "From my-name =".
While I could use a single ajax call within my-name, I'd don't want to do this for every custom element. I'd rather acquire my data in one call and have custom elements access the returned data.
How do I acquire 'name' within my-name without passing the value through the my-form element?
--- Update ---
For a little more clarity on desired outcomes, here's a little more info.
Ultimately I'd like to acquire a very large JSON string with multiple entries:
{
"name": "John",
"address": [{
"street" : "14 Baker Street",
"city" : "somewhereville"
}],
"phone": "123-1234"
...
}
And have custom elements handle each entry:
<dom-module id="my-form">
<template>
<my-name></my-name>
<my-address></my-address>
<my-phone></my-phone>
...
</template>

Below is the example snippet on how to pass data between elements. Instead of ajax call i've passed data from element's attribute. Some key points to be noted
In case you want to pass whole data to child elements you can replace name property in child element with data property of parent, names doesn't have to be same, eg if child has a property user with type Object(first letter caps) then your binding will be user={{data}}
If you want you can replace {{}} with [[]], unless you want to propagate change from child to parent in which case you will have to add another parameter to name property namely notify and set its value as true, something like
name:{
type:String,
notify:true
}
In your case(with ajax call) listing name and data inside properties is optional, but i'll recommend it as it makes it easy to add new features to a property(like notify) and also makes it easy to track all the properties in a large element.
For more details on properties and data-binding checkout these links. properties, data-binding
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="my-app">
<template>
Name from App: {{data.name}}
<br>
<my-form name={{data.name}}></my-form>
</template>
</dom-module>
<script>
Polymer({
is: 'my-app',
properties: {
data: {
type: Object
}
}
});
</script>
<dom-module id="my-form">
<template>
Name from Form: {{name}}
<br>
<my-name name={{name}}></my-name>
</template>
</dom-module>
<script>
Polymer({
is: 'my-form',
properties: {
name: {
type: String
}
}
});
</script>
<dom-module id="my-name">
<template>
Name from Name: {{name}}
</template>
</dom-module>
<script>
Polymer({
is: 'my-name',
properties: {
name: {
type: String
}
}
});
</script>
<my-app data='{"name":"User1"}'></my-app>

Related

Use moustache inside moustache brackets in vue?

I’m pretty new to Vue.js and love it so far. There’s only one problem I have: How do I render a string AND things inside the string?
For example:
//…
data:{
string:"hi, {{name}}",
name:"John Doe"
}
{{string}}<!—won’t work —>
<span v-html="string"></span><!—neither this —>
I want to insert “string” into the html and afterwords insert “name”.
The “Hello {{name}}” should inserted after an Ajax call and therefore not be featured in the HTML.
The output should be “hi, John Doe”.
To make it clear, what I want to have is:
{{string}}
<!-- becomes-->
hi, {{name}}
<!--finally becomes-->
hi, John Doe
Use a computed property to return string with data variable inside string;
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app">
<h1>{{this.stringWithVar}}</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
name: 'John Doe'
},
computed: {
stringWithVar: function () {
return `hi, ${this.name}`;
}
}
})
</script>
</body>
</html>

Drop down list with Vue JS

I code an html page with a drop-down list with HTML CSS and VueJS. I want that if I click on a certain option in my drop-down list, it display something in console.
My html code :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript" src="js/historique.js"></script>
<title>Historique</title>
</head>
<body>
<select id="selectionMachines" size="1">
<option v-for="todo in todos" v-on:click="action">
{{ todo.libelle }}
</option>
</select>
</body>
</html>
And my JS code :
window.onload = function () {
var machines = new Vue({
el:"#selectionMachines",
data: {
todos: [
{ libelle: "Machine 195", num: 195},
{ libelle: "Machine 196", num: 195}
]
},
methods: {
action: function() {
console.log(this.todo.num);
if (this.todos.num == 195)
console.log("J'ai choisi 195");
}
}
})
}
The action function doesn't work for the moment, undefined is log in console. Any idea ?
The event should be bound to <select>.
The event should pass the $event variable to the function
The options' values should be bound to the options themselves (:value=""). You'll get this value with the $event ($event.target.value)
Even though you literally click on a <select> and the <option>, the event is not click(), but input or change, as it's an input field. This is not VueJS question, but JavaScript.
Your action() method is a function, so if you want it to get executed, then you have to call it with parantheses (action()), and not it's "string value" (action). This is unlike computed in VueJS templates, but VueJS handles the difference.
Pay attention to your todos - both have a num key with value 195 (so you won't see a difference in the console, both will log 195 there), although their label (libelle) implies 195 and 196
You write console.log(this.todo.num); and this.todos.num == 195 in your action() method. I guess you tried to use the single todo item that's selected in the input. The problem with this is that the input (and the todo in todos) only exist in your template, your scripts (like methods) don't know about them - you have to pass the information somehow to those scripts ($event is good for that). If you want to get the whole single todo object to work with, then I suggest find() (or filter() as your num value is not unique).
new Vue({
el: "#selectionMachines",
data: {
todos: [{
libelle: "Machine 195",
num: 195
},
{
libelle: "Machine 196",
num: 195
}
]
},
methods: {
action: function(event) {
console.log(event.target.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<select id="selectionMachines" size="1" #change="action($event)">
<option v-for="todo in todos" :value="todo.num">
{{ todo.libelle }}
</option>
</select>
Try this:
<option v-for="todo in todos" #click="action(todo)">
{{ todo.libelle }}
</option>
action(todo) { if (todo.num === 195) console.log("J'ai choisi 195"); }

Ember.js use itemController if not following naming Conventions

According to official documentation, way to create itemcontroller is:
App.PostsController = Ember.ArrayController.extend({
itemController: 'post'
});
App.PostController = Ember.ObjectController.extend({
// the `title` property will be proxied to the underlying post.
titleLength: function() {
return this.get('title').length;
}.property('title')
});
But I'm not setting my ArrayController to App. It is set to a local variable behind a function scope. And the itemController property can only be string (according to documentation). So how do I set the itemController property?
My code looks like this:
var Channels=Ember.Object.extend({
list:Ember.ArrayController.create(
{
"model":[
{
"id":"display",
"label":"Display",
},{
"id":"social",
"label":"Social",
},{
"id":"email",
"label":"Email",
}
]
}
)
});
App.ChannelController=Ember.Controller.extend({
channels:Channels,
}));
<script type="text/x-handlebars" data-template-name='channel'>
<div>
{{#each channel in channels.list}}
{{channel.label}}
{{/each}}
</div>
</script>
I don't want to pollute App namespace with itemControllers that is to be used locally.
Update
Suppose my channels is like this:
var Channels=Ember.Object.extend({
list:Ember.ArrayController.create(
{
"model":[
{
"id":"display",
"label":"Display",
},{
"id":"social",
"label":"Social",
},{
"id":"email",
"label":"Email",
}
]
}
),
selected:"display"
});
and I want to something like this in template:
<script type="text/x-handlebars" data-template-name='channel'>
<h1>{{channels.selected}}</h1>
<div>
{{#each channel in channels.list}}
<div {{bind-attr class="channel.isselected:active:inactive"}}>{{channel.label}}</div>
{{/each}}
</div>
</script>
so that it outputs:
<h1>display</h1>
<div>
<div class="active">Display</div>
<div class="inactive">Social</div>
<div class="inactive">Email</div>
</div>
How do I do it with components?
You'll likely want to read the guide of components to get the full picture, but the gist of it is that you want to replace all item controllers with components. However, components will also replace the template inside of the each block as well. I don't entirely understand what's going on in your code, but here's an example roughly based on your code.
// Component
App.ChannelDisplayComponent = Ember.Component.extend({
channel: null,
isSelected: function() {
// Compute this however you want
// Maybe you need to pass in another property
}.property('channel')
});
{{! Component Template }}
<div {{bind-attr class="channel.isSelected:active:inactive"}}>
{{channel.label}}
</div>
{{!Channels Template}}
{{#each channel in channels.list}}
{{channel-component channel=channel}}
{{/each}}
The component is essentially your item controller, only it gets its own template as well.
You really shouldn't be worried about polluting the app namespace (unless you're having naming collisions, but that's a different issue). And as Kitler said, you should move to components instead of item controllers. But if you want to do this, the best way I can think of is overridding the (private) controllerAt hook.
var ItemController = Ember.Controller.extend({});
App.PostsController = Ember.ArrayController.extend({
controllerAt: function(idx, object, controllerClass) {
var subControllers = this._subControllers;
if (subControllers.length > idx) {
if (subControllers[idx]) {
return subControllers[idx];
}
}
var parentController = (this._isVirtual ? this.get('parentController') : this);
var controller = ItemController.create({
target: parentController,
parentController: parentController,
model: object
});
subControllers[idx] = controller;
return controller;
}
})

Data binding in nested custom polymer elements (recursive data-binding)

I try to bind custom sub elements to values of local storage by using polymer's template repeat functionality like this:
<polymer-element name="aw-outerElement">
<template>
<template repeat="{{group in grouplist}}">
<aw-innerElement groupId="{{group.groupId}}" name="{{group.name}}" val="{{group.val}}"></aw-innerElement>
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>
In the code above I try to pass the data binding this.prj.ke.groupVal100 and this.prj.ke.groupVal200
to my inner element aw-innerElement through the attribute val. The aw-innerElement is a custom paper-input element where the value attribute should be set to e.g. this.prj.ke.groupVal100. It seems that only the stored initial value 0 will be set and NOT the data-binding string this.prj.ke.groupVal100 inside the value attribute. Is there a way to make a data-binding with template repeat inside inner elements?
My inner elements looks like this:
<polymer-element name="aw-innerElement" attributes="groupId name val">
<template>
<paper-input type="number" floatingLabel label="{{groupId}} {{name}}" value="{{val}}" error="{{i18nnrerror}}"></paper-input>
</template>
<script>
Polymer('aw-innerElement', {
publish: {
groupId: 0,
name: '',
val: 0
},
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
...
}
</script>
As you can see above the value="{{val}}" of my innerElement should be set to this.prj.ke.groupVal100 and this.prj.ke.groupVal200.
Thanks in advance!
I know I'm digging up an old question, but for future searchers this might come in handy.
Polymer does not allow a variable as your key, so you need to pull it through a function like so:
...
<template is="dom-repeat" items="{{users}}">
<li>{{showValue(item)}}</li>
</template>
...
<script>
Polymer('aw-outerElement', {
// Standard Polymer code here
showValue: function(item){
return item[myVar];
}
});
</script>
You can then manipulate as much as you want in Javascript and return the output for that one item in items.
Passing in a value seems to work fine for me in this example: http://jsbin.com/kalih/4/edit
<polymer-element name="x-bar">
<template>
<paper-input id="input"
label="{{name}}"
value="{{val}}">
</paper-input>
<button on-tap="{{logVal}}">Log val</button>
</template>
<script>
Polymer('x-bar', {
publish: {
val: 0
},
logVal: function() {
console.log(this.$.input.value);
}
});
</script>
</polymer-element>
<polymer-element name="x-foo">
<template>
<template repeat="{{item in items}}">
<x-bar name="{{item.name}}" val="{{item.val}}"></x-bar>
</template>
</template>
<script>
Polymer('x-foo', {
ready: function() {
this.items = [
{
name: 'baz',
val: 28
},
{
name: 'qux',
val: 42
}
];
}
});
</script>
</polymer-element>
<x-foo></x-foo>
Btw, your aw-innerElement doesn't need to have an attributes attribute because you're using the publish object (they serve the same purpose, but publish lets you set default values, which you've done). Also, we recommend that you don't camel-case your element names, the HTML parser is actually going to lowercase them all anyway.
I'm new with polymer so maybe my answer is not correct. But I done something like this that you're trying to do.
here is a sample of my code
I guess that your problem is that you didn't use bind on your template
<template bind="{{grouplist}}">
<template repeat="{{group in grouplist}}">
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>

Binding child views and collections within an outlet in emberjs

I'm trying to render a view Team inside of an {{outlet}}. This Team view is comprised of a simple Person view (the team leader), and a collection of Person views (team members). The outlet is set up by calling connectOutlet() on the ApplicationController.
Although the Person child views are rendered in the markup as expected, all the values of name are missing. It sure seems like my bindings and/or controller are not set up properly. What am I missing?
Code and demo: http://jsfiddle.net/aek38/fkKFJ/
The relevant handlebar templates are:
<script type="text/x-handlebars" data-template-name="app">
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="person">
<em>Person name is:</em> {{name}}
</script>
<script type="text/x-handlebars" data-template-name="team">
<h3>Team Leader</h3>
<em>Leader name should be:</em>{{leader.name}}
{{view App.PersonView contentBinding="leader"}}
<h3>Team Members</h3>
{{#collection contentBinding="members"}}
{{view App.PersonView contentBinding="content"}}
{{/collection}}
</script>
Code snippet:
App = Ember.Application.create({
ready: function() {
this.initialize();
},
ApplicationController: Ember.Controller.extend(),
ApplicationView: Ember.View.extend({
templateName: "app"
}),
Person: Ember.Object.extend({
name: "Jane Doe"
}),
PersonController: Ember.ObjectController.extend(),
PersonView: Ember.View.extend({
templateName: "person"
}),
Team: Ember.Object.extend({
members: [],
leader: null
}),
TeamController: Ember.ObjectController.extend(),
TeamView: Ember.View.extend({
templateName: "team"
}),
// ...
You can use
{{view App.PersonView contextBinding="leader"}}
and use {{#each}} instead of {{#collection}} for the members
http://jsfiddle.net/LQTsV/1/
Not very sure whats going on but I tweaked your fiddle to get it working:
http://jsfiddle.net/lifeinafolder/sPcwv/
Seems as if bindings are not working properly in the sense:
contentBinding="this"
works but
contentBinding="this.leader"
doesn't.
Sorry but I couldn't work it out more.
You're setting the content variable on person view via contentBinding that should probably be personBinding. And then in your template you should get the view.person.name.
{{collection}} should be a {{each}} block.
{{#each members}}
{{view App.PersonView personBinding="this"}}
{{/each}}
And this person template will look in the right location for the name.
<script type="text/x-handlebars" data-template-name="person">
<em>Person name is:</em> {{view.person.name}}
</script>
Didn't change your js.
fiddle: http://jsfiddle.net/albertjan/fkKFJ/9/

Categories