AngularJS में विचारों के बीच परिवर्तन करते समय स्कोप का मॉडल बनाए रखें


152

मैं AngularJS सीख रहा हूं। चलो मैं कहना / View1 का उपयोग कर My1Ctrl , और / view2 का उपयोग कर My2Ctrl ; टैब का उपयोग करने के लिए नेविगेट किया जा सकता है, जहां प्रत्येक दृश्य का अपना सरल, लेकिन अलग रूप है।

मैं यह कैसे सुनिश्चित करूंगा कि व्यू 1 के रूप में दर्ज किए गए मान रीसेट नहीं होते हैं, जब कोई उपयोगकर्ता छोड़ता है और फिर view1 पर लौटता है ?

मेरे कहने का मतलब यह है कि दृश्य 1 की दूसरी यात्रा कैसे ठीक वैसी ही हो सकती है जैसे मैंने इसे छोड़ा था?

जवाबों:


174

मुझे यह काम करने में थोड़ा समय लगा कि ऐसा करने का सबसे अच्छा तरीका क्या है। मैं यह भी रखना चाहता था कि जब उपयोगकर्ता पृष्ठ छोड़ता है और फिर वापस बटन दबाता है, तो पुराने पृष्ठ पर वापस जाने के लिए; और न केवल अपने सारे डेटा को रद्दी में डाल दिया ।

अंतिम परिणाम प्रत्येक नियंत्रक के लिए एक सेवा है । नियंत्रक में, आपके पास केवल फ़ंक्शंस और वैरिएबल होते हैं जिनकी आपको परवाह नहीं है, अगर वे साफ़ हो जाते हैं।

नियंत्रक के लिए सेवा निर्भरता इंजेक्शन द्वारा इंजेक्ट की जाती है। चूंकि सेवाएँ एकल हैं, उनका डेटा नियंत्रक में डेटा की तरह नष्ट नहीं होता है।

सेवा में, मेरे पास एक मॉडल है। मॉडल में केवल डेटा है - कोई कार्य नहीं -। इस तरह से इसे जारी रखने के लिए JSON से आगे और पीछे परिवर्तित किया जा सकता है। मैंने दृढ़ता के लिए html5 लोकलस्टोरेज का उपयोग किया।

अंत में मैंने उपयोग किया window.onbeforeunloadऔर $rootScope.$broadcast('saveState');सभी सेवाओं को यह बताने के लिए कि उन्हें अपने राज्य को बचाना चाहिए, और $rootScope.$broadcast('restoreState')उन्हें अपने राज्य को पुनर्स्थापित करने के लिए बताने के लिए इस्तेमाल किया (जब उपयोगकर्ता पृष्ठ छोड़ता है और पृष्ठ पर वापस लौटने के लिए वापस बटन दबाता है)।

उदाहरण सेवा बुलाया userService मेरे लिए userController :

app.factory('userService', ['$rootScope', function ($rootScope) {

    var service = {

        model: {
            name: '',
            email: ''
        },

        SaveState: function () {
            sessionStorage.userService = angular.toJson(service.model);
        },

        RestoreState: function () {
            service.model = angular.fromJson(sessionStorage.userService);
        }
    }

    $rootScope.$on("savestate", service.SaveState);
    $rootScope.$on("restorestate", service.RestoreState);

    return service;
}]);

userController उदाहरण

function userCtrl($scope, userService) {
    $scope.user = userService;
}

दृश्य तो इस तरह बंधन का उपयोग करता है

<h1>{{user.model.name}}</h1>

और ऐप मॉड्यूल में , रन फ़ंक्शन के भीतर मैं saveState और रिस्टोरस्ट के प्रसारण को संभालता हूं

$rootScope.$on("$routeChangeStart", function (event, next, current) {
    if (sessionStorage.restorestate == "true") {
        $rootScope.$broadcast('restorestate'); //let everything know we need to restore state
        sessionStorage.restorestate = false;
    }
});

//let everthing know that we need to save state now.
window.onbeforeunload = function (event) {
    $rootScope.$broadcast('savestate');
};

जैसा कि मैंने बताया कि इस बिंदु पर आने में थोड़ा समय लगा। यह इसे करने का एक बहुत ही अच्छा तरीका है, लेकिन यह कुछ ऐसा करने के लिए इंजीनियरिंग का एक उचित सा है कि मुझे संदेह होगा कि कोणीय में विकसित होने पर यह एक बहुत ही सामान्य मुद्दा है।

मैं आसान देखना पसंद करूंगा, लेकिन उपयोगकर्ता के पेज पर वापस आने और वापस आने के दौरान नियंत्रकों के बीच स्थिति को संभालने के लिए स्वच्छ तरीके।


10
यह बताता है कि जब पृष्ठ ताज़ा होता है तो डेटा को कैसे बनाए रखना है, मार्ग बदलने पर इसे कैसे जारी रखना है, इसलिए यह प्रश्न का उत्तर नहीं देता है। इसके अलावा यह उन ऐप्स के लिए काम नहीं करेगा जो मार्गों का उपयोग नहीं करते हैं। प्लस यह नहीं दिखाता है कि कहां sessionStorage.restoreStateसेट किया गया है "true"। पेज रीफ्रेश होने पर डेटा को बनाए रखने के लिए एक सही समाधान के लिए, यहां देखें । मार्ग में परिवर्तन होने पर डेटा को बनाए रखने के लिए, carloscarcamo के उत्तर को देखें। आपको बस एक सेवा में डेटा डालना है।
ह्यूगो वुड

2
यह विचारों के बीच बना रहता है, ठीक उसी तरह जिस तरह से आप सुझाव देते हैं, इसे एक सेवा में संग्रहीत करें। दिलचस्प रूप से पर्याप्त है, यह पृष्ठ पर भी उसी तरह से बना रहता है जिस तरह से आप खिड़की के साथ सुझाव दे रहे हैं। हालांकि मुझे दोहरी जांच करने के लिए धन्यवाद। यह उत्तर एक साल से अधिक पुराना है और यह अभी भी कोणीय के साथ चीजों को करने का सबसे सरल तरीका लगता है।
एंटोन

यहाँ वापस आने के लिए धन्यवाद, भले ही यह पुराना है :)। सुनिश्चित करें कि आपका समाधान दोनों विचारों और पृष्ठों को शामिल करता है, लेकिन यह स्पष्ट नहीं करता है कि कौन सा कोड किस उद्देश्य से कार्य करता है, और यह पर्याप्त नहीं है कि यह प्रयोग करने योग्य हो सकता है । मैं केवल पाठकों को आगे के सवालों से बचने के लिए चेतावनी दे रहा था । यह बहुत अच्छा होगा यदि आपके पास अपने उत्तर को बेहतर बनाने के लिए संपादन करने का समय हो।
ह्यूगो वुड

क्या होगा अगर इसके बजाय आपके नियंत्रक में: $scope.user = userService;आपके पास ऐसा कुछ है $scope.name = userService.model.name; $scope.email = userService.model.email;:। क्या यह कार्य या परिवर्तनशील चर को सेवा में नहीं बदलेगा?
स्पोर्ट्स

2
मुझे लगता है कि कोणीय में ऐसी सामान्य चीज़ के लिए अंतर्निहित तंत्र का अभाव है
obe6

22

एक उत्तर के लिए थोड़ा देर से लेकिन कुछ बेहतरीन अभ्यास के साथ सिर्फ अपडेटेड फिडेल

jsfiddle

var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
    var userService = {};

    userService.name = "HI Atul";

    userService.ChangeName = function (value) {

       userService.name = value;
    };

    return userService;
});

function MyCtrl($scope, UserService) {
    $scope.name = UserService.name;
    $scope.updatedname="";
    $scope.changeName=function(data){
        $scope.updateServiceName(data);
    }
    $scope.updateServiceName = function(name){
        UserService.ChangeName(name);
        $scope.name = UserService.name;
    }
}

मैं भी इस inbetween पृष्ठ navigations (पृष्ठ ताज़ा नहीं) करना है। क्या ये सही है?
फुतुओदाद

मुझे लगता है कि मुझे इस उत्तर को पूरा करने के लिए भाग जोड़ना चाहिए, updateServiceनियंत्रक को नष्ट होने पर बुलाया जाना चाहिए - $scope.$on('$destroy', function() { console.log('Ctrl destroyed'); $scope.updateServiceName($scope.name); });
शिवेंद्र

10

$ rootScope एक बड़ा वैश्विक वैरिएबल है, जो वन-ऑफ़ चीजों या छोटे ऐप्स के लिए ठीक है। यदि आप अपने मॉडल और / या व्यवहार को एन्कैप्सुलेट करना चाहते हैं, तो एक सेवा का उपयोग करें (और संभवतः इसे कहीं और पुनः उपयोग करें)। उल्लिखित Google समूह पोस्ट के अलावा, देखे गए https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion भी देखें


8

कोणीय वास्तव में प्रदान नहीं करता है कि आप बॉक्स से बाहर क्या देख रहे हैं। निम्नलिखित ऐड ऑन्स का उपयोग करने के बाद मैं जो कर रहा हूं, उसे पूरा करने के लिए मैं क्या करूंगा

यूआई राउटर और यूआई राउटर एक्स्ट्रा

ये दोनों आपको राज्य आधारित रूटिंग और चिपचिपा राज्य प्रदान करेंगे, आप राज्यों के बीच टैब कर सकते हैं और बोलने के लिए "स्टे जिंदा" के दायरे के रूप में सभी जानकारी सहेज ली जाएगी।

दोनों पर प्रलेखन की जाँच करें क्योंकि यह बहुत सीधा है, ui राउटर एक्स्ट्रा में भी एक अच्छा प्रदर्शन है कि चिपचिपा राज्य कैसे काम करता है।


मैंने दस्तावेज़ पढ़े हैं, लेकिन उपयोगकर्ता के ब्राउज़र बैक बटन पर क्लिक करने पर फॉर्म इनपुट को संरक्षित करने का तरीका नहीं खोज सका। क्या आप मुझे बारीकियों की ओर संकेत कर सकते हैं? धन्यवाद!
Dalibor

6

मेरे पास एक ही समस्या थी, यह वही है जो मैंने किया था: मेरे पास एक ही पृष्ठ (ajax के बिना) में कई दृश्यों के साथ एक एसपीए है, इसलिए यह मॉड्यूल का कोड है:

var app = angular.module('otisApp', ['chieffancypants.loadingBar', 'ngRoute']);

app.config(['$routeProvider', function($routeProvider){
    $routeProvider.when('/:page', {
        templateUrl: function(page){return page.page + '.html';},
        controller:'otisCtrl'
    })
    .otherwise({redirectTo:'/otis'});
}]);

मेरे पास सभी विचारों के लिए केवल एक नियंत्रक है, लेकिन, समस्या एक ही है, सवाल हमेशा नियंत्रक डेटा को ताज़ा करता है, इस व्यवहार से बचने के लिए मैंने वही किया जो लोग ऊपर सुझाते हैं और मैंने उस उद्देश्य के लिए एक सेवा बनाई है, फिर इसे पास करें नियंत्रक के लिए निम्नानुसार है:

app.factory('otisService', function($http){
    var service = {            
        answers:[],
        ...

    }        
    return service;
});

app.controller('otisCtrl', ['$scope', '$window', 'otisService', '$routeParams',  
function($scope, $window, otisService, $routeParams){        
    $scope.message = "Hello from page: " + $routeParams.page;
    $scope.update = function(answer){
        otisService.answers.push(answers);
    };
    ...
}]);

अब मैं अपने किसी भी विचार से अपडेट फ़ंक्शन को कॉल कर सकता हूं, मानों को पास कर सकता हूं और अपने मॉडल को अपडेट कर सकता हूं, मुझे दृढ़ता से डेटा के लिए html5 एपिस का उपयोग करने की कोई आवश्यकता नहीं है (यह मेरे मामले में है, शायद अन्य मामलों में html5 का उपयोग करना आवश्यक होगा एपीस लोकलस्टोरेज और अन्य सामान)।


हाँ, एक आकर्षण की तरह काम किया। हमारे डेटा कारखाने को पहले से ही कोडित किया गया था और मैं कंट्रोलर सिंटैक्स का उपयोग कर रहा था, इसलिए मैंने इसे $ स्कोप कंट्रोलर के लिए फिर से लिखा है, इसलिए कोणीय इसे नियंत्रित कर सकता है। कार्यान्वयन बहुत सीधा था। यह बहुत पहले कोणीय ऐप I´m writtng है। tks
egidiocs

2

सेवाओं का एक विकल्प मूल्य स्टोर का उपयोग करना है।

अपने ऐप के बेस में मैंने इसे जोड़ा

var agentApp = angular.module('rbAgent', ['ui.router', 'rbApp.tryGoal', 'rbApp.tryGoal.service', 'ui.bootstrap']);

agentApp.value('agentMemory',
    {
        contextId: '',
        sessionId: ''
    }
);
...

और फिर मेरे कंट्रोलर में मैं सिर्फ वैल्यू स्टोर का संदर्भ देता हूं। मुझे नहीं लगता कि अगर उपयोगकर्ता ब्राउज़र को बंद कर देता है तो यह बात पकड़ लेता है।

angular.module('rbAgent')
.controller('AgentGoalListController', ['agentMemory', '$scope', '$rootScope', 'config', '$state', function(agentMemory, $scope, $rootScope, config, $state){

$scope.config = config;
$scope.contextId = agentMemory.contextId;
...

1

समाधान जो उन स्कोप के भीतर कई स्कोप और कई वेरिएबल्स के लिए काम करेगा

यह सेवा एंटोन के उत्तर पर आधारित थी, लेकिन अधिक एक्सटेंसिबल है और कई स्कोप में काम करेगी और एक ही दायरे में कई स्कोप वैरिएबल के चयन की अनुमति देती है। यह प्रत्येक स्कोप को इंडेक्स करने के लिए रूट पथ का उपयोग करता है, और फिर स्कोप वैरिएबल नामों को एक लेवल डीप इंडेक्स करने के लिए।

इस कोड के साथ सेवा बनाएं:

angular.module('restoreScope', []).factory('restoreScope', ['$rootScope', '$route', function ($rootScope, $route) {

    var getOrRegisterScopeVariable = function (scope, name, defaultValue, storedScope) {
        if (storedScope[name] == null) {
            storedScope[name] = defaultValue;
        }
        scope[name] = storedScope[name];
    }

    var service = {

        GetOrRegisterScopeVariables: function (names, defaultValues) {
            var scope = $route.current.locals.$scope;
            var storedBaseScope = angular.fromJson(sessionStorage.restoreScope);
            if (storedBaseScope == null) {
                storedBaseScope = {};
            }
            // stored scope is indexed by route name
            var storedScope = storedBaseScope[$route.current.$$route.originalPath];
            if (storedScope == null) {
                storedScope = {};
            }
            if (typeof names === "string") {
                getOrRegisterScopeVariable(scope, names, defaultValues, storedScope);
            } else if (Array.isArray(names)) {
                angular.forEach(names, function (name, i) {
                    getOrRegisterScopeVariable(scope, name, defaultValues[i], storedScope);
                });
            } else {
                console.error("First argument to GetOrRegisterScopeVariables is not a string or array");
            }
            // save stored scope back off
            storedBaseScope[$route.current.$$route.originalPath] = storedScope;
            sessionStorage.restoreScope = angular.toJson(storedBaseScope);
        },

        SaveState: function () {
            // get current scope
            var scope = $route.current.locals.$scope;
            var storedBaseScope = angular.fromJson(sessionStorage.restoreScope);

            // save off scope based on registered indexes
            angular.forEach(storedBaseScope[$route.current.$$route.originalPath], function (item, i) {
                storedBaseScope[$route.current.$$route.originalPath][i] = scope[i];
            });

            sessionStorage.restoreScope = angular.toJson(storedBaseScope);
        }
    }

    $rootScope.$on("savestate", service.SaveState);

    return service;
}]);

इस कोड को अपने ऐप मॉड्यूल में अपने रन फ़ंक्शन में जोड़ें:

$rootScope.$on('$locationChangeStart', function (event, next, current) {
    $rootScope.$broadcast('savestate');
});

window.onbeforeunload = function (event) {
    $rootScope.$broadcast('savestate');
};

अपने नियंत्रक में रिस्टोरस्कोप सेवा को इंजेक्ट करें (नीचे उदाहरण):

function My1Ctrl($scope, restoreScope) {
    restoreScope.GetOrRegisterScopeVariables([
         // scope variable name(s)
        'user',
        'anotherUser'
    ],[
        // default value(s)
        { name: 'user name', email: 'user@website.com' },
        { name: 'another user name', email: 'anotherUser@website.com' }
    ]);
}

उपर्युक्त उदाहरण संग्रहीत मूल्य पर $ गुंजाइश.user को प्रारंभ करेगा, अन्यथा प्रदान किए गए मूल्य के लिए डिफ़ॉल्ट होगा और उस बंद को बचाएगा। यदि पृष्ठ बंद है, ताज़ा किया गया है, या मार्ग बदल दिया गया है, तो सभी पंजीकृत स्कोप वैरिएबल के वर्तमान मानों को सहेज लिया जाएगा, और अगली बार मार्ग / पृष्ठ पर आने पर उसे पुनर्स्थापित कर दिया जाएगा।


जब मैंने इसे अपने कोड में जोड़ा तो मुझे एक त्रुटि मिली "TypeError: undefined की संपत्ति 'लोकल' नहीं पढ़ सकता" मैं इसे कैसे ठीक करूं? @brett
माइकल महोनी

क्या आप अपने मार्गों को संभालने के लिए कोणीय का उपयोग कर रहे हैं? मैं अनुमान लगा रहा हूँ कि $ मार्ग के बाद से "नहीं" है। वर्तमान में यह अपरिभाषित प्रतीत होता है।
ब्रेट पेनिंग्स

हां, कोणीय मेरी रूटिंग को संभाल रहा है। यहाँ मेरे पास रूटिंग में जो कुछ भी है उसका एक नमूना है: JBenchApp.config (['$ मार्गप्रोपिडर', '$ लोकेशनपॉइडर', फंक्शन ($ मार्गप्रोवाइडर, $ लोकप्रॉपिडर) {$ .Proider। partials / dashboard.html ', नियंत्रक:' JBenchCtrl '})
माइकल महोनी

मेरा एकमात्र अनुमान यह है कि आपका नियंत्रक आपके ऐप के कॉन्फ़िगर होने से पहले चल रहा है। किसी भी तरह से, आप शायद $ मार्ग के बजाय $ स्थान का उपयोग करके समस्या को हल कर सकते हैं और फिर मार्ग में पहुंचने के बजाय नियंत्रक से $ गुंजाइश में गुजर सकते हैं। इसके बजाय अपनी सेवा के लिए gist.github.com/brettpennings/ae5d34de0961eef010c6 का उपयोग करने का प्रयास करें और पुनर्स्थापना के अपने तीसरे पैरामीटर के रूप में $ गुंजाइश में पास करें। अपने नियंत्रक में GGOrRegisterSopeVariables। यदि यह आपके लिए काम करता है, तो मैं बस यह कर सकता हूं कि मेरा नया उत्तर। सौभाग्य!
ब्रेट पेनिंग्स

1

आप $locationChangeStartपिछले मूल्य को $rootScopeया किसी सेवा में संग्रहीत करने के लिए घटना का उपयोग कर सकते हैं । जब आप वापस आते हैं, तो पहले से संग्रहीत सभी मूल्यों को शुरू करें। यहां एक त्वरित डेमो का उपयोग किया गया है $rootScope

यहाँ छवि विवरण दर्ज करें

var app = angular.module("myApp", ["ngRoute"]);
app.controller("tab1Ctrl", function($scope, $rootScope) {
    if ($rootScope.savedScopes) {
        for (key in $rootScope.savedScopes) {
            $scope[key] = $rootScope.savedScopes[key];
        }
    }
    $scope.$on('$locationChangeStart', function(event, next, current) {
        $rootScope.savedScopes = {
            name: $scope.name,
            age: $scope.age
        };
    });
});
app.controller("tab2Ctrl", function($scope) {
    $scope.language = "English";
});
app.config(function($routeProvider) {
    $routeProvider
        .when("/", {
            template: "<h2>Tab1 content</h2>Name: <input ng-model='name'/><br/><br/>Age: <input type='number' ng-model='age' /><h4 style='color: red'>Fill the details and click on Tab2</h4>",
            controller: "tab1Ctrl"
        })
        .when("/tab2", {
            template: "<h2>Tab2 content</h2> My language: {{language}}<h4 style='color: red'>Now go back to Tab1</h4>",
            controller: "tab2Ctrl"
        });
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<body ng-app="myApp">
    <a href="#/!">Tab1</a>
    <a href="#!tab2">Tab2</a>
    <div ng-view></div>
</body>
</html>

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.