Ember switching between templates in {output} - html

I'm building my first ember app which has CRUD properties. Part of my index.html code looks like this-
<script type = "text/x-handlebars" id = "settings">
<table>
{{#each setting in controller}}
<tr>
<td>
<h2>{{setting.name}}</h2>
</td>
</tr>
<tr>
<td>{{#link-to "setting" setting}}More Details..{{/link-to}}</td>
</tr>
{{/each}}
</table>
{{outlet}}
</script>
<script type = "text/x-handlebars" id = "setting">
{{#if deleteMode}}
<div class="confirm-box">
<h4>Really?</h4>
<button {{action "confirmDelete"}}> yes </button>
<button {{action "cancelDelete"}}> no </button>
</div>
{{/if}}
<div class="col-xs-6">
<h4>{{name}}</h4>
<h4>{{address_line1}}</h4>
<h4>{{address_line2}}</h4>
<h4>{{address_city}}</h4>
<h4>{{address_state}}</h4>
<h4>{{address_zip}}</h4>
<h4>{{address_country}}</h4>
<button {{action "edit"}}>Edit</button>
</div>
<div class = "col-xs-6">
{{outlet}}
</div>
</script>
<script type = "text/x-handlebars" id = "setting/edit">
<div class="input-group">
<h5>Name:</h5>
{{input value=name}}
<h5>Address:</h5>
{{input value=address_line1}}
{{input value=address_line2}}
<h5>City:</h5>
{{input value=address_city}}
<h5>State:</h5>
{{input value=address_state}}
<h5>ZIP:</h5>
{{input value=address_zip}}
<h5>Country:</h5>
{{input value=address_country}}
<button {{action "save"}}> Save </button>
</div>
</script>
When I click on the edit button in the setting template, the setting/edit template with an edit form opens in the {outlet}of the setting template. I'm wondering how I would go about getting the setting/edit template to replace the setting template in the settings template's {outlet} so that they don't appear on the same page? New to ember dev and somewhat confused, any help appreciated.

Ember always routes to leaf route. when you are on settingRoute(means only setting template is rendered) in reality your leaf route is settingIndexRoute and in {outlet} of setting route setting/index template is rendered,which is generated by ember when it found out that you have not specified index template. so when you click on edit, setting/edit will replace setting/index template. so I would suggest that you move your setting template's content in setting/index.

Related

Why isn't HTML working in Bootstrap v5 Popover?

When I add HTML into my Bootstrap v5 popover, it does not display the HTML in the popover. I believe that I have all of the correct options passed as described in the docs. Almost all of the results which I've gotten from searches to try and understand this issue deal with Bootstrap v4 and v3, so they aren't of use to me.
The popover all of the way on the right should have the switch affordance ("Toggle") appear below "Hello World" in the popover. The HTML code is present.
The code of the above tests centers around these 3 affordances:
<!-- Popover Works -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="Title" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World"></span>
<!-- HTML Switch Works -->
<div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch1'><label class='form-check-label' for='customSwitch1'>Toggle</label></div>
<!-- Popover with HTML Switch Does Not Work -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="Title" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World<br><div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch2'><label class='form-check-label' for='customSwitch2'>Toggle</label></div>"></span>
Have I missed something? Please let me know. Thank you!
Ok, reading and re-reading the Bootstrap v5 documentation's fine print finally led me to the solution.
Even though data-bs-sanitize="false" is a valid option that can be passed, per the documentation... the documentation also notes elsewhere that:
Note that for security reasons the sanitize, sanitizeFn, and allowList
options cannot be supplied using data attributes.
So... in order for the HTML to execute, because data-bs-sanitize="false" is ignored, one has to manually add non-default values to the Sanitizer whitelist which are used with the HTML code inside the popover. In my case, they were as follows:
var myDefaultAllowList = bootstrap.Tooltip.Default.allowList
// To allow elements
myDefaultAllowList.input = ['type', 'checked']
myDefaultAllowList.label = ['for']
And now... it's working as intended. A lesson in sanitization.
I'm not sure if you initialize the popovers correctly as data-bs-sanitize="false" should allow any html used.
You are adding elements that are not in the bootstrap.Tooltip.Default.allowList object. So to allow the elements and attributes used in your code the following config options will work to render the popover as intended:
$(document).ready(function() {
const myDefaultAllowList = bootstrap.Tooltip.Default.allowList
// To allow elements and attributes on elements
myDefaultAllowList.input = ['type', 'checked']
myDefaultAllowList.label = ['for']
$('[data-bs-toggle="popover"]').popover({
allowList: myDefaultAllowList,
html: true
})
/* or just disable the sanitzer
$('[data-bs-toggle="popover"]').popover({
sanitize: false
})
*/
})
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing2.css">
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing3.css">
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing.css">
<div class="container">
<div class="row" style="padding-top: 15em !important;">
<!-- Body -->
<div class="col-lg-12 content py-4 mb-2 mb-sm-0 pb-sm-1 no-sidebar-padding-override">
<div class="row row-panel-hs">
<div class="row section-container-bottom row-section-hs">
<table class="table keep-table-fixed no-margin-bottom entities-table">
<tbody>
<tr>
<td>Work alone:
</td>
<td>
<!-- Popover Works -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World" data-bs-original-title="Title" aria-label="Title"></span>
</td>
<td>
<!-- HTML Switch Works -->
<div class="form-check form-switch"><input type="checkbox" class="form-check-input" id="customSwitch1"><label class="form-check-label" for="customSwitch1">Toggle</label></div>
</td>
<td>
</td>
<td>
</td>
<td>Fail combined:
</td>
<td>
<!-- Popover with HTML Switch Does Not Work -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World<br><div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch2'><label class='form-check-label' for='customSwitch2'>Toggle</label></div>"
data-bs-original-title="Title" aria-label="Title">...</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
Note:
You need to do more work in order to preserve the checked state of the toggle. As it is, the toggle reverts back to the initial state when closed and re-opened. Fixing this would be a different question.

Controller not recognized by HTML after $state.go()

I'm fairly new to Angular and very new to using the ui-router, so I could be missing something obvious.
If I launch my results.html page on its own, everything from the Web API is bound correctly through the controller and shows up as expected.
When I start from index.html and click on the button that calls $state.go(), I am navigated to the results.html page .. but none of the data from the controller shows up. The controller & service are still being called though - because I put console.log() statements to verify that the object I want is being returned after $state.go() and it is - the template just isn't registering it.
Here is my code for my ui-router and main controller:
// script.js
var app = angular.module('BN', ['ui.router']);
// configure our routes
app.config(function ($locationProvider, $stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('');
$stateProvider
.state('default',{
url:'/',
templateURL:'index.html',
controller: 'MainController'
})
.state('results', {
url:'/results',
controller: 'ResultsController',
controllerAs: 'vm',
templateUrl: 'Results/Results.html'
})
.state('instructor', {
url:'/api/instructors/:id',
templateUrl: 'Instructors/Instructor.html'
});
$locationProvider.html5Mode(true);
});
app.controller('MainController', MainController);
function MainController ($state) {
var vm = this;
vm.Submit=function( ){
$state.go('results');
};
}
So Results.html renders perfectly fine launched on it's own, but when navigated to - the controller is called but not bound to the html template.
You have your index.html file set up properly, but after that you're using ui-router incorrectly.
In your index.html file where you have:
<body ui-view>
this tells ui-router to load the contents of your templates into the body of your document, so your templates should just be snippets of html to be rendered within your body tag. So you should delete everything from results.html until all that remains is this:
<div id="headerBar" >
<div class="form-group" id="headerBar" >
<input class="form-control input-lg" type="text" id="inputlg" placeholder="Zipcode" />
<button id="sub" class="btn btn-default btn-lg" type="submit" ng-click="vm.getInstructors()">Find an Instructor!</button>
</div>
</div>
<table>
<tr ng-repeat="Instructors in vm.instructors">
<td style="padding-top:5px;">
<div>
<nav class="navbar">
<div class="container-fluid">
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" name="searchString" >
</div>
<button type="submit" class="btn btn-default" value="Filter">Search</button>
</form>
</div><!-- /.container-fluid -->
</nav>
</div>
</td>
</tr>
</table>
<div class="col-md-8">
</div>
<aside class="sidebar" ui-view="map">MAP</aside>
A few other things. You have the controller setup in your routes, so you don't need to specify it anywhere in results.html. Also you should not be able to navigate to results.html. Instead you would just navigate to (your domain)/results.

Knockout Clone Whole Item In foreach

I am trying to clone elements when clicking a button. I was trying to use ko.toJS. On page load it works fine, but when I want clone the items, it is unable to bind the items (like, value, Text, etc.).
Here is the HTML:
<div class="stockItems-inner" data-bind="foreach: StockItems">
<div data-bind="if: Type=='Input'">
<div class="stock_container_input">
<input type="text" data-bind="value: Value" />
</div>
</div>
<div data-bind="if: Type=='Radio'">
<div class="stock_container_control">
<div data-bind="foreach: Options">
<div class="stockLbl">
<input type="radio" data-bind="text: Text, checked:$parent.Value, attr:{'id':Id, 'name': $parent.Text, 'value': Value}" />
<label data-bind="attr:{'for':Id}, text: Text"></label>
</div>
</div>
</div>
</div>
</div>
<div class="addItem">
<button type="button" data-bind="click: CloneItem"><img src="images/add.png" alt="" /></button>
</div>
The View Model:
ConfigurationStockViewModel = function() {
var self = this;
this.StockItems = ko.observableArray();
this.ApplyData = function(data){
self.StockItems(data.Items);
}
this.CloneItem = function(StockItems){
self.StockItems.push(ko.toJS(StockItems));
};
};
When clicking the button, an error is thrown: Unable to process binding. I am using JSON data for binding.
Not exactly sure what end result you want without working code, but sounds like you want to clone the last item in array and add to array?
If so, I think you have an error - your add button click binding will never pass anything to the function you defined, since it is outside the foreach. You need something like this:
this.CloneItem = function() {
var toClone = self.StockItems()[self.StockItems().length - 1]
self.StockItems.push(toClone);
};
Here is a simplified example without radio buttons, etc:
http://jsfiddle.net/5J47L/

show a div on a button click using angularjs and HTML

I want to show a div on a button click using angularjs and HTML
I am learning angularJs.
I want to design a webpage in which on click of 'Add' button a div(EmployeeInfoDiv) is shown.
When I fill the textboxes in it and save it the data should reflect in table within the div(EmpDetTable).
Here is the HTML page code:
<body ng-app="MyApp">
<div ng-controller="EmpDetCtrl">
<div ng-model="EmpDetTable" class="container body" ng-hide="addEmp">
<label class="TabHeadings">Employee Details</label>
<div class="EmployeeInfo">
<table ng-model="Employee" border="1">
<thead><tr><th>Name</th><th>Designation</th><th>Salary</th></tr></thead>
<tbody>
<tr ng-repeat="emp in employees">
<td>{{emp.name}}</td>
<td>{{emp.desg}}</td>
</tr>
</tbody>
</table>
<button ng-model="AddEmp" ng-click="ShowAddEmployee()">Add</button>
</div>
</div>
<div ng-model="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;" ng-show="addEmp" >
<label class="TabHeadings" style="width:830px;">Employee Details</label>
<div class="EmployeeInfo">
<table>
<tr><td><label class="table_label">Name:</label></td><td><input type="text" ng-model="name" class="textbox"/></td>
<td><label class="table_label">Designation:</label></td><td><input type="text" ng-model="desg" class="textbox"/></td>
</tr>
</table>
<div ng-model="botton_container">
<button ng-model="save" class="save_buttons" ng-click="SaveData();">Save</button>
<button ng-model="cancel" class="cancel_buttons" ng-click="ShowViewEmployee();">Cancel</button>
</div>
</div>
</div>
</div>
</body>
Controller Code:
function EmpDetCtrl($scope) {
$scope.employees = [
{ name: 'A',desg: 'C'}
];
$scope.addNew = function () {
$scope.employees.push({
name: $scope.name,desg: $scope.desg
});
}
$scope.ShowAddEmployee= function () {
return $scope.EmployeeInfoDiv=true;
}
$scope.ShowViewEmployee=function () {
return $scope.EmployeeInfoDiv = false;
}
}
I don't know whether this code is correct or not.
I just tried it.
Can anyone please help me out.
Thanks in advance.
Your ng-click is updating the EmployeeInfoDiv variable. So, reference that in ng-show and ng-hide:
<div ng-hide="EmployeeInfoDiv" class="container body">
...
<div ng-show="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;">
You do not need ng-model in the div to make that work.
ng-model="EmployeeInfoDiv"
Update
A few more issues. Most important is that (at least in the code posted) MyApp isn't defined anywhere, and your controller is defined on the global scope. So you need to change your html to:
<div ng-app>
<div ng-controller="EmpDetCtrl">
...
However, note that this is not the recommended method. See the angular documentation for controllers.
Here is a working demo: http://jsfiddle.net/wittwerj/xrB77/

Angular JS - Having an empty array

I have a form that submits data into a function called ng-submit="newBirthday() this pushes data - $scope.bdayname, $scope.bdaydate; into an array called bdaes
My issue is that with all of the tutorials I have seen the array has predefined data is there a way that it can be an empty array that gets filled with data when it is submitted?
app.js:
var app = angular.module('birthdayToDo', []);
app.controller('main', function($scope){
// Start as not visible but when button is tapped it will show as true
$scope.visible = false;
// Create the array to hold the list of Birthdays
$scope.bdays = [{}];
// Create the function to push the data into the "bdays" array
$scope.newBirthday = function(){
$scope.bdays.push({name:$scope.bdayname, date:$scope.bdaydate});
$scope.bdayname = '';
$scope.bdaydate = '';
}
});
HTML:
<body ng-app="birthdayToDo" ng-controller="main">
<div id="wrap">
<!-- Begin page content -->
<div class="container">
<div class="page-header">
<h1>Birthday Reminders</h1>
</div>
<ul ng-repeat="bdays in bdays">
<li>{{bdae.name}} | {{bdae.date}}</li>
</ul>
<form ng-show="visible" ng-submit="newBirthday()">
<label>Name:</label>
<input type="text" ng-model="bdayname" placeholder="Name"/>
<label>Date:</label>
<input type="date" ng-model="bdaydate" placeholder="Date"/>
<button class="btn" type="submit">Save</button>
</form>
</div>
<div id="push"></div>
</div>
<div id="footer">
<div class="container">
<a class="btn" ng-click="visible = true"><i class="icon-plus"></i>Add</a>
</div>
</div>
<script type="text/javascript" src="js/cordova-2.5.0.js"></script>
<script type="text/javascript">
app.initialize();
</script>
</body>
Okay, there were a few of small issues.
An empty array must have no items; [{}] is an array with one item: an empty object. Changing to [] gets rid of the extra bullet.
The bigger issue was your ngRepeat. You used ng-repeat="bdays in bdays". What ngRepeat does is take an array and allow you to do a "for each" on the array, assigning each value to that temporary local variable. You named them the same. Instead, ng-repeat="bday in bdays" will add the DOM nodes inside of it for each item in bdays, giving you a local variable called bday to use to reference each item.
Inside the ngRepeat template, you used bdae, which doesn't reference anything.
I don't know what app.initialize() is, so I removed it. It was just erroring out in the console.
Here's a fixed Plunker: http://plnkr.co/edit/OFWY7o?p=preview