What is the best way to wire global data in multiple places using hyperHTML? - hyperhtml

I'm working on an application that reuses a lot of constants. I know that I can use wire IDs to use the same data in multiple places, but I'm not sure if I'm supposed to create a new globally unique ID every time I want to use that data.
This is a simplified example of what I'm working with:
const colors = [
{value: 'red', label: 'Red'},
{value: 'blue', label: 'Blue'}
]
class MyElement extends HTMLElement {
constructor() {
super()
this.html = hyperHTML.bind(this)
}
connectedCallback() {
this.html`
Hi, ${this.getAttribute('name')}! What is your favorite color?<br/>
<select>
${colors.map(option => hyperHTML.wire(option)`<option value=${option.value}>${option.label}</option>`)}
</select>
`
}
get name() {
return this.getAttribute('name')
}
}
customElements.define('my-element',MyElement);
hyperHTML.bind(document.body)`
<my-element name="Alice"></my-element>
<my-element name="Bob"></my-element>`
I know that unless I specify a wire ID, the options will only appear in one place. I could use :${this.name} as the wire ID, but I have to figure out what to do if there are two people with the same name, and if I want to have one form for "What is your favorite color?" and another for "What color is your shirt?", I have to make another unique ID for that.
Is there a way to somehow scope wire IDs or do I have to make globally unique ones?

There is a subtle issue in your logic, you'd like to map an array of items to themselves, instead of contextualizing their value.
Example: you are wiring the option object, out of usage, situation, context, instead of wiring the Custom Element instance to some data it's using to render itself.
As you can see in this CodePen example, you can simply wire the Custom Element context, and make the option information unique, in terms of scoped id, simply pointing at its value, through its owner:
hyperHTML.wire(this, `:option:${option.value}`)
At this point you'll have a single option element per each node so that you can easily select anything you want instead of moving the same option node globally all over the place.
Actually, the global ID is an anti-pattern as it is, I believe, in every other keyed framework such React or Vue so ... just use the instance, as your wire context/reference, and create a unique id for it with ease.
I hope this solved your issue.

Related

Apps script: use a form as a template for other forms creation

I have a form with a set of questions, and I want to use it for other forms creation. In other words, I want to iterate through this template form items, choose those that I need and append them to another, newly created form.
I had no problem with getting the items and choosing those that I need; however, I can't find the way to append these items to another form - all of the Form.addSomethingItem() methods don't take arguments (docs for example). Is it really impossible, or do I miss something foolishly simple?
There are two ways to achieve this:
Make a copy of the form using DriveApp and remove/modify items in-place on the new form copy.
Iterate over existing items, check it's type and add them to the new form. For example, The following gets Item1's enum and converts it to a camelCase string and executes add{Type}Item method, where Type is MultipleChoice in case of MULTIPLE_CHOICE Enum Item type.
const newForm2Item0 = Form2[
`add${String(Form1.getItems()[0].getType())
.toLowerCase()
.replace(/(^|_)[A-Z]/gi, match =>
match.toUpperCase().replace('_', '')
)}Item`
]();//equivalent to Form2.addMultipleChoiceItem()
You would then add choices and other configurations as needed in a similar manner.
Form methods like addTextItem() don't take arguments, rather they return Items classes (e.g. TextItem). Those Item classes have their own methods for assigning properties to a question on a form.
Examples
here's an example of adding a text item with a specific title
form.addTextItem().setTitle('I am a text question title');
here's an example of adding a multiple choice item to a form with some choices
var item = form.addMultipleChoiceItem();
var choices = ['choice 1', 'choice 2', 'choice 3'];
choices.forEach(function(choice) {
item.createChoice(choice);
})

Angular2 model-based parent/children form

I'm a newbie with Angular2 (beta1) and I'd like to implement a sort of simple editable grid, built of 2 components. Here I use two fake-data components to keep things simple. They are (see this Plunker: http://plnkr.co/edit/5cZfLTIlhLc82wWV4PQI):
the parent component, named contact. Say it represents a contact with a name.
the child component, named entry. Say it represents an entry for a contact, where each contact can include 0 or more entries. Each entry has an address and a zip code.
I'd like to create a form where the user can edit the contact's properties, and also its children entries: he could add a new entry, delete an existing entry, or edit an existing entry.
To this end, the views for both these components provide a form-based template.
I can think of this data flow:
contact: the user edits the form and then clicks a submit button to save
the whole thing. Thus, I can just have some code handling the submit button
and emitting an event as the component output. The contact has an entries
array property: I can thus use an ngFor directive in its template to render
an entry component for each of them.
entry: the entry has properties addressCtl and zipCtl which represent
the control directives included in the ControlGroup representing the whole
form. Also, I need a couple of properties to be bound as the input of the
component (address and zip), so that in the parent template I can do something like:
<tr *ngFor="#e of entries">
<td><my-entry [address]="e.address" [zip]="e.zip"></my-entry></td>
</tr>
Now, it's not clear to me how to shape the relation between the "model" properties representing the control's input, and the "form" directives properties. I should be able to get the address and zip values from the parent component through the [...] binding, and pass the updated values up through an event fired by the child component (e.g. blur?). Does this make sense in the NG2 world? Anyway, I'm missing a piece here: how can I connect the form controls values to the model properties values? Could anyone make this clearer or point to some good docs?
In fact, using the [...] binding only corresponds to a one-way binding. When the parent property is updated in the parent component, the value is also updated in the child component.
But if you want to update parent attributes from the child, you need to leverage events and #Ouput attribute.
Here is a sample with a labels component:
export class LabelsComponent implements OnInit {
#Input()
labels:string[];
#Output()
labelsChange: EventEmitter;
(...)
removeLabel(label:string) {
var index = this.labels.indexOf(label, 0);
if (index != undefined) {
this.labels.splice(index, 1);
this.labelsChange.emit(this.labels);
}
}
addLabel(label:string) {
this.labels.push(this.labelToAdd);
this.labelsChange.emit(this.labels);
this.labelToAdd = '';
this.addAreaDisplayed = false;
}
}
This way you can leverage two way binding on this component:
<labels [(labels)]="company.labels"></labels>
Hope it answers your question,
Thierry
Just moved the comment to answer...
You can pass the object e, instead of passing string.
i.e
<my-entry [entry] = "e"></my-entry>
then in your my-entry component, use ng-model for each input. so you automatically gets 2 way bindings.

How can I pull data in a controller and form for one model that is in a different model with out a foreign key relationship?

I am new to rails and haven't really done to much with data outside of the model.
I have a form that references a table controller for files. I want to add an dropdown that will display a list of projects from a project table for the user to assign a the file to a project if they want too. Assigning a project is not a requirement. The file can be unassigned to a project. I do have a project_id column in the file table for those projects assigned but it is allowed to be null and I did not build a relationship because I need to have cases where there are none.
Can someone please tell me how to do this?
When they evaluate the file, the screen posts back with a save or update button depending if it has already been saved in the database.
At the same time as the save and update pop up on the right I want to display a list box of the projects with an assign project button. If new just a list, if update either just a list because not assigned or display the selected value in the list if already assigned, while allowing them to change it from the list if desired.
In the file controller method that posts back to the UI I have this code:
#file_alias_filedata = FileAliasFiledata.all
#projects = Project.all
for update
#projects = Project.find(params[:id])
In the form I have this code:
<p> <label> Select Project to Assign:</label> <br />
<%= select_tag 'projects', (#projects.present? ? options_for_select(#projects, #selected_project) : []) %> </p>
The form runs but I get this in the dropdown box:
#<Project:0x))7ff531ab4518>
Can someone please help me figure out how to accomplish my task and why I see the strange box value?
What am I doing wrong?
Thank you for your help!
Assigning a project is not a requirement. The file can be unassigned
to a project. I do have a project_id column in the file table for
those projects assigned but it is allowed to be null
From the docs:
belongs_to(name, scope = nil, options = {}) public
Specifies a
one-to-one association with another class. This method should only be
used if this class contains the foreign key. If the other class
contains the foreign key, then you should use has_one instead. See
also ActiveRecord::Associations::ClassMethods’s overview on when to
use has_one and when to use belongs_to.
Methods will be added for retrieval and query for a single associated
object, for which this object holds an id:
association(force_reload = false)
Returns the associated object. nil is returned if none is found.
Likewise,
has_many(name, scope = nil, options = {}, &extension) public
Specifies
a one-to-many association. The following methods for retrieval and
query of collections of associated objects will be added:
collection(force_reload = false) Returns an array of all the
associated objects. An empty array is returned if none are found.
But belongs_to() and has_many() are supposed to make things more convenient for you. You certainly do not have to use them.
Next,
and why I see the strange box value? What am I doing wrong?
You see the strange value for the same reason the following two loops display different things:
class Dog
attr_reader :name
def initialize(name)
#name = name
end
end
#dogs = [
Dog.new("Sam"),
Dog.new("Betty"),
Dog.new("Pete"),
]
#dogs.each {|dog| puts dog}
#dog_names = #dogs.map {|dog| dog.name }
#dog_names.each {|dog_name| puts dog_name}
--output:--
#<Dog:0x0000010099a308>
#<Dog:0x0000010099a2b8>
#<Dog:0x0000010099a268>
Sam
Betty
Pete
You will see the same result if you do something like the following in a view:
<div>
<%= select_tag "dog", options_for_select(#dogs) %>
</div>
<div>
<%= select_tag "dog_name", options_for_select(#dog_names) %>
</div>
If you read the docs here:
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select
...you will see several examples, which should make it clear that options_for_select() takes an argument that is:
An array of Strings.
A Hash where both the keys and values are Strings.
An enumerable that iterates over some Strings.
etc.
Do you see a pattern? It's Strings! options_for_select() needs an argument that consists of Strings. If the argument is not a collection of Strings, e.g. an array of project objects, then options_for_select() tries to convert the objects to strings by calling to_s() on the objects. And the default to_s() method is Object#to_s() which is inherited by all objects and produces a string containing the class name and the object_id.
I am also new to rails, but I think you can try using the options_from_collection_for_select method.
<%= select_tag :search_state, options_from_collection_for_select(State.find(:all, :select => :name), :name, :name) %>
Hope this help. Cause it certainly helped me.

How can I get the Zend_Form object via the Zend_Form_Element child

I've built a Zend_Form_Decorator_Input class which extends Zend_Form_Decorator_Abstract, so that I could customize my form inputs -- works great. I ran into a problem in the decorate class, in trying to get the form name of the element, so as to built a unique id for each field (in case there are multiple forms with identical field names).
There is no method like this: Zend_Form_Element::getForm(); It seems Zend_Form_Decorator_Abstract doesn't have this ability either. Any ideas?
I don't think changing the id from the decorator is the right approach. At the time the decorator is called the element already has been rendered. Thus changing the id would have no effect to the source code. Additionally, as you already have pointed out, the relation between a form and its elements is unidirectional, i.e. (to my best knowledge) there is no direct way to access the form from the element.
So far the bad news.
The good news is, that there actually is a pretty easy solution to your problem: The Zend_Form option elementsBelongTo. It prevents that the same ID is assigned to two form elements that have the same name but belong to different forms:
$form1 = new Zend_Form(array('elementsBelongTo' => 'form1'));
$form1->addElement('Text', 'text1');
$form2 = new Zend_Form(array('elementsBelongTo' => 'form2'));
$form2->addElement('Text', 'text1');
Although both forms have a text field named 'text1', they have different ids: 'form1-text1' and 'form2-text1'. However, there is a major drawback to this: This also changes the name elements in such a way that they are in the format formname[elementname]. Therefore $this->getRequest()->getParam('formname') will return an associative array containing the form elements.

Autocompleter for Mootools to set multiple form values

I need a Mootools based autocompleter that retrieves data by ajax, and will fill in multiple form input elements when an option is selected. I.E, a user searches for "foo", and one of the options might be "foobar", which has associated with it the variables objecttype AND objectid, both of which need to be set in the form when the user clicks on "foobar".
I've been looking at two autocompleters: Meio.Autocomplete and AutoCompleter as my best bet, but neither of them really seem to handle what I need them to handle.
I'm considering either rewriting/extending one of those, or possibly using the Meio version (which handles a single value) and storing my multiple variable in the form in one field, possibly as a json object that can be parsed client side with jsonParse and inserted into the form with an additional function after the selection is made.
Does anyone know of a simpler solution?
You could use the Meio.Autocomplete's onSelect event with an identifier, that JSON encodes all of the needed properties.
var data = [
{value: 'name1', identifier: {
id: 'id1',
type: 'type1'
}},
...
}
I made a quick example