Triggering element visibility from handle_event - phoenix-live-view

My LiveView has buttons that switch between two panels on the client:
<button phx-click={JS.hide(to: "#panel1") |> JS.show(to: "#panel2")}>...
<button phx-click={JS.hide(to: "#panel2") |> JS.show(to: "#panel1")}>...
In panel2, I want to perform an action that triggers rendering via assign in handle_event and switches back to panel1:
<button phx-click="modify-data-and-switch-to-panel1">...
The problem is it doesn't want to show panel1.
I tries introducing a show_panel1 assign, setting it to true or false, then in render:
<%= if #show_panel1 do
JS.hide(to: "#panel2")
|> JS.show(to: "#panel1")
end %>
It renders the panels, but doesn't switch. How do I do this?

Related

Ng-repeat delete and Edit icon visibility issue on outside click

In ng-repeat I have a list of items and each item I have a Edit and delete icon like below,
Item1 (Edit) (Del)
Item2 (Edit) (Del)
Item3 (Edit) (Del)
Item4 (Edit) (Del)
When user click edit I changed content editable attribute to true and hide the Edit and delete options.
But when user click next Edit button or click outside I need to show to edit and delete options again.
Use ng-blur to detect when the element loses focus
Just store the state of your 'item' in item model. I believe, that yout items is just an array of json objects.
So you can do something like:
var vm = this;
vm.items = [...];
vm.onEdit = function(itemToEdit){
vm.items.forEach(function(item){
item.isEdit = item == itemToEdit;
});
}
And in a view:
<div ng-repeat="item in $ctrl.items">
<span>{{item.title}}</span>
<span ng-click="$ctrl.onEdit(item)"
ng-show="item.isEdit">Edit</span>
<span>Delete</span>
</div>
And about click outside part. You should use ng-blur, as #Leo mentioned, or
$document.on('click', function(){
vm.onEdit();
});
But! It'll fire on any click, so you should attach e.preventDefault() on every editable field click. The approach depends on situation. ng-blur is better.

Change Single Button Color from ngRepeat generated button list

I'm using ngRepeat to generate four buttons. Whenever I click one of the buttons, I want to change its color and also execute a function (for now, I'm just using console.log for sake of simplicity). If I click on another button, I want to change its color while reverting the previous button back to its original color.
I have a couple of issues - the first is that I can't seem to get ng-click to accept two commands (the first being the console.log function and the second being the instruction to change the button color). The other issue is that if I take out the console.log function, I end up changing all of the buttons when I click on one.
Any ideas? Here's the plunkr: http://plnkr.co/edit/x1yLEGNOcBNfVw2BhbWA. You'll see the console.log works but the button changing doesn't work. Am I doing something wrong with this ng-click?
<span class="btn cal-btn btn-default" ng-class="{'activeButton':selectedButt === 'me'}" ng-click="log(element);selectedButt = 'me'" data-ng-repeat="element in array">{{element}}</span>
You can create a simple function in your controller which handles this logic:
$scope.selectButton = function(index) {
$scope.activeBtn = index;
}
Then, you can simply check in your template if the current button is active:
<span class="btn cal-btn btn-default" ng-class="{true:'activeButton'}[activeBtn == $index]" ng-click="selectButton($index);" ng-repeat="element in array">{{element}}</span>
I also changed your plunkr
You may convert your element list from string array to object array first.
$scope.array = [
{"name":"first", "checked":false},
{"name":"second", "checked":false},
{"name":"third", "checked":false},
{"name":"fourth", "checked":false}
];
And your log function need to change to:
$scope.log = function(element) {
console.log(element.name);
angular.forEach($scope.array, function(elem) {
elem.checked = false;
});
element.checked = !element.checked;
}
Then, in your HTML:
<button class="btn cal-btn"
ng-repeat="element in array"
ng-click="log(element)"
ng-class="{activeButton: element.checked, 'btn-default': !element.checked}"
>{{element.name}}</button>
Please see the updated plunker here.

filter and unfilter on ng-click with buttons

hello i have a couple of buttons i want to filter data with, but i can't figure out how to make it unfilter when i click the button again. I use the normal filtering expression like:
<tr ng-repeat='ledata in datas | filter:thecondition'></tr>
where the thecondition may vary depending on the button clicked, the button's code is the following
ng-click="thecondition = {type: 1}"
also how can i mark it with ng-class? make the button look pressed? thanks.
My recommendation is that try to implementing your own custom filter.
Something close to:
custom filter:
angular.module('myApp', []).filter('myFilter', function() {
return function(items, param1, param2) {
return items.filter(function(item){ /* your custom conditions */ });
};
});
and in your html
<li ng-repeat="item in items | myFilter:'car': 2">
Of course you could extend and perform the actions that are more suited to your app needs.
The reason why I recommend the custom filter is because it will allow you a better separation from the view and the business logic; which is more aligned with the MVVM design pattern behind angular.
For toggling the filter, try this:
ng-click="thecondition = thecondition ? '' : {type: 1}"
The ternary operator will set thecondition to an empty string if it has a value, and to the object with type : 1 if it's empty.
To set the class with ng-class, try this:
ng-class="{depressed_button: thecondition}"
When thecondition is truthy, the class depressed_button will be applied. It's up to you to define the depressed_button class in a stylesheet or elsewhere.

How to expand/collapse all rows in Angular

I have successfully created a function to toggle the individual rows of my ng-table to open and close using:
TestCase.prototype.toggle = function() {
this.showMe = !this.showMe;
}
and
<tr ng-repeat="row in $data">
<td align="left">
<p ng-click="row.toggle();">{{row.description}}</p>
<div ng-show="row.showMe">
See the plunkr for more code, note the expand/collapse buttons are in the "menu".
However, I can't figure out a way to now toggle ALL of the rows on and off. I want to be able to somehow run a for loop over the rows and then call toggle if needed, however my attempts at doing so have failed. See them below:
TestCase.prototype.expandAllAttemptOne = function() {
for (var row in this) {
if (!row.showMe)
row.showMe = !row.showMe;
}
}
function expandAllAttemptOneTwo(data) {
for (var i in data) {
if (!data[i].showMe)
data[i].showMe = !data[i].showMe;
}
}
Any ideas on how to properly toggle all rows on/off?
Using the ng-show directive in combination with the ng-click and ng-init directives, we can do something like this:
<div ng-controller="TableController">
<button ng-click="setVisible(true)">Show All</button>
<button ng-click="setVisible(false)">Hide All</button>
<ul>
<li ng-repeat="person in persons"
ng-click="person.visible = !person.visible"
ng-show="person.visible">
{{person.name}}
</li>
</ul>
</div>
Our controller might then look like this:
myApp.controller('TableController', function ($scope) {
$scope.persons = [
{ name: "John", visible : true},
{ name: "Jill", visible : true},
{ name: "Sue", visible : true},
{ name: "Jackson", visible : true}
];
$scope.setVisible = function (visible) {
angular.forEach($scope.persons, function (person) {
person.visible = visible;
});
}
});
We are doing a couple things here. First, our controller contains an array of person objects. Each one of these objects has a property named visible. We'll use this to toggle items on and off. Second, we define a function in our controller named setVisible. This takes a boolean value as an argument, and will iterate over the entire persons array and set each person object's visible property to that value.
Now, in our html, we are using three angular directives; ng-click, ng-repeat, and ng-show. It seems like you already kinda know how these work, so I'll just explain what I'm doing with them instead. In our html we use ng-click to set up our click event handler for our "Show All" and "Hide All" buttons. Clicking either of these will cause setVisible to be called with a value of either true or false. This will take care of toggling all of our list items either all on, or all off.
Next, in our ng-repeat directive, we provide an expression for angular to evaluate when a list item is clicked. In this case, we tell angular to toggle person.visible to the opposite value that it is currently. This effectively will hide a list item. And finally, we have our ng-show directive, which is simply used in conjunction with our visible property to determine whether or not to render a particular list item.
Here is a plnkr with a working example: http://plnkr.co/edit/MlxyvfDo0jZVTkK0gman?p=preview
This code is a general example of something you might do, you should be able to expand upon it to fit your particular need. Hope this help!

Why is my click event called twice in jquery?

Why is my click event fired twice in jquery?
HTML
<ul class=submenu>
<li><label for=toggle><input id=toggle type=checkbox checked>Show</label></li>
</ul>
Javascript
$("ul.submenu li:contains('Show')").on("click", function(e) {
console.log("toggle");
if ($(this).find("[type=checkbox]").is(":checked")) console.log("Show");
else console.log("Hide");
});
This is what I get in console:
toggle menu.js:39
Show menu.js:40
toggle menu.js:39
Hide menu.js:41
> $("ul.submenu li:contains('Show')")
[<li>​ ]
<label for=​"toggle">​
<input id=​"toggle" type=​"checkbox" checked>​
"Show"
</label>​
</li>​
If I remember correctly, I've seen this behavior on at least some browsers, where clicking the label both triggers a click on the label and on the input.
So if you ignore the events where e.target.tagName is "LABEL", you'll just get the one event. At least, that's what I get in my tests:
Example with both events | Source
Example filtering out the e.target.tagName = "LABEL" ones | Source
I recommend you use the change event on the input[type="checkbox"] which will only be triggered once. So as a solution to the above problem you might do the following:
$("#toggle").on("change", function(e) {
if ($(this).is(":checked"))
console.log("toggle: Show");
else
console.log("toggle: Hide");
});
https://jsfiddle.net/ssrboq3w/
The vanilla JS version using querySelector which isn't compatible with older versions of IE:
document.querySelector('#toggle').addEventListener('change',function(){
if(this.checked)
console.log('toggle: Show');
else
console.log('toggle: Hide');
});
https://jsfiddle.net/rp6vsyh6/
This behavior occurs when the input tag is structured within the label tag:
<label for="toggle"><input id="toggle" type="checkbox" checked>Show</label>
If the input checkbox is placed outside label, with the use of the id and for attributes, the multiple firing of the click event will not occur:
<label for="toggle">Show</label>
<input id="toggle" type="checkbox" checked>
I found that when I had the click (or change) event defined in a location in the code that was called multiple times, this issue occurred. Move definition to click event to document ready and you should be all set.
Not sure why this wasn't mentioned. But if:
You don't want to move the input outside of the label (possibly because you don't want to alter the HTML).
Checking by e.target.tagName or even e.target doesn't work for
you because you have other elements inside the label
(in my case it had spans holding an SVG with a path so e.target.tagName sometimes showed SVG and other times it showed PATH).
You want the click handler to stay on the li (possibly because you have
other items in the li besides the checkbox).
Then this should do the trick nicely.
$('label').on('click', function(e) {
e.stopPropagation();
});
$('#toggle').on('click', function(e) {
e.stopPropagation();
$(this).closest('li').trigger('click');
});
Then you can write your own li click handler without worrying about events being triggered twice. Personally, I prefer to use a data-selected attribute that changes from false to true and vice versa each time the li is clicked instead of relying on the input's value:
$('ul.submenu li').on('click', function() {
let _li = $(this),
ticked = _li.attr('data-selected');
ticked = (ticked === 'false') ? true : false;
_li.attr('data-selected', ticked);
_li.find('#toggle').prop('checked', ticked);
});

Resources