4

The site that I'm working on has a custom CMS, and I'm using angularjs for inline page editing. The database stores HTML used to generate the pages, so I can't strictly use client-side templates and models.

I'm not using HTML5 pushState, so the URLs are like example.com/page#/widget/1/edit or example.com/page#/widget/new, and my current issue is that when I submit a form to edit a widget or add a widget, then I want the page to reload its content via injecting HTML into the DOM and change the URL to the default, which I'm trying with $location.path('/').

My current implementation has multiple issues (which you'll see in the demo), one of which is that in my form submit() callback, changing $location.path only changes the URL on a second form button click. I also try to change the URL which I click 'Delete', and that doesn't work either.

http://jsfiddle.net/sZrer/1

customPage.controller('PageEditCtrl', function($scope, $http, $routeParams, $location, $route, $compile, PageState) {
    var widgetId = $routeParams.id || widgetCount + 1;

    $scope.pageState = PageState;
    $scope.widget = { id: widgetId, name: 'Widget ' + widgetId };
    $scope.submit = function() {
      if ($scope.widget.id > widgetCount) widgetCount += 1;
      setTimeout(function() {
        var element = $compile(genHtml())($scope);
        angular.element('#page-content').html(element);
        $location.path('/');
      }, 100);
    };
  });
1
  • DOM manipulation in controller is not a best practice... Commented Dec 19, 2015 at 9:51

1 Answer 1

8

setTimeout() doesn't mix well with Angular (Angular uses an event loop in which it checks for changes to models, but to be able to do that it has to be able to control all parts of your app; setTimeout() is beyond the control of Angular, and when any changes to Angular-parts of your app are made in a setTimeout-handler, it usually takes another 'round' of the Angular event loop before those changes are picked up).

If you replace it with Angular's own $timeout() (be sure to also inject it), it works better (jsfiddle).

Sign up to request clarification or add additional context in comments.

4 Comments

Good to know, but I only used setTimeout as a replacement for the actual implementation that uses $http.get(...).then(callback), which also causes a problem where $location.path updates but none of the controllers are called and my ng-show and ng-click bindings don't work. I'll update the example to use $http so it's closer to the actual implementation.
Since you technically answered the question about the second click, I'll reward you the answer. But here is what I should've posted (jsfiddle.net/sZrer/2). You'll see that if you click Show Admin Controls > New Widget > Submit, and then click "Hide Admin Controls", the ng-show bindings in the new content body no longer work. I also don't like how I did had to set the delete link as a directive (doesn't feel idiomatic), but it's the only way I could get the click handler to work between controllers.
@joshm1 not entirely sure, but it looks like a scoping issue, esp since you're regenerating the Widget elements. Instead of creating new DOM elements, perhaps you could use an ng-repeat to render each widget?
This helped me as well, I had a problem where a $location.path('/') was not taking effect, but it was part of a callback function after some other events took place. Moving the location before the other functions sorted the problem out - I made the assumption the callback timing was messing with Angular's event loop

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.