currently i'm starting with Ember, and i'm loving it! I'm with some difficulties, especially when it comes to components.
For you to understand, I'm going through old code to Ember, and I would like to turn this code into a Component, but I do not know actually how to start, since I do not know how to catch the button being clicked, and I also realized that Ember has several helpers, maybe I do not need any of this giant code to do what I want.
This is the old code result: http://codepen.io/anon/pen/WQjobV?editors=110
var eventObj = {};
var eventInstances = {};
var actual;
var others;
var clicked;
var createEventInstance = function (obj) {
for (var key in obj) {
eventInstances[key] = new Event(obj[key]);
}
};
var returnStyle = function (inCommon) {
var $inCommon = inCommon;
$inCommon.css({
width: '342.4px',
minWidth: '342.4px'
});
$inCommon.find('.cta').removeClass('hidden');
$inCommon.find('.event-close').removeClass('inline');
$inCommon.find('.event-info_list').removeClass('inline');
$inCommon.removeClass('hidden');
$inCommon.find('.expanded').slideUp();
$inCommon.find('.expanded').slideUp();
$inCommon.find('.event-arrow').remove();
$inCommon.find('h2').find('ul').remove('ul');
};
var Event = function (id) {
this.id = id;
};
Event.prototype.expandForm = function () {
actual.css('width', '100%');
actual.find('.event-info_list').addClass('inline');
actual.find('.expanded').slideDown().css('display', 'block');
actual.find('.event-close').addClass('inline');
};
Event.prototype.close = function () {
returnStyle(actual);
returnStyle(others);
};
Event.prototype.hideElements = function () {
clicked.addClass('hidden');
others.addClass('hidden');
};
Event.prototype.maskPhone = function () {
$('[name$=phone]').mask('(99) 99999-9999', {
placeholder: '(00) 0000-0000'
});
};
$('.submit-form').on('click', function (e) {
e.preventDefault();
var id = '.' + $(this).data('id');
var name = $(id).children('#person-name').val();
var email = $(id).children('#person-email').val();
var guests = $(id).children('#person-obs.guests').val();
var phone = $(id).children('#person-phone').val();
var participants = $(id).children('#booking-participants').val();
if (name === '' || email === '' || phone === '' || participants === '' || guests === '') {
alert('Preencha os campos obrigatórios.');
} else {
$(id).submit();
}
});
Event.prototype.createDropDown = function () {
actual.find('h2').addClass('event-change')
.append('<span class="event-arrow" aria-hidden="true">▼</span>')
.append(function () {
var self = $(this);
var list = '<ul class="dropdown hidden">';
$('.event').each(function (index) {
if ($(this).find('h2')[0] != self[0]) {
list += '<li data-index="' + index + '">' + $(this).find('h2').text() + '</li>';
}
});
return list;
}).click(function () {
if ($(this).attr('data-expanded') == true) {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', false);
} else {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', true);
}
}).find('li').click(function (e) {
e.stopPropagation();
actual.find('.event-info_list').removeClass('inline');
actual.find('h2').attr('data-expanded', false);
actual.find('h2').removeClass('event-change');
actual.find('.expanded').slideUp().css('display', 'inline-block');
others.removeClass('hidden');
actual.find('.cta').removeClass('hidden');
actual.find('h2').find('.event-arrow').remove();
actual.find('h2').off('click');
actual.find('h2').find('ul').remove('ul');
$($('.event')[$(this).attr('data-index')]).find('.cta').trigger('click');
});
};
Event.prototype.open = function () {
actual = $('[data-id="' + this.id + '"]');
others = $('.event').not(actual);
clicked = actual.find('.cta');
this.hideElements();
this.expandForm();
this.createDropDown();
this.maskPhone();
};
$('.event').each(function (i, event) {
var prop = 'id' + $(event).data('id');
var value = $(event).data('id');
eventObj[prop] = value;
});
createEventInstance(eventObj);
Basically i have this boxes, which box represent one booking in some event (will be populate by the server). When the user clicks in one box, this boxes expands and the other disappear. But than a dropbox will be created with the other boxes, so the user can navigate in the events by this dropdown.
I didn't do much with Ember, i transform the "events" div into a component with the name "BookingBoxComponent" and two actions:
SiteApp.BookingBoxComponent = Ember.Component.extend({
actions:
open: function() {
// HOW COULD I ACCESS THE CLICKED BUTTON HERE?
},
close: function() {
}
});
As you can see, i put two actions, one for opening the box and other for closing, should i just put the logic in both, or i can improve this like a Ember way?
I don't know if i am asking to much here, so if i am, at least i would like to know how to access the button clicked in the open method, i was trying passing as a parameter, like:
<button {{action 'open' this}}></button>
But didn't work.
I could offer 50 of my points to someone who help transform the old cold in a Ember way code.
Thanks.
The event object will be passed with every action as the last parameter, so when you specified this you were actually passing whatever object has context in that block. In your open function, do not pass this and do
open: function(event) {
// event.currentTarget would be the button
}
And now you can do something like event.currentTarget or event.target
Related
I am working on a web application, I am just using javascript at the moment. The problem that I am trying to solve is that I have three different objects and only one HTML page. Based on the user click event, I want the objects for the chosen category to be loaded and displayed on the same page. For example, let's say the user is at the home page, if they click on category A from the navigation bar, the page will be loaded first and then the script will load the objects to the data structure. Finally, display them to the javascript generated HTML containers. The same thing should happen with a different category after the User click Event is fired. To be more precise I want to be able to reuse the HTML page for different objects without having to create a page for each category.
I already have created the code that does all of the data loading and HTML generation for the n objects I want to load. The code works fine when I am at the object's page but if the event is fired from another page nothing seems to happen. I am guessing this has to do with page loading timing.
I have posted the complete code of the part that I am working on.
var dataController = (function() {
var JSONurls = {
bags: "../JSON/bags.json",
bc: "../JSON/briefcases.json",
belts: "../JSON/belts.json",
accs: "../JSON/accs.json",
};
ProductObj = function(name, des, colors, price, pics, type, ID) {
this.name = name;
this.description = des;
this.colors = colors;
this.price = price;
this.pics = pics;
this.type = type;
this.ID = ID;
};
var dataStruc = {
allProducts: {
bags: [],
briefcases: [],
belts: [],
accessories: [],
},
};
return {
addProd: function(obj) {
var newProd, ID;
if (dataStruc.allProducts[obj.type].length > 0) {
ID =
dataStruc.allProducts[obj.type][
dataStruc.allProducts[obj.type].length - 1
].ID + 1;
} else {
ID = 0;
}
newProd = new ProductObj(
obj.name,
obj.description,
obj.colors,
obj.price,
obj.pics,
obj.type,
obj.ID
);
dataStruc.allProducts[obj.type].push(newProd);
return newProd;
},
getDataStruct: function() {
return dataStruc;
},
getJsonUrls: function() {
return JSONurls;
},
loadJSON: function(url, cat, callback) {
var requestURL, request, JsonObj;
requestURL = url;
request = new XMLHttpRequest();
request.open("GET", requestURL);
request.responseType = "text";
request.send();
request.onload = function() {
JsonObj = JSON.parse(request.response);
dataStruc.allProducts[cat] = JsonObj[cat];
callback(cat);
};
},
};
})();
var UIcontroller = (function() {
var DomStrings = {
shopCatg: ".shop-catg",
productCont: ".product-container",
};
//public methods
return {
// function display the object based on the category based on the event target
displayObjectToPage: function(cat) {
var deafultHtml;
// 1. loop over the product category
dataController.getDataStruct().allProducts[cat].forEach(function(cur) {
deafultHtml =
'<div class="col-lg-4 col-md-6 col-sm-10">' +
'<img class="img-fluid" src="../img/' +
cur.type +
"/" +
cur.pics[0] +
'.jpg">' +
'<h6 class="text-center">' +
cur.name +
"</h6>" +
'<div class="text-center text-muted">' +
cur.price +
"</div>" +
"</div>";
document
.querySelector(DomStrings.productCont)
.insertAdjacentHTML("beforeend", deafultHtml);
});
},
getDomStrings: function() {
return DomStrings;
},
};
})();
var mainController = (function(UIctrl, dataCrtl) {
var setUpEvents = function() {
var doneLoading = false;
var DOM = UIctrl.getDomStrings();
document.querySelector(DOM.shopCatg).addEventListener("click", function() {
InitializeData(event, function(cat) {
UIcontroller.displayObjectToPage(cat);
});
});
};
InitializeData = function(event, callback) {
var category = event.target.textContent;
if (event.target.textContent === "bags") {
dataController.loadJSON(
dataController.getJsonUrls().bags,
category,
callback
);
} else if (event.target.textContent === "briefcases") {
dataController.loadJSON(dataController.getJsonUrls().bags, "briefcases");
} else if (event.target.textContent === "belts") {
dataController.loadJSON(dataController.getJsonUrls().bags, "belts");
} else {
dataController.loadJSON(dataController.getJsonUrls().bags, "accs");
}
};
displayObject = function() {};
return {
init: function() {
setUpEvents();
},
};
})(UIcontroller, dataController);
mainController.init();
I'm not sure, but I noticed this potential issue:
request.send();
request.onload = function() {
// ...
}
I believe when you call send, the request should start asynchronously. If the request comes back before onload is assigned, you might be seeing it get skipped. I haven't used XHR directly in years, though.
Normally you'd want to add the onload callback before calling send() to avoid this issue.
I also just noticed that you're missing the event in the arguments of the callback function here:
▽
document.querySelector(DOM.shopCatg).addEventListener("click", function() {
▽ event is undefined
InitializeData(event, function(cat) {
UIcontroller.displayObjectToPage(cat);
});
});
Working on a practice app with localStorage, but the stored data is getting cleared on page refresh. Based on answers to similar questions, I've used JSON.stringify(); on setItem, and JSON.parse(); on getItem, but still no luck. Am I using those methods in the wrong way? For reference, #petType and #petName are input IDs, and #name and #type are ul IDs. Thanks!
var animalArray = [];
var addPet = function(type,name) {
var type = $("#petType").val();
var name = $("#petName").val();
localStorage.setItem("petType", JSON.stringify(type));
localStorage.setItem("petName", JSON.stringify(name));
animalArray.push(type,name);
};
var logPets = function() {
animalArray.forEach( function(element,index) {
//empty array
animalArray.length = 0;
//empty input
$("input").val("");
var storedName = JSON.parse(localStorage.getItem("petName"));
var storedType = JSON.parse(localStorage.getItem("petType"));
//append localStorage values onto ul's
$("#name").append("<li>" + storedName + "</li>");
$("#type").append("<li>" + storedType + "</li>");
});
};
//click listPets button, call logPets function
$("#listPets").on("click", function() {
logPets();
$("#check").html("");
});
//click enter button, call addPet function
$("#enter").on("click", function() {
addPet(petType,petName);
$("#check").append("<i class='fa fa-check' aria-hidden='true'></i>");
});
It appears to clear because you are not loading data from it when the page loads. There are multiple bugs in the code:
It appears that you're only saving the last added pet to localStorage, which would create inconsistent behaviour
Setting animalArray.length to 0 is incorrect
animalArray.push(type, name); is probably not what you want, since it adds 2 items to the array, do something like animalArray.push({type: type, name: name});
logPets can just use the in memory array, since it's identical to the one saved
Fixed code:
var storedArray = localStorage.getItem("animalArray");
var animalArray = [];
if(storedArray) {
animalArray = JSON.parse(storedArray);
}
var addPet = function(type,name) {
var type = $("#petType").val();
var name = $("#petName").val();
animalArray.push({type: type, name: name});
localStorage.setItem("animalArray", JSON.stringify(animalArray));
};
var logPets = function() {
animalArray.forEach( function(element,index) {
//empty input
$("input").val("");
//append localStorage values onto ul's
$("#name").append("<li>" + element.name + "</li>");
$("#type").append("<li>" + element.type + "</li>");
});
};
//click listPets button, call logPets function
$("#listPets").on("click", function() {
logPets();
$("#check").html("");
});
//click enter button, call addPet function
$("#enter").on("click", function() {
addPet(petType,petName);
$("#check").append("<i class='fa fa-check' aria-hidden='true'></i>");
});
A quick fiddle to demo it: https://jsfiddle.net/rhnnvvL0/1/
I am trying to change this demo:
http://maxwells.github.io/bootstrap-tags.html
into a responsive version in which I can set it to readOnly and remove it from readOnly as I like. This code:
var alltags = ["new tag", "testtag", "tets", "wawa", "wtf", "wtf2"];
$(document).ready(function() {
var tagbox = $('#my-tag-list').tags({
suggestions: alltags
});
var tagenable = true;
$('#my-tag-list').focusout(function() {
if (tagenable) {
tagbox.readOnly = true;
$('#my-tag-list').empty();
tagbox.init();
tagenable = false;
}
});
$('#my-tag-list').click(function() {
if(!tagenable) {
tagbox.readOnly = false;
$('#my-tag-list').empty();
tagbox.init();
tagenable = true;
}
});
});
seems to work fairly well, it makes everything readonly after focusout and editable when I click it. However, the editing does not work since I cannot insert new tags nor delete them (seems to be like event handling was lost or something like that).
I am guessing that emptying the #my-tag-list div is causing this, but I cannot yet find a way to use for instance "detach" instead that removes everything inside (not the element itself) and putting it back in again.
I tried to make a JS Fiddle, but it isn't really working so well yet:
http://jsfiddle.net/tomzooi/cLxz0L06/
The thing that does work is a save of the entire website, which is here:
https://www.dropbox.com/sh/ldbfqjol3pppu2k/AABhuJA4A6j9XTxUKBEzoH6za?dl=0
this link has the unminimized JS of the bootstrap-tags stuff I am using:
https://github.com/maxwells/bootstrap-tags/blob/master/dist/js/bootstrap-tags.js
So far I managed to do this with some modifications of the bootstrap javascript code. I use two different tagbox which I hide and unhide with some click events.
var tagbox = $('#my-tag-list').tags({
suggestions: alltags,
tagData: tmp_tags,
afterAddingTag: function(tag) { tagboxro.addTag(tag); },
afterDeletingTag: function(tag) {tagboxro.removeTag(tag); }
});
var tagboxro = $('#my-tag-listro').tags({
suggestions: alltags,
tagData: tmp_tags,
readOnly: 'true',
tagSize: 'sm',
tagClass: 'btn-info pull-right'
});
$(document).mouseup(function (e) {
var container = $("#my-tag-list");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) { // ... nor a descendant of the container
if (tagsave) {
$("#my-tag-listro").show();
$("#my-tag-list").hide();
var tags = tagbox.getTags();
$.post("%basedir%/save.php", {
editorID:"new_tags",
tags:tags
}, function(data,status){
//alert("Data: " + data + "\nStatus: " + status);
});
tagsave = false;
}
}
});
$('#my-tag-listro').click(function() {
tagsave = true;
//$(".tag-list").toggle();
$("#my-tag-list").show();
$("#my-tag-listro").hide();
});
I had to modify the code of bootstrap-tags.js to allow for this since it normally deletes all of the usefull functions when it is considered readonly in the init function:
if (this.readOnly) {
this.renderReadOnly();
this.removeTag = function(tag) {
if (_this.tagsArray.indexOf(tag) > -1) {
_this.tagsArray.splice(_this.tagsArray.indexOf(tag), 1);
_this.renderReadOnly();
}
return _this;
};
this.removeTagClicked = function() {};
this.removeLastTag = function() {};
this.addTag = function(tag) {
_this.tagsArray.push(tag);
_this.renderReadOnly();
return _this;
};
this.addTagWithContent = function() {};
this.renameTag = function() {};
return this.setPopover = function() {};
}
would be awesome if this feature was incorporated in a somewhat less hacky way though :)
Here is I have table built with knockout 'foreach'. After user select some rows, these rows will contain class 'success'. So I want to use self.create event that fire after user press button (button located outside element that ViewModel binded to) in order to handle such table rows. But Firebug said: TypeError: GrafikViewModel.books is undefined.
Here is the code:
function InfoViewModel(baseUri) {
//some viewmodel here
}
//This is viewmodel I'm talking about.
function GrafikViewModel(grafikUri) {
var self = this;
self.books = ko.observableArray();
self.create = function () {
//Here we will handle tr with class 'success'
alert("!!!");
}
$.getJSON(grafikUri, function (data) {
self.books(data.$values);
});
}
$(document).ready(function () {
var url = location.href.split("/");
var baseUri;
var tkod = url[5];
if (url[4].toString = 'x') {
baseUri = '/api/xTourist/' + tkod;
}
else if (url[4].toString = 'y') {
baseUri = '/api/yTourist/' + tkod;
}
var grafikUri = '/api/grafik/' + tkod;
ko.applyBindings(InfoViewModel(baseUri), document.getElementById('info'));
ko.applyBindings(new GrafikViewModel(grafikUri), document.getElementById('grafik'));
$('#book').click(function () {
//Here I'm trying to call ViewModel.
GrafikViewModel.books.create(ko.dataFor(this));
});
});
try new GrafikViewModel().books.create(ko.dataFor(this));
I have some jQuery plugin that changes some elements, i need some event or jQuery plugin that trigger an event when some text input value changed.
I've downloaded jquery.textchange plugin, it is a good plugin but doesn't detect changes via external source.
#MSS -- Alright, this is a kludge but it works:
When I call boxWatcher() I set the value to 3,000 but you'd need to do it much more often, like maybe 100 or 300.
http://jsfiddle.net/N9zBA/8/
var theOldContent = $('#theID').val().trim();
var theNewContent = "";
function boxWatcher(milSecondsBetweenChecks) {
var theLoop = setInterval(function() {
theNewContent = $('#theID').val().trim();
if (theOldContent == theNewContent) {
return; //no change
}
clearInterval(theLoop);//stop looping
handleContentChange();
}, milSecondsBetweenChecks);
};
function handleContentChange() {
alert('content has changed');
//restart boxWatcher
theOldContent = theNewContent;//reset theOldContent
boxWatcher(3000);//3000 is about 3 seconds
}
function buttonClick() {
$('#theID').value = 'asd;lfikjasd;fkj';
}
$(document).ready(function() {
boxWatcher(3000);
})
try to set the old value into a global variable then fire onkeypress event on your text input and compare between old and new values of it. some thing like that
var oldvlaue = $('#myInput').val();
$('#myInput').keyup(function(){
if(oldvlaue!=$('#myInput').val().trim())
{
alert('text has been changed');
}
});
you test this example here
Edit
try to add an EventListner to your text input, I don't know more about it but you can check this Post it may help
Thanks to #Darin because of his/her solution I've marked as the answer, but i have made some small jQuery plugin to achieve the same work named 'txtChgMon'.
(function ($) {
$.fn.txtChgMon = function (func) {
var res = this.each(function () {
txts[0] = { t: this, f: func, oldT: $(this).val(), newT: '' };
});
if (!watchStarted) {
boxWatcher(200);
}
return res;
};
})(jQuery);
var txts = [];
var watchStarted = false;
function boxWatcher(milSecondsBetweenChecks) {
watchStarted = true;
var theLoop = setInterval(function () {
for (var i = 0; i < txts.length; i++) {
txts[i].newT = $(txts[i].t).val();
if (txts[i].newT == txts[i].oldT) {
return; //no change
}
clearInterval(theLoop); //stop looping
txts[i].f(txts[i], txts[i].oldT, txts[i].newT);
txts[i].oldT = $(txts[i].t).val();
boxWatcher(milSecondsBetweenChecks);
return;
}
}, milSecondsBetweenChecks);
}