I use a simple binary Vue variable named binary_state to control the class of an element, named "controlled_element" in the rest of this question. "controlled_element" has two classes "class_true" and "class_fasle" as determined by the value of binary_state. The value of binary_state itself switches between true and false using a button on the page. Let's call that button "controlling_element".
Every time the value of binary_state changes using "controlling_element", the updated value is sent to the server. On the other hand, every time the page is refreshed, an updated value for binary_state is retrieved from the server. Once that happens, the class of the "controlled_element" gets adjusted too.
It is a pretty straightforward scenario. Now consider the following steps:
Refresh the page, so a fresh value of binary_state gets retrieved from the server. For simplicity, let's assume the fresh value is true, i.e., binary_state=true. This will set the class of "controlled_element" to "class_true".
Press the "controlling_element" an odd number of times. This will set binary_state and the class of "controlled_element" to "false" and "class_false", respectively.
Navigate away from the page and come back to it using the back arrow (button) of the browser.
I expect the value of binary_state to continue to be false. That seems to be actually the case, as seen on the Vue devtool plugin.
I also expect the class of "controlled_element" to continue to be "class_false". However, to my surprise, the class is "class_true", which is the latest value received from the server regardless of all the changes that were applied in step 2.
Note that when navigating back to the page, the sync (reactivity) between binary_state and the class of "controlled_element" gets violated: As mentioned in step 4 above, the value of binary_state continues to be false (as indicated by the Vue devtool plugin), but the class of "controlled_element" is "class_true".
NOTE. I have seen this issue in chrome (Version 108.0.5359.125 (Official Build) (64-bit)) and Edge (108.0.1462.76 (Official build) (64-bit)), but everything is fine with Firefox (108.01.1 (64-bit)).
Three final remarks.
I have seen this related question. The author indicates they were able to solve the issue by setting autocomplete="on" of the form. I am not sure if that is helpful for me, because I do not have a form, to begin with.
I have seen some suggestions on using vuex or localStorage, but I am not sure if that is necessary in my case because, as mentioned in step 4, the Vue value binary_state seems to have the correct value.
I am not using vue-router if that has a bearing on this topic.
Well, to be honest, I have some suggestions depending on which version of vue you are using. For example, if you are using Vue2 or Vue3 with the cli, your state management problems can be solved with Vuex (however at this point vuex is not the officially recommended state management system anymore). The best solution, if you're using Vue3, is to go with Pinia https://pinia.vuejs.org which is very helpful, modern, and surprisingly easier to learn and use, compared to Vuex.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
localStorage is an alternate solution, but the tradeoff is that you will have to write code to erase that localStorage.
https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
sessionStorage is similar to localStorage but will automatically erase the data after the window closes.
I solved the issue by manually reloading the page when a user visits the page using the browser back/forward buttons. The following code snippet does it:
<script>
if (performance) {
// get the PerformanceNavigationTiming object
const perfEntries = performance.getEntriesByType("navigation");
const perfNavTiming = perfEntries && perfEntries[0];
if (perfNavTiming) {
// get the type of navigation
const navType = perfNavTiming.type;
// reload if it is back_forward or prerender.
if ((navType === "back_forward") ||
(navType === "prerender")) {
window.location.reload();
}
}
}
</script>
It is not the neatest solution, but I guess I should go for it, given my time limit. I guess some learning is required before I can use the suggested solutions using localStorage, sessionStorage, and Piani, as I am not sure how to use these techniques to re-initiate the Vue two-way binding.
Related
I have an existing site with a lot of javascript included. My javascript is adding a query string parameter (using history.replaceState) but right after the adding, it gets removed. How can I find the line of javascript who is removing my query string parameter? (I've been trying to place breakpoints in the Chrome dev tools on certain method calls but no luck so far.)
There is no real network navigation happening here, all changes come from javascript and angularjs.
With given information I can only say that its probably a redirection that must be removing your query parameter. Try enabling preserve network logs and then track the network calls to see if there are any redirections happening.
Following steps solved my problem:
First I've hijacked the pushState and replaceState methods to see when they are triggered. Everywhere where expected they were triggered. No other known calls are made. The catch is in the AngularJS $location component. It checks what happens onUrlChange and removes data which is no hash and is different from the baseUrl (as far as i can understand from checking briefly)
When developing a web app with jQuery or normal JavaScript, it is commonplace to check for feature availability first. So for example, if I want to use the document.oncopy event, I should first have something like this to ensure my code doesn't break for lesser browsers:
if ("oncopy" in document) {
// Feature is available
}
I'm a bit puzzled about how this would work in Angular2. I could still use the same if I expect to only run in the browser, but I'm specifically told to leave the DOM alone if I want to use Angular Universal and depend on templates or the DomRenderer instead. This allows the page to be pre-rendered on the server and provides a truly impressive performance gain.
But suppose I want a specific div to be invisible if the document.oncopy is unavailable. My understanding is that this is not recommended:
<div *ngIf="hasFeature()">...</div>
and
hasFeature() {
return 'oncopy' in document;
}
because then I'm still manipulating the DOM. Note that my example is about the document.oncopy but I could choose any feature whatsoever that doesn't have universal support.
I tested this using Chris Nwamba's tutorial on Scotch and added the following to the end of his Home template:
<div *ngIf="hasFeature()">Feature is supported</div>
<div *ngIf="!hasFeature()">Feature is NOT supported</div>
Update: Interestingly, it gave different results on different browsers. On Chrome 55, it executed as it would normally and showed the "Feature is supported" message. On IE11, I received the "not supported" message. In both instances the server log shows a EXCEPTION: document is not defined message, but the page still seems perfectly okay.
So what is the correct way to check for browser features if I want to use Angular Universal?
Update:
I also toyed around with using a field in the template and assigning that field from one of the life cycle hooks. ngAfterContentInit seemed like a fine candidate, but also causes an error on the server. It still runs fine in the browser with no weird effects (that I have noticed so far).
There are two ways to approach this:
Do the check only once the server is done rendering and the client is completely initialised (including the replay of user events done by preboot.js).
Return a reasonable default when the page is running on the server and perform the actual check only in the browser.
I started looking at the first option, but none of the Angular2 life cycle events will help with this. In fact, you can clearly see them all executing on the server and only then on the client.
I then started looking for something usable in preboot.js but quickly realised it was more complex than it needed to be.
So onto option 2 I went. It turns out checking for the browser is as easy as importing and checking isBrowser.
import { isBrowser } from "angular2-universal";
#Component({
// All the usual stuff
})
export class MyComponent {
// ...
hasFeature(): boolean {
return isBrowser && 'oncopy' in document;
}
// ...
}
And then use the template as I showed in the question.
To check if you're running on the server, import and use isNode in exactly the same way. There doesn't seem to be an obvious way to distinguish between Node and ASP.NET Core, but perhaps it's best not to write too much code that specific to platform.
I've got a language problem with my SAPUI5 controls.
If I execute e.g.:
sap.ui.getCore().getConfiguration().setLanguage("de");
My i18n files are loaded correctly and all labels are translated to German. But the controls are still in English.
The only way to get German controls is with the URL parameter:
sap-ui-language=DE
But I can't use a parameter in my case. Any idea?
Please note that sap.ui.getCore().setLanguage() explicitly states
The framework does not guarantee that already created, language dependent objects will be updated by this call. It therefore remains best practice for applications to switch the language early, e.g. before any language dependent objects are created. Applications that need to support more dynamic changes of the language should listen to the localizationChanged event and adapt all language dependent objects that they use (e.g. by rebuilding their UI).
Besides that, I fully support Nabi's answer (but I'm not allowed to vote it up).
I just would like to add that controls (like FilterBar) better should use the hook approach:
FilterBar.prototype.onlocalizationChanged = function(oEvent) {
// .. same bundle update code as in Nabi's proposal
}
Using the hook in controls avoids the need for adding attach + detach calls in init / exit and keeps the event registry small.
I can easily confirm the behavior you described by testing the Explored App Example. There, just open the console and hit sap.ui.getCore().getConfiguration().setLanguage("de");
I also checked the implementation of the FacetFilter and I would call this a bug in the Control implementation. It comes from how the texts are loaded inside the control. Just in case you are interested:
The message bundles all contain the correct translations for FACETFILTER_INFOBAR_NO_FILTERS (for en the translation comes from the "default" bundle):
messagebundle.properties
messagebundle_de.properties
The FacetFilter has a hidden aggregation called SummaryBar. The SummaryBar contains the text you see. Of course, this text comes from a bundle.
However, the bundle is initialized exactly once in init() by calling sap.ui.getCore().getLibraryResourceBundle("sap.m");. Here the API docs say:
If only one argument is given, it is assumed to be the libraryName.
The locale then falls back to the current session locale.
This means the bundle is cached and therefor changes to the localization (e.g. language) do not trigger the bundle to load a new translation file. Thus, we will always see the initial language no matter what we try (even rerendering() does not help).
A solution would be to fix the control by adding the following code right after the the bundle gets loaded inside the init:
sap.ui.getCore().attachLocalizationChanged(function(oEvent){
var oChanges = oEvent.getParameter("changes");
if (oChanges && oChanges.language){
this._bundle = sap.ui.getCore().getLibraryResourceBundle("sap.m", oChanges.language);
this.rerender();
}
}.bind(this));
You can try this out in the explored app linked above, it worked for me just fine...
I just opened an issue on github.
I have a login page and a home page in my sample POC.
When user completed signed in, then i have redirect to home page from login page.
I have tried this below codes for redirecting to home page from login page using by angular.$location
Code 1
$location.path("/home");
This code is working good.No issues.
Code 2
$location.$$path="/home";
This code does not working probably, because the page is redirected, but URL is shown previous URL,does not changed the redirected URL.
Why is this happened?
What is the difference between code1 and code2
$$path is internal property of the angular you should not touch that.
if you see this code of location service in angular code base (https://github.com/angular/angular.js/blob/master/src/ng/location.js#L480).
$location.$$path = 'somepath', will only sets the $$path value
whereas,
$location.path('somepath') will do some other things also like calling $$compose internally and updating this.$$url and this.$$absUrl etc.
Angular uses a single dollar ($) for some of their API methods, properties, and event names. Those methods are considered "public" and safe to use by developers.
They are documented and not likely to change much, but if they do change then the angular team will tell us about it.
When you see two dollars ($$) then you're best off just leaving that alone. You can of course use them any way you want (debugging is a good use), but you shouldn't need to. If you use them for your own purposes there's no guarantee it will even be there on the next minor version update so your code becomes more brittle.
I am working on a website, and I would like to refresh a portion of the page after an ActiveX component has been installed. I have a general idea of how to do this with polling, which I am working on getting going :
function detectComponentThenSleep(){
try{
// Call what I want ActiveX for, if the method is available, or
// ActiveXComponent.object == null --- test for existance
document.getElementById("ActiveXComponent").someMethod();
}
catch{
// Try again, if the method is not available
setTimeout(detectComponentThenSleep, 100);
}
}
However, what I would REALLY like to do is something like this:
ActiveXObject.addListener("onInstall", myfunction);
I don't actually have the source for the ActiveX component, but I have complete control of the page I am hosting it on. I would like to use JavaScript, if possible, to accomplish this.
So, my question is 1.) will this actually work with the polling method? and 2.) Is there an interrupt/listener like way of doing this? I am sure I am missing something with connecting the dots here, I can already detect if the component is present, but I am having trouble doing this asynchronously.
Thank you very much for your time and help,
-Brian J. Stinar-
1.) This didn't work at all with the polling method.
2.) I couldn't find an interrupt / listener way of doing this.
I finally ended up just putting this entire ActiveX compnenent on it's own page. ActiveX does a page refresh on install, so I simply had the default page behavior as the what I wanted to happen when the component wasn't available. This is different than what I was trying to do, but it worked OK for my purposes.
My recommendation to anyone in a similar situation is to just put the ActiveX component on it's own page, and pass data back and forth from this page. Otherwise, you are probably going to have a lot of the problems that I had.
-Brian J. Stinar-