• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[GELÖST] AngularJS - Wert aus Service in den $scope des ViewController

alphakanal

New member
Hi!

Auf meiner Seite lasse ich mir $scope.meinTheme des ViewControllers mit {{ meinTheme }} ausgeben.

meinTheme ist abhängig von einem Service der externe Daten lädt ( ohne $http-service ). Doch der Wert steht erst fest, nachdem alle Daten geladen wurden - also noch nicht vorhanden wenn die Seite aufgerufen wird.

Jetzt ist die Frage wie kann ich meinTheme an den ViewController weitergeben sobald er bereit steht? Ein Promise ? Wenn ja wie kann ich das so angeben dass der Service das Promise als return Wert ausgibt? Steck hier gerade a bissi fest im Promise-Neuland...:confused:
 
Zuletzt bearbeitet von einem Moderator:
service:
Code:
service.read = function ()
{
  return $http.get(path).then(function (resp)
  {
    return resp.data;
  });
};

controller:
Code:
service.read().then(function(data)
{
  $scope.data = data;
});
 
Hey danke!

Momentan siehts im Service so aus:
Code:
function getFile(){

            return $http({
                method:'GET',
                url: 'assets/json/test.json'
            })
            .then(sendResult)
            .catch(sendError);

       } 

       function sendResult(response){
           return response.data,
           console.log(response.data); // -> [COLOR="#008000"][object Object],[object Object],[object Object][/COLOR]
       }

       function sendError(response){
           return $q.reject('Error: ' + response.status );
       }

Im Kontroller hab ich mal dein Beispiel übernommen und gibt eine Fehlermeldung aus: TypeError: dbg is undefined.
Controller:
Code:
        $scope.myPromise = loadingService.getFile()
            .then(function (res) {
            return res,
            console.log(res); // Ergibt [COLOR="#FF0000"]TypeError: dbg is undefined[/COLOR]
                
            });

         console.log($scope.myPromise);  // [COLOR="#008000"]Ergibt [object Object][/COLOR]

- - - Aktualisiert - - -

UPDATE: Jetzt funktioniert's - hab nicht gewusst dass ich im Controller auch den $http Service hinzufügen muss..dankschee!! :eagerness:

- - - Aktualisiert - - -

Jetzt hätte ich doch noch eine kleine Frage:

Mein Service sieht jetzt so aus:
Code:
 var getFile = function () {

            return $http({
                method: 'GET',
                url: 'mein-pfad-zum.json'
            })
                .then(sendResult)
                .catch(sendError);

        };


        var sendResult = function (response) {

            [COLOR="#008000"]// extra ladevorgang anstossen ->[/COLOR]
            queue.addEventListener("complete", handleComplete); [COLOR="#008000"]// wird aufgerufen wenn extra ladevorgang fertig[/COLOR]
            queue.loadManifest(...); // laden starten
            ...
            return response.data;
        };

Nachdem das JSON geladen wurde, stosse ich einen weiteren Ladevorgang an - der das JSON vorraussetzt.

Wie kann man das hinkriegen dass sendResult erst seinen return wert zurückgibt nachdem auch handleComplete() ausgeführt wurde :confused:

Mit while( warte bis in handleComplete was auf true gesetzt wird ) {} vor return response.data; würgt mir das die Seite ab..
 
Zuletzt bearbeitet:
über ein 2. promise welches du in handleComplete resovest. dieses 2. und das welches $http zurückgibt packst du in
https://docs.angularjs.org/api/ng/service/$q#all und erhällst damit ein 3. promise welches resolved wird, wenn beide inneren resolved sind.
 
Hey danke!
In die Richtung ging ich auch..wobei bei es jetzt so im MainController aussieht:
Code:
            var promiseJSON = dataService.initLoadJSON();
            var promiseIMG = dataService.initLoadIMG();

            $q.all ([promiseJSON,promiseIMG])
                    .then(loadSucces)
                    .catch(loadError);
Das aber nicht das gelbe vom Ei ist da initLoadIMG erst starten soll wenn initLoadJSON fertig ist....
Wobei grade fällt mir ein -> warum nicht initLoadIMG erst im loadSuccess von initLoadJSON starten?
 
Um asynchrone Operationen nacheinander durchzuführen, nutzt man sog. "promise chains". Hier ein Beispiel: AngularJS Promise Chain. Das Prinzip dahinter: in der then-Methode einen neuen Promise zurückliefern. Dadurch hält man seinen Code "flach" und übersichtlich. Generell gilt die Faustregel bei .then() immer ein Ergebnis zurückliefern.

Btw: was soll das mit den Controllern und $scope? Seit Version 1.5 sollte man wenn möglich nur noch mit .component() arbeiten.
 
Zuletzt bearbeitet:
Servus + danke für das Chain Beispiel -> das ist immer das schwierigste die richtige Code Struktur zu finden :)

Das mit $scope.myPromise = hat sich mittlerweile in var myPromise = geändert -> der Variablen im Scope werden dann in der Success Funktion gesetzt
 
Zuletzt bearbeitet:
Ahso..also die $scope.XY benutze ich um mit {{ xy }} im HTML auszugeben. Ist das so ein Spezialfall ? Ist ja mein first-contact mit Angular
 
Zuletzt bearbeitet:
Wenn du gerade mit Angular anfängst, dann rate ich dir direkt folgende Faustregeln zu befolgen: verwende niemals ng-controller und verwende niemals $scope - und arbeite niemals mit scope-Inheritance. Damit vermeidest du die größten Pitfalls im Design deiner Anwendung. Nutze für alles erstmal component() und services. Hier ein Beispiel: AngularJS Component.

Ein Spezielfall für den Einsatz von $scope wäre bspw. wenn andere Komponenten via $on die Lifecycle-Hooks oder Events einer Komponente abhören wollen. Mal ein Beispiel dazu: AngularJS $scope.$on.
 
Zuletzt bearbeitet:
Oje :dejection:

Dabei bin ich gerade so happy dass es momentan rund läuft und ich zu 98% verstehe wieso :)

Glaub das werd ich rückwirkend einbauen bzw umändern wenn alles steht und fehlerfrei läuft ( bin nämlich kurz davor ). Denk dann tu ich mich auch leichter wenn beim Abändern irgendwelche Fehlermeldungen kommen - und das werden sie...

- - - Aktualisiert - - -

Tu mich immer schwer beim Lesen/Verstehen von Code wenn anonyme Funktionen mit im Spiel sind...und da ist mir bei der PromiseChain zwei Dinge ( rot ) noch ned so recht klar:
Code:
function createPromise(res){ 
 [COLOR="#FF0000"]// wird im Controller aufgerufen aber ohne Parameter für res ->
// wie kommt hier der Aufruf von createPromise('#1') zustande?[/COLOR]

        return $q(function(resolve, reject){
      	$timeout(function(){
        	resolve(res);
        }, 3000);
      });
    }
    
    createPromise('#1')
    	.then(function(res){
      	console.log(res);
      	return createPromise('#2');
      })
    	.then(function(res){
      	console.log(res);
        // throw 'something happend';
      	return createPromise('#3');
      })
      .then(function(res){
      	console.log(res);
      	return res;
      })
      .catch(function(err){
      	console.log(err);
      })
    	.finally(function(){
				console.log('promis-chain done');
      });
Aufruf im Controller:
Code:
            dataService.createPromise()
            .then(loadSuccesChain)
            .catch(loadErrorChain);
// Success  für createPromise :
               function loadSuccesChain (res){
               console.log("ChainPromise fertig " + res);
[COLOR="#FF0000"]// res = undefined...wie kann ich hier nen return wert bekommen?[/COLOR]
                 }
Danach gäbe es noch eine Frage - jedoch will ich die 2 Nüsse erst geknackt kriegen...:confused:
 
Zuletzt bearbeitet:
// wird im Controller aufgerufen aber ohne Parameter für res ->
Eigentlich wird createPromise in meinem Beispiel immer mit einem Parameter aufgerufen (#1, #2 und #3):
Code:
createPromise([color="#FF0000"][B]'#1'[/B][/COLOR])
  .then(function([COLOR="#FF0000"][B]res[/B][/COLOR]){
    console.log([COLOR="#FF0000"][B]res[/B][/COLOR]); [COLOR="#A9A9A9"]// "#1"[/COLOR]
    return createPromise([COLOR="#0000FF"][B]'#2'[/B][/COLOR]);
  })
  .then(function([COLOR="#0000FF"][B]res[/B][/COLOR]){
    console.log([COLOR="#0000FF"][B]res[/B][/COLOR]); [COLOR="#A9A9A9"]// "#2"[/COLOR]
    // throw [COLOR="#EE82EE"][B]'something happend'[/B][/COLOR];
    return createPromise([COLOR="#00FF00"][B]'#3'[/B][/COLOR]);
  })
  .then(function([COLOR="#00FF00"][B]res[/B][/COLOR]){
    console.log([COLOR="#00FF00"][B]res[/B][/COLOR]); [COLOR="#A9A9A9"]// "#3"[/COLOR]
    return [COLOR="#00FF00"][B]res[/B][/COLOR];
  })
  .catch(function([COLOR="#EE82EE"][B]err[/B][/COLOR]){
    console.log([COLOR="#EE82EE"][B]err[/B][/COLOR]); [COLOR="#A9A9A9"]// "something happend"[/COLOR]
  })
  .finally(function(){
    console.log('promis-chain done');
  });

Edit:

Ich habe mal farblich markiert wie die Werte durch die Promise-Chain laufen. Vielleicht hilft das ein wenig das gedanklich umzusetzen. Und entschuldige das hässlich grüne :-D

---

// res = undefined...wie kann ich hier nen return wert bekommen?
Damit ein Wert übergeben wird, muss das Promise mit einem Wert "resolved" werden. Wenn du bspw. Ajax verwendest, reicht es aus, wenn dein Aufruf von dataService.createPromise im innern folgendes tut: return $http(..). Wobei die Benennung nicht so optimal ist. Sinnvoller wäre bspw. dataService.users.fetch(..).


Tu mich immer schwer beim Lesen/Verstehen von Code wenn anonyme Funktionen mit im Spiel sind
Das solltest du aber schleunigst ändern, denn das sind JavaScript-Fundamentals. Ich rate dir sogar dazu ganz bewusst mit anonymous functions zu arbeiten damit du den Umgang damit lernst. In vielen Fällen ist es schlichtweg nicht notwendig ein Callback über eine separate Funktion abzubilden.
 
Zuletzt bearbeitet:
Hey, danke erstmal für die ausführliche Antwort :)
Eigentlich wird createPromise in meinem Beispiel immer mit einem Parameter aufgerufen (#1, #2 und #3)
Den Ablauf in dieser Kette ( mit Parametern ) kann ich 1a nachvollziehen - nur in dem Beispiel
https://jsfiddle.net/rckd/cfu1fmvm/ steht ja zuerst die Definition der Funktion mit creatPromise(res) da und erst im "zweiten Rutsch" danach wird die Funktion mit Parameter aufgerufen...
Verstehe ich das so richtig dass hier eine Funktionserstellung und anschließende Ausführung in einem passiert?

Das solltest du aber schleunigst ändern, denn das sind JavaScript-Fundamentals. Ich rate dir sogar dazu ganz bewusst mit anonymous functions zu arbeiten damit du den Umgang damit lernst. In vielen Fällen ist es schlichtweg nicht notwendig ein Callback über eine separate Funktion abzubilden.

Da bin ich dran - ist halt im ersten Moment echt schwer wenn mehrere anonyme Ketten aufeinander folgen - und dann evtl noch fiese Kleinigkeiten wie fehlende Kommas oder Semikolons große Auswirkung haben.
 
Zuletzt bearbeitet:
Verstehe ich das so richtig dass hier eine Funktionserstellung und anschließende Ausführung in einem passiert?
Von Zeile 8-14 wird lediglich eine Funktion "createPromise" definiert. Wenn man diese aufruft, wird ein Promise erzeugt, ein timeout eingeleitet und 3 Sekunden später das Promise mit dem übergebenen Parameter resolvd.

In Zeile 16 wird diese Funktion dann zum ersten Mal aufgerufen.

Da bin ich dran - ist halt im ersten Moment echt schwer wenn mehrere anonyme Ketten aufeinander folgen - und dann evtl noch fiese Kleinigkeiten wie fehlende Kommas oder Semikolons große Auswirkung haben.
Kein Stress. Mein alter Gitarrenlehrer hat immer gesagt: übe immer das was dir am schwersten fällt, dann kommst du schneller voran. Und er hatte recht - zumindest bezogen auf die Softwareentwicklung :-D

Ich habe dir die createPromise-Funktion mal etwas umgeschrieben - vielleicht ist der Ablauf so leichter nachvollziehbar für dich:
PHP:
function createPromise(res){
  // create promise
  var defer = $q.defer();
      
  $timeout(function(){
    // resolve it with res (happens 3 sec later, async)
    defer.resolve(res);
  }, 3000);
      
  // return the promise
  return defer.promise;
}


...


// usage example

function logResult(res){
  console.log(res);
}

var promise = createPromise('hello world');
promise.then(logResult);

Hier das fiddle.
 
Zuletzt bearbeitet:
Super, danke! Das ist was mir in der Geschichte gefehlt hat - das deferred Object mit seinen resolve() und reject() ! :eagerness: Wahrscheinlich eine weitere fiese Kleinigkeit die mit verkürzten Schreibweisen zu tun hat?

Mir fehlen noch 2 Schlüssel zum endgültigen Durchstieg...nur kurz zu diesem Beispiel :
Wie ich das sehe, wird hier wird ja am Ende `hello world`zurückgegeben. Also quasi der Parameter mit dem ich createPromise('hello world') aufgerufen habe.
Was wenn ich - wie im vorherigen Beispiel mit der Chain - im Controller createPromise() ohne Parameter aufrufe und so die PromiseChain (#1,#2,#3) lostrete
Code:
            dataService.createPromise()
            .then(loadSucces)
            .catch(loadError);

---

           function loadSucces (res){
            
              console.log(" loadSucces aufgerufen - Rückgabewert : " + res ); // -> momentan undefined


            }
Wird loadSucces ohne Rückgabewert aufgerufen bevor die komplette Chain abgearbeitet ist. Wie kann ich das machen dass am Ende der Chain einen Rückgabewert an dataService sende den ich dann mit loadSucces weiterverarbeite? Das wär der erste Schlüssel.

P.S. Dein alter Gitarrenlehrer würde mir bestimmt empfehlen den kleinen Finger der Griffhand abzuschneiden -> bei dem ist Hopfen und Malz verloren :devilish:
 
Zuletzt bearbeitet:
Das ist ja so gewollt - also im Controller ohne Parameter die Chain anstoßen. Doch am Ende der Chain sollte ein Wert an den Controller zurückgegeben werden.

Denn momentan wird bei mir im Controller das so aufgerufen:
Code:
            dataService.createPromise()
            .then(loadSucces)
            .catch(loadError);

.then(loadSucces) wird hier sofort nach dem die Chain angestoßen ist ausgeführt - das sollte erst nachdem die Chain fertig ist geschehen. Und zudem noch ein Rückgabewert an den Controllen enthalten...

Ich versuch das auch mal in dem API-Service Fiddle - da glaub ich kann ich was draus machen:)

- - - Aktualisiert - - -

So hab mal den API Fiddle umgeändert in das was ich bräuchte. Funktioniert -> aber ob das so "sauber umgesetzt" ist bin i mir ned sicher...

- - - Aktualisiert - - -

Der Fiddle ist jetzt vorerst einmal fertig - wenn der so passt, wäre noch eine Frage offen :icon6:
 
Zuletzt bearbeitet:
Uff...hab jetzt endlich meinen Code in Fiddle endlich soweit gebracht, dass das letzte fehlende Stück evtl auch bald gefunden wird. Habe versucht das alles in Kommentaren unterzubringen, falls jemand sich die Mühe machen möchte :)

Ablauf ist folgender: Im Controller wird erst das Laden eines JSONs durch api.users.loadJSON() angestoßen, wenn das fertig ist wird das Laden von Bildern durch api.users.loadIMG() angestoßen.

Hier ist loadIMG() in der factory das was noch nicht ganz rund läuft. Die Zeilen 40 - 56 ergeben den Returnwert imgArray. Das wird momentan zwar richtig zurückgegeben, aber nur weil $timeout mit im Spiel ist, denn der eigentliche Wert von imgArray wird in Zeile 52-56 von handeleComplete() festgelegtn nachdem alle Bilder geladen wurden.
 
Zuletzt bearbeitet:
Das ist ja so gewollt - also im Controller ohne Parameter die Chain anstoßen. Doch am Ende der Chain sollte ein Wert an den Controller zurückgegeben werden.
Du solltest nicht zu sehr auf die Chain beharren. Eine Chain ist vorallem dann sinnvoll, wenn man mehrere asynchrone (und synchrone) Operationen nacheinander abarbeiten will. Du unterteilst einen Prozess also in mehrere Schritte. Ein simples Beispiel wäre eine Registrierung: Schritt 1: E-Mail validieren, Schritt 2: prüfen ob E-Mail bereits vergeben ist, Schritt 3: Benutzer erzeugen, Schritt 3: Willkommens-E-Mail an den Nutzer versenden lassen. Hier ein Beispiel dazu: AngularJS sign-up process with promise-chain.

.then(loadSucces) wird hier sofort nach dem die Chain angestoßen ist ausgeführt - das sollte erst nachdem die Chain fertig ist geschehen.
Die Funktion loadSuccess wird erst dann ausgeführt, wenn das durch createPromise erzeugte Promise resolved wurde.

Und zudem noch ein Rückgabewert an den Controllen enthalten...
Die Funktion loadSuccess erhält dann als ersten Parameter jenen Wert mit dem das Promise resolved wurde.

Der Fiddle ist jetzt vorerst einmal fertig - wenn der so passt, wäre noch eine Frage offen :icon6:
Ich weiß nicht wozu du das createPromise immer einbaust. Das hatte ich ursprünglich nur verwendet um ein Fake-Promise zu erzeugen. In deiner fertigen API ist sowas aber nicht notwendig. Da arbeitet man direkt mit Funktionen wie api.users.fetch usw. - da diese ja bereits Promises liefern.
 
Zurück
Oben