Check if node has been saved and published in property editor
Hello,
I'm creating a property editor which shows the urls of the current node. It does the exact same thing as Link to document on the Properties tab, but our users want to see the urls on the first tab instead of the last tab.
I've already got it working, but the problem is that if you update the node name (or in this example the Url segment) the property editor isn't updated and still shows the old urls until you reopen the node. Can I check in the property editor if the save and publish button has been clicked and everything is updated?
@Dave's answer is probably "good enough", but I think it'll trigger on save too.
If you couldn't find anything on the scope.content graph, I'd make a directive that attaches a DOM eventhandler to the save & publish button(s). Push that event to an observable angular service and listen to it in the property-editor.
(I'm sure you know how to. :) )
Lars-Erik
[edit] PS. There really is a noticeable lack of events in the content-editor "scope". Not sure how to organize, but would be good to add some input on issues.umbraco.org.
model changed Mon Feb 01 2016 20:52:08 GMT+0100 (W. Europe Standard Time)
published to server Mon Feb 01 2016 20:52:22 GMT+0100 (W. Europe Standard Time)
saved to server Mon Feb 01 2016 20:52:36 GMT+0100 (W. Europe Standard Time)
TestController is my property editor controller.
The service and interceptor can live elsewhere.
Controller:
(function (ng) {
var umbraco = ng.module("umbraco");
function TestController(scope, httpEvents) {
scope.$watch("model", function () {
// does not get destroyed, nor changed on save &| publish.
scope.message = (scope.message || "") + "\n" +
"model changed " + new Date();
}, true);
function posted(type) {
scope.message = (scope.message || "") + "\n" +
type + " to server " + new Date();
}
httpEvents.register(posted);
scope.$on("$destroy", function() {
httpEvents.unregister(posted);
});
}
umbraco.controller("test-controller", [
"$scope",
"content-events-service",
TestController
]);
}(angular));
Service and interceptor:
(function (ng) {
var umbraco = ng.module("umbraco");
function ContentEventsService() {
var listeners = [];
this.register = function (listener) {
listeners.push(listener);
}
this.unregister = function (listener) {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
}
this.raise = function (type) {
var i;
for (i = 0; i < listeners.length; i++) {
listeners[i](type);
}
}
}
function createInterceptor(httpEvents, q) {
return {
'response': function(response) {
if (response.config.url === "/umbraco/backoffice/UmbracoApi/Content/PostSave") {
if (response.config.data.value.action === "publish") {
httpEvents.raise("published");
} else {
httpEvents.raise("saved");
}
}
return response || q.when(response);
}
};
}
umbraco.service("content-events-service", [
ContentEventsService
]);
umbraco.factory("content-http-interceptor", [
"content-events-service",
"$q",
createInterceptor
]);
umbraco.config([
"$httpProvider", function(httpProvider) {
httpProvider.interceptors.push("content-http-interceptor");
}
]);
}(angular));
Hope you can use it.
Looks like it solves a couple of my own annoyances too, so thanks for asking the question. :D
Thanks for the example. I'm still pretty new to Angular. I added the service and interceptor and than updated my property editor controller. The problem is that when I'm trying ot use httpEvents in my controller I get the following exception:
In the controller constructor, you can now call it whatever you want (httpEvents).
But if you don't use the array/string injection form, you'll have to name the service something legal for JavaScript, and you can't minify the code.
Thanks for your help! I am familiar with dependency injection in angular, but most of the time it just worked automagically ;-). I got it working now and the url preview gets updated when the node name or url segment is updated.
There are some events being thrown around in Umbraco core angular services, but it's pretty undocumented and probably lacks this one. Hopefully someone gives it some love sooner or later.
I've had a closer look at the example you provided which I've marked as the solution. It's a lot of code just to check when it's "published". Do I really need all this code? I might release the url preview as a package so I want the code to be clean and understandable. Could you maybe add some comments to your example to what it does and why?
Well, about the urls to the content..
If you recurse through $scope.$parent, you'll eventually find parent.content.
That's the content you're editing, and incidentally it has .urls...
So the code might be completely useless. ;)
What my code does though, is to hook into angulars http interceptors, so it fires for any and all http calls made from the backoffice.
It can be used to detect calls to anything serverside, and fire an event based on the URL or parameters when the reply is received.
Pretty cool, but might be overkill for your needs if you can pick the urls from the content on the ancestral scope. :D
Hmm maybe that works. The urls are also available on the editorState, but not updated if you change the node name. Think I'll keep it like this because I can hook into the published event. Thanks for explaining.
I can see I totally forgot to update the thread with new knowledge. You don't have to recurse up the scope tree to find content. It's available on the editorState service. If you inject it, you'll have the current content on editorState.current.
But otherwise, the events are non-existing, so interceptor is the way to go.
Any idea how to force validation again with the editorState service or any other way ? I have some editor that changes the node name on the "formSubmitting" event , but actually if the form was invalid (no node name specified) it does not push to the server, even if the validation appears ok after ... I need to push the save button again to make it happen.
Check if node has been saved and published in property editor
Hello,
I'm creating a property editor which shows the urls of the current node. It does the exact same thing as Link to document on the Properties tab, but our users want to see the urls on the first tab instead of the last tab.
I've already got it working, but the problem is that if you update the node name (or in this example the Url segment) the property editor isn't updated and still shows the old urls until you reopen the node. Can I check in the property editor if the save and publish button has been clicked and everything is updated?
Jeroen
Hi Jeroen,
You can implement a event handler for this in your controller
Dave
Maybe you can save the editorState in another variable on load, and then check when something changes? Not sure if it's updated though.
Hi guys,
@Dave's answer is probably "good enough", but I think it'll trigger on save too.
If you couldn't find anything on the scope.content graph, I'd make a directive that attaches a DOM eventhandler to the save & publish button(s). Push that event to an observable angular service and listen to it in the property-editor.
(I'm sure you know how to. :) )
Lars-Erik
[edit] PS. There really is a noticeable lack of events in the content-editor "scope". Not sure how to organize, but would be good to add some input on issues.umbraco.org.
Hello,
I tried onValueChanged, but that never is hit. Probably because nothing is changed. Not even after saving.
I guess I could attach to the save & publish buttons, but I was hoping there would be a better way.
Jeroen
I added a few suggestions to the issue tracker.
Feel free to comment/vote.
http://issues.umbraco.org/issue/U4-7874
Voted :-). Even if I can hook into the save & publish eventhandler is there a way to know when it's done posting?
Jeroen
This is quickly becoming an ugly hack, but you could always intercept the http call. 😊
Pasted from https://docs.angularjs.org/api/ng/service/$http
mm, could try this (reference to first property on first tab only here as an example)
So this annoyed the hell out of me. :)
Here's a working solution.
Testing scope.message output:
TestController is my property editor controller.
The service and interceptor can live elsewhere.
Controller:
Service and interceptor:
Hope you can use it.
Looks like it solves a couple of my own annoyances too, so thanks for asking the question. :D
Hi,
Thanks for the example. I'm still pretty new to Angular. I added the service and interceptor and than updated my property editor controller. The problem is that when I'm trying ot use httpEvents in my controller I get the following exception:
Do I need to do something extra for the httpEvents?
Jeroen
And here I thought you were a wiz. with anything before even knowing about it. ;)
I guess you haven't caught on to the whole dependency injection form in angular? If the service is registered as such:
You have to inject it in your controller as such:
See the string matching the name of the service?
In the controller constructor, you can now call it whatever you want (httpEvents).
But if you don't use the array/string injection form, you'll have to name the service something legal for JavaScript, and you can't minify the code.
More info here: https://docs.angularjs.org/guide/di
Thanks for your help! I am familiar with dependency injection in angular, but most of the time it just worked automagically ;-). I got it working now and the url preview gets updated when the node name or url segment is updated.
Jeroen
Great! :)
There are some events being thrown around in Umbraco core angular services, but it's pretty undocumented and probably lacks this one. Hopefully someone gives it some love sooner or later.
Hi Lars,
I've had a closer look at the example you provided which I've marked as the solution. It's a lot of code just to check when it's "published". Do I really need all this code? I might release the url preview as a package so I want the code to be clean and understandable. Could you maybe add some comments to your example to what it does and why?
Jeroen
Well, about the urls to the content..
If you recurse through
$scope.$parent
, you'll eventually findparent.content
.That's the content you're editing, and incidentally it has
.urls
...So the code might be completely useless. ;)
What my code does though, is to hook into angulars http interceptors, so it fires for any and all http calls made from the backoffice.
It can be used to detect calls to anything serverside, and fire an event based on the URL or parameters when the reply is received.
Pretty cool, but might be overkill for your needs if you can pick the urls from the content on the ancestral scope. :D
Hmm maybe that works. The urls are also available on the editorState, but not updated if you change the node name. Think I'll keep it like this because I can hook into the published event. Thanks for explaining.
Jeroen
Hello,
I've released a package which can show the node URLs on any tab: https://our.umbraco.org/projects/backoffice-extensions/url-preview/
It uses the interceptor technique discussed in this topic.
Jeroen
I can see I totally forgot to update the thread with new knowledge. You don't have to recurse up the scope tree to find content. It's available on the
editorState
service. If you inject it, you'll have the current content oneditorState.current
.But otherwise, the events are non-existing, so interceptor is the way to go.
Hi Lars,
Any idea how to force validation again with the editorState service or any other way ? I have some editor that changes the node name on the "formSubmitting" event , but actually if the form was invalid (no node name specified) it does not push to the server, even if the validation appears ok after ... I need to push the save button again to make it happen.
is working on a reply...