I'm new to Angularjs and I got a project on it, but here's where the problem comes, am planning to have a toggle button, when the page loads the sidenav should be opened, and when I click on the toggle button than the sidenav and content both should move together.
I've designed my following code, but in responsive i'm getting issues.
I googled allot, but dint get any code with my requirements.
Fiddle Link
angular
.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('AppCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.toggleLeft = buildDelayedToggler('left');
$scope.toggleRight = buildToggler('right');
$scope.isOpenRight = function(){
return $mdSidenav('right').isOpen();
};
/**
* Supplies a function that will continue to operate until the
* time is up.
*/
function debounce(func, wait, context) {
var timer;
return function debounced() {
var context = $scope,
args = Array.prototype.slice.call(arguments);
$timeout.cancel(timer);
timer = $timeout(function() {
timer = undefined;
func.apply(context, args);
}, wait || 10);
};
}
/**
* Build handler to open/close a SideNav; when animation finishes
* report completion in console
*/
function buildDelayedToggler(navID) {
return debounce(function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav(navID)
.toggle()
.then(function () {
$log.debug("toggle " + navID + " is done");
});
}, 200);
}
function buildToggler(navID) {
return function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav(navID)
.toggle()
.then(function () {
$log.debug("toggle " + navID + " is done");
});
}
}
})
.controller('LeftCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.close = function () {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav('left').close()
.then(function () {
$log.debug("close LEFT is done");
});
};
})
.controller('RightCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.close = function () {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav('right').close()
.then(function () {
$log.debug("close RIGHT is done");
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/svg-assets-cache.js"></script>
<script src="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.js"></script>
<link href="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.css" rel="stylesheet" />
<link href="https://material.angularjs.org/1.1.1/docs.css" rel="stylesheet" />
<div ng-controller="AppCtrl" layout="column" style="height:500px;" ng-cloak="" class="sidenavdemoBasicUsage" ng-app="MyApp">
<section layout="row" flex="">
<md-sidenav class="md-sidenav-left" md-component-id="left" md-is-locked-open="$mdMedia('gt-md')" md-whiteframe="4">
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Sidenav Left</h1>
</md-toolbar>
<md-content layout-padding="" ng-controller="LeftCtrl">
<md-button ng-click="close()" class="md-primary" hide-gt-md="">
Close Sidenav Left
</md-button>
<p hide="" show-gt-md="">
This sidenav is locked open on your device. To go back to the default behavior,
narrow your display.
</p>
</md-content>
</md-sidenav>
<md-content flex="" layout-padding="">
<div layout="column" layout-align="top center">
<p>
The left sidenav will 'lock open' on a medium (=960px wide) device.
</p>
<p>
The right sidenav will focus on a specific child element.
</p>
<div>
<md-button ng-click="toggleLeft()" class="md-primary" >
Toggle left
</md-button>
</div>
<div>
<md-button ng-click="toggleRight()" ng-hide="isOpenRight()" class="md-primary">
Toggle right
</md-button>
</div>
</div>
<div flex=""></div>
</md-content>
<md-sidenav class="md-sidenav-right md-whiteframe-4dp" md-component-id="right">
<md-toolbar class="md-theme-light">
<h1 class="md-toolbar-tools">Sidenav Right</h1>
</md-toolbar>
<md-content ng-controller="RightCtrl" layout-padding="">
<form>
<md-input-container>
<label for="testInput">Test input</label>
<input type="text" id="testInput" ng-model="data" md-autofocus="">
</md-input-container>
</form>
<md-button ng-click="close()" class="md-primary">
Close Sidenav Right
</md-button>
</md-content>
</md-sidenav>
</section>
</div>
The container is a flex element, but the sidebar has position absolute applied and so isn't accounted in the flex layout.
Just change it to position relative and it will work
angular
.module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('AppCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.toggleLeft = buildDelayedToggler('left');
$scope.toggleRight = buildToggler('right');
$scope.isOpenRight = function() {
return $mdSidenav('right').isOpen();
};
/**
* Supplies a function that will continue to operate until the
* time is up.
*/
function debounce(func, wait, context) {
var timer;
return function debounced() {
var context = $scope,
args = Array.prototype.slice.call(arguments);
$timeout.cancel(timer);
timer = $timeout(function() {
timer = undefined;
func.apply(context, args);
}, wait || 10);
};
}
/**
* Build handler to open/close a SideNav; when animation finishes
* report completion in console
*/
function buildDelayedToggler(navID) {
return debounce(function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav(navID)
.toggle()
.then(function() {
$log.debug("toggle " + navID + " is done");
});
}, 200);
}
function buildToggler(navID) {
return function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav(navID)
.toggle()
.then(function() {
$log.debug("toggle " + navID + " is done");
});
}
}
})
.controller('LeftCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.close = function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav('left').close()
.then(function() {
$log.debug("close LEFT is done");
});
};
})
.controller('RightCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.close = function() {
// Component lookup should always be available since we are not using `ng-if`
$mdSidenav('right').close()
.then(function() {
$log.debug("close RIGHT is done");
});
};
});
#sdleft {
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/svg-assets-cache.js"></script>
<script src="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.js"></script>
<link href="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.css" rel="stylesheet" />
<link href="https://material.angularjs.org/1.1.1/docs.css" rel="stylesheet" />
<div ng-controller="AppCtrl" layout="column" style="height:500px;" ng-cloak="" class="sidenavdemoBasicUsage" ng-app="MyApp">
<section layout="row" flex="">
<md-sidenav id="sdleft" class="md-sidenav-left" md-component-id="left" md-is-locked-open="$mdMedia('gt-md')" md-whiteframe="4">
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Sidenav Left</h1>
</md-toolbar>
<md-content layout-padding="" ng-controller="LeftCtrl">
<md-button ng-click="close()" class="md-primary" hide-gt-md="">
Close Sidenav Left
</md-button>
<p hide="" show-gt-md="">
This sidenav is locked open on your device. To go back to the default behavior, narrow your display.
</p>
</md-content>
</md-sidenav>
<md-content flex="" layout-padding="">
<div layout="column" layout-align="top center">
<p>
The left sidenav will 'lock open' on a medium (=960px wide) device.
</p>
<p>
The right sidenav will focus on a specific child element.
</p>
<div>
<md-button ng-click="toggleLeft()" class="md-primary">
Toggle left
</md-button>
</div>
<div>
<md-button ng-click="toggleRight()" ng-hide="isOpenRight()" class="md-primary">
Toggle right
</md-button>
</div>
</div>
<div flex=""></div>
</md-content>
<md-sidenav class="md-sidenav-right md-whiteframe-4dp" md-component-id="right">
<md-toolbar class="md-theme-light">
<h1 class="md-toolbar-tools">Sidenav Right</h1>
</md-toolbar>
<md-content ng-controller="RightCtrl" layout-padding="">
<form>
<md-input-container>
<label for="testInput">Test input</label>
<input type="text" id="testInput" ng-model="data" md-autofocus="">
</md-input-container>
</form>
<md-button ng-click="close()" class="md-primary">
Close Sidenav Right
</md-button>
</md-content>
</md-sidenav>
</section>
</div>
Related
I'm trying to display a preview of some images when a user selects them before he uploads them, but I'm new to vue and I can't figure out how to apply the background image url style to each div (it must be with background image), here is my UploadComponent so far:
<template>
<div class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles"/>
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div v-for="image in images" v-bind:key="image" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'UploadComponent',
data: () => ({
title: 'Select your images first and then, submit them.',
images: []
}),
methods:{
selectFiles(event){
var selectedFiles = event.target.files;
var i = 0;
for (i = 0; i < selectedFiles.length; i++){
this.images.push(selectedFiles[i]);
}
for (i = 0; i < this.images.length; i++){
let reader = new FileReader(); //instantiate a new file reader
reader.addEventListener('load', function(){
console.log(this);
}.bind(this), false); //add event listener
reader.readAsDataURL(this.images[i]);
}
},
uploadImages(){
const data = new FormData();
data.append('photos', this.images);
axios.post('url')
.then(res => {
console.log(res);
});
}
}
}
</script>
The divs are being created but I'm missing the part where I assign the background image, how can I solve this?
You can't just put a File directly as the parameter for background-image. You started using FileReader but didn't do anything with the result.
Here's what I would do below, I converted your images array into a list of objects that have a file property and a preview property, just to keep the file and preview linked together.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: "#app",
data: () => {
return {
title: 'Select your images first and then, submit them.',
images: []
}
},
methods: {
selectFiles(event) {
var selectedFiles = event.target.files;
for (var i = 0; i < selectedFiles.length; i++) {
let img = {
file: selectedFiles[i],
preview: null
};
let reader = new FileReader();
reader.addEventListener('load', () => {
img.preview = reader.result;
this.images.push(img);
});
reader.readAsDataURL(selectedFiles[i]);
}
},
uploadImages() {
const data = new FormData();
data.append('photos', this.images.map(image => image.file));
axios.post('url')
.then(res => {
console.log(res);
});
}
}
});
.image-result {
width: 100px;
height: 100px;
background-size: contain;
background-repeat: no-repeat;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles" />
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div id="target-photos">
</div>
<div v-for="image in images" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image.preview + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>
I have a testbed for CropperJS - I have included a working sample. It displays your camera, and when you click take a snapshot, it copied the current image to the canvas. However, I cannot get the following to work:
When the image is cropped, I need to put the cropped image in the canvas instead of the image control.
I need to move the upload an image from actually clicking on the image to clicking the upload button.
<link href="Styles/bootstrap/v4.1.2/bootstrap.min.css" rel="stylesheet" />
<link href="Styles/cropper.css" rel="stylesheet" />
<link href="Styles/Site.css" rel="stylesheet" />
<div class="row">
<div class="col">
<video playsinline autoplay></video>
<canvas id="imageCanvas"></canvas>
</div>
</div>
<div class="row">
<button>Take snapshot</button>
<button id="btnUploadImage">Upload Image</button>
</div>
<div class="row">
<label class="label" data-toggle="tooltip" title="Upload A New Image">
<img class="rounded" id="avatar" src="./Images/Cardholders_72x72.png" alt="avatar">
<input type="file" class="sr-only" id="input" name="image" accept="image/*">
</label>
<!-- Modal -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel">Crop your image.</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="img-container">
<img id="image" src="./Images/Cardholders_72x72.png">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="crop">Crop</button>
</div>
</div>
</div>
</div>
</div>
<script src="Scripts/jquery-3.3.1.min.js"></script>
<script src="Scripts/bootstrap.bundle.min.js"></script>
<script src="Scripts/cropper.js"></script>
<!-- This Script is for the Cropper -->
<script>
window.addEventListener('DOMContentLoaded', function ()
{
var avatar = document.getElementById('avatar');
var image = document.getElementById('image');
var input = document.getElementById('input');
var $modal = $('#modal');
var cropper;
input.addEventListener('change', function (e)
{
var files = e.target.files;
var done = function (url) {
input.value = '';
image.src = url;
$modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0)
{
file = files[0];
if (URL)
{
done(URL.createObjectURL(file));
}
else if (FileReader)
{
reader = new FileReader();
reader.onload = function (e)
{
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
$modal.on('shown.bs.modal', function ()
{
cropper = new Cropper(image,
{
aspectRatio: 1,
viewMode: 3
});
}).on('hidden.bs.modal', function ()
{
cropper.destroy();
cropper = null;
});
document.getElementById('crop').addEventListener('click', function () {
var initialAvatarURL;
var canvas;
$modal.modal('hide');
if (cropper) {
canvas = cropper.getCroppedCanvas({
width: 160,
height: 160
});
initialAvatarURL = avatar.src;
avatar.src = canvas.toDataURL();
canvas.toBlob(function (blob)
{
var formData = new FormData();
formData.append('avatar', blob, 'avatar.jpg');
});
}
});
});
</script>
<!-- This script is for the camera and snapshot -->
<script>
'use strict';
// Put variables in global scope to make them available to the browser console.
const video = document.querySelector('video');
const canvas = window.canvas = document.querySelector('canvas');
canvas.width = 480;
canvas.height = 360;
const button = document.querySelector('button');
button.onclick = function ()
{
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
};
var constraints = {
audio: false,
video: {
width: { min: 320, ideal: 320, max: 640 },
height: { min: 400, ideal: 400, max: 480 },
aspectRatio: { ideal: 1.25 }
}
};
function handleSuccess(stream) {
window.stream = stream; // make stream available to browser console
video.srcObject = stream;
}
function handleError(error) {
console.log('navigator.getUserMedia error: ', error);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
</script>
So I am following this tutorial to extend the autodesk forge viewer. I have compelted all of the steps and no button is showing, I assume this is due to an error with the loading.
https://forge.autodesk.com/blog/extension-skeleton-toolbar-docking-panel
I have also tried this tutorial, with the same issue:
http://learnforge.autodesk.io/#/viewer/extensions/selection?id=conclusion
My issue is I am not getting an error, the extension just isn't showing... does anyone know why?
I'm assuming theres an error in either the viewer or the index.
Below is my code: (index & forge viewer)
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Autodesk Forge Tutorial</title>
<meta charset="utf-8" />
<!-- Common packages: jQuery, Bootstrap, jsTree -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<script src="/js/MyAwesomeExtension.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" />
<!-- Autodesk Forge Viewer files -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/style.min.css?v=v6.0" type="text/css">
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js?v=v6.0"></script>
<!-- this project files -->
<link href="css/main.css" rel="stylesheet" />
<script src="js/ForgeTree.js"></script>
<script src="js/ForgeViewer.js"></script>
</head>
<body>
<!-- Fixed navbar by Bootstrap: https://getbootstrap.com/examples/navbar-fixed-top/ -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<ul class="nav navbar-nav left">
<li>
<a href="http://developer.autodesk.com" target="_blank">
<img alt="Autodesk Forge" src="//developer.static.autodesk.com/images/logo_forge-2-line.png" height="20">
</a>
</li>
</ul>
</div>
</nav>
<!-- End of navbar -->
<div class="container-fluid fill">
<div class="row fill">
<div class="col-sm-4 fill">
<div class="panel panel-default fill">
<div class="panel-heading" data-toggle="tooltip">
Buckets & Objects
<span id="refreshBuckets" class="glyphicon glyphicon-refresh" style="cursor: pointer"></span>
<button class="btn btn-xs btn-info" style="float: right" id="showFormCreateBucket" data-toggle="modal" data-target="#createBucketModal">
<span class="glyphicon glyphicon-folder-close"></span> New bucket
</button>
</div>
<div id="appBuckets">
tree here
</div>
</div>
</div>
<div class="col-sm-8 fill">
<div id="forgeViewer"></div>
</div>
</div>
</div>
<form id="uploadFile" method='post' enctype="multipart/form-data">
<input id="hiddenUploadField" type="file" name="theFile" style="visibility:hidden" />
</form>
<!-- Modal Create Bucket -->
<div class="modal fade" id="createBucketModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Cancel">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Create new bucket</h4>
</div>
<div class="modal-body">
<input type="text" id="newBucketKey" class="form-control"> For demonstration purposes, objects (files) are
NOT automatically translated. After you upload, right click on
the object and select "Translate".
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createNewBucket">Go ahead, create the bucket</button>
</div>
</div>
</div>
</div>
</body>
</html>
ForgeViewer.js:
var viewerApp;
function launchViewer(urn) {
var options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken
};
var documentId = 'urn:' + urn;
Autodesk.Viewing.Initializer(options, function onInitialized() {
viewerApp = new Autodesk.Viewing.ViewingApplication('forgeViewer');
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.Private.GuiViewer3D, { extensions: ['MyAwesomeExtension'] });
viewerApp.loadDocument(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
});
}
function onDocumentLoadSuccess(doc) {
// We could still make use of Document.getSubItemsWithProperties()
// However, when using a ViewingApplication, we have access to the **bubble** attribute,
// which references the root node of a graph that wraps each object from the Manifest JSON.
var viewables = viewerApp.bubble.search({ 'type': 'geometry' });
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Choose any of the available viewables
viewerApp.selectItem(viewables[0].data, onItemLoadSuccess, onItemLoadFail);
}
function onDocumentLoadFailure(viewerErrorCode) {
console.error('onDocumentLoadFailure() - errorCode:' + viewerErrorCode);
}
function onItemLoadSuccess(viewer, item) {
// item loaded, any custom action?
}
function onItemLoadFail(errorCode) {
console.error('onItemLoadFail() - errorCode:' + errorCode);
}
function getForgeToken(callback) {
jQuery.ajax({
url: '/api/forge/oauth/token',
success: function (res) {
callback(res.access_token, res.expires_in)
}
});
}
MyAwesomeExtension.js:
// *******************************************
// My Awesome Extension
// *******************************************
function MyAwesomeExtension(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
this.panel = null;
}
MyAwesomeExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
MyAwesomeExtension.prototype.constructor = MyAwesomeExtension;
MyAwesomeExtension.prototype.load = function () {
if (this.viewer.toolbar) {
// Toolbar is already available, create the UI
this.createUI();
} else {
// Toolbar hasn't been created yet, wait until we get notification of its creation
this.onToolbarCreatedBinded = this.onToolbarCreated.bind(this);
this.viewer.addEventListener(av.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
}
return true;
};
MyAwesomeExtension.prototype.onToolbarCreated = function () {
this.viewer.removeEventListener(av.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
this.onToolbarCreatedBinded = null;
this.createUI();
};
MyAwesomeExtension.prototype.createUI = function () {
var viewer = this.viewer;
var panel = this.panel;
// button to show the docking panel
var toolbarButtonShowDockingPanel = new Autodesk.Viewing.UI.Button('showMyAwesomePanel');
toolbarButtonShowDockingPanel.onClick = function (e) {
// if null, create it
if (panel == null) {
panel = new MyAwesomePanel(viewer, viewer.container,
'awesomeExtensionPanel', 'My Awesome Extension');
}
// show/hide docking panel
panel.setVisible(!panel.isVisible());
};
// myAwesomeToolbarButton CSS class should be defined on your .css file
// you may include icons, below is a sample class:
/*
.myAwesomeToolbarButton {
background-image: url(/img/myAwesomeIcon.png);
background-size: 24px;
background-repeat: no-repeat;
background-position: center;
}*/
toolbarButtonShowDockingPanel.addClass('myAwesomeToolbarButton');
toolbarButtonShowDockingPanel.setToolTip('My Awesome extension');
// SubToolbar
this.subToolbar = new Autodesk.Viewing.UI.ControlGroup('MyAwesomeAppToolbar');
this.subToolbar.addControl(toolbarButtonShowDockingPanel);
viewer.toolbar.addControl(this.subToolbar);
};
MyAwesomeExtension.prototype.unload = function () {
this.viewer.toolbar.removeControl(this.subToolbar);
return true;
};
Autodesk.Viewing.theExtensionManager.registerExtension('MyAwesomeExtension', MyAwesomeExtension);
MyAwesomePanel:
// *******************************************
// My Awesome (Docking) Panel
// *******************************************
function MyAwesomePanel(viewer, container, id, title, options) {
this.viewer = viewer;
Autodesk.Viewing.UI.DockingPanel.call(this, container, id, title, options);
// the style of the docking panel
// use this built-in style to support Themes on Viewer 4+
this.container.classList.add('docking-panel-container-solid-color-a');
this.container.style.top = "10px";
this.container.style.left = "10px";
this.container.style.width = "auto";
this.container.style.height = "auto";
this.container.style.resize = "auto";
// this is where we should place the content of our panel
var div = document.createElement('div');
div.style.margin = '20px';
div.innerText = "My content here";
this.container.appendChild(div);
// and may also append child elements...
}
MyAwesomePanel.prototype = Object.create(Autodesk.Viewing.UI.DockingPanel.prototype);
MyAwesomePanel.prototype.constructor = MyAwesomePanel;
Yes, you are missing the CSS for the Buttons and also the reference to the JS files pertaining to the extensions in your HTML file.
<script src="your_folder/MyExtensionFileName.js"></script>
http://learnforge.autodesk.io/#/viewer/extensions/selection?id=toolbar-css
Check this for the CSS of your extension buttons.
I am trying out w3.css for styling, along with knockout, and when I use a footer, it covers the content near the bottom of the page.
I have a button at the bottom of my content. When the page resizes or is small enough, the footer covers the button.
See codepen, or the code below
function setting(color) {
this.color = ko.observable(color);
this.colorClassName = ko.computed(function() {
return "w3-hover-" + this.color();
}, this);
}
function myInput() {
this.data = ko.observable("");
this.nameValid = ko.computed(function() {
return !(this.data() == null || this.data().length == 0);
}, this);
this.error = ko.computed(function() {
//if (this.data() == null || this.data().length == 0)
if (this.nameValid() == false) {
return "Enter name";
} else {
return "";
}
}, this);
this.display = ko.computed(function() {
if (this.nameValid() == false) {
return "block";
} else {
return "none";
}
}, this);
this.ageData = ko.observable();
this.ageValid = ko.computed(function() {
var age = this.ageData() + "";
var patt = new RegExp(/^[0-9]+$/g); /// ^-from start, $-to end, [0-9] - 0 to 9 numbers only
var res = patt.test(age);
if (isNaN(age) == true || res == false) {
return false;
} else {
return true;
}
}, this);
this.ageError = ko.computed(function() {
if (this.ageValid() == false) {
return "Enter a valid age";
} else {
return "";
}
}, this);
this.ageDisplay = ko.computed(function() {
if (this.ageValid() == true) {
return "none";
} else {
return "block";
}
}, this);
this.phone = ko.observable('http://digitalbush.com/projects/masked-input-plugin/');
this.allValid = ko.computed(function() {
return this.ageValid() && this.nameValid();
}, this);
}
function myModel() {
this.name = "Ice-Cream";
this.items = [{
name: "Chocolate",
price: 10
}, {
name: "Vanilla",
price: 12
}];
this.style = new setting('pale-green');
this.input = new myInput();
this.changeColor = function() {
if (this.style.color().indexOf('blue') == -1) {
this.style.color('pale-blue');
} else {
this.style.color('pale-green');
}
};
}
ko.applyBindings(new myModel());
<script type='text/javascript' src='http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js'></script>
<link href="http://www.w3schools.com/lib/w3.css" rel="stylesheet" type="text/css">
<body class="w3-content w3-pale-blue" style="max-width:100%">
<header class="w3-container w3-pale-green w3-border">
<h1>Hello</h1>
</header>
<div class="w3-container w3-pale-yellow w3-border w3-padding-hor-16 w3-content" style="max-width:100%">
W3.CSS
<p>
The item is <span data-bind="text: name"></span> today.
<br />Available flavours are:
</p>
<div class="w3-container">
<ul data-bind="foreach: items" class="w3-ul w3-left w3-border w3-border-red">
<li class="w3-ul w3-hoverable w3-border-red " data-bind="css: $parent.style.colorClassName()">
<span data-bind="text: name"></span> is $<span data-bind="text:price" />
</li>
</ul>
</div>
<label class="w3-label w3-text-blue w3-xlarge">Name</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.data">
<label class="w3-label w3-text-red w3-large" data-bind="text: input.error(), style: { display: input.display()}"></label>
<br />
<label class="w3-label w3-text-blue w3-xlarge">Age</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.ageData">
<label class="w3-label w3-text-red w3-large" data-bind="text: input.ageError(), style: { display: input.ageDisplay()}"></label>
<br />
<label class="w3-label w3-text-blue w3-xlarge">Phone</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.phone">
<!--<label class="w3-label w3-text-red w3-large" data-bind="text: input.phoneError(), style: { display: input.phoneDisplay()}"></label>-->
<br />
<button class="w3-btn w3-border w3-border-teal w3-round w3-teal" data-bind="click: changeColor, enable: input.allValid()">Test</button>
</div>
<footer class="w3-light-grey w3-bottom">
<div class="w3-container">
<p>This is my footer</p>
</div>
</footer>
My solution was to add another div element with the same content as my footer, but make it invisible. This way it will fill the space behind the real footer.
In the code i above i will add the following
<div class="w3-container" style="opacity:0">
<p>This is my footer</p>
</div>
The updated codepen shows the solution.
The elements .w3-top, .w3-bottom have a position of :fixed, so they're always going to stick to the page.
Remove this from their stylesheet or add an alternative to your own.
E.g.
.w3-bottom {
position: static;
}
The class of your footer is w3-bottom, and by defaut, its position is fixed, so you need to change it to relative:
.w3-bottom {
position: relative !important;
}
Another way could be to place the "button" in its own "div" to better control its attributes, remove the "div" from the footer and the bottom class. Something like:
<div class="w3-container w3-padding-bottom-32">
<button class="w3-btn w3-border w3-border-teal w3-round w3-teal" data-bind="click: changeColor, enable: input.allValid()">Test</button>
</div>
<footer class="w3-container w3-light-grey">
<p>This is my footer</p>
</footer>
Please let me know if it works for you.
Kindly, Edwin
Try to use the styles from Sticky footer example from the bootstrap.
But this method have one disadvantage: footer have fixed height :(
Example:
/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 80px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
overflow: hidden;
/* Set the fixed height of the footer here */
height: 80px;
background-color: #f5f5f5;
}
<!DOCTYPE html>
<html>
<title>W3.CSS</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://www.w3schools.com/lib/w3.css">
<body>
<div class="w3-container w3-teal">
<h1>Header</h1>
</div>
<div class="w3-container">
<p>The w3-container class can be used to display headers.</p>
</div>
<div class="footer w3-container w3-teal">
<h5>Footer</h5>
<p>Footer information goes here</p>
</div>
</body>
</html>
I am working on my first ionic/angularjs app and have hit a snag. I can get the page to load like I want with a tabbed navigation and side menu but the content in the side menu won't show for some reason. It is in the page, I can see it in view source but it will not show no matter what manipulation I try to the styling. Being new to this I am at a loss for what to do now. Below is the code.
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<script src="js/user-object.js"></script>
<script src="js/news-feed-object.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<!-- your controllers js -->
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
</head>
<body ng-app="wgn">
<ion-nav-bar class="bar-energized nav-title-slide-ios7 ">
<div ng-controller="MenuCtrl">
<ion-side-menus>
<fade-bar></fade-bar>
<ion-pane ion-side-menu-content>
<header class="bar bar-header bar-energized nav-title-slide-ios7">
<button class="button button-icon" ng-click="openLeft()"><i class="icon ion-navicon"></i>
</button>
<h1 class="title">Test App</h1>
</header>
<ion-content has-header="true" padding="true">
<!-- Center content -->
</ion-content>
</ion-pane>
<ion-side-menu side="left">
<header class="bar bar-header bar-dark" fade-header>
<h1>Left</h1>
</header>
<ion-content has-header="true">
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
</ion-content>
</ion-side-menu>
</ion-side-menus>
</div>
<ion-nav-back-button class="button-icon ion-arrow-left-c ">
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view animation="slide-left-t">
</ion-nav-view>
</body>
</html>
tabs.html
<ion-tabs class="tabs-icon-top tabs-energized">
<ion-tab title="Home" icon="ion-home" href="#/tab/events">
<ion-nav-view name="home-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="About" icon="ion-ios7-information" href="#/tab/about">
<ion-nav-view name="about-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="Sign-Out" icon="ion-log-out" href="#/sign-in">
</ion-tab>
</ion-tabs>
app.js
// Ionic Starter App
// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
angular.module('wgn', ['ionic', 'wgn.controllers', 'wgn.services'])
.run(function ($ionicPlatform) {
$ionicPlatform.ready(function () {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider.state('signin', {
url: "/sign-in",
templateUrl: "templates/sign-in.html",
controller: 'SignInCtrl'
}).state('forgotpassword', {
url: "/forgot-password",
templateUrl: "templates/forgot-password.html"
}).state('slidemenu', {
url: "/slide-menu.html",
abstract: true,
templateUrl: 'templates/slide-menu.html'
}).state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
}).state('tabs.home', {
url: "/home",
views: {
'home-tab': {
templateUrl: "templates/home.html",
controller: 'HomeTabCtrl'
}
}
}).state('tabs.eventdetails', {
url: "/eventdetails/:eventid",
views: {
'home-tab': {
templateUrl: "templates/event-details.html",
controller: 'EventDetailsCtrl'
}
}
}).state('tabs.addevents', {
url: "/addevents",
views: {
'home-tab': {
templateUrl: "templates/addevents.html"
}
}
}).state('tabs.about', {
url: "/about",
views: {
'about-tab': {
templateUrl: "templates/about.html"
}
}
}).state('tabs.navstack', {
url: "/navstack",
views: {
'about-tab': {
templateUrl: "templates/nav-stack.html"
}
}
}).state('tabs.contact', {
url: "/contact",
views: {
'contact-tab': {
templateUrl: "templates/contact.html"
}
}
});
$urlRouterProvider.otherwise("/sign-in");
});
controller.js
angular.module('wgn.controllers', [])
.controller('SignInCtrl', function ($scope, $state) {
$scope.signIn = function (user) {
console.log('Sign-In', user);
$state.go('tabs.home');
};
}).controller('HomeTabCtrl', function ($scope, WGNEvents, $ionicModal) {
console.log('HomeTabCtrl');
$scope.wgnEvents = WGNEvents.all();
$scope.user = userObject;
$scope.newsFeedList = newsFeedObjectList;
//Add Event
$ionicModal.fromTemplateUrl('templates/addevents.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function (modal) {
$scope.modal = modal;
});
$scope.addEvent = function () {
$scope.modal.show();
console.log('show modal');
};
$scope.closeModal = function () {
$scope.modal.hide();
};
//Cleanup the modal when we're done with it!
$scope.$on('$destroy', function () {
$scope.modal.remove();
});
// Execute action on hide modal
$scope.$on('modal.hidden', function () {
// Execute action
});
// Execute action on remove modal
$scope.$on('modal.removed', function () {
// Execute action
});
}).controller('EventDetailsCtrl', function ($scope, WGNEvents, $stateParams) {
console.log('EventDetailsCtrl');
var eventID = $stateParams.eventid;
console.log('event-id: ' + eventID);
$scope.wgnEvents = WGNEvents.get(eventID);
console.log($scope.wgnEvents);
}).controller('HomePageCtrl', function ($scope, WGNEvents, $ionicModal) {
console.log('HomePageCtrl');
$scope.getClass = function (score, par) {
return {
'below-par': (par - score) > 0,
'above-par': (par - score) < 0,
'text-dark': (par - score) == 0
};
}
$scope.getOverUnder = function (score, par) {
var total = score - par;
if (total === 0) {
total = 'E';
} else if (total > 0) {
total = '+' + total;
}
return total;
}
}).controller('MenuCtrl', function($scope) {
// Our controller
})
// The fadeBar directive
.directive('fadeBar', function($timeout) {
return {
restrict: 'E',
template: '<div class="fade-bar"></div>',
replace: true,
link: function($scope, $element, $attr) {
// Run in the next scope digest
$timeout(function() {
// Watch for changes to the openRatio which is a value between 0 and 1 that says how "open" the side menu is
$scope.$watch('sideMenuController.getOpenRatio()', function(ratio) {
// Set the transparency of the fade bar
$element[0].style.opacity = Math.abs(ratio);
});
});
}
}
});
Side Menus should not be in your ionNavBar container. I've got an example here showing how to use SideMenu, Tabs, and Navigation elements together. You're close, its just some things don't really work well when they are placed in the wrong place.
http://codepen.io/gnomeontherun/pen/EfKgJ
Markup
<ion-side-menus>
<ion-pane ion-side-menu-content>
<ion-nav-bar class="bar-positive nav-title-slide-ios7">
<ion-nav-back-button class="button-icon"><span class="icon ion-ios7-arrow-left"></span></ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</ion-pane>
<ion-side-menu side="left">
<ion-header-bar class="bar bar-header bar-dark"></ion-header-bar>
<ion-content has-header="true">
<ion-list>
<ion-item href="#/" ng-click="sideMenuController.toggleLeft()">Home</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
<ion-side-menu side="right" >
<ion-header-bar class="bar bar-header bar-dark" title="Search"></ion-header-bar>
<ion-content has-header="true">
</ion-content>
</ion-side-menu>
</ion-side-menus>
<script id="home.html" type="text/ng-template">
<ion-view title="Home">
<ion-nav-buttons side="right">
<button class="button button-icon button-clear ion-plus" ng-click="openModal()"></button>
</ion-nav-buttons>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
</ion-nav-buttons>
<ion-tabs class="tabs-positive">
<ion-tab title="Stooges">
<h4>The Stooges</h4>
<ion-list>
<ion-item ng-repeat="stooge in stooges" href="#/{{stooge.name}}">{{stooge.name}}</ion-item>
</ion-list>
</ion-tab>
<ion-tab title="Tab 2">
<h2>Just another tab, for another reason</h2>
</ion-tab>
</ion-tabs>
</ion-view>
</script>
<script id="modal.html" type="text/ng-template">
<div class="modal">
<ion-header-bar class="bar bar-header bar-positive">
<h1 class="title">New Stooge</h1>
<button class="button button-clear button-primary" ng-click="modal.hide()">Cancel</button>
</ion-header-bar>
<ion-content>
<div class="padding">
<div class="list">
<label class="item item-input">
<span class="input-label">Name</span>
<input ng-model="form.name" type="text" name="name" />
</label>
<button class="button button-full button-positive" ng-click="addStooge()">Create</button>
</div>
</div>
</ion-content>
</div>
</script>
<script id="item.html" type="text/ng-template">
<ion-view title="{{item}}">
<ion-content>
<h1>{{item}}</h1>
</ion-content>
</ion-view>
</script>
JavaScript
angular.module('ionicApp', ['ionic'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
controller: 'HomeCtrl',
templateUrl: 'home.html'
})
.state('item', {
url: '/:item',
controller: 'ItemCtrl',
templateUrl: 'item.html'
});
$urlRouterProvider.otherwise('/');
})
.controller('HomeCtrl', function($scope, $ionicSideMenuDelegate, $ionicModal) {
$scope.stooges = [{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}];
$ionicModal.fromTemplateUrl('modal.html', {
animation: 'slide-in-up',
scope: $scope
}).then(function (modal) {
$scope.modal = modal;
});
$scope.openMenu = function () {
$ionicSideMenuDelegate.toggleLeft();
}
$scope.openModal = function () {
$scope.modal.show();
}
$scope.form = {};
$scope.addStooge = function () {
console.log($scope);
$scope.stooges.push({name: $scope.form.name});
$scope.modal.hide();
};
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
})
.controller('ItemCtrl', function ($scope, $stateParams) {
$scope.item = $stateParams.item;
});