I'm trying to put together an email list template Using Handlebars.js with requirejs and Backbone.js, the initial rendering shows up as expected - a single email input with an add icon to add more.
var EmailView = bb.View.extend({
tagName: 'ul',
className: 'emailList',
events: {
"click .addEmail" : "addEmail",
"click .deleteEmail" : "deleteEmail"
},
initialize : function () {
this.template = hb.compile(hbTemplate);
},
render : function () {
this.$el.htmlPolyfill(this.template(this.model.toJSON()));
this.updateIcons();
return this;
},
...
The addEmail handler (I've tried appendPolyfill(), appendPolyFillTo, and the current updatePolyFill(). All produce the same results. The new line item is added, but all placeholders disappear (this is true for controls outside of $el, it appears to be the whole page.)
addEmail : function(e) {
this.$el.append( this.template({}) );
this.$el.updatePolyfill();
this.updateIcons();
}
What I want is for existing controls to maintain their placeholder text and the new one added showing the placeholder text as well. What am I missing?
If it helps, template looks like this ...
<li>
<span class="requiredPrompt">*</span>
<img class="icon" alt="" src="/images/emailIcon.png" />
<input type="email" class="emailAddress" value="" placeholder="Email Address" maxlength="50" required/>
<a class="deleteEmail" href="javascript:void(0)">
<img class="icon" alt="" src="/images/delFile.png" />
</a>
<a class="addEmail" href="javascript:void(0)">
<img class="icon enabled" alt="" src="/images/addFile.png" />
<img class="icon disabled" alt="" src="/images/addFile-disabled.png" />
</a>
</li>
As a quick fix you can simply return false or preventDefault() on your click handler. Here is a modfied jsfidlle.
jQuery('.addEmail').click(function () {
jQuery('.emailList').appendPolyfill(emailItem);
return false;
});
Webshims thinks the page unloaded and clears all placeholders.
Related
I am trying to refactor from class based to functional. While doing so I will need to use hooks instead of setting this.state etc.. I am trying to get a FORM to open when i click a button. The button will also change from "add reply" to "submit comment" once the form opens. I am stumped. This is the best thing I could come up with... Doesnt work. in fact, it makes my "add reply" button completely disappear. Any thoughts on this? Here is the code that I have written. inside of the comment I am trying to return a component using ternary....
image of component as-is
import React, {useState} from 'react';
import FormOpen from './FormOpen';
const CommentCreated = (props) => {
const [resource, setResource] = useState([{visible: false, addReply: 'Add Reply'}]);
return (
<div className="ui threaded comments">
<h3 className="ui dividing header">Comments</h3>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src= {props.avatar} />
</a>
<div className="content">
<a href="/" className="author">{props.author}
</a>
<div className="metadata">
<span className="date">Today at 5:42PM</span>
</div>
<div className="text">{props.content}
</div>
<div className="actions">
{resource.visible ? <FormOpen /> : null}
<a className="reply" onClick={() => {setResource([{visible: true, addReply: 'Submit Comment'}]);}}>{resource.addReply}
</a>
<a className="save">Save</a>
<a className="hide">Hide</a>
</div>
</div>
</div>
</div>
);
};
export default CommentCreated;
You should replace the setResource(['Submit Comment', true]) with :
setResource([{
visible: true,
addReply: 'Submit Comment'
}])
To match the shape of your state.
EDIT:
Here is a working example based on your code.
As I can see from your example, your resource state doesn't need to be an array but simply an object.
Furthermore, as you are using hyperlinks <a />, you must use preventDefault() on your event being triggered if you don't want the page to refresh.
Currently I'm trying to learn some more about Angular 2 by creating the amazing one of a kind shopping list application. However got stuck on creating a click event on my search results.
view.html
<form class="search">
<input type="text" name="query" class="form-control" [(ngModel)]="query" (ngModelChange)="change()" (blur)="onBlur()" />
<ul class="results" [class.hidden]="!showResults">
<li *ngFor="let result of results">
<a (click)="selectResult(result)">
{{ result.title }}
</a>
</li>
</ul>
</form>
view.ts
import { Component, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'search',
templateUrl: 'app/search/view.html',
styleUrls: ['app/search/view.css']
})
export class Search
{
... Some interesting things here
// selectResult
// ------------------------------ -->
selectResult (selected: any)
{
console.log(123);
}
// ------------------------------ -->
... More interesting things here
}
When someone starts typing the results will change dynamically. When someone clicks on a result nothing happens, no console.log(123).
Anyone knows how to attach click to a ngFor that changes?
You forget about one little thing, namely remove default href action. In your HTML file:
<a href="#" (click)="selectResult($event, result)">
{{ result.title }}
</a>
and in your TypeScript file:
selectResult (event: any, selected: any) {
event.preventDefault();
console.log(123);
}
I hope that it will help.
I found a way to solve this problem.
Somehow the click event is not bound when you hide the element. (wtf...?)
This is the final solution:
view.html
<form class="search">
<input type="text" name="query" class="form-control" [(ngModel)]="query" (ngModelChange)="change()" (blur)="onBlur()" />
{{ results.length }}
<ul class="results" *ngIf="results.length != 0">
<li *ngFor="let result of results">
<a href="#" (click)="selectResult(result)">
{{ result.title }}
</a>
</li>
</ul>
</form>
I have a list of search options grouped by their section and collapsed using accordion. The user expands a section and drags their selections to the "Saved List". These fields are saved and used for custom columns on tables in my site. Everything is working quite nicely except for a couple minor things.
I want to lock down the parents (Personal, Education, Subject) and only have the li elements inside be draggable. Right now the user can drag the whole group to the top which isn't desirable because I have a limit of fields they can search. Dragging a whole section can exceed that limit and break my tables.
In IE, after collapsing a parent, the li elements "blip" visible for a second before becoming hidden.
Not a huge deal but can be annoying, especially to some of my finicky users :)
Code is below. For styling see the Fiddle of my setup.
I've gotten to the point where I can't see the forests for the trees so if I could just be pointed in the right direction, that would be much appreciated!
HTML:
<div class="demo">
<div id="swaplist" style="height: 0;"></div>
<ul id="sortable2" class='saved'>
<div class="container" style="overflow:auto;">
<div align='center'>
<h2>Saved List</h2>
</div>
<div class="group"></div>
</div>
</ul>
<br />
<ul id="sortable1" class='available'>
<div align='center'>
<h2>Available Fields</h2>
</div>
<br />
<div id="accordion" style="width:950px;">
<div class="group">
<h2><span class="text">Personal</span></h2>
<div class="container">
<ul id="sortable1" class='available' style="width:850px;">
<li class='ui-state-default'><b>First Name</b>
<br />Section: A</li>
<li class='ui-state-default'><b>Last Name</b>
<br />Section: A</li>
<li class='ui-state-default'><b>Date of Birth</b>
<br />Section: A</li>
</ul>
</div>
</div>
<br />
<div class="group">
<h2><span class="text">Education</span></h2>
<div class="container">
<ul id="sortable1" class='available' style="width:850px;">
<li class='ui-state-default'><b>Associate's</b>
<br />Section: B</li>
<li class='ui-state-default'><b>Bachelor's</b>
<br />Section: B</li>
<li class='ui-state-default'><b>Master's</b>
<br />Section: B</li>
<li class='ui-state-default'><b>Doctorate</b>
<br />Section: B</li>
<li class='ui-state-default'><b>Other</b>
<br />Section: B</li>
</ul>
</div>
</div>
<br />
<div class="group">
<h2><span class="text">Subject</span></h2>
<div class="container">
<ul id="sortable1" class='available' style="width:850px;">
<li class='ui-state-default'><b>Science</b>
<br />Section: C</li>
<li class='ui-state-default'><b>Business</b>
<br />Section: C</li>
<li class='ui-state-default'><b>Liberal Arts</b>
<br />Section: C</li>
</ul>
</div>
</div>
<br />
</div>
<!-- End accordion -->
</ul>
<br clear="both" />
</div>
javascript:
$(function () {
$("#accordion")
.accordion({
active: false,
collapsible: true,
animate: false,
heightStyle: "content",
autoHeight: false,
header: "> div > h2"
})
.sortable({
axis: "y",
handle: "h2",
stop: function (event, ui) {
// IE doesn't register the blur when sorting
// so trigger focusout handlers to remove .ui-state-focus
ui.item.children("h2").triggerHandler("focusout");
}
});
$("ul.available").sortable({
connectWith: "ul",
scroll: true,
helper: 'clone', //keeps children visible when pulling out of container
appendTo: '#swaplist' //temporarily stores children in hidden div
});
$("ul.saved").sortable({
connectWith: "ul",
receive: function (event, ui) {
if ($(this).children().length > 9) {
//ui.sender: will cancel the change.
//Useful in the 'receive' callback.
$(ui.sender).sortable('cancel');
}
},
items: "li[id!=nomove]",
update: function () {
var order = $(this).sortable("serialize") + '&action=update';
$.post("ajax_file", order, function (theResponse) {
$("#info").html(theResponse);
});
},
helper: 'clone', //keeps children visible when pulling out of container
appendTo: '#swaplist' //temporarily stores children in hidden div
});
$("#sortable1, #sortable2").disableSelection();
$("#sortable1, #sortable2").disableSelection();
});
UPDATE:
I found out a third thing I needed to fix. I need to make sure the user doesn't select duplicate options. See the answer below for this.
I figured out a way to lock down the parents! It may not be the most ideal way but it works. See the full Fiddle here.
For #1, I applied the answer at How to implement a button on a jQuery Sortable accordion header to lock the parents in place. I made a class called "DontMove" and placed them on <h2> and then set that as my cancel option in every sortable, cancel: ".DontMove". Below is a snippet for implementing it.
$("#accordion")
.accordion({
active: false,
collapsible: true,
animate: false,
heightStyle: "content",
autoHeight: false,
header: "> div > h2"
})
.sortable({
axis: "y",
handle: "h2",
cancel: ".DontMove",
stop: function (event, ui) {
ui.item.children("h2").triggerHandler("focusout");
}
});
For #3 there was a solution thanks to prevent duplicated item in jQueryUI sortable.
This snippet is what did the trick. I placed it in the receive section.
var id = ui.item.attr('id');
var ele = $('#sortable2').find('li[id="'+id+'"]');
if (ele.length != 1) {
$(ui.sender).sortable('cancel');
}
The problem was initially that I couldn't get a list of current elements in the Saved List before the sortable receive function would fire and add the new element. So whenever I would check to see if the added element already existed, it would always cancel since it was already added, thus meeting the condition.
Instead of fighting it, I just got all line elements with the same id. If there were more than 1 then length > 1 so cancel the drop. I know it's not good practice to have duplicate id's but since the lists are database driven there is a chance of that happening and I want to cover all bases.
As far as issue #2, I don't see it happening in Firefox or Chrome so I'm going to leave it alone since I was most worried about #1 and #3.
I'm trying to launch an ng-click function inside an ng-repeat but I get a problem, while clicking the element, the function always pick the last occurrence of my loop.
To be more precise here's an example:
<div ng-controller="qualityController">
<div ng-repeat="quality in qualities">
<img src ={{ asset('bundles/mybundle/img/jauge_normal.png') }} width="160" height="160" alt="Circle" usemap="#circle">
<div class="g-performance-rate-knob">
<input type="text" data-angleOffset=180 data-angleArc=360 data-fgColor="#ff7200" data-width="116" data-height="116" data-step="0.1" data-min="0" data-max="100" data-readOnly=true knob class="dial" ng-model="quality.note">
</div>
<map name="circle" id="circle" ng-click="loadQualityDetail(quality.id)">
<area shape="circle" coords="80,80,80" data-toggle="modal" data-target="#modal-stars-{[{ quality.id }]}" class="more" alt=""/>
</map>
</div>
</div>
Js:
app.controller('qualityController', function ($scope,$http)
{
qualities = [];
qualities.push({id : 1});
qualities.push({id : 2});
qualities.push({id : 3});
qualities.push({id : 4});
$scope.loadQualityDetail = function(qualityId)
{
console.log(qualityId);
}
});
Wherever I click, I get "4" in my console.
Note: I use jQuery Knob library. But it seems like the problem appear on map or area elements.
If you need more information please tell me I could make a fiddle.
Thanks by advance,
I'm using mustache to render a series of images. Each image is accompanied by a text input for setting the image's caption, an update button for updating the caption in the database via Ajax, and a delete button for deleting the image from the database (also via Ajax):
{{#logos}}
<div class="logo">
<div class="logo-input">
<input type="text" placeholder="Caption" value="{{caption}}" />
</div>
<div class="logo-buttons">
<button>Update</button>
<button>Delete</button>
</div>
</div>
{{/logos}}
I'm new to backbone, and I can't figure out how to "associate" each update/delete button with its corresponding logo/caption. What's the best way to do this, considering that the number of images is unknown at run time?
You have two problems:
How do you know which button is pressed?
How do you know which logo you're working with?
The easiest way to solve the first problem is to attach a class to the buttons:
<button type="button" class="update">Update</button>
<button type="button" class="delete">Delete</button>
Then you can bind the events directly to the buttons using the view's events:
events: {
'click .update': 'update_caption',
'click .delete': 'delete_caption'
}
Also, you should always specify the type attribute when using <button>, the spec says that <button type="button"> is the default but some browsers use <button type="submit"> instead. If you always specify type="button" you don't have to worry about what sort of nonsense the browsers will get up to.
Now you have to figure out which logo you're working with.
One way is to keep using a single view and attach a data attribute somewhere easy to find. For example:
{{#logos}}
<div class="logo" data-logo="{{id}}">
<!-- ... -->
</div>
{{/logos}}
Then you can do things like this in the click handlers:
update_caption: function(ev) {
var id = $(ev.currentTarget).closest('.logo').data('logo');
var logo = this.collection.get(id);
//...
}
Demo: http://jsfiddle.net/ambiguous/DwkPV/
Alternatively, you could use one sub-view for each logo. Here you'd have one view per-logo:
var LogoView = Backbone.View.extend({
className: 'logo',
events: {
'click .update': 'update_caption',
'click .delete': 'delete_caption'
},
//...
});
and a template without the {{#logos}} loop or the outer per-logo <div>:
<div class="logo-input">
<input type="text" placeholder="Caption" value="{{caption}}" />
</div>
<div class="logo-buttons">
<button>Update</button>
<button>Delete</button>
</div>
and the click handlers would simply look at this.model:
update_caption: function() {
var logo = this.model;
//...
}
Then a main view to iterate over the logos and render the subviews:
var LogosView = Backbone.View.extend({
render: function() {
this.collection.each(function(logo) {
var v = new LogoView({ model: logo });
this.$el.append(v.render().el);
}, this);
return this;
},
//...
});
Demo: http://jsfiddle.net/ambiguous/9A756/