i'm trying create a counter function on object compilated with handlebars.js. I'm trying to make a project site for a moving company, and I'm trying to create a cost estimation system in it, depending on what things and how many they have to move in their new house
handlebars compilated divs
problem
So I created objects in js which contain things to move such as fridge, bed, table etc... their are compilated right into HTML with handlebars template.
I want to be able to increase and decrease the numbers of things separately, so I was able to create one function that does this, but the problem is that it works only on the first compilated object and all other objects are not affected with this code, i don't even know if it is possible to do such thing with handlebars template and vanilla js, I know I can do this for each objects individually but there will be way too much duplicated codes in HTML and JS files..
here is the handlebars template on HTML file:
<script src="https://cdn.jsdelivr.net/npm/handlebars#latest/dist/handlebars.js"></script>
<script id="templateHB" type="text/x-handlebars-template">
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button id="increase-{{this.index}}">+</button>
<p>{{this.quantity}}</p>
<button id="decrease-{{this.index}}">-</button>
</div>
</div>
{{/each}}
here is the js file code:
const source = document.getElementById('templateHB').innerHTML;
const template = Handlebars.compile(source);
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
let compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
// here start the function
let quantity = contextSalon.lesMeublesSalon[0].quantity;
let addOneBtn = document.getElementById("increase-0");
let removeOneBtn = document.getElementById("decrease-0");
function updateQuantity(quantity) {
contextSalon.lesMeublesSalon[0].quantity = quantity;
compiledHtmlSalon = template(contextSalon);
injectionObjetSalon.innerHTML = compiledHtmlSalon;
addOneBtn = document.getElementById("increase-0");
removeOneBtn = document.getElementById("decrease-0");
addOneBtn.addEventListener("click", function() {
updateQuantity(quantity + 1);
});
removeOneBtn.addEventListener("click", function() {
updateQuantity(quantity - 1);
});
}
updateQuantity(0);
if the thing i'm trying to do is impossible with js and handlebars.js, what other tech can you suggest me? any js framework such as node.js and express.js?
I just created function to increase and decrease number based on ID of the template inside html file with handlebars, I was expecting it to work with all others compilated objects to work the same way.
It is possible that a rendering framework like React or Vue could be used for this, but they come with learning curves. The conditions as you have outlined them are fairly simple and so I think you can get by with Handlebars.
But we will need to make some modifications to your code.
The first issue I see is that you have index: 0 set on all objects in your lesMeublesSalon array. You could correct those to be sequential (0, 1...), but I think a better option would be to use Handlebars' built-in #index variable to set the index value for each item.
The next problem I would address is that you are trying to re-render the Handlebars template with each click of the increment/decrement button. The problem I see with this is that it is that you will have to re-attach your event listeners each time you re-render the HTML because the DOM will have new button elements that need to be listened to.
I think a better approach would be to render the initial HTML with Handlebars, but then to use JavaScript to directly update the DOM when your counts change.
Here is how I would implement this:
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button data-increase="{{#index}}">+</button>
<p data-quantity="{{#index}}">{{this.quantity}}</p>
<button data-decrease="{{#index}}">-</button>
</div>
</div>
{{/each}}
const source = document.getElementById('templateHB').innerHTML;
const template = Handlebars.compile(source);
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
// Initially render our HTML with Handlebars.
const compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
// Note the use of data-* attributes in the template.
// This allows us to query for ALL increment/decrement buttons
// and to attach a listener to each.
// Getting them by ID allowed us to get only one increment
// and one decrement button.
const addOneBtns = document.querySelectorAll("[data-increase]");
const removeOneBtns = document.querySelectorAll("[data-decrease]");
const quantityDisplays = document.querySelectorAll("[data-quantity]");
// This is the function that will directly manipulate the displayed
// quantity value for each item.
// It relies on the indexes of the elements to match the indexes in
// our lesMeublesSalon so that the correct quantity will be set.
function renderQuantities () {
quantityDisplays.forEach((quantityDisplay, index) => {
const quantity = contextSalon.lesMeublesSalon[index].quantity;
quantityDisplay.textContent = String(quantity);
})
};
// We loop through EACH increment button and attach a click listener.
addOneBtns.forEach(addOneBtn => {
addOneBtn.addEventListener('click', function (event) {
// We get the index from the `[data-increase]` attribute.
const index = Number(event.target.dataset.increase);
// We use that index to increment the quantity on the
// corresponding item in our array.
contextSalon.lesMeublesSalon[index].quantity += 1;
// We re-render the quantities because we know there is a change.
renderQuantities();
});
});
// This is basically the same as above, except for decrementing.
removeOneBtns.forEach(removeOneBtn => {
removeOneBtn.addEventListener('click', function (event) {
const index = Number(event.target.dataset.decrease);
// We use Math.max() so the quantity can't become less than zero.
contextSalon.lesMeublesSalon[index].quantity = Math.max(contextSalon.lesMeublesSalon[index].quantity - 1, 0);
renderQuantities();
});
});
I have created a Codepen for reference.
I've tesed your code and it works well , thanks you! however I forgot to mention that I have 4 categories of objects , as you can see on the picture , I got (Salon, Chambre, Cuisine, Bain) , how can we make so that it works on all of these categories ?
So it works on the "Salon" categories but not in the others,
here you have all others object :
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
// Initially render our HTML with Handlebars.
const compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
const contextChambre = {
lesMeublesChambre: [
{
image: 'images/bed.svg.png',
element: 'Lit double',
quantity: 0,
index: 0
}
]
}
const compiledHtmlChambre = template(contextChambre);
const injectionObjetChambre = document.getElementById('meuble-chambre');
injectionObjetChambre.innerHTML = compiledHtmlChambre;
const contextCuisine = {
lesMeublesCuisine: [
{
image: 'images/frigo.svg',
element: 'Frigo',
quantity: 0,
index: 0
}
]
}
const compiledHtmlCuisine = template(contextCuisine);
const injectionObjetCuisine = document.getElementById('meuble-cuisine');
injectionObjetCuisine.innerHTML = compiledHtmlCuisine;
const contextBain = {
lesMeublesBain: [
{
image: 'images/machine-a-laver.svg',
element: 'Machine à laver',
quantity: 0,
index: 0
}
]
}
const compiledHtmlBain = template(contextBain);
const injectionObjetBain = document.getElementById('meuble-bain');
injectionObjetBain.innerHTML = compiledHtmlBain;
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button data-increase="{{#index}}">+</button>
<p data-quantity="{{#index}}">{{this.quantity}}</p>
<button data-decrease="{{#index}}">-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesChambre}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesCuisine}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesBain}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
Related
I'm trying make my Coordinates, Dispatcher and Captured At printout the correct totals that correspond to it's ID. Unfortunately, it's only assigning the first ID's totals in its HTML element. Having a bit of a braindead moment trying to fix this.
I know I'm missing something to identify the individual html instance, but I can't figure out how to make that happen.
HTML -
<div v-if="zoomLevel > 10" class="search-data">
<div><strong>Dispatcher:</strong> <span id="dispatcher">{{ dispatcher}}</span></div>
<div><strong>Lng/Lat:</strong> <span id="coordinates">{{ coordinates }}</span></div>
<div><strong>Captured At:</strong> <span id="capturedAt">{{ capturedAt }}</span></div>
</div>
JS -
export default {
data() {
return {
coordinates: '',
dispatcher: '',
capturedAt: '',
}
},
}
Method -
let totalTargets = Object.keys(e.source.data.features).length;
for (let i = 0; i < totalTargets; i++) {
console.log(e)
this.coordinates = e.source.data.features[i].geometry.coordinates.toString().replace("[", "").replace("]"," ").replace(",", ", ");
this.dispatcher = e.source.data.features[i].properties.name;
this.capturedAt = e.source.data.features[i].properties.capturedAt;
}
Console.log printout of method results
You need to iterate on the HTML, with v-for, create a reactive property to your list and can use like this:
<div v-if="zoomLevel > 10" class="search-data" v-for="(feature, index) in features" :key="index">
<div><strong>Dispatcher:</strong> <span id="dispatcher">{{ feature.dispatcher}}</span></div>
<div><strong>Lng/Lat:</strong> <span id="coordinates">{{ feature.coordinates }}</span></div>
<div><strong>Captured At:</strong> <span id="capturedAt">{{ feature.capturedAt }}</span></div>
</div>
I'm working with BootstrapVue. First I want to explain my code shortly:
I have a v-for with inputs, these I can create as often as I want with a b-button -> addElement. Or I can select an item from my b-dropdown - there I am checking the length of my selected and push the amount of my arrayLength as my inputs.
-> e.g. selected array = ['1111', '2222', '3333'] -> arrayLength = 3 -> 3 inputs
Now I need to get a function fired automatically (marked as ???) when exactly this case happend that I'm selecting something from my dropdown - e.g. is my arrayLength = 3, it should be fired 3 times with correct id, indexParent and indexChild. How can I solve that?
I need this above named parameters in my methods..
Thanks in advance!
<div v-for="(id, indexChild) in inputs" :key="indexChild">
<b-dropdown text="Select" right variant="success">
<b-dropdown-item v-for="(item, indexDropdown) in json" :key="indexDropdown" #click="trySomething(item.Number)">{{item.Name}}</b-dropdown-item>
</b-dropdown>
<b-button v-b-toggle="'newElement'+indexParent+indexChild">Element {{indexChild + 1}}></b-button>
<b-collapse :id="'newElement'+indexParent+indexChild"
<div ???="getValues(id, indexParent, indexChild)"> <!-- how can I fire this when created? -->
<!-- Do some more code -->
</div>
</b-collapse>
</div>
<!-- create new "item"-elements -->
<div>
<b-button #click="addElement()">Add element</b-button>
</div>
methods: {
addProduct() {
this.inputs.push({})
},
trySomething(itemValue) {
var array = [];
const products = this.otherJSON.find((i) => i.Number === itemValue);
for (let key in products.ID) {
array.push(products.ID[key]);
}
this.getLengthArray = array.length;
this.inputs.splice(0) //at first I need to delete all to push exact inputs based on arrayLength
for (let i = 0; i < this.getLengthArray; i++) {
this.inputs.push({});
}
},
getValues(id, indexParent, indexChild) { //Here I need the parameters id, indexParent, indexChild
}
},
data() {
return {
inputs: [{}],
}
},
props: ["indexParent"]
I've just used:
<div v-if="getValues(id, indexParent, indexChild)" v-once></div>
and it worked out for me!
I would like to display some card elements in HTML. I would like to get the variables of the card element from a javascript array.(Such as title etc..).
The card element number will also depend on the Javascript array size. I have looked at other questions such as generating dynamic tables but as I have a long customized html code block, it doesn't work.
This is for a website that I am building. I already got the variables from an api end point with a HTTP get request but am unable to display as I wish to. I have looked at many similar questions but can't find the answer that I am looking for.
This is the script for getting the variables with the HTTP get request
<script>
const request = new XMLHttpRequest();
request.open('GET', 'api/seminars', true);
request.onload = function() {
// Begin accessing JSON data here
const data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
data.forEach(resultArray => {
document.getElementById('output').innerHTML = resultArray.name;
document.getElementById('description').innerHTML =
resultArray.description;
document.getElementById('date').innerHTML = resultArray.date;
});
} else {
console.log('error');
}
};
request.send();
</script>
HTML CODE :
<div id="accordion">
<div class="card">
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data- target="#collapseOne" aria-expanded="true"
aria-controls="collapseOne">
</button>
</h5>
</div>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<h5 id="name"></h5>
<p id="description"></p>
<p id="description"></p>
...
</div>
</div>
</div>
</div>
And I have continued html code from here on not relevant ..
If there are 3 objects in my array, I would like to create 3 different cards and display name,description.. attributes. But my code only creates one card and displays last object's attributes.
You code never really "creates" elements based on your API call - It just updates (ie, overwrites) the existing dom elements by updating the innerHTML of fixed elements referenced by their IDs.
If my interpretation of your code is correct, you should be only seeing the LAST item in your API result. There are also some other weird stuff going on like duplicate IDs which Im guessing are typos
To fix this, create a new div.card-body for each item your API returns and append it to your container
const apiResult = [{
title: "title1",
description: "desc1",
output: "out1"
}, {
title: "title2",
description: "desc2",
output: "out2"
}, {
title: "title3",
description: "desc3",
output: "out3"
}];
const container = document.getElementById('accordion');
apiResult.forEach((result, idx) => {
// Create card element
const card = document.createElement('div');
card.classList = 'card-body';
// Construct card content
const content = `
<div class="card">
<div class="card-header" id="heading-${idx}">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse-${idx}" aria-expanded="true" aria-controls="collapse-${idx}">
</button>
</h5>
</div>
<div id="collapse-${idx}" class="collapse show" aria-labelledby="heading-${idx}" data-parent="#accordion">
<div class="card-body">
<h5>${result.title}</h5>
<p>${result.description}</p>
<p>${result.output}</p>
...
</div>
</div>
</div>
`;
// Append newyly created card element to the container
container.innerHTML += content;
})
.card {
padding: 1rem;
border: 1px solid black;
margin: 1rem;
}
<div id="accordion">
</div>
Note:
While this works, it's not very scalable. There are some great templating libraries out there with much more advanced interpolation features that you can consider if you have to do something like this in many places (and maintain it)
jsRender
Underscore Templates
Handlebars
UI frameworks like Vue, React, Angular wrap templating with binding to data models and handle auto updating the DOM for you to make things like this easier. Worth investigating if you have a lot of dynamically updating parts on your webpage
Suppose you have already queried the API, a way of doing it could be:
// Supposing you already have queried the API and have your data
let data = [
{name: 'name0', description: 'description', date: 'XX/XX/XXXX'},
{name: 'name1', description: 'description', date: 'XX/XX/XXXX'},
{name: 'name2', description: 'description', date: 'XX/XX/XXXX'},
]
data.forEach(res => {
let card = document.createElement("div");
let name = document.createTextNode('Name:' + res.name + ', ');
card.appendChild(name);
let description = document.createTextNode('Description:' + res.description + ', ');
card.appendChild(description);
let date = document.createTextNode('date:' + res.date);
card.appendChild(date);
let container = document.querySelector("#container");
container.appendChild(card);
});
<!-- At one point where you want to generate the cards you put this container -->
<div id="container"></div>
Yours was not working since you updated always the same elements instead of appending new ones :)
You are overwriting the description's innerHTML with every repetition of your for loop.
Change this
document.getElementById('output').innerHTML = resultArray.name;
document.getElementById('description').innerHTML = resultArray.description;
document.getElementById('date').innerHTML = resultArray.date;
to this
var element = document.createElement('div'); // create new div
var name = document.createElement('h4');
name.innerHTML = resultArray.name;
element.appendChild(name);
var description = document.createElement('p');
description.innerHTML = resultArray.description;
element.appendChild(description);
var date = document.createElement('span');
date.innerHTML = resultArray.date;
element.appendChild(date);
document.body.appendChild(element);
I'm having trouble making proper table with nested ng-repeat.
What I wanted is this https://jsbin.com/razamagabo/1/edit?output
but I'm stuck at here https://plnkr.co/edit/d5voXIpzYL81sSl9BSY2?p=preview
I don't mind my markup is not table but I'm still stuck with div
<div class="row">
<div class="col-xs-6" ng-repeat="obj in data">
{{obj.date}}
<div ng-repeat="user in obj.users">
<br>
{{user.name}}
<br>
{{user.mark}}
</div>
</div>
</div>
In order for you to be able to display your data in the desired way, it will probably be easiest if you restructure your data in the JS before trying to render it.
It will be very complicated to try and match on the user names when they are in separate objects in the data array.
I would suggest processing your scope.data in the controller. (I'm assuming that you don't have much control on how you are receiving the data).
For example after you get your data...
$scope.data = [
{
date:'1-1-2016',
users:[
{
'name':'james',
'mark':18
},
{
'name':'alice',
'mark':20
}
]
},
{
date:'2-1-2016',
users:[
{
'name':'james',
'mark':60
},
{
'name':'alice',
'mark':55
}
]
}
]
var userData = {};
var possibleDates = [];
for (dataObj of Object.entries($scope.data)) {
for (userObj of dataObj) {
if ( !userData[userObj.name] ) {
userData[userObj.name] = {};
}
userData[userObj.name][dataObj.date] = userObj.mark;
if (dates.indexOf(dataObj.date) < 0) {
dates.push(dataObj.date);
}
}
}
$scope.users = userData;
$scope.dates = possibleDates;
this will give you an object like this on your scope
$scope.users = {
'james': {
'1-1-2016': 18,
'2-1-2016': 60
},
'alice': {
'1-1-2016': 20,
'2-1-2016': 55
}
};
$scope.dates = ['1-1-2016', '2-1-2016'];
This to me seems easier to structure for your template. Though this assumes each user has an entry for each date.
<div>
<div id='header-row'>
<div id='empty-corner></div>
<div class='date-header' ng-repeat='date in $scope.dates></div>
</div>
<div class='table-row' ng-repeat='{key, value} in $scope.users'>
<div class='user-name'>{{ key }}</div>
<div class='user-data' ng-repeat='date in $scope.dates>
{{ value[date] }}
</div>
</div>
</div>
As long as you apply inline-block styles to the rows/elements this should give you what you are looking for.
Though you can also think of ways to simplify your data even further. You could instead of having each user have an object where the dates are keys, you could just push the values into an array.
With your current data structure it is not possible to display it like you want. You are trying to loop over date-users objects in data array but then you want to display user from inside users array in separate rows. With ng-repeat you can loop through rows tr but not through columns. First you would need to map your data array to group elements that are supposed to be visible in 1 row into 1 object in array. Currently you have them in 2 separate objects:
James mark: 18 and James mark: 60.
I'm a rank newbie to Angular, and I'm attempting to port an old jQuery-based app to NG. The JSON I'm starting with (parsed from an XML file) looks like this:
{
"setUps":{
"cartImage":
{
"_cartIm":"addtocart.gif",
"_cartIm_o":"addtocart.gif"
}
,"_supportImsPath":"/"
},
"product":
{
"matrix":
{
"thumbnails":[
{
"_myLabel":"Antique Brass Distressed",
"_thumbImage":"3",
"_cartCode":"160"
},
{
"_myLabel":"Antique Brass Light",
"_thumbImage":"156",
"_cartCode":"156"
},
{
"_myLabel":"Old Iron",
"_thumbImage":"ap",
"_cartCode":"157"
},
{
"_myLabel":"Oil-Rubbed Bronze",
"_thumbImage":"ob",
"_cartCode":"3"
}
],
"_myLabel":"Finishes"
},
"_Title":"Flower Cabinet Knob",
"_itNum":"100407x"
}
}
What I need to do with this is output specific elements on my template - first and foremost, the matrix object with its associated thumbnails. In this example, there is only one matrix, but for many of the products there are multiples, each with their own thumbnail arrays.
This is the controller:
var XMLt = angular.module("XMLtest",[]);
XMLt.factory("Xfactory",function($http){
var factory = [];
factory.getXML = function(){
return $http.get(productXML);
}
return factory;
});
XMLt.controller("Xcontroller",function($scope,Xfactory) {
$scope.Xcontroller = [];
loadXML();
function loadXML() {
Xfactory.getXML().success(function (data) {
var x2js = new X2JS();
prodData = x2js.xml_str2json(data);
$scope.thisItem = prodData.contents;
$scope.matrices = [];
angular.forEach($scope.thisItem.matrix,function(value,key)
{
$scope.matrices.push(value,key);
});
});
}
});
And this is my view template:
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div ng-repeat="thisMatrix in thisItem.product" class="matrix">
{{ thisMatrix._myLabel }}
</div>
</div>
My problem is that this ng-repeat, not surprisingly, returns a div for every child element of the product that it finds, not just the matrix. So I wind up with a couple of empty divs (for _Title and _itNum) in addition to the matrix div.
I've seen quite a few examples of filtering by comparing value literals, but they don't seem to apply in this case. I also tried writing a custom filter:
$scope.isObjType = function(input) {
return angular.isObject(input);
};
<div ng-repeat="thisMatrix in thisItem.product | filter:isObjType(matrix)">
That seemed to have no effect, still returning the extraneous divs. I can't seem to wrap my head around how I'd limit the repeat to a specific object type. Am I thinking of this the completely wrong way? If so, I'd welcome any input.
Since you only have one matrix, you don't need the repeat for the matrix label. You only need it for the thumbnails.
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div class="matrix">
{{ thisItem.product.matrix._myLabel }}
<div ng-repeat="thisThumbnail in thisItem.product.matrix.thumbnails" class="thumbnail">
{{thisThumbnail._myLabel}}
</div>
</div>
</div>
If it were possible to have multiple matrixes, the object would need to be modified to be able to represent that (by wrapping the matrix object in an array.)
Update per comments:
If you have the possiblity of multiple matrixes, you will need to modify the object to ensure that it is consistent when there is 1 vs when there are 2+.
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div ng-repeat="thisMatrix in thisItem.product.matrix" class="matrix">
{{ thisMatrix._myLabel }}
<div ng-repeat="thisThumbnail in thisMatrix.thumbnails" class="thumbnail">
{{thisThumbnail._myLabel}}
</div>
</div>
</div>
and in controller:
XMLt.controller("Xcontroller",function($scope,Xfactory) {
$scope.Xcontroller = [];
loadXML();
function loadXML() {
Xfactory.getXML().success(function (data) {
var x2js = new X2JS();
prodData = x2js.xml_str2json(data);
// must always have an array of matrixes
if (!prodData.contents.product.matrix.slice) {
prodData.contents.product.matrix = [prodData.contents.product.matrix];
}
$scope.thisItem = prodData.contents;
$scope.matrices = [];
angular.forEach($scope.thisItem.matrix,function(value,key)
{
$scope.matrices.push(value,key);
});
});
}
});