Expand collapse list item using angular material - javascript

Can anyone give any suggestions for adding an Expand/Collapse view shown here.
I'd like to use Angular material with AngularJS only and not be dependant on Bootstrap etc however I can't find anything suitable in the AngularMaterial docs.
Thanks

One way is to use 2 consecutive md-list-item.
Here is the HTML Code.
<md-list flex>
<md-list-item ng-click="toggle.list1 = !toggle.list1">
<md-icon>explore</md-icon>
<span flex>Item List 1</span>
<md-icon ng-show="!toggle.list1">expand_more</md-icon>
<md-icon ng-show="toggle.list1">expand_less</md-icon>
</md-list-item>
<md-list-item ng-repeat="item in data" ng-show="toggle.list1">
<span flex-offset="5">{{item}}</span>
</md-list-item>
<md-list-item ng-click="toggle.list2 = !toggle.list2">
<md-icon>explore</md-icon>
<span flex>Item List 2</span>
<md-icon ng-show="!toggle.list2">expand_more</md-icon>
<md-icon ng-show="toggle.list2">expand_less</md-icon>
</md-list-item>
<md-list-item ng-repeat="item in data" ng-show="toggle.list2">
<span flex-offset="5">{{item}}</span>
</md-list-item>
</md-list>
JS Code:
angular.module('myApp',['ngMaterial'])
.controller('TempController', function($scope){
$scope.data = [ "Item 1", "Item 2", "Item 3", "Item 4"]
$scope.toggle = {};
});;
Here is the working Codepen.

This doesn't seem to use bootstrap.
https://github.com/LukaszWatroba/v-accordion
this should be possible code to make your own accordion with material
http://blog.sodhanalibrary.com/2016/02/accordion-with-angularjs-material-ui.html#.WKxqI1UrJaQ

Related

How to make md-list-item active when selected during ng-repeat?

Here is my scenario
When i select any one item in list(<md-list-item>) an active class should be appended for the particular item.
When iam trying to do it, active class getting appended for all the items. Please help me if anyone knows the solution. Iam new to material design.
<md-list-item class="md-3-line" ng-repeat="review in oReviews" ng-click="fnReviewEmployeeId(review.empId)">
<img ng-src="https://x1.xingassets.com/assets/frontend_minified/img/users/nobody_m.original.jpg" class="md-avatar">
<div class="md-list-item-text" layout="column">
<h3>
{{review.name }}
</h3>
<span class="review-subtext">{{review.info}}</span >
<p class="review-status">{{review.status}}</p>
</div>
<md-divider></md-divider>
</md-list-item>
Here's one way of doing it - CodePen
Markup
<div ng-controller="AppCtrl" ng-cloak="" ng-app="MyApp">
<md-list>
<md-list-item class="md-3-line" ng-repeat="review in oReviews" ng-click="fnReviewEmployeeId($index)" ng-class="{selectedIndex: selectedIndex===$index}">
<img ng-src="https://x1.xingassets.com/assets/frontend_minified/img/users/nobody_m.original.jpg" class="md-avatar">
<div class="md-list-item-text" layout="column">
<h3>
{{review.name }}
</h3>
<span class="review-subtext">{{review.info}}</span >
<p class="review-status">{{review.status}}</p>
</div>
<md-divider></md-divider>
</md-list-item>
</md-list>
</div>
JS
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache', 'ngDialog'])
.controller('AppCtrl', function($scope) {
$scope.selectedIndex = null;
$scope.oReviews = [
{name: "Cheese", info: "Dairy", status: "Delicious"},
{name: "Beef", info: "Cow", status: "Versatile"},
{name: "Bread", info: "Yeast", status: "Everywhere"},
];
$scope.fnReviewEmployeeId = function (index) {
if ($scope.selectedIndex === null) {
$scope.selectedIndex = index;
}
else if ($scope.selectedIndex === index) {
$scope.selectedIndex = null;
}
else {
$scope.selectedIndex = index;
}
}
});
CSS
.selectedIndex {
background: yellow;
}
Can you add a boolean type property to the oReviews object? You could update that property when they click on it and then you can use ngClass to add the active class
I think Material Design handles selection differently, under List in the Docs, the example shows using a checkbox to indicate selection based on ng-model:
//I added the ng-click
<md-list-item ng-repeat="topping in toppings"
ng-click="topping.wanted = !topping.wanted">
<p> {{ topping.name }} </p>
<md-checkbox class="md-secondary" ng-model="topping.wanted"></md-checkbox>
</md-list-item>
Look for Section List Controls: https://material.angularjs.org/latest/demo/list

Display overview text when dropdown item is selected in AngularJS

I am looking to display an overview of each widget category to appear above the filtered results when that widget category is selected.
I am assuming this will require a ng-show directive so will perhaps require some controller code too. But any pointers on linking up select dropdown with my ng-repeat and linking up with ng-show would be great.
Here is what I am aiming for:
Before
After
<ion-view title="Select Box Filter" id="page6" class=" ">
<ion-content padding="true" class="has-header">
<ion-list id="tListSelectFilter-list11" class=" ">
<label class="item item-select " id="tListSelectFilter-select1">
<span class="input-label">Select</span>
<select></select>
</label>
<ion-item id="tListSelectFilter-list-item25" class=" ">Widget Range 1</ion-item>
<ion-item id="tListSelectFilter-list-item26" class=" ">Widget Range 2</ion-item>
<ion-item id="tListSelectFilter-list-item27" class=" ">Widget Range 3</ion-item>
</ion-list>
<ion-item ng-repeat="product in products | filter:select" class="item-thumbnail-left item-text-wrap"
href="#/tab/list/{{product.item}}">
<h2>Product Name: {{product.name}}</h2>
<h3>Quantity: {{product.quantity}}</h3>
<h2>Price: £{{product.price}}</h2>
</ion-item>
</ion-content>
</ion-view>
<!--Widget Range 1 Overview Text - Here is an example of the overview text for Widget Range 1 to be produced when this specific dropdown is selected.
Widget Range 2 Overview Text - Here is an example of the overview text for Widget Range 2 to be produced when this specific dropdown is selected.
Widget Range 3 Overview Text - Here is an example of the overview text for Widget Range 3 to be produced when this specific dropdown is selected.-->
https://plnkr.co/edit/0WrinKY2X7Ijq32hBzms
Here would be your ng-repeat
<span>{{description}}</span>
<ion-item ng-repeat="product in products | filter:select"
class="item-thumbnail-left item-text-wrap" ng-click="showDescription(product)" >
<h2>Product Name: {{product.name}}</h2>
<h3>Quantity: {{product.quantity}}</h3>
<h2>Price: £{{product.price}}</h2>
</ion-item>
This would be inside the controller
// description initialized to nothing
$scope.description = '';
$scope.showDescription = function(product) {
$scope.description = product.description;
}
Now this assumes that the description for each product is apart of the product object - just as the name, quantity, and price.
I would create json object array for categories as
$scope.categories = [
{"name":"Category 1", "description": "This is description of category1"}
{"name":"Category 2", "description": "This is description of category2"}
{"name":"Category 3", "description": "This is description of category1"}
]
I will bing this array to create the category list.
<ion-list id="tListSelectFilter-list11" class=" ">
<label class="item item-select " id="tListSelectFilter-select1">
<span class="input-label">Select</span>
<select></select>
</label>
<ion-item id="tListSelectFilter-list-item25" class=" " ng-repeat="c in categories" ng-model="selected.category">
{{c.name}}
</ion-item>
</ion-list>
<span>{{selected.category.description || ""}}</span>
This is how you can do it.
Keep your data as json obj array in controller. This will contain : Select item names and related descriptions.
Keep a place holder in controller for the currently selected option, you can use this too display the information on your page within the controller scope.
P.S : I have done it in simple HTML to show how this can be achieved. In case of any doubts do comment.
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function() {
this.selected = "";
this.data = [{
"name": "Widget 1",
"desc": "Here is an example of the overview text for Widget Range 1 to be produced when this specific dropdown is selected."
}, {
"name": "Widget 2",
"desc": "Here is an example of the overview text for Widget Range 2 to be produced when this specific dropdown is selected."
}, {
"name": "Widget 3",
"desc": "Here is an example of the overview text for Widget Range 3 to be produced when this specific dropdown is selected."
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl as ctrl">
<select ng-model="ctrl.selected" ng-options="widget.name for widget in ctrl.data">
<option value="">Please select</option>
</select>
<div>{{ctrl.selected.desc}}</div>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>
</div>

ngClick on mdListItem and secondary button

I use angular-material (1.0.7) on my project and I would like to create a clickable list (copied from the official doc) with secondary button.
The main issue is the list-item clickable event is also fired when I click on secondary button. In the doc, they show a dialog with targetEvent but it is not what I try to do.
There is my code:
<md-list-item class="md-2-line" ng-repeat="tag in tagsCtrl.showedTags|orderBy:'title'" ng-click="tagsCtrl.navigate(tag)">
<ng-md-icon icon="label"></ng-md-icon>
<div class="md-list-item-text">
<h3>{{ tag.title }}</h3>
<p>{{ tag.slug }}</p>
</div>
<span class="md-secondary" ng-show="tag.onProcess">
<md-progress-circular md-mode="indeterminate" md-diameter="24"></md-progress-circular>
</span>
<span class="md-secondary" ng-hide="tag.onProcess">
<md-button class=" md-icon-button md-hue-3" ng-click="tagsCtrl.editTag(tag, $event)" aria-label="Edit tag">
<ng-md-icon icon="create"></ng-md-icon>
</md-button>
<md-button class=" md-icon-button md-hue-3" ng-click="tagsCtrl.deleteTag(tag)" aria-label="Delete tag">
<ng-md-icon icon="delete"></ng-md-icon>
</md-button>
</span>
</md-list-item>
The navigation function is not a dialog.
Do you know how I can fix this?
I don't know if I understood it right, but you should stop the propagation of the event from bubbling to its parent (in this case from the md-button to the md-list-item)
Check more documentation here jQuery event.stopPropagation() Method
So where you have:
ng-click="tagsCtrl.editTag(tag, $event)"
ng-click="tagsCtrl.deleteTag(tag)"
you can replace with
ng-click="tagsCtrl.editTag(tag, $event) && $event.stopPropagation()"
ng-click="tagsCtrl.deleteTag(tag) && $event.stopPropagation()"

Ng-click/ ng-repeat issues

I'm trying to create an accordion using angular js and angular material. The problem is that when i'm using ng-repeat, all accordions in the array open when I click the button. I want only want to open the one that i click. Any ideas how I accomplish this? I have tried to google but i'm not finding exactly what i'm looking for
This is my html
<div class="accordionwrapper" layout="column" layout-align="center center">
<div class="accordion" ng-repeat="question in questions">
<div class="accordionheader" layout="row" layout-align="space-between center">
<h3>{{question.q}}</h3>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion()" ng-if="!accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion()" ng-if="accordionOpen">
<md-icon md-svg-icon="images/minus.svg"></md-icon>
</md-button>
</div>
<div class="accordioncontent" ng-show="accordionOpen">
<p>{{question.a}}</p>
</div>
</div>
And my js
$scope.accordionOpen = false;
$scope.toggleaccordion = function () {
$scope.accordionOpen = !$scope.accordionOpen;
console.log($scope.accordionOpen)
}
Thanks!
You need to keep track of the state of each individual accordion, using an array. This means that accordionOpen should be an array and that toggleaccordion should be like this :
$scope.toggleaccordion = function($index){
$scope.accordionOpen[$index] = !$scope.accordionOpen[$index]
Finally, you should call the function using the $index variable that is provided by angular inside an ng-repeat :
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion($index)" ng-if="accordionOpen[$index]">
All your accordions created by using ng-repeat depend on one variable, which is accordionOpen. Create an array of boolean flags and put it into questions array so that each of the accordions would have its own flag.
When you're calling $scope.accordionOpen = !$scope.accordionOpen; the scope is the parent of all the ngRepeat scopes. They inherit the accordionOpen value.
There might be more solutions to this - set the accordionOpen = true in ngClick instead of calling controller function: EDIT: Note: This won't probably work because the md-button ng-if uses it's own scope. It's always better to stick to some kind of model object, that other answers or second solution suggest.
<md-button
class="md-icon-button md-accent"
aria-label="Favorite"
ng-click="accordionOpen = true"
ng-if="!accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
or add a property to the question itself
js:
$scope.toggleaccordion = function (question) {
question.$accordionOpen = !question.$accordionOpen;
}
html
<md-button ng-click="toggleaccordion(question)" ng-if="!question.$accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<div class="accordioncontent" ng-show="question.$accordionOpen">
<p>{{question.a}}</p>
</div>
Read more on scopes: https://docs.angularjs.org/guide/scope
You are using a single variable in scope to toggle a collection of accordian , because of this all accordians toggle on the change of single varialbe .
To avoid it every accordian should have it's own set of toggle flag.To achieve it keep the flag with the record itself( in your case the question object)
<div class="accordionheader" layout="row" layout-align="space-between center">
<h3>{{question.q}}</h3>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion(question)" ng-if="!question.accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion(question)" ng-if="question.accordionOpen">
<md-icon md-svg-icon="images/minus.svg"></md-icon>
</md-button>
</div>
<!-- In ng-show use a variable which will make sure that every object will get it's own toggle status field -->
<div class="accordioncontent" ng-show="question.accordionOpen">
<p>{{question.a}}</p>
</div>
</div>
And in your js use like this
$scope.questions.forEach(function(question){
question.accordionOpen = false ;
});
$scope.toggleaccordion = function (question) {
question.accordionOpen = !question.accordionOpen;
}

Retrieving a select option on another page with a different controller

I'm trying to build a cordova app. In on of the functions, I wanted to add a dropdown select option that users can select and can get stored in order to be emailed as a request later. I added the select option in an options.html page and I wish to populate the selected option in order.html page. What would be the best way to go about this?
Here is my current options.html, which users optionsController:
<md-card ng-repeat="item in items.results | filter:true">
<img ng-src="{{items.img}}"
class="md-card-image"
alt="">
<md-card-content class="content">
<h2 class="md-title"><div ng-bind-html="item.name"></div></h2>
<h4>{{ item.price | currency }}</h4>
<h4>Qty {{ item.qty }}</h4>
<md-list>
<h2 class="md-title" style="color:#3F51B5;">Select Your Side</h2>
<md-divider></md-divider>
<md-list-item layout="row">
<md-select aria-label="side set" class="md-accent" ng-model="item.type">
<md-option ng-value="side.name" ng-repeat="side in item.sides">{{ side.name }}</md-option>
</md-select>
</md-list-item>
</md-list>
</md-card-content>
<md-action-bar layout="row"
layout-align="end center">
<md-button class="md-fab md-accent fab"
aria-label="Remove From Cart"
ng-click="remove(item);showRemovedToast();"
ng-class="{active:item.active}">
<md-icon md-svg-src="img/icons/remove.svg"></md-icon>
</md-button>
</md-action-bar>
</md-card>
I'm retrieving JSON data under side.name, the data is as follows:
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope',
function MyCtrl($scope) {
$scope.items = {
"results": [{
"active": false,
"desc": "With arugula, smoked almonds \u0026 chipotle vinaigrette",
"img": "https://signsrestaurant.ca/wp-content/uploads/2015/09/Watermelon-Quinoa-Jimaca-Salad.jpg",
"name": "Watermelon Quinoa Jicama Salad (\u003cspan style=\"color: lightblue;\"\u003eVE\u003c/span\u003e, \u003cspan style=\"color: goldenrod;\"\u003eGF\u003c/span\u003e, \u003cspan style=\"color: yellow;\"\u003eDF\u003c/span\u003e)",
"price": 14,
"sides": [{
"active": false,
"name": "Soup"
}, {
"active": false,
"name": "Salad"
}, {
"active": false,
"name": "Fries"
}],
}, {
"active": false,
"desc": "Buffalo mozzarella, tomato, marinated artichoke hearts, black olives, pesto \u0026 balsamic drizzle",
"img": "https://signsrestaurant.ca/wp-content/uploads/2015/09/Mediterranean-Salad.jpg",
"name": "Mediterranean Salad (\u003cspan style=\"color: lightgreen;\"\u003eV\u003c/span\u003e, \u003cspan style=\"color: goldenrod;\"\u003eGF\u003c/span\u003e)",
"price": 15,
"sides": [{
"active": false,
"name": "Soup"
}, {
"active": false,
"name": "Salad"
}, {
"active": false,
"name": "Fries"
}],
}]
};
}
]);
My order.html is as follows, which uses orderController:
<md-card>
<md-card-content>
<h3 class="md-subhead" align="center">Review And Submit Order</h3>
<md-divider></md-divider>
<md-list ng-repeat="item in items.results | filter:true">
<md-list-item layout="row">
<h3><div ng-bind-html="item.name"></div> Qty:{{item.qty}}</h3>
<span flex></span>
<h3>{{ item.price | currency }}</h3>
</md-list-item>
<md-list-item layout="row">
<div id="side"></div>
</md-list-item>
</md-list>
<md-divider></md-divider>
<md-list>
<md-list-item layout="row">
<h3 class="md-subhead">Order Total:</h3>
<span flex></span>
<h3>{{ total(items.results) | currency }}</h3>
</md-list-item>
</md-list>
</md-card-content>
</md-card>
<md-card ng-if="(items.results | filter : {active: true}).length > 0">
<md-card-content layout-padding>
<form name="order">
<md-input-container flex>
<label>Name</label>
<input ng-model="name">
</md-input-container>
<md-input-container flex>
<label>Phone</label>
<input ng-model="phone">
</md-input-container>
<md-input-container flex>
<label>Address</label>
<input ng-model="address">
</md-input-container>
<md-input-container flex>
<label>Email</label>
<input ng-model="email">
</md-input-container>
<md-input-container flex>
<label>Special Requests</label>
<textarea ng-model="requests"
columns="1"
md-maxlength="150"></textarea>
</md-input-container>
</form>
</md-card-content>
<md-card-content layout="row"
layout-align="end center">
<md-button class="md-raised md-primary"
ng-controller="EmailController"
ng-click=sendMail()>
Place Order
</md-button>
</md-card-content>
</md-card>
I wish to populate the selected Side in the div 'side' - how do I go about this? I tried localstorage, but not sure how to store and populate on a different page. Any help would be appreciated.
Method 1 - Using a service
You create a service specialized in passing data among controllers/directives or other services.
Method 2 - Using events
Broadcast/emit an event with the data you need which you can later listen in every part of your application.
Method 3 - Passing data as route parameter
If you are using the angular router or ui-router (or probably any other router) you can pass data as a route paramater, which you can listen later in the controller attached to that route.
So, you can think of all this 3 methods and pick-up the one more suitable for you. In this case, you should go with the event method since you want to pass data created by an UI interaction.
Pass values to another page, Easy way :
how to pass more than one value to next page in angularjs
using broadcast:
inside controller 1
$scope.$root.$broadcast("triggerEvent", { pass_this_value: yourValue});
this is the Event listner
$scope.$on("triggerEvent", function (event, args) {
//do what u want to
});

Categories

Resources