$(function() {
var model = {
currentCat: null,
cats: [{
name: "Felix",
clickCounter: 0,
srcImage: "cat0.jpg"
},
{
name: "Lucas",
clickCounter: 0,
srcImage: "cat1.jpg"
},
{
name: "Martin",
clickCounter: 0,
srcImage: "cat2.jpg"
},
{
name: "Pedro",
clickCounter: 0,
srcImage: "cat3.jpg"
}
]
};
var octopus = {
init: function() {
indexView.init();
displayView.init();
},
getCats: function() {
return model.cats;
},
getCurrentCat: function() {
return model.currentCat;
},
setCurrentCat: function(cat) {
model.currentCat = cat;
},
updateClickCounter: function() {
model.currentCat.clickCounter++;
displayView.render();
}
};
var displayView = {
init: function() {
this.imgSection = document.getElementById("imageSection");
this.catImg = document.getElementById("cat-img");
this.catName = document.getElementById("cat-name");
this.catCounter = document.getElementById("cat-counter");
this.catImg.addEventListener("click", function() {
octopus.updateClickCounter();
})
this.render()
},
render: function() {
var cat = octopus.getCurrentCat();
this.catName.textContent = cat.name;
this.catCounter.textContent = cat.clickCounter;
this.catImg.src = cat.srcImage;
}
};
var indexView = {
init: function() {
this.list = $("#list")
this.render();
},
render: function() {
cats = octopus.getCats();
for (i = 0; i < cats.length; i++) {
cat = cats[i];
listElement = document.createElement("li");
listElement.textContent = cat.name;
listElement.addEventListener("click", (function(copyCat) {
octopus.setCurrentCat(copyCat);
displayView.render();
})(cat));
};
}
};
octopus.init();
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Cat clicker</title>
<link rel="stylesheet" href="css/cat.css">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" type="text/css" />
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="js/cat.js"></script>
<h1 id="header"> Gatitos! </h1>
<div id="catIndex">
<h2 id="indexTitle">Index</h2>
<ul id="list">
<!-- here we have the index with the cats names -->
</ul>
</div>
<div id="imageSection">
<h2 id="cat-name"></h2>
<div id="cat-counter"></div>
<img src="" id="cat-img">
</div>
</body>
</html>
in the displayView object, I can only acces the html elements that I got with getElementById inside the method they were initialized in (I can acces catImg when I add the event listener in the init method). The problem comes when I try to acces those elements in the render method. When you run this it returns undefined when you call all the elements from the init method (this.catImg, this.catName and this.catCounter). Why does it return undefined?
you have to bind 'this' to your event handler, check out Javascript scope addEventListener and this on how to scope this to your event listener.
Related
I want to write a program with Vue Js. The program is about having 2 images that continuously alternate every 3 seconds.
Please someone help me.
Here is the code
<script>
const app = Vue.createApp({
data() {
return{
src : "farkas.jpg"
}
},
methods: {
callFunction: function() {
var v = this;
setInterval(function() {
v.src = "mokus.jpg"
}, 3000);
}
},
mounted () {
this.callFunction()
}
})
const vm = app.mount('#app')
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image changer</title>
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<div id="app">
<img :src="src">
</div>
</body>
</html>
You can define the list of sources in the data:
data() {
return {
src : "farkas.jpg",
srcs: ["farkas.jpg", "mokus.jpg"]
}
}
And in the function, define i initially 0, and every time increment/reset it:
callFunction: function() {
let i = 0;
setInterval(() => {
this.src = this.srcs[i];
if(++i === this.srcs.length) i = 0;
}, 3000);
}
this.interval = setInterval(function() {
if (v.src === "mokus.jpg") v.src = "farkas.jpg"
else v.src = "mokus.jpg" }, 3000);
And destroy the interval with this code in the beforeDestroy hook.
clearInterval(this.interval)
Can You Try:
const app = Vue.createApp({
data() {
return{
currentSrc : 0,
srcs: ["img1.jpg", "img2.jpg"]
}
},
methods: {
callFunction: function() {
setInterval(() => {
this.currentSrc = this.currentSrc === this.srcs.length - 1 ? 0 : this.currentSrc + 1;
}, 1000);
}
},
computed: {
src: function(){
return this.srcs[this.currentSrc]
}
},
mounted () {
this.callFunction()
}
})
const vm = app.mount('#app')
I have some question about this problem, and I still not found the way and I wanna ask in here. Okay for the problem is I wanna delete all my empty array and not show into user view and just show the not empty array, like this for the code and result :
const objectFruits = [
{fruits: 'apple'},
{fruits: 'banana'},
{fruits: 'strawberry'},
{fruits: 'manggo'},
];
const objectMyFavorite = [
{fruits: 'apple'},
{fruits: 'apple'},
{fruits: 'banana'},
];
const getAllFruits = { fruits: objectFruits.map(item => item.fruits) }
const getAllMyFavorite = { fruits: objectMyFavorite.map(item => item.fruits) }
let myView = '';
for (let i = 0; i < getAllFruits['fruits'].length; i++) {
const nameFruits = getAllFruits['fruits'][i];
const filterMyFavorite = getAllMyFavorite['fruits'].filter(function (fruitss) {
return fruitss == nameFruits
});
// and how I delete the empty and just show array if value is not null and show it to user
myView += `
<p>${filterMyFavorite.length == 0 ? `delete this array and delete this row and not show in user view` : `is not empty => expected (apple, apple) and (banana)`}</p>
`;
}
document.getElementById("test").innerHTML = myView
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>test</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="test">
</div>
<script src="script.js"></script>
</body>
</html>
If I make a some mistake in my word you can direct again to me, thank you
You're too close for the solution. Check whether the below code fulfills your task:
const objectFruits = [{
fruits: 'apple'
},
{
fruits: 'banana'
},
{
fruits: 'strawberry'
},
{
fruits: 'manggo'
},
];
const objectMyFavorite = [{
fruits: 'apple'
},
{
fruits: 'apple'
},
{
fruits: 'banana'
},
];
const getAllFruits = {
fruits: objectFruits.map(item => item.fruits)
}
const getAllMyFavorite = {
fruits: objectMyFavorite.map(item => item.fruits)
}
let myView = '';
for (let i = 0; i < getAllFruits['fruits'].length; i++) {
const nameFruits = getAllFruits['fruits'][i];
const filterMyFavorite = getAllMyFavorite['fruits'].filter(function(fruitss) {
return fruitss == nameFruits
});
// and how I delete the empty and just show array if value is not null and show it to user
if (filterMyFavorite.length > 0){
myView += `<p>${filterMyFavorite}</p>`
}
}
document.getElementById("test").innerHTML = myView
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>test</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="test">
</div>
<script src="script.js"></script>
</body>
</html>
You can append to myView only if the value is valid:
if (filterMyFavorite.length > 0) {
myView += `
<p>Add item here</p>
`;
}
Use a simple conditional:
...
if (filterMyFavorite.length !== 0) {
myView += "<p>" + filterMyFavorite.join(",") + "</p>";
}
...
As mentioned in a comment you can't delete the array, but you can hide it - try this:
if(filterMyFavorite.length > 0) {
filterMyFavorite.map(f => { myView += `<p>${f}</p>` })
}
I have below custom control created by extending sap.m.Input that lets the user enter only numbers. However, when there is actually an error the state of control changes to Error with red borders but the valueStateText is not displayed when it has focus. How can I get the valueStateText for my custom control? Shouldn't it inherit from sap.m.Input?
Custom Control code:
sap.ui.define([
"sap/m/Input"
], function (Control) {
"use strict";
return Control.extend("sample.control.NumericInput", {
metadata: {
properties: {},
aggregations: {},
events: {}
},
init: function () {
if (sap.ui.core.Control.prototype.onInit) {
sap.ui.core.Control.prototype.onInit.apply(this, arguments);
}
this.attachLiveChange(this.onLiveChange);
},
renderer: function (oRM, oControl) {
sap.m.InputRenderer.render(oRM, oControl);
},
onLiveChange: function (e) {
var _oInput = e.getSource();
var val = _oInput.getValue();
val = val.replace(/[^\d]/g, "");
_oInput.setValue(val);
}
});
});
XML code:
<hd:NumericInput value="{path:'payload>/MyNumber',type:'sap.ui.model.type.String',constraints:{minLength:1,maxLength:10}}" valueStateText="My Number must not be empty. Maximum 10 characters."/>
In your init override you need to call the init of the parent control (not onInit of sap.ui.core.Control). The init of the sap.m.InputBase (sap.m.Input's parent class) sets up the valuestate initial values and rendering so it's missing all that code out and not working correctly as you've found.
Check out this example based on your code:
sap.ui.define([
"sap/m/Input"
], function (Control) {
"use strict";
return Control.extend("sample.control.NumericInput", {
metadata: {
properties: {},
aggregations: {},
events: {}
},
init: function () {
Control.prototype.init.apply(this, arguments);
this.attachLiveChange(this.onLiveChange);
},
renderer: function (oRM, oControl) {
sap.m.InputRenderer.render(oRM, oControl);
},
onLiveChange: function (e) {
var _oInput = e.getSource();
var val = _oInput.getValue();
val = val.replace(/[^\d]/g, "");
_oInput.setValue(val);
}
});
});
// Small model
const model = new sap.ui.model.json.JSONModel({
MyNumber: "10000000000000000000",
});
// Create an example of the control (JS not XML but idea is the same)
const input = new sample.control.NumericInput({
valueState: "Error",
valueStateText:"My Number must not be empty. Maximum 10 characters.",
value: {
path:'/MyNumber',
type:'sap.ui.model.type.String',
constraints:{minLength:1,maxLength:10}
}
});
input.setModel(model);
input.placeAt('content');
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-libs="sap.m"></script>
</head>
<body class="sapUiBody sapUiSizeCompact">
<div id='content'></div>
</body>
</html>
you can greatly reduce your code to
Input.extend('NumericInput', {
renderer: {
},
onAfterRendering: function () {
if (Input.prototype.onAfterRendering) {
Input.prototype.onAfterRendering.apply(this, arguments);
}
this.$().find("INPUT").each(function(i, input) {
$(input).on("keypress keyup blur", function(event) {
$(this).val($(this).val().replace(/[^\d].+/, ""));
if ((event.which < 48 || event.which > 57)) {
event.preventDefault();
}
});
});
},
});
https://jsbin.com/muberid/1/edit?js,output
I am trying to add a click event listener to the dynamically created li's so that they work independent of each other(currently, all three fire when you click one of them). Every solution I see involves a closure which I thought I was doing but i guess not. Also, the view keeps getting updated with last element in the model array and I can't figure out why. Any help is greatly appreciated.
$(document).ready(function() {
//Data displayed in the view
var model = {
carOnDisplay: null,
listOfCars: [{
numClicks: 0,
carName: "Challenger",
color: 'grey'
},
{
numClicks: 0,
carName: "Charger",
color: 'blue'
},
{
numClicks: 0,
carName: "Mustang",
color: 'orange'
},
]
};
var controller = {
init: function() {
// set the default car to be displayed
model.carOnDisplay = model.listOfCars[0];
carView.init();
carListView.init();
},
getListOfCars: function() {
return model.listOfCars;
},
setCurrentCar: function(car) {
model.carOnDisplay = car;
},
getCurrentCar: function() {
return model.carOnDisplay;
},
incrementClicks: function() {
model.carOnDisplay.numClicks++;
carView.render();
}
};
var carView = {
init: function() {
// cached DOM
this.car = $('#car');
this.name = $('h3.carName');
this.clicks = $('h4.clicks');
// add click listeners to car
this.car.click(function() {
controller.incrementClicks();
});
this.render();
},
render: function() {
var currentCar = controller.getCurrentCar();
this.car.css('background-color', currentCar.color);
this.name.html(currentCar.carName);
this.clicks.html(currentCar.numClicks);
}
};
var carListView = {
init: function() {
// cache DOM elements
this.carList = $('#carList');
this.render();
},
render: function() {
// this.links = $('#carList').getElementsByTagName('li');
var car = controller.getListOfCars(),
currentCar = controller.getCurrentCar();
for (var i = 0; i < car.length; i++) {
var carNow = car[i];
$('#carList').append("<li>" + carNow.carName + "</li>");
$('#carList').on('click', one());
// // controller.setCurrentCar(carNow);
// // carView.render();
};
function one() {
return function(event) {
controller.setCurrentCar(carNow);
carView.render();
}
}
}
} // end of carListView
controller.init();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
<div class="col-3">
<ul id="carList">
</ul>
</div>
<div id="content" class="col-9">
<div id="car" class="col-4">
<h3 class="carName">x</h3>
<h4 class="clicks"></h4>
</div>
</div>
</div>
With the help of Ryan Niemeyer's blog post http://www.knockmeout.net/2012/02/revisiting-dragging-dropping-and.html I have written code that allows dragging and dropping between nested observablearrays. The problem I'm facing is when I remove all items under "Encounter", and want to return an item, it can't find the container or drop area to properly work. It will drop as if it does work, but if you look at the JSON output, you'll see that it does not properly update. The same problem exists when I remove all encounters, and try to move one encounter back... it doesn't find it's "drop zone". I'm hoping it is something trivial and appreciate the help.
I have a demo of it here http://plnkr.co/edit/n7IGItDOYTzCSfDHlwJS?p=preview
Here is the script:
$(function() {
//control visibility, give element focus, and select the contents (in order)
ko.bindingHandlers.visibleAndSelect = {
update: function(element, valueAccessor) {
ko.bindingHandlers.visible.update(element, valueAccessor);
if (valueAccessor()) {
setTimeout(function() {
$(element).find("input").focus().select();
}, 0); //new encounters are not in DOM yet
}
}
};
var Dataset = function(name) {
var self = this;
self.dName = name;
self.type = "Dataset";
};
// Encounter construction
var Encounter = function(name, dss) {
var self = this;
self.name = name;
self.datasets = ko.observableArray([]);
self.type = "Encounter";
$.each(dss, function(i, p) {
self.datasets.push(new Dataset(p.dName));
});
};
// Patient construction
var Patient = function(id, encounters) {
var self = this;
self.id = ko.observable(id);
self.encounters = ko.observableArray([]);
$.each(encounters, function(i, p) {
self.encounters.push(new Encounter(p.name, p.Datasets));
});
};
my.vm = {
selectedItem: ko.observable(),
patients: ko.observableArray([]),
targetPatients: ko.observableArray([]),
isSelected: function(selectedItem) {
return selectedItem === this.selectedItem();
},
isDropSpotMatch: function(arg) {
//if (!my.vm.isSourceEncounterSelected && !my.vm.isTargetEncounterSelected) {
// arg.cancelDrop = true;
//}
//var t = ko.toJSON(arg);
//console.log(arg);
//alert(ko.toJSON(arg.sourceParent()[0]));
console.log(arg.sourceParent()[0]);
console.log(arg.targetParent()[0]);
if (arg.sourceParent()[0].type != arg.targetParent()[0].type){
arg.cancelDrop = true;
}
},
clearItem: function(data, event) {
if (data === self.selectedItem()) {
my.vm.selectedItem(null);
}
if (data.name() === "") {
my.vm.patients.remove(data);
my.vm.targetPatients.remove(data);
}
},
// loading the observable array with sample data
load: function() {
$.each(my.sourceData.data.Patients, function(i, p) {
my.vm.patients.push(new Patient(p.Id, p.Encounters));
});
$.each(my.targetData.data.Patients, function(i, p) {
my.vm.targetPatients.push(new Patient(p.Id, p.Encounters));
});
}
};
ko.bindingHandlers.sortable.beforeMove = my.vm.isDropSpotMatch;
//ko.bindingHandlers.sortable.afterMove = my.vm.isDropSpotMatch;
my.vm.load();
ko.applyBindings(my.vm);
});
Here is my html:
<!DOCTYPE html>
<script data-require="knockout#*" data-semver="3.0.0" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<script src="knockout-sortable.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="data.js"></script>
<script src="script.js"></script>
</head>
<body>
<div class="page">
<div id="main">
<div class="showroom">
<table>
<tr>
<td>
<span data-bind="sortable: { template: 'taskTmpl', data: patients }"></span>
</td>
<td>
<span data-bind="sortable: { template: 'taskTmpl', data: targetPatients }"> </span>
</td>
</tr>
</table>
<script id="taskTmpl" type="text/html">
<ul data-bind="sortable: encounters">
<div class="container"><div class="item">
<li>
<div class="encounterItem">
<span data-bind="visible: !$root.isSelected($data)">
</span>
<span data-bind="visibleAndSelect: $root.isSelected($data)">
<input data-bind="value: name, event: { blur: $root.clearItem }" />
</span>
</div>
</div>
<ul data-bind="sortable: datasets">
<li>
<div class="datasetItem">
<span data-bind="visible: !$root.isSelected($data)">
</span>
<span data-bind="visibleAndSelect: $root.isSelected($data)">
<input data-bind="value: dName, event: { blur: $root.clearItem }" />
</span>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div></script>
<div>
JSON OUTPUT
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
</div>
</div>
</div>
</div>
</body>
</html>
Here is the css:
.container {
background-color: #BBB;
}
.datasetItem {
background-color: #BBB;
cursor: move;
text-align: left;
width: 100px;
}
.encounterItem {
background-color: #BBB;
cursor: move;
text-align: left;
width: 100px;
}
.encounterItem input {
width: 40px;
}
.datasetItem input {
width: 40px;
}
.ko_container {
width: 255px;
min-height: 50px;
background-color: #ADDA;
}
And finally my namespace and dummy data:
var my = my || { }; //my namespace
my.dataservice = (function (my) {
"use strict";
var getPatients = function () {
return my.sampleData;
};
return {
getPatient: getPatients
};
})(my);
my.sourceData = (function (my) {
"use strict";
var data = { Patients: [
{ "Id": "1stId", "Encounters": [ { "name": "1stEncounter", "Datasets": [ { "dName": "1stDataset"} ] }, { "name": "2ndEncounter", "Datasets": [ { "dName": "2ndDataset"} ] } ] }
]
};
return {
data: data
};
})(my);
my.targetData = (function (my) {
"use strict";
var data = { Patients: [
{ "Id": "T1stId", "Encounters": [ { "name": "21stEncounter", "Datasets": [ { "dName": "21stDataset"} ] }, { "name": "22ndEncounter", "Datasets": [ { "dName": "22ndDataset"} ] } ] }
]
};
return {
data: data
};
})(my);
I know this is silly, but add a minimum height to the list container.