This is what I want to do:
I get json from an api: ✓
I use ng-repeat to display all the data as a button: ✓
The user can click a button: ✓
if the data attribute for that particular button was "premier", log "premier", if it was "random", then log random: ✓
if the data attribute of this button was "premier", the class should be "btn-danger disabled" : ✗
I'm working in a laravel blade document, so I have to skip angular's double curly brackets by placing an # in front, like this: #{{ xxx }}.
The code at the moment correctly logs the data-correct attribute. But it changes the class of all the buttons, not only the one clicked.
My code:
html:
<a href ng-click="validateClick(premier.correct, $index)"
ng-class="{premier: isPremier, random: isRandom}"
ng-repeat="premier in premiers"
class="btn btn-default btn-game"
data-correct="#{{premier.correct}}"
href="#"
role="button">
#{{premier.name}}
</a>
app.js:
$scope.validateClick = function(row, index) {
if (row == "premier") {
$scope.isPremier = true;
console.log($scope.isPremier + index)
}
else {
$scope.isRandom = true;
console.log($scope.isRandom + index)
}
}
Instead of assigning to the common scope variable assign it to the premier object and use it in the class
HTML
<div ng-click="validateClick(premier, $index)" ng-class="{premier: premier.isPremier, random: premier.isRandom}" ng-repeat="premier in premiers" class="btn btn-default btn-game" data-correct="{{premier.correct}}" href="#" role="button">
{{premier.name}}
</div>
Controller
$scope.validateClick = function (premier, index) {
if (premier.correct == "premier") {
premier.isPremier = true;
console.log(premier.isPremier + index)
} else {
premier.isRandom = true;
console.log(premier.isRandom + index)
}
}
Working sample is available in the jsFiddle
http://jsfiddle.net/cce2g6y2/1/
Related
I'm working on a simple project where I need to make an item in a list editable and then update a JS item to store this.
I'm using Content Editable = True, and it works when I comment out my handleItemEdit function, but when I turn it on, I can only insert one character at a time, forcing me to keep clicking to edit.
Clearly this problem stems from my function, but I can't seem to figure out why.
//Responsible for listening for an edit and updating my object with the new text.
function handleEditItem() {
$('.js-shopping-item').on('input', function(event) {
const itemIndex = getItemIndexFromElement(event.currentTarget); //assigning the index of the the editted item to itemIndex
const updatedItem = STORE.items[itemIndex];
updatedItem.name = event.currentTarget.innerHTML;
renderShoppingList();
});
}
//Returns the index of an Item in the Store
function getItemIndexFromElement(item) {
const itemIndexString = $(item)
.closest('.js-item-index-element')
.attr('data-item-index');
return parseInt(itemIndexString, 10);
}
//Function responsible for returning the template HTHML to insert into the html.
function generateItemElement(item) {
let itemIndex = STORE.items.indexOf(item);
return `
<li class="js-item-index-element" data-item-index="${itemIndex}">
<span contentEditable='true' class="shopping-item js-shopping-item ${item.checked ? 'shopping-item__checked' : ''}">${item.name}</span>
<div class="shopping-item-controls">
<button class="shopping-item-toggle js-item-toggle">
<span class="button-label">check</span>
</button>
<button class="shopping-item-delete js-item-delete">
<span class="button-label">delete</span>
</button>
</div>
</li>`;
}
I am only posting the necessary code and solving this much will clear rest of my doubts. I am new to angularjs, so kindly forgive if I am asking something stupid.
I am using ng-repeat to generate a list which uses an array defined in the controller scope. When I click on 'Add Another' button, a new element is created. I want to get access of this element to add a class to it. But when I use 'getElementById' function in the same function 'addNewForm' I get 'null'.
However, when I call function 'fn' by hitting 'Find Untitled' button, I get the correct element. Could anybody explain and solve this? Any help is appreciated. Thanks in advance!
I am posting the code below:
HTML:
<div ng-controller="myctrl3">
<ul id ="menu_Ul">
<li ng-repeat="x in list">
<button id="{{ 'navAppsButtonID-' + $index }}">{{x}}</button>
<br>
</li>
<li>
<button ng-click="addNewForm()">Add another</button>
</li>
</ul>
<button ng-click="fn()">Find Untitled</button>
</div>
JS:
.controller("myctrl3", function($scope) {
var list = ['abcd', 'efgh', 'ijkl', 'mnop'];
$scope.list = list;
$scope.abc = function () {
var listPush = function () {
$scope.list.push("Untitled Menu");
for(var i = 0;i<$scope.list.length-1;i++) {
var element = document.getElementById('navAppsButtonID-'+i);
element.classList.remove('current');
}
};
var listLen = $scope.list.length;
if($scope.list[listLen-1] === undefined) {
listPush();
}
else if ($scope.list[listLen-1] == "Untitled Menu") {
alert("Cannot open more than one Untitled Menu at the same time.");
}
else {
listPush();
}
};
$scope.addNewForm = function() {
$scope.abc();
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
$scope.fn = function () {
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
})
You're thinking too much jQuery and too little angular. If the goal is to add a class to the last element of ng-repeat, this is how you do that:
<li ng-repeat="x in list">
<button ng-class="{ current: $last }">{{ x }}</button>
</li>
$last is a variable available inside ng-repeat, and if it's true, ng-class will set the class current on the element.
You don't assign unique ids to elements to getElementById from somewhere else when working in angular.
In Vuejs 2.0 I've a following set of data:
const tags = {
Investor:[
{display:"Mutual Fund", value:"Investor - Mutual Funds"},
{display:"Insurance", value:"Investor - Insurance"},
{display:"FII", value:"Investor - FII"},
],
Research:[
{display:"Research - Tier I", value:"Research - Tier I"},
{display:"Research - Tier II", value:"Research - Tier II"},
]
}
I'm having following set of buttons which shows these tags:
<div class="col-sm-4 border-right">
<div>
<button v-for="(obj, key) in tags"
:key="key"
#click.prevent="currentTag = key"
class="btn btn-primary btn-xs">
{{key}}
</button>
</div>
</div>
<div class="col-sm-6">
<div>
<button v-for="tag in tags[currentTag]"
:key="tag"
class="btn btn-xs"
:class="tagClass(tag)"
#click.prevent="selectedTag = tag">
{{tag.display}}
</button>
</div>
</div>
To get the selected tags I'm having a variable named:
currentTag: '',
selectedTag: '',
Now i'm having tagClass(tag) to toggle the class:
tagClass(tag){
return {
'btn-warning': this.selectedTag === tag,
'btn-primary': !(this.selectedTag === tag)
}
},
Now while getting the update page I'm putting values in current tag and selectedTag like this:
this.currentTag = response.data.contact.parentTag
this.selectedTag = response.data.contact.selectedTag
Now I'm able to view the child tags selected from the parent but I'm unable to have selected class in it. I want the data which is being set to seletedTag should have class btn-warning
In backend PHP I'm calculating and passing the value as
$selectedTag['value'] = $contact->tag;
$tags = explode('-', $contact->tag);
$contact->parentTag = $tags[0];
$selectedTag['display'] = $tags[1];
$contact->selectedTag = $selectedTag;
The issue here is that the tagClass method is checking to see if the tag is equal to the selectedTag. Since tag and selectedTag are objects, when you set it manually, selectedTag is never going to be equal to any of your tags. Instead, look for the tag that you have locally that matches the tag that you received from the server.
Wherever you are doing this
this.currentTag = response.data.contact.parentTag
this.selectedTag = response.data.contact.selectedTag
Change it to this
this.currentTag = response.data.contact.parentTag
const selectedTag = response.data.contact.selectedTag
this.selectedTag = this.tags[this.currentTag].find(t => t.value === selectedTag.value)
Example.
For a Meteor application I'd like the users to be able to add divs to a column by clicking on a button, with each of these having a button to remove the div. Here's an extract of the code:
<template name="MainTemplate">
....
<a class="btn btn-sm" class="add-dynamic"></a>
<div id="add-stuff-here"></div>
....
</template>
<template name="DynamicTemplate">
<div id="dynamic-{{uniqid}}">
<a class="btn btn-sm delete-dynamic" name="{{uniqid}}"></a>
</div>
</template>
...and in the javascript file:
Template.MainTemplate.events({
'click .add-dynamic'(event) {
const random = String.fromCharCode(65 + Math.floor(Math.random() * 26));
const uniqid = random + Date.now();
Blaze.renderWithData(Template.DynamicTemplate, {uniqid: uniqid}, $("#add-stuff-here")[0])
},
})
Template.DynamicTemplate.events({
'click .delete-dynamic'(event){
const target = event.target || event.srcElement;
const id = target.id;
console.log("Button ID: " + id); // This is null
new = $('#dynamic-' + id);
new.parentNode.removeChild(new); // Therefore, this fails
}
});
Adding the templates works as expected but deleting them fails as the id of the clicked button appears to be nil.
In any case I'm probably going about this in completely the wrong way (I haven't used Meteor for a long time and didn't know it very well anyway), and so would appreciate any suggestions as to how this might be accomplished.
ETA: The possible answer suggested in the comments includes:
UI.remove(self.view);
...which "expects a template rendered with Blaze.render". I'm not able to determine how to pass the identity of the template to be removed to the function which is run on the button click - does anyone have any suggestions?
In the end I worked around it by doing this:
<template name="DynamicTemplate">
<input type="text" class="dynamic-input" placeholder="Some text" />
</template>
Template.DynamicTemplate.events({
'click .delete-dynamic'(event){
inputs = $(".dynamic-input").length;
if ( inputs > 1 ) {
const element = $(".dynamic-input")[dynamic - 1];
element.parentNode.removeChild(element);
}
}
});
That seems to do more-or-less what I was after and avoids the problem of the null ID which I mentioned in the original post.
I have a list of items, and I have enable/disable method as an option for each item in a list.
I want to toggle only one item in a list:
Current implementation toggles all items in a list, and changes class icons for all.
HTML
<div class="device" ng-repeat="item in items" ng-class="{'open': item.isOpen}">
<!-- Enable/Disable-->
<a href="#" class="m-r-20" ng-click="test.toggleMethod(item.Id)">
<span class="{{test.buttonClassIcon}}" title="{{test.title}}"></span>
</a>
</div>
Controller
model.enabled = true;
model.toggleMethod = function (deviceId) {
if (model.enabled) {
locationService.start(deviceId).then(deviceList);
} else {
locationService.stop(deviceId).then(deviceList);
}
model.enabled = !model.enabled;
model.buttonClassIcon = model.enabled ? 'fa fa-bell' : 'fa fa-bell-slash';
model.title = model.enabled ? 'Enable' : 'Disable';
};
When I click bell, it changes class to all, and toggles global variable.
You should have an enabled property for each element in the list.
model.items.map(function(item){
item.enabled = false; // or other default value
});
and the in the html :
<a href="#" class="m-r-20" ng-click="test.toggleMethod(item)">
<span ng-if="item.enabled" class="fa fa-bell" title="Enable"></span>
<span ng-if="!item.enabled" class="fa fa-bell-slash" title="Disable"></span>
</a>
or you can use ng-class or create functions like getTitle(item.enabled) which returns the corresponding title.
and the toggleMethod:
model.toggleMethod = function (device) {
var deviceId = device.Id;
if (device.enabled) {
locationService.start(deviceId).then(deviceList);
} else {
locationService.stop(deviceId).then(deviceList);
}
device.enabled = !device.enabled;
};
Here is the fiddle.
Hope this is what you were trying to achieve.
Inside your function you dont enable/disable the selected item , instead you enable/disable a model object.
I am going to make an assumption , according to your snippet.
Since you have the item id or you can pass the $index(the position into the items array) you could do :
//also pass $index into ng-click function
<span class="{{test.buttonClassIcon}}" title="{{test.title}}"></span>
and into function body check the selected item:
model.toggleAlarmMethod = function (deviceId,indx) {
if ($scope.items[indx].enabled) {
locationService.start(deviceId).then(deviceList);
} else {
locationService.stop(deviceId).then(deviceList);
}
//enable/disable selected item
$scope.items[indx].enabled = !$scope.items[indx].enabled;
$scope.items[indx].buttonClassIcon = $scope.items[indx].enabled ? 'fa fa-bell' : 'fa fa-bell-slash';
$scope.items[indx].title = $scope.items[indx].enabled ? 'Enable' : 'Disable';
};