CSS keyframe animations only running once in Angular app - angularjs

Updated: In short, my problem seems to be that the DOM stops updating whenever I am calling a function which switches from one state to another in the application.
More interestingly though, this actually only happens the second time this function is called from one particular element.
For specific details, I'm developing an single page app in AngularJS 1.2.28 and one I've stripped my html template, display-widgets.html to contain only 3 elements as follows:
<p data-ng-click="widgetCtrl.onWidgetSelected(widgetCtrl.model.widgetData[0].widget)">widget 1</p>
<p data-ng-click="widgetCtrl.onWidgetSelected(widgetCtrl.model.widgetData[0].widget)">also widget 1</p>
<p data-ng-click="widgetCtrl.onWidgetSelected(widgetCtrl.model.widgetData[1].widget)">widget 2</p>
<p>{{widgetCtrl.interstitialMode}}</p>
The controller looks something like as follows:
widgetShop.widgetCtrl.prototype.showWidgetHome = function (widget) {
this.state.go('widgetHome', {
widgetId: widget.widgetID
});
};
widgetShop.widgetCtrl.prototype.setupInterstitial = function () {
this.interstitialMode = true;
};
widgetShop.widgetCtrl.prototype.onWidgetSelected = function(widget) {
this.setupInterstitial();
this.showWidgetHome(widget);
};
The intended functionality is that after the widget is selected, onWidgetSelected() is called passing the selected widget as a parameter where the animation variable is updated (with the intention to add class with ng-class in the DOM) with setupInterstitial() before another function is called to switch the state. Some interaction will occur on this state, (e.g. add widget to basket) and once complete, the state is switched back to the display-widgets state.
I've stripped the html down here to be just p tags for testing but I've found if you select the same widget(p tag) again, the element is NOT updated in the page as before, and more interesting still is that no DOM updates occur at all. Console logs and alerts show me that angular has updated the correct variables in the controller but the state transition seems to prevent any DOM updates occurring. Selecting the widget 2 will update the DOM as expected, but as with the first widget, this will not occur more than once. Selecting 'also widget 1' after selecting widget 1 will not update the DOM as though it were the second time it were selected, but selecting this before widget 1 will have the same affect as selecting widget 1 first.
Does anybody know if this is a specific issue with browsers not performing animation again because it is a single page app? Or is there likely to be an issue elsewhere in my code? I haven't been able to find anything via google or stack overflow with people having a similar issue.

Sounds like once you've added the class, it is never removed again.
Once the class is added, the animation starts. When you add it again nothing changes because it already had that class and all the styling was already applied.

Related

Creating new Stock List Item does not open Materialize Modal, but initial state items work

This is my first post on Stack Overflow. Excited to be a part of the community!
I am creating a Stock Market App project. I ran into an interesting problem. I am using Materialize in my project over Bootstrap. I am coding in React. I have 2 stocks in my initial state, Apple and FaceBook. Whenever I click on them, the modal opens with more stock info on each. But whenever I add a new stock (like JPM), the item shows up, but whenever I click, it will not open.
In materialize, you initialize the modal with jquery like below in (index.html). Then you make the href and ID match on each item like I did. I inspected the new like created for JPMorgan and it says href="#JPM" and id="JPM" just like the others that work. The only thing I can think of is that maybe whenever the jquery initializes, that when I add an item, it is somehow not initialized as well? But I'm not sure about that because it is just a simple line of code:
$('.modal').modal();
Def pretty stumped here. The github repository is below. I'll also post some screen shots of my StockItem.js file. https://github.com/Christopher-Bowling/Stock-Market-App
Pictures:
Index.html StockItem.js
You need to run the component initialisation each time you create a new element that needs initialising.
When you run $('.modal').modal(), jQuery looks for any element in the dom and runs the .modal() function on it - once. Any dynamically added elements (added after this initial has run) will behave as if you never ran the init at all. It is touched upon on the select doc page:
Initialization
In addition, you will need a separate call for any dynamically
generated select elements your page generates.You must initialize the select element as shown below.
https://materializecss.com/select.html

How do I return focus to an element when the entire page changes?

I have a complicated setup. My application is driven by a set of "rules" which dictate what the user interface is. The UI is rendered by looping through the rules and creating the individual dropdowns. Initially, everything renders properly. However, once a user makes a change to the UI, other rules may be affected. In my application, an api call is made, which then returns a modified set of rules. In the attached plunker, I've simplified things such that only the new set of rules is applied, which causes the page to re-render. The problem is that my users would like to be able to tab between all of the entries on the page and make changes. However, once the page is re-rendered, the currently selected page element is now gone nothing has the focus. I've tried to put the focus back on the proper element by tracking a common Id, but to no avail.
Using either of these doesn't seem to work.
var el = document.getElementById(focusId);
el.focus();
angular.element(el).focus();
I've also tried using the autofocus attribute on the dropdown that I want to have focus, but that didn't work either. I'm using angularjs 1.2. Any ideas are appreciated.
http://plnkr.co/edit/ND9PKqULIOlixWR4XChN?p=preview
If you want to assign auto focus dynamically to a element on the DOM from angular you can use this:
var myEl = angular.element(document.querySelector('select'));
myEl.attr('autofocus',"attr val");
You can also pass in an id like: angular.element(document.querySelector('#focusId'));
You can look here for a prior answer which may be of some more help!
-Cheers!
Problem here is, you are trying to focus the element before the blur event completes. So you need to execute the focus code after blur event. Async execution of your focus code would solve the problem. You can use either setTimeout or $timeout.
setTimeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
or
$timeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
dont forgot to inject $timeout to your controller if you are using second code. Hope this helps :)

Call function when a tab in tabbar is first opened (onsen-ui)

I need to load a specific JavaScript function after one of the tabs in the tabbar finishes loading (after it is clicked for the first time). I don't really understand how to set up an event listener with the onsen-ui tabbar (ons-tabbar), and the docs were not very clear.
Basically, what I am trying to do, is create a few graphs on one of the pages. This page is loaded up when a ons-tab is selected and the partial loads. However, I need the JavaScript that creates the graphs to only load after the page is loaded (the JavaScript looks for the elements to position the graph in the partial, hence my problem right now is that the JS is loading first and not finding the html elements in the partial because they have't been loaded yet). How do I create an event listener to detect when one of the tabs are selected to run a specific code of JavaScript?
To be a little bit more clear, I am using the persistent attribute on this specific tab, so essentially I only need this event listener to run the JS code once, when the tab is first opened. However, I don't understand how to get the once event listener to work...
For the reference: http://onsen.io/reference/ons-tabbar.html#methods-summary
There are three tabbar methods related to events: on, once and off.
That means the way to use them is myTabbar.on(...), for example. You can see in that link their parameters as well. Also, right below there are the three tabbar events. In this case I think you want to use postchange, right?
In your HTML you create a tabbar and assign it to a variable with var attribute, as I am sure you already know:
<ons-tabbar var="myTabbar">
<ons-tab page="tab0.html" label="Tab0" active="true"></ons-tab>
<ons-tab page="tab1.html" label="Tab1"></ons-tab>
<ons-tab page="tab2.html" label="Tab2"></ons-tab>
</ons-tabbar>
Now, you have to wait until the app is initialised and Onsen UI is loaded in order to set the event listeners, and you can use ons.ready() for that.
Going back to the tabbar events method, since you only need to trigger the functionality with a specific tab you need to filter the tabs inside the listener. Therefore, if you use myTabbar.once('postchange', ...) the listener itself will be triggered only once regardless the filter you set inside, so perhaps it will be triggered with a different tab and then deleted for the tab you want. You cannot use it in this case.
Instead of that, you can set a listener with myTabbar.on('postchange', ...) that triggers always, then filter the tabs inside and remove the listener only when you want with myTabbar.off('postchange'). Something like this:
ons.ready(function() {
myTabbar.on('postchange', function(event) {
// Only with the tab we want, we can use the index or the name
if (event.index === 2) {
console.log('postchange and do stuff with the tab');
console.log(event.tabItem);
// Delete this listener so it's only triggered the first time
myTabbar.off('postchange');
}
});
Working here: http://codepen.io/frankdiox/pen/QbYEaq
In case you have other listener over postchange, you can use the second parameter of myTabbar.off(...) to remove the exact listener you want and not the others.
Hope it helps!

When I update my model in ng repeat the directive ng-show does not change

I have a page where I update my model dynamically
<div ng-repeat="action in actions">
<div ng-show="action.showMyDiv">Hello </div>
<div>
when I update my model dynamically from an action in the current page with
$scope.actions[id]['showMyDiv']= true,
the div does not display.
Please , could you help me ?
Thanks in advance .
If you are updating the value manually, you can wrap it in $scope.$apply():
$scope.$apply(function(){
$scope.actions[id]['showMyDiv']= true
})
EDIT:
2 cents on $scope.$apply:
The way angular works out bindings is by doing dirty checking at every render (plenty sources about the process if you search around). As you use a angular default method (ng-click, for instance) a flag is set to warn the app that the page needs "refreshing", and you see the changes happen.
If you change anything manually, you need to warn the app that it needs refreshing, and you can do that with $apply.
See this example for clarification. Notice that two changes are made "manually", at 2s and 4s in, using window.timeout (which you shouldn't use, but rather use the $timeout service), but only after the second one are the changes propagated to the view.

Webshims - Show invalid form fields on initial load

(Follow on questions from Placeholder Hidden)
I'd like my form to validate existing data when it is loaded. I can't seem to get that to happen
I jQuery.each of my controls and call focus() and blur(), is there a better way than this? I tried to call ctrl.checkValidity(), but it wasn't always defined yet. When it was, it still didn't mark the controls.
I seem to have a timing issue too, while the focus and blur() fire, the UI does not update. It's as if the Webshims are not fully loaded yet, even though this fires in the $.webshims.ready event.
I also tried to call $('#form').submit(), but this doesn't fire the events as I expected. The only way I could make that happen was to include an input type='submit'. How can I pragmatically case a form validation like clicking a submit button would?
Here's a jsFiddle that demonstrates the problem. When the form loads, I want the invalid email to be marked as such. If you click the add button it will be marked then, but not when initially loaded. Why?
Focus and blur in the control will cause it to be marked.
BUT, clicking ADD will too (which runs the same method that ran when it was loaded). Why does it work the 2nd time, but not when initially loaded?
updateValidation : function () {
this.$el.find('[placeholder]').each(function (index, ctrl) {
var $ctrl = $(ctrl);
if( $ctrl.val() !== "" && (ctrl.checkValidity && !ctrl.checkValidity()) ) {
// alert('Do validity check!');
$ctrl.focus();
$ctrl.blur();
}
});
}
I see this in FF 17.0.5. The problem is worse in IE9, sometimes taking 2 or 3 clicks of ADD before the fields show in error. However, I get errors on some of the js files I've liked 'due to mime type mismatch'.
This has to do with the fact, that you are trying to reuse the .user-error class, which is a "shim" for the CSS4 :user-error and shouldn't be triggered from script. The user-error scripts are loaded after onload or as soon as a user seems to interact with an invalid from.
From my point of view, you shouldn't use user-error and instead create your own class. You can simply check for validity using the ':invalid' selector:
$(this)[ $(this).is(':invalid') ? 'addClass' : 'removeClass']('invalid-value');
Simply write a function with similar code and bind them to events like change, input and so on and call it on start.
In case you still want to use user-error, you could do the following, but I would not recommend:
$.webshims.polyfill('forms');
//force webshims to load form-validation module as soon as possible
$.webshims.loader.loadList(['form-validation']);
//wait until form-validation is loaded
$.webshims.ready('DOM form-validation', function(){
$('input:invalid')
.filter(function(){
return !!$(this).val();
})
.trigger('refreshvalidityui')
;
});

Resources