Bidirectional linking using URL query parameters and form inputs (select boxes and sliders)

Presently, I am able to retrieve the GET parameters using $location.$$search.

Nevertheless, I am still unsure about how to implement 2-way binding for both the URL and FORM in the following scenario.

In the demo image below, when a user updates the FORM elements, the corresponding URL should be:

https://lazyair.co/en/user/quick_search/index#?from=TOKYO&to=TAIPEI&depart=2016/06/03~2016/06/06&return=2016/06/08~2016/06/11&chart_type=column&depart_slider=10:00~24:00

Demo page:

Sliderbar directive JavaScript code

'use strict';

  quick_search_app.directive('ionslider',function($timeout){

      var get_hour_minute, getHHMMformat, isDepartureAtInInterval;

      get_hour_minute = function(value) {
        var hours, minutes;
        hours = Math.floor(value / 60);
        minutes = value - (hours * 60);
        if (hours.length === 1) {
          hours = '0' + hours;
        }
        if (minutes.length === 1) {
          minutes = '0' + minutes;
        }
        return [hours, minutes];
      };

      getHHMMformat = function(values) {
        var hours, minutes;

              hours = values[0].toString();
              minutes = values[1].toString();
              if (hours.length === 1) {
                hours = '0' + hours;
              }
              if (minutes.length === 1) {
                minutes = '0' + minutes;
              }
              return hours + ':' + minutes;
            }
      isDepartureAtInInterval = function(departure_at, slider){
          var t = new Date(Date.parse(departure_at))
          var HHMM_in_minutes = t.getUTCHours()*60 + t.getMinutes();
          return slider.from <= HHMM_in_minutes && slider.to >= HHMM_in_minutes;
      }
      var updateFlighSeries = function(slider, flight_series) {
        $.each(flight_series, function() {
            var current_series = this;
            angular.forEach(current_series.data, function(value, key) {
                  if(isDepartureAtInInterval(value.departure_at, slider)){
                      this.visible = true ;
                  }else{
                      this.visible = false ;
                  }
              }, current_series);
        });
      }
      return{
          restrict:'AE',
          scope: false,
          controller: 'quick_search_ctrl',

          link:function(scope, element, attr, ctrl){
              $(element).ionRangeSlider({
                      hide_min_max: true,
                      keyboard: true,
                      min: 0,
                      max: 1440,
                      from: 0,
                      to: 1440,
                      type: 'double',
                      step: 30,
                      prefix: "",
                      chartConfig: element.attr("chart-config"),
                      grid: true,
                      prettify: function (value) {
                        return getHHMMformat(get_hour_minute(value));
                      },
                      onChange: function(slider) {
                          var _this = this;
                          updateFlighSeries(slider, scope[_this.chartConfig].series)
                          angular.forEach(scope.chart_names, function(chart_cfg_name){
                                scope.$apply(function () {
                                  scope.lowestFlights[chart_cfg_name]  = angular.copy(scope.filterLowestPrice(scope[chart_cfg_name]))
                                  console.log(scope.lowestFlights[chart_cfg_name])
                                });
                          }, scope)
                      }
              });
          }
      }
  });

HTML

<ui-select.selectpicker{:theme => "select2", "ng-disabled" => "disabled", "ng-model" => "from", :name => "from", :theme => "select2", "ng-change"=>"updateDeparture(from)", :style => "width: 200px;", :required => "" }
  <ui-select-match{ "ng-cloak"=>"", :placeholder => t("from") } {{$select.selected.t_name}}  {{$select.selected.name}}</ui>
</ui>
<ui-select.selectpicker{"ng-disabled" => "disabled", "ng-model" => "to", :name => "to", :theme => "select2", "ng-change"=>"updateArrival(to)", :style => "width: 200px;", :required => ""}
  <ui-select-match.selectpicker{"ng-cloak"=>"", :placeholder => t("to")}  {{$select.selected.t_name}} {{$select.selected.name}}</ui>
  <ui-select-choices{:repeat => "node in arrivals | filter: $select.search" }
    <span ng-bind-html="node.t_name | highlight: $select.search"></span>
    <span ng-bind-html="node.name | highlight: $select.search"></span>
  </ui>
</ui>

url params were cleared in $rootScope.Scope#$digest cycle

I placed a breakpoint inside $locationChangeSuccess and discovered that the url parameters were cleared during the $rootScope.Scope#$digest cycle

app.run(function ($rootScope) {
    $rootScope.$on('$locationChangeSuccess', function () {
        debugger
        console.log('$locationChangeSuccess changed!', new Date());
    });
});

The 2-way binding not functioning on directive

The 2-way binding does not operate on the directive. Actually, the 2-way binding functions properly in the View, but is not effective with URL parameters.

DEMO page

controller(register departChartName and display its value with input box)

  $scope.departChartName = "yoyoyo"
  urlBinder.bind($scope, "departChartName", "DPNAME")

slider directive

app.directive('ionslider',function($timeout){
    return{
        restrict:'AE',
        scope: false,
        link:function(scope, element, attr, ctrl){
            $(element).ionRangeSlider({
                    chartName: element.attr("chart-name"),
                    onChange: function(slider) {
                        scope[this.chartName] = slider.from+"~"+slider.to
                        scope.$apply();
                    }

            });
        }

    }
});

Answer №1

If you need to establish a two-way binding with a URL parameter, you can create a service specifically for that purpose:

angular.module('app').service('urlBinder', ['$location', function($location) {
    this.bind = function(
        scope,         // angular scope
        varName,       // string : name of the variable on the scope to bind to
        urlParamName   // string : name of the url parameter to bind to
        ) {

        // Update the URL when the scope variable changes
        var unhookUrlUpdater = scope.$watch(varName, function(newValue) {
            $location.search(urlParamName, newValue);
        });

        // Update the scope variable when the URL changes
        var unhookScopeUpdater = scope.$on('$locationChangeSuccess', function() {
            var value = $location.search()[urlParamName];

            if (!angular.equals(scope[varName], value)) {
                scope[varName] = value;
            }
        });

        // Return a function to remove the bindings
        return function() {
            unhookUrlUpdater();
            unhookScopeUpdater();
        };
    };
}]);

You could also use getter and setter functions instead of varName if your variables are not directly on the scope:

angular.module('app').service('urlBinder', ['$location', function($location) {
    this.bind = function(scope, getter, setter, urlParamName) {
        var unhookUrlUpdater = scope.$watch(getter, function(newValue) {
            $location.search(urlParamName, newValue);
        });

        var unhookScopeUpdater = scope.$on('$locationChangeSuccess', function() {
            var value = $location.search()[urlParamName];

            if (!angular.equals(getter(), value)) {
                setter(value);
            }
        });

        return function() {
            unhookUrlUpdater();
            unhookScopeUpdater();
        };
    };
}]);

Usage in your controller would look like this:

var someVariable;
urlBinder.bind(
    $scope,
    function() { return someVariable; },
    function(value) { someVariable = value; },
    'url-name');

Answer №2

When working with your HTML, you'll need to utilize $routeProvider. In your config, it might look something like this:

app.config(['$routeProvider', "$locationProvider",
    function ($routeProvider, $locationProvider) {

        /**
         * Redirect Using Code:
         * Internal: $location.path([path]);
         * External: $window.location.href([link]);
         */
        $routeProvider
        .when("/Taraz/:parent", {
                templateUrl: "/App/pages/taraz/index.html",
                controller: "TarazIndexController",
                resolve: {
                    urlId: ["$route",  function ($route) {
                        return Utility.parseUrlId($route.current.params.parent);//Parent HsbCod
                    }]
                }
            })

Here, I'm using /taraz/{code} to access my data. You can use //{your data} to customize the URL as needed. Additionally, I'm resolving urlId, which is similar to services in that you pass it to your controller. Note that if you navigate to the same page through multiple routes, you always have to resolve it (whether empty or filled).

app.controller('TarazIndexController',
    ['$rootScope', '$scope', '$location', '$uibModal', 'urlId', 'FinYearService', 'SarfaslService', 'TarazService',
        function ($rootScope, $scope, $location, $uibModal, urlId, FinYearService, SarfaslService, TarazService) {

You can use location.path() to send data and change routes.

................................................................

Another approach is utilizing ui-router, which is more complex and feature-rich. It's similar to ng-view, but since I haven't personally used it, I can't provide guidance on that.

Answer №3

Understanding the $location object in AngularJS is vital as it provides both a getter and setter functionality.

Visit the official AngularJS documentation for detailed information on $location

url([url]);
This method serves as a getter and setter for the URL.

path([path]);
This method allows you to get or set the path.

In addition, you have the option to use search or replace methods:

this.$location.search(urlParamName, newValue);

replace();
When invoked, any changes to $location within the current $digest cycle will replace the existing history record instead of creating a new one.

The .when('/about/:name',{}) syntax allows for defining routes in AngularJS applications.

app.controller("aboutCtrl",function($scope,$routeParams,$route,$location){
    $scope.variable = "'From About Controller'";
    $scope.params = $routeParams.name;
    $scope.locationpath = $location.path();
    $scope.absolutelocationurl = $location.absUrl();
    $scope.locationurl = $location.url();    
    $scope.templateurl = $route.current.templateUrl;    
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Extract data from an API endpoint using JavaScript or React

I have the primary website link, which necessitates an authorization header for access every time. //console.log contains this information - accounts:[{categoryId:"some info"... }] api/v2/accounts To extract accountId and categoryId from the i ...

Dividing an array of characters within an ng-repeat and assigning each character to its individual input tag

Hello, I'm currently learning Angular and I have a unique challenge. I want to take the names in a table and break each name into individual <input> tags, so that when a user clicks on a letter, only that letter is selected in the input tag. For ...

Tips for effectively closing div elements

How can I modify this accordion to close when a user clicks on another link, and also change the image of the open "p" tag? Currently, clicking on a link toggles the content and changes the image. However, what I am struggling with is closing the previousl ...

When you try to import from another file, the method is not defined

When I attempt to import a method from another file, I am encountering an issue where it returns undefined. The structure involves 3 files with one calling the next in sequence. File1: const { methodFromFile2 } = require('./file2'); methodFromFi ...

What is the method for obtaining the input value of an input type number in HTML?

Within my form, there is a number field where users can input scores: <input type="number" min="0" max="100" class="form-control" name="total_score" id='total_score' value="<?php echo $total_score;?>" >(Please input a score from 0-10 ...

PHP failing to retrieve information

Having trouble with this specific file as it seems to be missing data in the address fields. Additionally, whenever "notes" are inputted, the Address data disappears. Any thoughts on how to resolve this issue? <tbody> ' ; $message .=&a ...

Avoiding page refresh while utilizing the ng5-slider component in Angular

I am currently working with an ng5-slider that has a customizable range from 0 to 1000. However, I have encountered an issue when adjusting the slider at the bottom of the page - it refreshes and automatically takes me back to the top of the page. I would ...

The focus is on the last row that was selected

I've noticed that when I check and select a row in ui-grid, only the last selected row is focused with a background color. Should all rows be automatically painted by default? Thank you. ...

Executing password validation on login/register form using Node.js and EJS

In order to demonstrate a simple login page, I have created a form that requests typical information like username, password, etc. Additionally, it prompts the user to confirm their password, and if the lengths do not match, an error is triggered to notify ...

When Angular is loaded, it appears to modify the browser's stylesheet

After adding the angularjs JavaScript file to my app, I noticed that some text was appearing smaller on the page and causing issues with padding. Has anyone else encountered this problem? Do you know what changes angular is making to the browser's sty ...

Implementing AJAX to dynamically insert content into div elements on the page

Currently facing a small issue with my AJAX implementation for creating comments on posts. The functionality is working well, but the problem arises when executing it in the index.html.erb view. The create.js.erb file locates the initial div labeled "comme ...

Are strings in an array being truncated by Firebug console log?

I have a unique function for logging messages to the firebug console that I'd like to share: // Just having fun with names here function ninjaConsoleLog() { var slicer = Array.prototype.slice; var args = slicer.call(arguments); console.lo ...

What's the best way to refactor the `await nextEvent(element, 'mousemove')` pattern in my code once it is no longer necessary?

Within my React component, the code includes the following: class MyComponent extends React.Component { // ... trackStats = false componentDidMount() { this.monitorActivity() } componentWillUnmount() { this.trackStat ...

How can you pre-load SVG images in an Ionic view?

After developing a mobile app using Ionic, I encountered a slow loading time for one specific view that includes a large SVG image of 202KB. The delay in loading the view/page can be frustrating as it takes around 3-4 seconds to fully load and display. Is ...

Leverage Selenium WebDriver to validate a JavaScript variable through an assertion

During my Selenium WebDriver testing of a webpage, I encountered a situation where I needed to make an assertion on a JavaScript file but was uncertain about the process. The specific assertion I wanted to make was regarding the length of the servers arra ...

Retrieving an Instance of Google Maps Object with the Help of JQuery

I'm currently working on a script that, when executed, will retrieve an instance of a Google Maps object from the webpage using JQuery. For example, if there is a map present on the page, it will be structured like this: <div class="map">....& ...

leveraging jQuery mobile for asynchronous requests

I've been attempting to print a jQuery mobile element using ajax, but I'm running into an issue where the result isn't being encoded as jQuery mobile is intended to do. Below is a simplified excerpt of the JavaScript code responsible for t ...

Ways to insert HTML text into an external webpage's div element without using an iframe

Is it possible to generate HTML and SVG code based on the position of a Subscriber on a web page or within a specific div? I would like to provide some JavaScript code that can be inserted inside a particular div on the page. Using an iframe is not ideal ...

The outcome is not displayed in the appropriate section of the text

There seems to be an issue as the console is not displaying the response to the input, such as "the answer is correct" or "the answer is either empty or incorrect". <!DOCTYPE html> <html lang="en"> <head> <title>Hello!</ ...

Adding a marker to Google Maps in React that displays the user's current location

I successfully integrated Google Maps into my website and now I want to add a marker for the user's location. The first file is Map.js, and the second one is MapContainer.js. In MapContainer.js, I have all the necessary data for map rendering, includi ...