I am trying to create a menu in my ionic app that has the following behaviour.
Tap once to show the description for that menu item.
Tap again to navigate to that page or run the specified function.
I did have my whole menu hardcoded in the template however figure it would be nicer to have an object with the menu items in. This brings me to my main stumbling block of how can I have a function contained within an object.
OR
Am I tackling this completely the wrong way, in which case your advice would be greatly appreciated.
Example code is:
angular.module('ionicApp', ['ionic'])
.controller('AppCtrl', function($scope, $state) {
$scope.menuItems = [{
"title": "Create a Programe",
"icon": "ion-compose",
"description" : "Search and add exercises to your prgoramme before sending to your patient"
/* "action": "openCreateFullProgrammeModal()"
"action": $state.go('app/clients')
"action": $state.go('app/programmes')*/
}, {
"title": "My Clients",
"icon": "ion-android-people",
"description" : "view all your clients"
/* "action": "openCreateFullProgrammeModal()"
"action": $state.go('app/clients')
"action": $state.go('app/programmes')*/
}, {
"title": "Programmes",
"icon": "ion-clipboard",
"description" : "Browse your programe library"
/* "action": "openCreateFullProgrammeModal()"
"action": $state.go('app/clients')
"action": $state.go('app/programmes')*/
}
]
});
Template:
<div class="descriptionArea">
<p>testing</p>
</div>
<div class="home-bottom">
<div ng-repeat="item in menuItems">
<ion-item class="item-icon-left home-links" href="#" ng-click="openCreateFullProgrammeModal()">
<i ng-class="item.icon" class="icon home-link-icon"></i> {{item.title}}
<i class="icon ion-android-arrow-forward home-icon-right"></i>
</ion-item>
</div>
</div>
A codepen can be found here
Many thanks.
EDIT
This is what I did in the end...
First your approach in hardcoding the menu is not practical you should use ng-repeat for that.
Second here is a codepen for the tapping functionality, I did an example to show you how to make it and you can extend it and implement the go functionality.
here is the codepin
Related
I have a nav menu that is rendered using a navigation.ts json file for the menu items. When it gets to the navitem component it uses a ngIf to check if the item from the navigation file has a "function" key and if it does, the desired behavior is for it to use the string value from item.function in the object to fill the value for the (click) event.
In reality, the console throws an error saying "_co.item.function is not a function"
HTML
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</span>
Navigation.ts
[{
"id": "accounting",
"title": "Accounting",
"type": "collapse",
"children": [
{
"id" : "salesmenSalesLocation",
"title": "Salesmen Sales Location",
"type": "item",
"function": "handleSelect(ReportTypes.SalesmenSalesLocations)"
},
{
"id": "laggingLedgerEntries",
"title": "Lagging Ledger Entries",
"type": "item",
"function": "handleSelect(ReportTypes.LaggingLedgerEntries)"
}
]}]
I have also tried it as (click)="item.function" with no success.
I'm assuming you can change the data source here, because otherwise I don't see any good solution.
A string is not a function, and while you can turn it into one with eval that is a bad idea. What you should really do instead is just pass in a value that tells the function what to use.
Change your data to something like this:
{
"id" : "salesmenSalesLocation",
"title": "Salesmen Sales Location",
"type": "item",
"reportTypeSource": "SalesmenSalesLocations"
},
{
"id": "laggingLedgerEntries",
"title": "Lagging Ledger Entries",
"type": "item",
"reportTypeSource": "LaggingLedgerEntries"
}
Then pass that value to your function and use that to tell it where to look:
handleSelect (reportTypeSource: string) {
const reportType = ReportTypes[reportTypeSource]
// continue as before
}
And call it in your HTML like this:
(click)="handleSelect(item.reportTypeSource)"
Problem lies here:
"function": "handleSelect(ReportTypes.LaggingLedgerEntries)"
And here:
(click)="item.function()"
You cannot simply pass a string and expect the component to execute a function and also know exactly what to do. Here you need to pass the actual function.
Your setup looks over-config'd. I would tear the config down and put the logic into the component itself. Don't be afraid to have more template as well, if anything it makes things more legible (as opposed to the config)
Does that function exist in the component or just the model? If it is just on the model it won't work. (click) is looking for a method on the component. It is, ostensibly just a string in this instance.
update: Plunker session added on request. https://plnkr.co/edit/0XjAoxzjIoSUelFcfmrx (no experience so will likely need help getting it to run on Plunker)
Building up a menu system using Node, Angular 2 and Typescript. Creating the output with manually added content works just fine, but having trouble wanting to mod this to iterate over a more complex array to create menu and sub-menu items. I see examples of for(var a in array) and for(var a of array), but perplexed as to which is the best way to go. Here is the current setup:
my menu.component.ts:
import {Component} from 'angular2/core';
export class MenuContent {
constructor (
public id: number,
public name: string
) { }
}
#Component({
selector: 'jimmmenu',
templateUrl: 'app/menu.html'
})
export class MenuComponent {
menuTitle = 'Menu!';
menuContent = [
new MenuContent(1, 'menu item'),
new MenuContent(2, 'menu item'),
new MenuContent(3, 'menu item'),
new MenuContent(4, 'menu item')
]
Menu = this.menuContent[0];
}
the menu.html template:
<div>
<h2>{{menuTitle}}</h2>
<ul>
<li *ngFor="#menu of menuContent">{{menu.name}} {{menu.id}}</li>
</ul>
</div>
and the index.ejs snippet where the menu is placed:
<body>
<jimmmenu></jimmmenu>
<jsTurfApp>Loading...</jsTurfApp>
<script src='/socket.io/socket.io.js'></script>
</body>
As I said, everything works fine like this, producing a simple list of menu items. But when I try to do something like:
menuData = {
"menu": [
{ "id": 0, "name": "DasnBoard", "image":"/Images/dashboard_on.gif", "link": "page0.html",
"subMenu": [
{ "id": 10, "name": "Main", "image": null, "link": "page1.html", "subMenu": null },
{ "id": 11, "name": "ET & Moisture", "image": null, "link": "page2.html", "subMenu": null }
]
},
]
};
for(var m in menuData) {
menuContent = [ new MenuContent(this.menuData.menu[m].id, this.menuData.menu[m].name); ]
}
...things start going awry, such as the Chrome Inspector crying 'Uncaught SyntaxError: Unexpected token ;' on the line with the for(). And yes I know that the loop would only add the last one... it's a bad example, for sure.
Essentially I'm trying to do the following, but looping through the menuData array instead of populating menuContent by hand with long drawn-out menu/submenu assignments:
menuContent = [
new MenuContent(this.menuData.menu[0].id, this.menuData.menu[0].name),
new MenuContent(this.menuData.menu[0].subMenu[0].id, this.menuData.menu[0].subMenu[0].name),
...
]
Admittedly my Typescript and Angular 2 knowledge is small and the available google-results seems sparse on similar ideas, which is why I turn to you all for assistance or prodding in the right direction. Appreciate any insights.
You could use this approach for simplicity:
<ul class="menu">
<li *ngFor="#item of items">
<a>{{item.name}}</a>
<ul class="sub-menu">
<li *ngFor="#subitem of item.subMenu">
<label>{{subitem.name}}</label>
</li>
</ul>
</li>
</ul>
I am working on an AngularJS tutorial
This tutorial covers the ng-repeat directive, an AngularJS directive used repeating data.
To show an example of ng-repeat, The author enters periodic table elements in a JSON format, covering element's name, element #, etc into controller logic($scope)
To display the elements(code below), the author simply used the directive with a html un-ordered list
<ul>
<li data-ng-repeat="element in periodic.elements">{{element.name}} </li>
</ul>
I tried doing the same JsFiddle but the list of elements isn't showing up, only {{element.name}}
At first I thought this was an AngularJS syntax issue but I checked over the scope attribute, if the controller names match, etc.... I made sure to enable the AngularJS option in JsFiddle as well.
Does anyone know what the issue is or why this list isn't showing up?
You forget completing controller sytax '});' at the end of the code.
'use strict';
var chemistryApp = angular.module('chemistryApp', []);
chemistryApp.controller(
'chemistryController',
function chemistryController($scope) {
$scope.periodic = {elements: [
{
"atomicNumber": 1,
"name": "Hydrogen",
"atomicWeight": 1.00794,
"phase": "Gas",
"ionization": 13.5984,
"melting": -259.15,
"boiling": -252.87
},
{
"atomicNumber": 2,
"name": "Helium",
"atomicWeight": 4.002602,
"phase": "Gas",
"ionization": 24.5874,
"melting": 0,
"boiling": -268.93
},
{
"atomicNumber": 3,
"name": "Lithium",
"atomicWeight": 6.941,
"phase": "Solid",
"ionization": 5.3917,
"melting": 180.54,
"boiling": 1342
}
]
};
});
Working Fiddle
I followed this answer and fork an example:
How to implement a scrollspy in angular.js the right way?
My purpose is to populate dynamic content using template and there are images in there:
http://plnkr.co/edit/OKrzSr
**HTML code difference:((
<div ng-repeat="item in items">
<h4 id="{{ item.id }}">{{ item.id }}</h4>
<p ng-repeat="img in [1,2,3,4,5]"><img ng-src="{{ item.src }}"></p>
</div>
Javascript code difference:
angular.module('scrollSpyPlunk')
.controller('scrollSpyCtrl', function ($scope, $anchorScroll)
{
$scope.items = [{
"id": "section1",
"src": "http://placehold.it/400x400"
},{
"id": "section2",
"src": "http://placehold.it/400x400"
},{
"id": "section3",
"src": "http://placehold.it/400x400"
}]
});
It seems that the scrollspy feature doesn't work as expected. It activated the menu way too early when I scroll down. I think it treated images as just one line of text. I am not sure.
Any help to fix this?
I wrote my own Scrollspy directive here https://github.com/quanghoc/angular-bootstrap-scrollspy
The key is to add this event
$rootScope.$on('scrollspy.refresh', function() {
refresh(attrs);
});
Here is the basic setup, which has a default noemployee.html partial: as the ng-view
Index.html content:
<div id="container" ng-controller="EmployeeCtrl">
<!-- Side Menu -->
<span id="smenuSpan">
<ul id="thumbList">
<li ng-repeat="employee in employees | filter:categories">
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
</li>
</ul>
</span>
<!-- Content -->
<span id="contentSpan">
<div ng-view></div>
</span>
</div>
My Route Provider:
var EmployeeModule = angular.module('EmployeeModule', [], function($routeProvider, $locationProvider) {
$routeProvider.when('/', { templateUrl: 'content/app/partials/noemployee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.when('Employee/:id', { templateUrl: 'content/app/partials/employee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.otherwise({ redirectTo: '/' });
$locationProvider.html5Mode(true);
My Controller:
function EmployeeCtrl($scope, $http, $routeParams, $timeout) {
$scope.employees = [
{ "id": 1, "category": "ones", "image": "person1.jpg", "description": "person 1 description", name:"Jane Smith" },
{ "id": 2, "category": "twos", "image": "person2.jpg", "description": "person 2 description", name: "Mark Sharp" },
{ "id": 3, "category": "threes", "image": "person3.jpg", "description": "person 3 description", name: "Kenny Suave" },
{ "id": 4, "category": "fours", "image": "person4.jpg", "description": "person 4 description", name: "Betty Charmer" },
{ "id": 5, "category": "fives", "image": "person5.jpg", "description": "person 5 description", name: "John Boss" }
];
$scope.employeesCategories = [];
$scope.currentEmployee = {};
$scope.params = $routeParams;
$scope.handleEmployeesLoaded = function (data, status) {
//$scope.images = data;
// Set the current image to the first image in images
$scope.currentEmployee = _.first($scope.employees);
// Create a unique array based on the category property in the images objects
$scope.employeeCategories = _.uniq(_.pluck($scope.employees, 'category'));
}
$scope.fetch = function () {
$http.get($scope.url).success($scope.handleEmployeesLoaded);
};
$scope.setCurrentEmployee = function (employee) {
$scope.currentEmployee = employee;
};
// Defer fetch for 1 second to give everything an opportunity layout
$timeout($scope.fetch, 1000);
}
Observations:
At present, if I click on any employee, no 'Employee/??' is added to the address bar path [ which isn't a crime to me], however, the main content div does not change the partial to the employee.html.
If I comment out "$locationProvider.html5Mode(true);", the default localhost is now "http://localhost:31219/#/" and when I click on any employee the address bar shows 'http://localhost:31219/Employee/1', and the page is navigated away to a 404 error page.
I know I am bastardizing something here that the solution is so simple it escapes me.
Goals:
I really would like to avoid hash tags in my address bar.
It would be nice but no req that the employee/id not show up in the address bar but I suspect the partial cannot change w/o it. and, naturally
I want the partial to change to the 'employee.html" page when an employee is clicked.
Does anyone see where I am going wrong with this code?
Thanks in Advance!
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out
'$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?
In order to use html5mode, your server has to serve up the main app index file for otherwise invalid routes.
So, for example, if your server side code handles CRUD operations on paths like: /api/employees, /api/employees/:id, etc...
and it serves up static content, images, html, css, js, etc.
For any other request, that would otherwise be a 404, it should, instead of responding with a 404, respond with a 200 code, and serve up the index.html file.
This way any non static and non server side route gets handled by the angular app.
This is mentioned in the Angular guide on this page: http://docs.angularjs.org/guide/dev_guide.services.$location
Note the 'server side' comment at the end:
Html link rewriting
When you use HTML5 history API mode, you will need
different links in different browsers, but all you have to do is
specify regular URL links, such as: link
When a user clicks on this link:
In a legacy browser, the URL changes to /index.html#!/some?foo=bar
In a modern browser, the URL changes to /some?foo=bar In cases like the
following, links are not rewritten; instead, the browser will perform
a full page reload to the original link.
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite
all your links to entry point of your application (e.g. index.html)
This was the problem:
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out '$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?