I am loading a video in the DOM using a directive named load-video when the displayVideo property is true.
<figure id = "positionVideo" ng-show = "displayVideo">
<load-video></load-video>
</figure>
loadVideo directive:
angular.module('homePage')
.directive('loadVideo', function($document, $window) {
return {
restrict: 'E',
templateUrl: 'partials/video/video.html',
link: function(scope, element) {
element.data('loadVideo',true);
angular.element($document[0].body).on('click',function(e) {
var inElem = angular.element(e.target).inheritedData('loadVideo');
if (inElem) {
scope.displayVideo = true;
} else {
scope.displayVideo = false;
}
})
}
};
});
video.html
<video height = "50%" width = "150%" id = "playVideo" ng-click="playIt()" poster = "images/eagle.jpg" controls>
<source src = "images/lion.mp4" type = "video/mp4">
</video>
The figure tag has access to this controller:
angular.module('homePage').controller('watchVideo', ['$scope', '$location', function($scope, $location) {
$scope.displayVideo = false;
$scope.videoAvailable = function () {
$scope.displayVideo = true;
};
$scope.closeVideo = function() {
$scope.displayVideo = false;
};
$scope.playIt = function() {
if (jQuery("#playVideo").get(0).paused) {
jQuery("#playVideo").get(0).play();
}
else {
jQuery("#playVideo").get(0).pause();
}
}
}]);
I do not understand why the video element is not hiding when I am changing the displayVideo property to false.
I am providing my entire application below for context:
<div class="firstView" ng-controller = "watchVideo">
<figure class="logo" ng-controller = "logo" ng-click="goToUrl('/home')"> </figure>
<cite>Every brand has a story.</cite>
<h2 id = "h2Heading"> <a ng-click = "videoAvailable()">Watch the video </a></h2>
<aside> → </aside>
<figure id = "positionVideo" ng-show = "displayVideo">
<load-video></load-video>
</figure>
<summary ng-controller = "buttonViewCtrl">
<button type="button" ng-hide = "buttonDisplay" ng-show = "!displayVideo" class="btn btn-default btn-lg navButton" aria-label="Center Align" ng-click="nav()">
<span class="glyphicon glyphicon-align-justify" aria-hidden="true"></span>
</button>
<button type="button" class="close" data-dismiss="alert" aria-label="Close" id = "closeButton" ng-show = "buttonDisplay" ng-hide = "!displayVideo" ng-click = "closeNav(); closeVideo()"><span aria-hidden="true">×</span> </button>
<div ng-show = "buttonDisplay" id = "buttonDisplayContent" class = "cssFade">
<navigate></navigate>
</div>
</summary>
<main ng-controller="ScrollCtrl">
<div id = "arrow">
<img src = "images/pointer.png" alt = "arrow" ng-click="gotoElement('panda')">
</div>
</div>
<div class = "panda" id = "panda">
<button type="button" class="btn btn-default btn-lg work"> SEE OUR WORK </button>
</div>
<main>
<div class = "experience">
<h1> Our team builds great brands and experiences. </h1>
<button type="button" class="btn btn-default btn-lg team"> MEET THE TEAM </button>
</div>
<section class = "about">
<h5> WHAT ARE WE? </h5>
<h2> Anchour is a branding agency based in Maine. </h3>
<p> We weave together creative solutions to build personal brands and experiences. We work closely with your brand to understand your needs and create solutions that produce real results. By integrating the power of identity, digital, and sensory design, we bring new life to brands everywhere.
</p>
</section>
<div class = "goodWork">
<div class = "spotlight">
<h3> Spotlight: Amanda Brill of Las Vegas </h3>
<p> Amanda Brill is a Designer at Anchour. Fueled by the purpose of helping others, she works to bring the identity of a brand to life through a creative and intensive design process. to help brands effectively communicate who they she works to bring the identity of a brand to life through a creative... </p>
<footer> Read More </footer>
</div>
<div class = "spotlight">
<h3> Spotlight: Amanda Brill of California </h3>
<p> Amanda Brill is a Designer at Anchour. Fueled by the purpose of helping others, she works to bring the identity of a brand to life through a creative and intensive design process. Her goal is to use design as a way to help brands effectively communicate who they sponsor and supporter Fueled by the purpose of.. </p>
<footer> Read More </footer>
</div>
<div class = "spotlight">
<h3> Varney Agency: Protecting What You </h3>
<p> Anchour produced Varney Agencys latest spot featuring Matt Albrecht, owner of River Drive. Working with companies from all around the world, River Drive buys, sells, reconditions and recycles reconditions and ecycles owner of used. River Drive buys, sells Varney Agencys latest spot featuring Matt Albrecht.</p>
<footer> Read More </footer>
</div>
<div class = "spotlight">
<h3> Announcing support of Not Here </h3>
<p> This week is Human Trafficking Awareness Week, so it’s great timing to share how proud we are to be a sponsor and supporter of Not Here latest spot featuring Matt Albrecht, reconditions and recycles owner of River Drive. Working with companies from all around the,a River Drive buys, sells.... </p>
<footer> Read More </footer>
</div>
</div>
<div class = "start">
<h2 id = "startWork"> Want to work with us? </h2>
<button type="button" class="btn btn-default btn-lg"> Get Started → </button>
</div>
<div id = "end">
<footer>
Home
About us
Our Team
Blog
Services
Portfolio
Contact
Sitemap
</footer>
</div>
</div>
The video is not hiding because Angular doesn't know that the model has changed. In fact, element.on() is just the plain old jQuery on function, it isn't Angular-aware.
You may have noticed that the video disappears when you click outside then inside it : that's because the ng-click handler, which gets fired first being the innermost listener, triggers the digest phase for you, causing ng-hide to update the view.
The proper way to handle this is $scope.$apply() :
var body = angular.element($document[0].body);
body.on('click',function(e) {
scope.$apply(function() {
var inElem = angular.element(e.target).inheritedData('loadVideo');
console.log(inElem);
if (inElem) {
scope.displayVideo = true;
} else {
scope.displayVideo = false;
jQuery("#playVideo").get(0).pause();
}
});
});
See this fiddle.
Related
I'm making a custom element for my website. I made 2 elements, the 2nd one works however the first one doesn't. Here are the 2 elements:
class Popup extends HTMLElement {
connectedCallback() {
this.dataset.rating = 1; /*document.querySelector("rating-inter").dataset.rating*/
console.log("Debug: tenplate");
this.template = `
<!-- Thank you state start -->
<p class="typog">
You selected ${this.dataset.rating} out of 5
</p>
<h1>
Thank you!
</h1>
<p>
We appreciate you taking the time to give a rating. If you ever need more support, don’t hesitate to get in touch!
<!-- Thank you state end -->
</p>
`
this.ele = document.createElement("div")
this.ele.innerHTML = this.ele
this.appendChild(this.ele)
}
}
class Rating extends HTMLElement {
connectedCallback() {
this.template = `
<div class="star">
<img class="star" src="images/icon-star.svg" alt="Star">
</div>
<!-- Rating state start -->
<h1>
How did we do?
</h1>
<p class="typog">
Please let us know how we did with your support request. All feedback is appreciated
to help us improve our offering!
</p>
<div class="btns">
<button class="numbers">1</button> <button class="numbers">2</button>
<button class="numbers">3</button> <button class="numbers">4</button>
<button class="numbers">5</button>
</div>
<div id="submit">
<button id="submitBtn">Submit</button>
</div>
<!-- Rating state end -->
`
this.ele = document.createElement("div")
this.ele.innerHTML = this.ele
this.appendChild(this.ele)
this.dataset.rating = null
}
}
The first one simply gives me a blank view, with no error message. I can't tell why this is happening. No console.log() inside of the connectedCallback work
I have not much experience when it comes to use AngularJS, but I am not a newbie. Worked with it already a little bit.
However, I am using AngularJS with Zurb Foundation 6.
The Controller in my reveal Modal will not use any angular.
For example I can write {{hallo}} and he will not render it, he will show me {{hallo}} and not the value of this variable.
You have to know, that this modal is in a template file for angular, because I am using angular-route.
The strange thing is, that I am already using a modal in the index.html file and there angular in the modal works. But the modal in the template file (called dashboard.html) is not working.
That's my modal:
<div class="row">
<div class="columns large-12 medium-12 small-12" ng-controller="LoadAllUsers">
<button class="button" data-open="newUserModal" ng-click="load()">User hinzufügen</button>
<!-- newUserModal -->
<div class="tiny reveal" id="newUserModal" data-reveal>
<div class="large-12 medium-12 small-12 columns">
<div class="row">
<h4 class="text-center">User zur Miete hinzufügen</h4>
<br>
<label>Username oder Name des neuen Users eingeben</label>
<input type="text" placeholder="Max Mustermann" name="newuser" ng-model="userSearch" ng-change="search()">
<h5>Suchtreffer</h5>{{hallo}}
<button class="button" data-close>Abbrechen</button>
<button class="button float-right">User hinzufügen</button>
</div>
</div>
<button class="close-button" data-close aria-label="Close modal" type="button">
<span aria-hidden="true">×</span>
</button>
</div>
<!-- /newUserModal -->
<button class="button">Miete zurückziehen</button>
</div>
</div>
</div>
My controller looks like this
routingApp.controller("LoadAllUsers", function($scope, $http) {
$scope.load = function() {
var allUsers = [];
$http({
method: 'GET',
url: 'someURL',
params: {parameters: "values"}
}).then(function successCallback(response) {
angular.forEach(response.data, function(value, key) {
allUsers[key] = {};
angular.forEach(value, function(subvalue, subkey) {
allUsers[key][subkey] = subvalue;
});
});
$scope.hallo = "AN";
console.log(allUsers);
}, function errorCallback(error) {
console.log(JSON.stringify(error.data));
});
};
$scope.search = function() {
console.log($scope.userSearch);
};
$scope.hallo = "HAAAAAAAAAAALLO";
});
I would appreciate, if anyone can help me.
Thanks
I found the solution: I hat to user Angular Foundation 6. What you have to know is that it is now currently under work, so it may be not as stable as Angular Foundation 5.
However, it helped me solving my problem.
I am trying to create a directive for two views. basically we have a modal, with header and footer. When we click on continue button in footer, the modal body should be alone changed displaying question 2 as a new view with header and footer fixed. How would i achieve this using directives?
Do i need to create individual directives for each view?
Here is my code
modal.html -
<section>
<header class="modal-header">
</header>
<div class="modal-body">
question 1
</div>
<footer>
<button>{{'BACK' | translate}}</button>
<button type="submit" ng-click="showQuestion(2)">{{'CONTINUE' | translate}}</button>
</footer>
</section>
directive.js
'use strict';
angular.module('OrderApp').directive('modalQuestions', [function () {
function getQuestions() {
return {
restrict: 'A',
templateUrl: 'modules/common/modals/modal.html',
scope: true
};
}
]);
It sounds like you may want to use the ngSwitch directive:
<section>
<header class="modal-header">
</header>
<div class="modal-body" ng-switch="questionNumber">
<div ng-switch-when="1">
question 1
</div>
<div ng-switch-when="2">
question 2
</div>
</div>
<footer>
<button>{{'BACK' | translate}}</button>
<button type="submit" ng-click="showQuestion(2)">{{'CONTINUE' | translate}}</button>
</footer>
</section>
Then on your controller:
$scope.questionNumber = 1;
$scope.showQuestion = function (questionNumber) {
$scope.questionNumber = questionNumber;
}
When $scope.questionNumber is updated, ngSwitch will change which of the divs it shows. Everything else on the page will remain the same.
So the situation is as follows:
I have an input bar where a user can search up a business name or add a person's name (and button to select either choice). Upon hitting enter I want to append a unique instance of a template (with the information entered by the user added). I have 2 templates I've created depending of if the user is searching for a business or a person.
One approach I've thought about is creating an object with the data and adding it with ng-repeat, however I can't seem to get the data loaded, and even then don't know how I can store reference to a template in my collection.
The other idea I've come across is adding a custom directive. But even then I've yet to see an example where someone keeps appending a new instance of a template with different data.
Here is the code so far:
payments.js:
angular.module('payment-App.payments',['ngAutocomplete'])
.controller('paymentController', function($scope, $templateRequest/*, $cookieStore*/) {
$scope.businessID;
$scope.address;
$scope.isBusiness = false;
$scope.payees = [];
$scope.newPayee = function () {
this.businessID = $scope.businessID;
this.address = $scope.address;
}
$scope.submit = function () {
var text = document.getElementById("businessID").value.split(",");
$scope.businessID = text[0];
$scope.address = text.slice(1).join("");
$scope.newPayee();
}
$scope.addPayee = function () {
$scope.submit();
$scope.payees.push(new $scope.newPayee());
console.log($scope.payees);
}
$scope.selectBusiness = function () {
//turns on autocomplete;
$scope.isBusiness = true;
}
$scope.selectPerson = function () {
//turns off autocomplete
$scope.isBusiness = false;
}
$scope.fillAddress = function () {
// body...
}
})
.directive("topbar", function(){
return {
restrict: "A",
templateUrl: 'templates/businessTemplate.html',
replace: true,
transclude: false,
scope: {
businessID: '=topbar'
}
}
})
payments.html
<h1>Payments</h1>
<section ng-controller="paymentController">
<div>
<div class="ui center aligned grid">
<div class="ui buttons">
<button class="ui button" ng-click="selectBusiness()">Business</button>
<button class="ui button arrow" ng-click="selectPerson()">Person</button>
</div>
<div class="ui input" ng-keypress="submit()">
<input id="businessID" type="text" ng-autocomplete ng-model="autocomplete">
</div>
<div class="submit">
<button class="ui button" id="submit" ng-click="addPayee()">
<i class="arrow right icon"></i>
</button>
</div>
</div>
<div class="search"></div>
<div class="payments" ng-controller="paymentController">
<li ng-repeat="newPayee in payees">{{payees}}</li>
</div>
<!-- <topbar></topbar> -->
</div>
(example template)
businessTemplate.html:
<div class="Business">
<div class="BusinessName" id="name">{{businessID}}</div>
<div class="Address" id="address">{{address}}</div>
<button class="ui icon button" id="hoverbox">
<i class="dollar icon"></i>
</button>
</div>
I ended up using a workaround with ng-repeat here. Still curious about the original question though.
I'm stuck on an older WebForms project and I'd like to know if there's a recommended approach for my scenario.
Goal
I have a feedback form in a modal dialog that I bound up using KnockoutJS.
I would like the feedback form to be available on all pages, via a link in the footer of the site.
I would like to have several other pages using knockout as well with their own individual scripts & bindings, irrespective of the feedback form bindings in the modal.
I have some pages that do not use knockout at all. I would like them not to have to insert code to accomplish this.
I would like to avoid global variables, if possible, in favor of namespaced JavaScript.
In essence, I would like for the viewmodels on the page and the feedback viewmodel not to be aware of each others' existence.
Current Setup
Our footer links are in a Site.master file, and so that's where I've placed the Feedback.js script and the div for the modal which has the bindings. So on the master page, I call ko.applyBindings(vm, referenceToFeedbackDiv), which works fine to wire up the feedback form.
Our individual pages occasionally have a knockout viewmodel, and so they may call ko.applyBindings(vm), since to their knowledge they'd like to apply the vm to their entire page.
Problem
This causes a conflict in knockout because one vm is being applied to the feedback form via the Site.master call, and one vm is being applied to the entire body by the page after it.
Question
How can I enable these two things -- a modal dialog across all pages that uses knockout, and individual knockout pages -- to work in harmony?
Demonstration of the Issue in (the Current) Code
Remember, the issue is that I want to be able to have one feedback VM that applies only to the feedback div across the client site, and I want to have other VMs able to be applied that aren't required to know anything about the feedback vm.
Master Page file (Site.Master) -- Excerpt
This is on every page:
<div class="page">
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
<div class="footer">
© <%=DateTime.Now.Year.ToString() %> Company, Inc. | Home | About |
<!-- begin feedback area -->
<span id="FeedbackArea">
<a data-bind="click: showModal">Feedback</a>
<div id="feedback-modal" title="What's on your mind?">
<div class="btn-group" id="feedbackButtonGroup">
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-warning fa-2x fa-align-center"></i>
<br />
<span>Problem</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-question-circle fa-2x fa-align-center"></i>
<br />
<span>Question</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-lightbulb-o fa-2x fa-align-center"></i>
<br />
<span>Suggestion</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-thumbs-o-up fa-2x fa-align-center"></i>
<br />
<span>Praise</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-info-circle fa-2x fa-align-center"></i>
<br />
<span>General</span>
</button>
</div>
<br />
<br />
<textarea rows="5" placeholder="Enter feedback here" data-bind="value: feedbackText, valueUpdate: 'afterkeydown'"></textarea>
<br />
<br />
<button>Send Feedback</button>
<button data-bind="click: CancelFeedback">Cancel</button>
<h3>Other Information: </h3>
<ul>
<li><strong>Feedback Type:</strong> <span data-bind="text: feedbackType"></span></li>
<li><strong>Current URL:</strong> <span data-bind="text: pageUserIsOn"></span></li>
<li><strong>Current User: </strong><%=hdnLoggedInUsername.Value %></li>
<li><strong>Current Client: </strong>[Not yet captured]</li>
<li><strong>Current Tab: </strong>[Not yet captured]</li>
</ul>
</div>
</span>
<!-- End feedback area -->
</div>
Feedback.JS -- This is also included in every page
...a somewhat-namespaced definition of a FeedbackVM:
var FeedbackNamespace = FeedbackNamespace || {};
..the definition of the namespace itself:
FeedbackNamespace = {
ViewModel: function () {
// etc. etc.
}
};
...and the declaration of a VM variable plus wiring it up on document.ready():
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#FeedbackArea')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
Other Pages without Knockout / JS
Other pages may or may not have any javascript on them at all, let alone knockout. On these pages, the FeedbackVM currently works fine.
Pages with their own Knockout ViewModel
These pages would have their own namespaced JS file with their own document.ready() event, that creates a vm of say invoiceUploaderVM = new InvoiceUploader.ViewModel(), and then calls ko.applyBindings(invoiceUploaderVM).
This is where we run into trouble.
Update: One potential Approach and a little trouble
In the Site.master page, I wrapped my entire footer in a "stopBindings: true" div:
<div data-bind="stopBindings: true">
<div class="footer" id="footerDiv">
<!-- Feedback Viewmodel stuff in here -->
</div>
</div>
I've defined stopBindings as:
ko.bindingHandlers.stopBindings = {
init: function () {
return { controlsDescendantBindings: true };
}
};
My Feedback.js file, loaded on every page as part of a global JS file, has:
var FeedbackNamespace = FeedbackNamespace || {};
FeedbackNamespace = {
// defines viewmodel, etc. etc.
};
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#footerDiv')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
This approach works perfectly well -- as long as there are no other viewmodels being bound. On the pages that inherit from my master page, I might have something like:
$(document).ready(function () {
'use strict';
vm = new invoiceUploader.ViewModel();
ko.applyBindings(vm);
});
I would expect that this:
Sets up the feedback viewmodel applied to the div, stopping other viewmodels
Sets up the invoiceUploader viewmodel and applies it to the body (which is then stopped by the stopBindings div)
However, instead I get an error upon loading the child page along the lines of:
Commenting the line to apply the feedback bindings makes this work just fine again.
What am I doing wrong?
I think I would put the view model for your modal in a global object and do whatever you need to do with it aside from applying the bindings in a shared script:
window.feedbackModal = {
foo: ko.observable("Whatever you need to do here"),
bar: ko.observable("assuming it can be done the same on every page")
};
Then in the Site.master
<div class="feedback-modal" data-bind="with: feedbackModal">
<p data-bind="text: foo"></p>
<p data-bind="text: bar"></p>
</div>
And in every individual page's script:
function ViewModel() {
this.individualProperty = ko.observable(true);
this.specificAction = function() { /* do something specific to this page */ };
this.feedbackModal = window.feedbackModal;
}
ko.applyBindings(new ViewModel());
So window.feedbackModal could be undefined and it won't cause you problems, but if you ko.applyBindings, you have to have a feedbackModal property exposed in the view model or you'll get errors applying those bindings.
Of course, there are more clever ways you could implement this basic idea in order to fit your patterns the best, but the big point is, as you know, you can't apply bindings twice, so you need to defer that task to your most specific code and expose your reusable code to to it.
Here is another strategy for separation of common modules from page dependant modules:
// An example of a module that runs on everypage
var modalDialog = function(){
this.name = "dialog1";
this.title = ko.observable("My Modal Title");
this.content = ko.observable("My Modal content is also something");
}
// An example of a module that runs on everypage
var modalDialog2 = function(){
this.name = "dialog2";
this.title = ko.observable("My Modal Title 2");
this.content = ko.observable("My Modal content is also something 2");
}
// Either generate it automatically or by hand
// to represent which modules are common
var commonModules = [modalDialog, modalDialog2];
// An example of a module only for this page
var pageModule = function(){
this.pageFunction = function(){
alert("Called page function");
}
}
// Composition is the final object you will actually bind to the page
var composition = {
pageMod: new pageModule()
}
// Let's add the common modules to the composition
ko.utils.arrayForEach(commonModules, function(item){
var module = new item();
composition[module.name] = module;
});
// Bind the composition
ko.applyBindings(composition);
example HTML for this would be:
<div class="modalDialog">
<h2 data-bind="text: dialog1.title"><h2>
<h2 data-bind="text: dialog1.content"><h2>
</div>
<div class="modalDialog">
<h2 data-bind="text: dialog2.title"><h2>
<h2 data-bind="text: dialog2.content"><h2>
</div>
<div id="content">
<h2>Welcome to page</h2>
<div id="somePageStuff">
Click me
</div>
</div>
Link to the jsfille for this
You can set this up by using a technique to not have scope your bindings to a specific area in your page.
Check out: How to stop knockout.js bindings evaluating on child elements
Example:
http://jsfiddle.net/anAgent/RfM2R/
HTML
<div id="Main">
<label data-bind="text: ViewModel.Name">default</label>
<div data-bind="stopBindings: true">
<div id="ChildBinding">
<label data-bind="text: AnotherViewModel.Name">default</label>
</div>
</div>
</div>
JavaScript
$(function () {
ko.bindingHandlers.stopBindings = {
init: function () {
return {
controlsDescendantBindings: true
};
}
};
var data = {
ViewModel: {
Name: "Testing"
}
};
var data2 = {
AnotherViewModel: {
Name: "More Testing"
}
};
ko.applyBindings(data, $("#Main")[0]);
ko.applyBindings(data2, $("#MyModalHtml")[0]);
});